summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/xe
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/xe')
-rw-r--r--drivers/gpu/drm/xe/Kconfig1
-rw-r--r--drivers/gpu/drm/xe/Kconfig.debug10
-rw-r--r--drivers/gpu/drm/xe/Makefile46
-rw-r--r--drivers/gpu/drm/xe/abi/guc_actions_abi.h6
-rw-r--r--drivers/gpu/drm/xe/abi/guc_actions_slpc_abi.h22
-rw-r--r--drivers/gpu/drm/xe/abi/guc_actions_sriov_abi.h190
-rw-r--r--drivers/gpu/drm/xe/abi/guc_errors_abi.h57
-rw-r--r--drivers/gpu/drm/xe/abi/guc_klvs_abi.h40
-rw-r--r--drivers/gpu/drm/xe/abi/guc_messages_abi.h41
-rw-r--r--drivers/gpu/drm/xe/abi/guc_relay_actions_abi.h170
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/gem/i915_gem_object_types.h11
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/gem/i915_gem_stolen.h (renamed from drivers/gpu/drm/xe/compat-i915-headers/i915_gem_stolen.h)0
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/gt/intel_gt_types.h (renamed from drivers/gpu/drm/xe/compat-i915-headers/intel_gt_types.h)0
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/i915_drv.h67
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/intel_runtime_pm.h51
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/intel_uncore.h5
-rw-r--r--drivers/gpu/drm/xe/display/ext/i915_irq.c1
-rw-r--r--drivers/gpu/drm/xe/display/intel_fb_bo.c7
-rw-r--r--drivers/gpu/drm/xe/display/intel_fbdev_fb.c37
-rw-r--r--drivers/gpu/drm/xe/display/intel_fbdev_fb.h21
-rw-r--r--drivers/gpu/drm/xe/display/xe_display.c64
-rw-r--r--drivers/gpu/drm/xe/display/xe_display.h12
-rw-r--r--drivers/gpu/drm/xe/display/xe_dsb_buffer.c9
-rw-r--r--drivers/gpu/drm/xe/display/xe_fb_pin.c29
-rw-r--r--drivers/gpu/drm/xe/display/xe_hdcp_gsc.c3
-rw-r--r--drivers/gpu/drm/xe/display/xe_plane_initial.c23
-rw-r--r--drivers/gpu/drm/xe/display/xe_tdf.c13
-rw-r--r--drivers/gpu/drm/xe/instructions/xe_gpu_commands.h (renamed from drivers/gpu/drm/xe/regs/xe_gpu_commands.h)0
-rw-r--r--drivers/gpu/drm/xe/instructions/xe_mi_commands.h5
-rw-r--r--drivers/gpu/drm/xe/regs/xe_bars.h11
-rw-r--r--drivers/gpu/drm/xe/regs/xe_engine_regs.h12
-rw-r--r--drivers/gpu/drm/xe/regs/xe_gt_regs.h81
-rw-r--r--drivers/gpu/drm/xe/regs/xe_guc_regs.h2
-rw-r--r--drivers/gpu/drm/xe/regs/xe_lrc_layout.h8
-rw-r--r--drivers/gpu/drm/xe/regs/xe_oa_regs.h100
-rw-r--r--drivers/gpu/drm/xe/regs/xe_pcode_regs.h7
-rw-r--r--drivers/gpu/drm/xe/regs/xe_regs.h3
-rw-r--r--drivers/gpu/drm/xe/regs/xe_sriov_regs.h3
-rw-r--r--drivers/gpu/drm/xe/tests/Makefile1
-rw-r--r--drivers/gpu/drm/xe/tests/xe_args_test.c221
-rw-r--r--drivers/gpu/drm/xe/tests/xe_gt_sriov_pf_service_test.c232
-rw-r--r--drivers/gpu/drm/xe/tests/xe_migrate.c88
-rw-r--r--drivers/gpu/drm/xe/tests/xe_mocs.c8
-rw-r--r--drivers/gpu/drm/xe/tests/xe_rtp_test.c64
-rw-r--r--drivers/gpu/drm/xe/xe_args.h143
-rw-r--r--drivers/gpu/drm/xe/xe_assert.h4
-rw-r--r--drivers/gpu/drm/xe/xe_bb.c2
-rw-r--r--drivers/gpu/drm/xe/xe_bo.c82
-rw-r--r--drivers/gpu/drm/xe/xe_bo.h1
-rw-r--r--drivers/gpu/drm/xe/xe_debugfs.c58
-rw-r--r--drivers/gpu/drm/xe/xe_devcoredump.c27
-rw-r--r--drivers/gpu/drm/xe/xe_devcoredump_types.h2
-rw-r--r--drivers/gpu/drm/xe/xe_device.c221
-rw-r--r--drivers/gpu/drm/xe/xe_device.h15
-rw-r--r--drivers/gpu/drm/xe/xe_device_sysfs.c4
-rw-r--r--drivers/gpu/drm/xe/xe_device_types.h33
-rw-r--r--drivers/gpu/drm/xe/xe_drm_client.c131
-rw-r--r--drivers/gpu/drm/xe/xe_exec.c22
-rw-r--r--drivers/gpu/drm/xe/xe_exec_queue.c85
-rw-r--r--drivers/gpu/drm/xe/xe_exec_queue.h10
-rw-r--r--drivers/gpu/drm/xe/xe_exec_queue_types.h42
-rw-r--r--drivers/gpu/drm/xe/xe_execlist.c12
-rw-r--r--drivers/gpu/drm/xe/xe_force_wake.c102
-rw-r--r--drivers/gpu/drm/xe/xe_force_wake.h13
-rw-r--r--drivers/gpu/drm/xe/xe_ggtt.c81
-rw-r--r--drivers/gpu/drm/xe/xe_ggtt.h1
-rw-r--r--drivers/gpu/drm/xe/xe_ggtt_types.h12
-rw-r--r--drivers/gpu/drm/xe/xe_gsc.c8
-rw-r--r--drivers/gpu/drm/xe/xe_gsc.h3
-rw-r--r--drivers/gpu/drm/xe/xe_gsc_proxy.c5
-rw-r--r--drivers/gpu/drm/xe/xe_gsc_submit.c1
-rw-r--r--drivers/gpu/drm/xe/xe_gt.c183
-rw-r--r--drivers/gpu/drm/xe/xe_gt.h29
-rw-r--r--drivers/gpu/drm/xe/xe_gt_ccs_mode.c19
-rw-r--r--drivers/gpu/drm/xe/xe_gt_clock.c21
-rw-r--r--drivers/gpu/drm/xe/xe_gt_clock.h1
-rw-r--r--drivers/gpu/drm/xe/xe_gt_debugfs.c31
-rw-r--r--drivers/gpu/drm/xe/xe_gt_freq.c8
-rw-r--r--drivers/gpu/drm/xe/xe_gt_idle.c82
-rw-r--r--drivers/gpu/drm/xe/xe_gt_idle.h4
-rw-r--r--drivers/gpu/drm/xe/xe_gt_mcr.c33
-rw-r--r--drivers/gpu/drm/xe/xe_gt_mcr.h25
-rw-r--r--drivers/gpu/drm/xe/xe_gt_pagefault.c156
-rw-r--r--drivers/gpu/drm/xe/xe_gt_printk.h3
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf.c44
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf.h10
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_config.c198
-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.h4
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_control.c21
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_control.h1
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_debugfs.c427
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_debugfs.h18
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_monitor.c147
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_monitor.h27
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_monitor_types.h22
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_service.c550
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_service.h36
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_service_types.h52
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_types.h10
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_vf.c979
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_vf.h30
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_vf_debugfs.c72
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_vf_debugfs.h14
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_vf_types.h84
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sysfs.c4
-rw-r--r--drivers/gpu/drm/xe/xe_gt_throttle.c (renamed from drivers/gpu/drm/xe/xe_gt_throttle_sysfs.c)30
-rw-r--r--drivers/gpu/drm/xe/xe_gt_throttle.h17
-rw-r--r--drivers/gpu/drm/xe/xe_gt_throttle_sysfs.h16
-rw-r--r--drivers/gpu/drm/xe/xe_gt_tlb_invalidation.c125
-rw-r--r--drivers/gpu/drm/xe/xe_gt_tlb_invalidation.h3
-rw-r--r--drivers/gpu/drm/xe/xe_gt_topology.c14
-rw-r--r--drivers/gpu/drm/xe/xe_gt_topology.h3
-rw-r--r--drivers/gpu/drm/xe/xe_gt_types.h38
-rw-r--r--drivers/gpu/drm/xe/xe_guc.c389
-rw-r--r--drivers/gpu/drm/xe/xe_guc.h4
-rw-r--r--drivers/gpu/drm/xe/xe_guc_ads.c71
-rw-r--r--drivers/gpu/drm/xe/xe_guc_ads.h3
-rw-r--r--drivers/gpu/drm/xe/xe_guc_ct.c93
-rw-r--r--drivers/gpu/drm/xe/xe_guc_ct.h2
-rw-r--r--drivers/gpu/drm/xe/xe_guc_ct_types.h2
-rw-r--r--drivers/gpu/drm/xe/xe_guc_db_mgr.c3
-rw-r--r--drivers/gpu/drm/xe/xe_guc_id_mgr.c3
-rw-r--r--drivers/gpu/drm/xe/xe_guc_klv_helpers.c12
-rw-r--r--drivers/gpu/drm/xe/xe_guc_klv_helpers.h15
-rw-r--r--drivers/gpu/drm/xe/xe_guc_klv_thresholds_set.h64
-rw-r--r--drivers/gpu/drm/xe/xe_guc_klv_thresholds_set_types.h68
-rw-r--r--drivers/gpu/drm/xe/xe_guc_pc.c225
-rw-r--r--drivers/gpu/drm/xe/xe_guc_pc.h10
-rw-r--r--drivers/gpu/drm/xe/xe_guc_pc_types.h4
-rw-r--r--drivers/gpu/drm/xe/xe_guc_relay.c17
-rw-r--r--drivers/gpu/drm/xe/xe_guc_submit.c616
-rw-r--r--drivers/gpu/drm/xe/xe_guc_submit.h5
-rw-r--r--drivers/gpu/drm/xe/xe_guc_types.h9
-rw-r--r--drivers/gpu/drm/xe/xe_huc.c32
-rw-r--r--drivers/gpu/drm/xe/xe_huc.h3
-rw-r--r--drivers/gpu/drm/xe/xe_hw_engine.c202
-rw-r--r--drivers/gpu/drm/xe/xe_hw_engine.h4
-rw-r--r--drivers/gpu/drm/xe/xe_hw_engine_class_sysfs.c30
-rw-r--r--drivers/gpu/drm/xe/xe_hw_engine_types.h28
-rw-r--r--drivers/gpu/drm/xe/xe_hw_fence.c60
-rw-r--r--drivers/gpu/drm/xe/xe_hw_fence.h7
-rw-r--r--drivers/gpu/drm/xe/xe_hwmon.c126
-rw-r--r--drivers/gpu/drm/xe/xe_irq.c12
-rw-r--r--drivers/gpu/drm/xe/xe_irq.h1
-rw-r--r--drivers/gpu/drm/xe/xe_lrc.c392
-rw-r--r--drivers/gpu/drm/xe/xe_lrc.h64
-rw-r--r--drivers/gpu/drm/xe/xe_lrc_types.h12
-rw-r--r--drivers/gpu/drm/xe/xe_migrate.c126
-rw-r--r--drivers/gpu/drm/xe/xe_mmio.c453
-rw-r--r--drivers/gpu/drm/xe/xe_mmio.h22
-rw-r--r--drivers/gpu/drm/xe/xe_mocs.c353
-rw-r--r--drivers/gpu/drm/xe/xe_mocs.h11
-rw-r--r--drivers/gpu/drm/xe/xe_module.c10
-rw-r--r--drivers/gpu/drm/xe/xe_module.h1
-rw-r--r--drivers/gpu/drm/xe/xe_oa.c2512
-rw-r--r--drivers/gpu/drm/xe/xe_oa.h27
-rw-r--r--drivers/gpu/drm/xe/xe_oa_types.h242
-rw-r--r--drivers/gpu/drm/xe/xe_observation.c93
-rw-r--r--drivers/gpu/drm/xe/xe_observation.h20
-rw-r--r--drivers/gpu/drm/xe/xe_pat.c1
-rw-r--r--drivers/gpu/drm/xe/xe_pci.c113
-rw-r--r--drivers/gpu/drm/xe/xe_pci_sriov.c170
-rw-r--r--drivers/gpu/drm/xe/xe_pci_sriov.h20
-rw-r--r--drivers/gpu/drm/xe/xe_pci_types.h4
-rw-r--r--drivers/gpu/drm/xe/xe_pcode.c8
-rw-r--r--drivers/gpu/drm/xe/xe_pm.c42
-rw-r--r--drivers/gpu/drm/xe/xe_pm.h9
-rw-r--r--drivers/gpu/drm/xe/xe_preempt_fence.c16
-rw-r--r--drivers/gpu/drm/xe/xe_pt.c77
-rw-r--r--drivers/gpu/drm/xe/xe_query.c87
-rw-r--r--drivers/gpu/drm/xe/xe_reg_sr.h4
-rw-r--r--drivers/gpu/drm/xe/xe_reg_whitelist.c24
-rw-r--r--drivers/gpu/drm/xe/xe_res_cursor.h2
-rw-r--r--drivers/gpu/drm/xe/xe_ring_ops.c73
-rw-r--r--drivers/gpu/drm/xe/xe_rtp.c41
-rw-r--r--drivers/gpu/drm/xe/xe_rtp.h59
-rw-r--r--drivers/gpu/drm/xe/xe_rtp_helpers.h28
-rw-r--r--drivers/gpu/drm/xe/xe_rtp_types.h3
-rw-r--r--drivers/gpu/drm/xe/xe_sched_job.c187
-rw-r--r--drivers/gpu/drm/xe/xe_sched_job.h10
-rw-r--r--drivers/gpu/drm/xe/xe_sched_job_types.h20
-rw-r--r--drivers/gpu/drm/xe/xe_sriov.c11
-rw-r--r--drivers/gpu/drm/xe/xe_sriov.h6
-rw-r--r--drivers/gpu/drm/xe/xe_sync.c15
-rw-r--r--drivers/gpu/drm/xe/xe_sync.h1
-rw-r--r--drivers/gpu/drm/xe/xe_tile_sysfs.c4
-rw-r--r--drivers/gpu/drm/xe/xe_trace.h392
-rw-r--r--drivers/gpu/drm/xe/xe_trace_bo.c9
-rw-r--r--drivers/gpu/drm/xe/xe_trace_bo.h247
-rw-r--r--drivers/gpu/drm/xe/xe_trace_guc.c9
-rw-r--r--drivers/gpu/drm/xe/xe_trace_guc.h110
-rw-r--r--drivers/gpu/drm/xe/xe_ttm_stolen_mgr.c2
-rw-r--r--drivers/gpu/drm/xe/xe_uc.c72
-rw-r--r--drivers/gpu/drm/xe/xe_uc.h5
-rw-r--r--drivers/gpu/drm/xe/xe_uc_fw.c5
-rw-r--r--drivers/gpu/drm/xe/xe_uc_fw.h2
-rw-r--r--drivers/gpu/drm/xe/xe_vm.c944
-rw-r--r--drivers/gpu/drm/xe/xe_vm.h5
-rw-r--r--drivers/gpu/drm/xe/xe_vm_doc.h24
-rw-r--r--drivers/gpu/drm/xe/xe_vm_types.h29
-rw-r--r--drivers/gpu/drm/xe/xe_vram.c368
-rw-r--r--drivers/gpu/drm/xe/xe_vram.h13
-rw-r--r--drivers/gpu/drm/xe/xe_vram_freq.c4
-rw-r--r--drivers/gpu/drm/xe/xe_wa.c48
-rw-r--r--drivers/gpu/drm/xe/xe_wa.h2
-rw-r--r--drivers/gpu/drm/xe/xe_wa_oob.rules2
207 files changed, 13481 insertions, 2807 deletions
diff --git a/drivers/gpu/drm/xe/Kconfig b/drivers/gpu/drm/xe/Kconfig
index 63f1e2d1649f..7bbe46a98ff1 100644
--- a/drivers/gpu/drm/xe/Kconfig
+++ b/drivers/gpu/drm/xe/Kconfig
@@ -25,7 +25,6 @@ config DRM_XE
select BACKLIGHT_CLASS_DEVICE if ACPI
select INPUT if ACPI
select ACPI_VIDEO if X86 && ACPI
- select ACPI_BUTTON if ACPI
select X86_PLATFORM_DEVICES if X86 && ACPI
select ACPI_WMI if X86 && ACPI
select SYNC_FILE
diff --git a/drivers/gpu/drm/xe/Kconfig.debug b/drivers/gpu/drm/xe/Kconfig.debug
index df02e5d17d26..bc177368af6c 100644
--- a/drivers/gpu/drm/xe/Kconfig.debug
+++ b/drivers/gpu/drm/xe/Kconfig.debug
@@ -61,16 +61,6 @@ config DRM_XE_DEBUG_MEM
If in doubt, say "N".
-config DRM_XE_SIMPLE_ERROR_CAPTURE
- bool "Enable simple error capture to dmesg on job timeout"
- default n
- help
- Choose this option when debugging an unexpected job timeout
-
- Recommended for driver developers only.
-
- If in doubt, say "N".
-
config DRM_XE_KUNIT_TEST
tristate "KUnit tests for the drm xe driver" if !KUNIT_ALL_TESTS
depends on DRM_XE && KUNIT && DEBUG_FS
diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile
index b165bbf52aef..628c245c4822 100644
--- a/drivers/gpu/drm/xe/Makefile
+++ b/drivers/gpu/drm/xe/Makefile
@@ -3,31 +3,8 @@
# Makefile for the drm device driver. This driver provides support for the
# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
-# Unconditionally enable W=1 warnings locally
-# --- begin copy-paste W=1 warnings from scripts/Makefile.extrawarn
-subdir-ccflags-y += -Wextra -Wunused -Wno-unused-parameter
-subdir-ccflags-y += -Wmissing-declarations
-subdir-ccflags-y += $(call cc-option, -Wrestrict)
-subdir-ccflags-y += -Wmissing-format-attribute
-subdir-ccflags-y += -Wmissing-prototypes
-subdir-ccflags-y += -Wold-style-definition
-subdir-ccflags-y += -Wmissing-include-dirs
-subdir-ccflags-y += $(call cc-option, -Wunused-but-set-variable)
-subdir-ccflags-y += $(call cc-option, -Wunused-const-variable)
-subdir-ccflags-y += $(call cc-option, -Wpacked-not-aligned)
-subdir-ccflags-y += $(call cc-option, -Wformat-overflow)
+# Enable W=1 warnings not enabled in drm subsystem Makefile
subdir-ccflags-y += $(call cc-option, -Wformat-truncation)
-subdir-ccflags-y += $(call cc-option, -Wstringop-truncation)
-# The following turn off the warnings enabled by -Wextra
-ifeq ($(findstring 2, $(KBUILD_EXTRA_WARN)),)
-subdir-ccflags-y += -Wno-missing-field-initializers
-subdir-ccflags-y += -Wno-type-limits
-subdir-ccflags-y += -Wno-shift-negative-value
-endif
-ifeq ($(findstring 3, $(KBUILD_EXTRA_WARN)),)
-subdir-ccflags-y += -Wno-sign-compare
-endif
-# --- end copy-paste
# Enable -Werror in CI and development
subdir-ccflags-$(CONFIG_DRM_XE_WERROR) += -Werror
@@ -47,9 +24,12 @@ $(obj)/generated/%_wa_oob.c $(obj)/generated/%_wa_oob.h: $(obj)/xe_gen_wa_oob \
$(call cmd,wa_oob)
uses_generated_oob := \
+ $(obj)/xe_ggtt.o \
$(obj)/xe_gsc.o \
+ $(obj)/xe_gt.o \
$(obj)/xe_guc.o \
$(obj)/xe_guc_ads.o \
+ $(obj)/xe_guc_pc.o \
$(obj)/xe_migrate.o \
$(obj)/xe_ring_ops.o \
$(obj)/xe_vm.o \
@@ -89,7 +69,7 @@ xe-y += xe_bb.o \
xe_gt_mcr.o \
xe_gt_pagefault.o \
xe_gt_sysfs.o \
- xe_gt_throttle_sysfs.o \
+ xe_gt_throttle.o \
xe_gt_tlb_invalidation.o \
xe_gt_topology.o \
xe_guc.o \
@@ -115,6 +95,8 @@ xe-y += xe_bb.o \
xe_mmio.o \
xe_mocs.o \
xe_module.o \
+ xe_oa.o \
+ xe_observation.o \
xe_pat.o \
xe_pci.o \
xe_pcode.o \
@@ -135,6 +117,8 @@ xe-y += xe_bb.o \
xe_tile.o \
xe_tile_sysfs.o \
xe_trace.o \
+ xe_trace_bo.o \
+ xe_trace_guc.o \
xe_ttm_sys_mgr.o \
xe_ttm_stolen_mgr.o \
xe_ttm_vram_mgr.o \
@@ -143,6 +127,7 @@ xe-y += xe_bb.o \
xe_uc_debugfs.o \
xe_uc_fw.o \
xe_vm.o \
+ xe_vram.o \
xe_vram_freq.o \
xe_wait_user_fence.o \
xe_wa.o \
@@ -155,6 +140,8 @@ xe-$(CONFIG_HWMON) += xe_hwmon.o
# graphics virtualization (SR-IOV) support
xe-y += \
+ xe_gt_sriov_vf.o \
+ xe_gt_sriov_vf_debugfs.o \
xe_guc_relay.o \
xe_memirq.o \
xe_sriov.o
@@ -163,10 +150,14 @@ xe-$(CONFIG_PCI_IOV) += \
xe_gt_sriov_pf.o \
xe_gt_sriov_pf_config.o \
xe_gt_sriov_pf_control.o \
+ xe_gt_sriov_pf_debugfs.o \
+ xe_gt_sriov_pf_monitor.o \
xe_gt_sriov_pf_policy.o \
+ xe_gt_sriov_pf_service.o \
xe_lmtt.o \
xe_lmtt_2l.o \
xe_lmtt_ml.o \
+ xe_pci_sriov.o \
xe_sriov_pf.o
# include helpers for tests even when XE is built-in
@@ -204,7 +195,8 @@ xe-$(CONFIG_DRM_XE_DISPLAY) += \
display/xe_dsb_buffer.o \
display/xe_fb_pin.o \
display/xe_hdcp_gsc.o \
- display/xe_plane_initial.o
+ display/xe_plane_initial.o \
+ display/xe_tdf.o
# SOC code shared with i915
xe-$(CONFIG_DRM_XE_DISPLAY) += \
@@ -214,6 +206,7 @@ xe-$(CONFIG_DRM_XE_DISPLAY) += \
# Display code shared with i915
xe-$(CONFIG_DRM_XE_DISPLAY) += \
i915-display/icl_dsi.o \
+ i915-display/intel_alpm.o \
i915-display/intel_atomic.o \
i915-display/intel_atomic_plane.o \
i915-display/intel_audio.o \
@@ -256,6 +249,7 @@ xe-$(CONFIG_DRM_XE_DISPLAY) += \
i915-display/intel_dsi.o \
i915-display/intel_dsi_dcs_backlight.o \
i915-display/intel_dsi_vbt.o \
+ i915-display/intel_encoder.o \
i915-display/intel_fb.o \
i915-display/intel_fbc.o \
i915-display/intel_fdi.o \
diff --git a/drivers/gpu/drm/xe/abi/guc_actions_abi.h b/drivers/gpu/drm/xe/abi/guc_actions_abi.h
index 79ba98a169f9..43ad4652c2b2 100644
--- a/drivers/gpu/drm/xe/abi/guc_actions_abi.h
+++ b/drivers/gpu/drm/xe/abi/guc_actions_abi.h
@@ -128,7 +128,6 @@ enum xe_guc_action {
XE_GUC_ACTION_CONTEXT_RESET_NOTIFICATION = 0x1008,
XE_GUC_ACTION_ENGINE_FAILURE_NOTIFICATION = 0x1009,
XE_GUC_ACTION_HOST2GUC_UPDATE_CONTEXT_POLICIES = 0x100B,
- XE_GUC_ACTION_SETUP_PC_GUCRC = 0x3004,
XE_GUC_ACTION_AUTHENTICATE_HUC = 0x4000,
XE_GUC_ACTION_GET_HWCONFIG = 0x4100,
XE_GUC_ACTION_REGISTER_CONTEXT = 0x4502,
@@ -153,11 +152,6 @@ enum xe_guc_action {
XE_GUC_ACTION_LIMIT
};
-enum xe_guc_rc_options {
- XE_GUCRC_HOST_CONTROL,
- XE_GUCRC_FIRMWARE_CONTROL,
-};
-
enum xe_guc_preempt_options {
XE_GUC_PREEMPT_OPTION_DROP_WORK_Q = 0x4,
XE_GUC_PREEMPT_OPTION_DROP_SUBMIT_Q = 0x8,
diff --git a/drivers/gpu/drm/xe/abi/guc_actions_slpc_abi.h b/drivers/gpu/drm/xe/abi/guc_actions_slpc_abi.h
index c165e26c0976..85abe4f09ae2 100644
--- a/drivers/gpu/drm/xe/abi/guc_actions_slpc_abi.h
+++ b/drivers/gpu/drm/xe/abi/guc_actions_slpc_abi.h
@@ -246,4 +246,26 @@ struct slpc_shared_data {
#define HOST2GUC_PC_SLPC_REQUEST_MSG_1_EVENT_ARGC (0xffu << 0)
#define HOST2GUC_PC_SLPC_REQUEST_MSG_N_EVENT_DATA_N GUC_HXG_REQUEST_MSG_n_DATAn
+/**
+ * DOC: SETUP_PC_GUCRC
+ *
+ * +---+-------+--------------------------------------------------------------+
+ * | | Bits | Description |
+ * +===+=======+==============================================================+
+ * | 0 | 31 | ORIGIN = GUC_HXG_ORIGIN_HOST_ |
+ * | +-------+--------------------------------------------------------------+
+ * | | 30:28 | TYPE = GUC_HXG_TYPE_FAST_REQUEST_ |
+ * | +-------+--------------------------------------------------------------+
+ * | | 27:16 | DATA0 = MBZ |
+ * | +-------+--------------------------------------------------------------+
+ * | | 15:0 | ACTION = _`GUC_ACTION_HOST2GUC_SETUP_PC_GUCRC` = 0x3004 |
+ * +---+-------+--------------------------------------------------------------+
+ * | 1 | 31:0 | **MODE** = GUCRC_HOST_CONTROL(0), GUCRC_FIRMWARE_CONTROL(1) |
+ * +---+-------+--------------------------------------------------------------+
+ */
+
+#define GUC_ACTION_HOST2GUC_SETUP_PC_GUCRC 0x3004u
+#define GUCRC_HOST_CONTROL 0u
+#define GUCRC_FIRMWARE_CONTROL 1u
+
#endif
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 c1ad09b36453..181180f5945c 100644
--- a/drivers/gpu/drm/xe/abi/guc_actions_sriov_abi.h
+++ b/drivers/gpu/drm/xe/abi/guc_actions_sriov_abi.h
@@ -172,6 +172,36 @@
#define VF2GUC_RELAY_TO_PF_REQUEST_MSG_NUM_RELAY_DATA GUC_RELAY_MSG_MAX_LEN
/**
+ * DOC: GUC2PF_ADVERSE_EVENT
+ *
+ * This message is used by the GuC to notify PF about adverse events.
+ *
+ * This G2H message must be sent as `CTB HXG Message`_.
+ *
+ * +---+-------+--------------------------------------------------------------+
+ * | | Bits | Description |
+ * +===+=======+==============================================================+
+ * | 0 | 31 | ORIGIN = GUC_HXG_ORIGIN_GUC_ |
+ * | +-------+--------------------------------------------------------------+
+ * | | 30:28 | TYPE = GUC_HXG_TYPE_EVENT_ |
+ * | +-------+--------------------------------------------------------------+
+ * | | 27:16 | DATA0 = MBZ |
+ * | +-------+--------------------------------------------------------------+
+ * | | 15:0 | ACTION = _`GUC_ACTION_GUC2PF_ADVERSE_EVENT` = 0x5104 |
+ * +---+-------+--------------------------------------------------------------+
+ * | 1 | 31:0 | DATA1 = **VFID** - VF identifier |
+ * +---+-------+--------------------------------------------------------------+
+ * | 2 | 31:0 | DATA2 = **THRESHOLD** - key of the exceeded threshold |
+ * +---+-------+--------------------------------------------------------------+
+ */
+#define GUC_ACTION_GUC2PF_ADVERSE_EVENT 0x5104
+
+#define GUC2PF_ADVERSE_EVENT_EVENT_MSG_LEN (GUC_HXG_EVENT_MSG_MIN_LEN + 2u)
+#define GUC2PF_ADVERSE_EVENT_EVENT_MSG_0_MBZ GUC_HXG_EVENT_MSG_0_DATA0
+#define GUC2PF_ADVERSE_EVENT_EVENT_MSG_1_VFID GUC_HXG_EVENT_MSG_n_DATAn
+#define GUC2PF_ADVERSE_EVENT_EVENT_MSG_2_THRESHOLD GUC_HXG_EVENT_MSG_n_DATAn
+
+/**
* DOC: GUC2PF_VF_STATE_NOTIFY
*
* The GUC2PF_VF_STATE_NOTIFY message is used by the GuC to notify PF about change
@@ -214,6 +244,73 @@
#define GUC_PF_NOTIFY_VF_FIXUP_DONE 4u
/**
+ * DOC: VF2GUC_MATCH_VERSION
+ *
+ * This action is used to match VF interface version used by VF and GuC.
+ *
+ * This message must be sent as `MMIO HXG Message`_.
+ *
+ * +---+-------+--------------------------------------------------------------+
+ * | | Bits | Description |
+ * +===+=======+==============================================================+
+ * | 0 | 31 | ORIGIN = GUC_HXG_ORIGIN_HOST_ |
+ * | +-------+--------------------------------------------------------------+
+ * | | 30:28 | TYPE = GUC_HXG_TYPE_REQUEST_ |
+ * | +-------+--------------------------------------------------------------+
+ * | | 27:16 | DATA0 = MBZ |
+ * | +-------+--------------------------------------------------------------+
+ * | | 15:0 | ACTION = _`GUC_ACTION_VF2GUC_MATCH_VERSION` = 0x5500 |
+ * +---+-------+--------------------------------------------------------------+
+ * | 1 | 31:24 | **BRANCH** - branch ID of the VF interface |
+ * | | | (use BRANCH_ANY to request latest version supported by GuC) |
+ * | +-------+--------------------------------------------------------------+
+ * | | 23:16 | **MAJOR** - major version of the VF interface |
+ * | | | (use MAJOR_ANY to request latest version supported by GuC) |
+ * | +-------+--------------------------------------------------------------+
+ * | | 15:8 | **MINOR** - minor version of the VF interface |
+ * | | | (use MINOR_ANY to request latest version supported by GuC) |
+ * | +-------+--------------------------------------------------------------+
+ * | | 7:0 | **MBZ** |
+ * +---+-------+--------------------------------------------------------------+
+ *
+ * +---+-------+--------------------------------------------------------------+
+ * | | Bits | Description |
+ * +===+=======+==============================================================+
+ * | 0 | 31 | ORIGIN = GUC_HXG_ORIGIN_GUC_ |
+ * | +-------+--------------------------------------------------------------+
+ * | | 30:28 | TYPE = GUC_HXG_TYPE_RESPONSE_SUCCESS_ |
+ * | +-------+--------------------------------------------------------------+
+ * | | 27:0 | DATA0 = MBZ |
+ * +---+-------+--------------------------------------------------------------+
+ * | 1 | 31:24 | **BRANCH** - branch ID of the VF interface |
+ * | +-------+--------------------------------------------------------------+
+ * | | 23:16 | **MAJOR** - major version of the VF interface |
+ * | +-------+--------------------------------------------------------------+
+ * | | 15:8 | **MINOR** - minor version of the VF interface |
+ * | +-------+--------------------------------------------------------------+
+ * | | 7:0 | **PATCH** - patch version of the VF interface |
+ * +---+-------+--------------------------------------------------------------+
+ */
+#define GUC_ACTION_VF2GUC_MATCH_VERSION 0x5500u
+
+#define VF2GUC_MATCH_VERSION_REQUEST_MSG_LEN (GUC_HXG_REQUEST_MSG_MIN_LEN + 1u)
+#define VF2GUC_MATCH_VERSION_REQUEST_MSG_0_MBZ GUC_HXG_REQUEST_MSG_0_DATA0
+#define VF2GUC_MATCH_VERSION_REQUEST_MSG_1_BRANCH (0xffu << 24)
+#define GUC_VERSION_BRANCH_ANY 0
+#define VF2GUC_MATCH_VERSION_REQUEST_MSG_1_MAJOR (0xffu << 16)
+#define GUC_VERSION_MAJOR_ANY 0
+#define VF2GUC_MATCH_VERSION_REQUEST_MSG_1_MINOR (0xffu << 8)
+#define GUC_VERSION_MINOR_ANY 0
+#define VF2GUC_MATCH_VERSION_REQUEST_MSG_1_MBZ (0xffu << 0)
+
+#define VF2GUC_MATCH_VERSION_RESPONSE_MSG_LEN (GUC_HXG_RESPONSE_MSG_MIN_LEN + 1u)
+#define VF2GUC_MATCH_VERSION_RESPONSE_MSG_0_MBZ GUC_HXG_RESPONSE_MSG_0_DATA0
+#define VF2GUC_MATCH_VERSION_RESPONSE_MSG_1_BRANCH (0xffu << 24)
+#define VF2GUC_MATCH_VERSION_RESPONSE_MSG_1_MAJOR (0xffu << 16)
+#define VF2GUC_MATCH_VERSION_RESPONSE_MSG_1_MINOR (0xffu << 8)
+#define VF2GUC_MATCH_VERSION_RESPONSE_MSG_1_PATCH (0xffu << 0)
+
+/**
* DOC: PF2GUC_UPDATE_VGT_POLICY
*
* This message is used by the PF to set `GuC VGT Policy KLVs`_.
@@ -367,4 +464,97 @@
#define GUC_PF_TRIGGER_VF_FLR_START 4u
#define GUC_PF_TRIGGER_VF_FLR_FINISH 5u
+/**
+ * DOC: VF2GUC_VF_RESET
+ *
+ * This action is used by VF to reset GuC's VF state.
+ *
+ * This message must be sent as `MMIO HXG Message`_.
+ *
+ * +---+-------+--------------------------------------------------------------+
+ * | | Bits | Description |
+ * +===+=======+==============================================================+
+ * | 0 | 31 | ORIGIN = GUC_HXG_ORIGIN_HOST_ |
+ * | +-------+--------------------------------------------------------------+
+ * | | 30:28 | TYPE = GUC_HXG_TYPE_REQUEST_ |
+ * | +-------+--------------------------------------------------------------+
+ * | | 27:16 | DATA0 = MBZ |
+ * | +-------+--------------------------------------------------------------+
+ * | | 15:0 | ACTION = _`GUC_ACTION_VF2GUC_VF_RESET` = 0x5507 |
+ * +---+-------+--------------------------------------------------------------+
+ *
+ * +---+-------+--------------------------------------------------------------+
+ * | | 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_VF_RESET 0x5507u
+
+#define VF2GUC_VF_RESET_REQUEST_MSG_LEN GUC_HXG_REQUEST_MSG_MIN_LEN
+#define VF2GUC_VF_RESET_REQUEST_MSG_0_MBZ GUC_HXG_REQUEST_MSG_0_DATA0
+
+#define VF2GUC_VF_RESET_RESPONSE_MSG_LEN GUC_HXG_RESPONSE_MSG_MIN_LEN
+#define VF2GUC_VF_RESET_RESPONSE_MSG_0_MBZ GUC_HXG_RESPONSE_MSG_0_DATA0
+
+/**
+ * DOC: VF2GUC_QUERY_SINGLE_KLV
+ *
+ * This action is used by VF to query value of the single KLV data.
+ *
+ * This message must be sent as `MMIO HXG Message`_.
+ *
+ * +---+-------+--------------------------------------------------------------+
+ * | | Bits | Description |
+ * +===+=======+==============================================================+
+ * | 0 | 31 | ORIGIN = GUC_HXG_ORIGIN_HOST_ |
+ * | +-------+--------------------------------------------------------------+
+ * | | 30:28 | TYPE = GUC_HXG_TYPE_REQUEST_ |
+ * | +-------+--------------------------------------------------------------+
+ * | | 27:16 | MBZ |
+ * | +-------+--------------------------------------------------------------+
+ * | | 15:0 | ACTION = _`GUC_ACTION_VF2GUC_QUERY_SINGLE_KLV` = 0x5509 |
+ * +---+-------+--------------------------------------------------------------+
+ * | 1 | 31:16 | MBZ |
+ * | +-------+--------------------------------------------------------------+
+ * | | 15:0 | **KEY** - key for which value is requested |
+ * +---+-------+--------------------------------------------------------------+
+ *
+ * +---+-------+--------------------------------------------------------------+
+ * | | Bits | Description |
+ * +===+=======+==============================================================+
+ * | 0 | 31 | ORIGIN = GUC_HXG_ORIGIN_GUC_ |
+ * | +-------+--------------------------------------------------------------+
+ * | | 30:28 | TYPE = GUC_HXG_TYPE_RESPONSE_SUCCESS_ |
+ * | +-------+--------------------------------------------------------------+
+ * | | 27:16 | MBZ |
+ * | +-------+--------------------------------------------------------------+
+ * | | 15:0 | **LENGTH** - length of data in dwords |
+ * +---+-------+--------------------------------------------------------------+
+ * | 1 | 31:0 | **VALUE32** - bits 31:0 of value if **LENGTH** >= 1 |
+ * +---+-------+--------------------------------------------------------------+
+ * | 2 | 31:0 | **VALUE64** - bits 63:32 of value if **LENGTH** >= 2 |
+ * +---+-------+--------------------------------------------------------------+
+ * | 3 | 31:0 | **VALUE96** - bits 95:64 of value if **LENGTH** >= 3 |
+ * +---+-------+--------------------------------------------------------------+
+ */
+#define GUC_ACTION_VF2GUC_QUERY_SINGLE_KLV 0x5509u
+
+#define VF2GUC_QUERY_SINGLE_KLV_REQUEST_MSG_LEN (GUC_HXG_REQUEST_MSG_MIN_LEN + 1u)
+#define VF2GUC_QUERY_SINGLE_KLV_REQUEST_MSG_0_MBZ GUC_HXG_REQUEST_MSG_0_DATA0
+#define VF2GUC_QUERY_SINGLE_KLV_REQUEST_MSG_1_MBZ (0xffffu << 16)
+#define VF2GUC_QUERY_SINGLE_KLV_REQUEST_MSG_1_KEY (0xffffu << 0)
+
+#define VF2GUC_QUERY_SINGLE_KLV_RESPONSE_MSG_MIN_LEN GUC_HXG_RESPONSE_MSG_MIN_LEN
+#define VF2GUC_QUERY_SINGLE_KLV_RESPONSE_MSG_MAX_LEN (GUC_HXG_RESPONSE_MSG_MIN_LEN + 3u)
+#define VF2GUC_QUERY_SINGLE_KLV_RESPONSE_MSG_0_MBZ (0xfffu << 16)
+#define VF2GUC_QUERY_SINGLE_KLV_RESPONSE_MSG_0_LENGTH (0xffffu << 0)
+#define VF2GUC_QUERY_SINGLE_KLV_RESPONSE_MSG_1_VALUE32 GUC_HXG_REQUEST_MSG_n_DATAn
+#define VF2GUC_QUERY_SINGLE_KLV_RESPONSE_MSG_2_VALUE64 GUC_HXG_REQUEST_MSG_n_DATAn
+#define VF2GUC_QUERY_SINGLE_KLV_RESPONSE_MSG_3_VALUE96 GUC_HXG_REQUEST_MSG_n_DATAn
+
#endif
diff --git a/drivers/gpu/drm/xe/abi/guc_errors_abi.h b/drivers/gpu/drm/xe/abi/guc_errors_abi.h
index ec83551bf9c0..2c627a21648f 100644
--- a/drivers/gpu/drm/xe/abi/guc_errors_abi.h
+++ b/drivers/gpu/drm/xe/abi/guc_errors_abi.h
@@ -7,8 +7,43 @@
#define _ABI_GUC_ERRORS_ABI_H
enum xe_guc_response_status {
- XE_GUC_RESPONSE_STATUS_SUCCESS = 0x0,
- XE_GUC_RESPONSE_STATUS_GENERIC_FAIL = 0xF000,
+ XE_GUC_RESPONSE_STATUS_SUCCESS = 0x0,
+ XE_GUC_RESPONSE_ERROR_PROTOCOL = 0x04,
+ XE_GUC_RESPONSE_INVALID_STATE = 0x0A,
+ XE_GUC_RESPONSE_UNSUPPORTED_VERSION = 0x0B,
+ XE_GUC_RESPONSE_INVALID_VFID = 0x0C,
+ XE_GUC_RESPONSE_UNPROVISIONED_VF = 0x0D,
+ XE_GUC_RESPONSE_INVALID_EVENT = 0x0E,
+ XE_GUC_RESPONSE_NOT_SUPPORTED = 0x20,
+ XE_GUC_RESPONSE_UNKNOWN_ACTION = 0x30,
+ XE_GUC_RESPONSE_ACTION_ABORTED = 0x31,
+ XE_GUC_RESPONSE_NO_PERMISSION = 0x40,
+ XE_GUC_RESPONSE_CANNOT_COMPLETE_ACTION = 0x41,
+ XE_GUC_RESPONSE_INVALID_KLV_DATA = 0x50,
+ XE_GUC_RESPONSE_INVALID_PARAMS = 0x60,
+ XE_GUC_RESPONSE_INVALID_BUFFER_RANGE = 0x70,
+ XE_GUC_RESPONSE_INVALID_BUFFER = 0x71,
+ XE_GUC_RESPONSE_INVALID_GGTT_ADDRESS = 0x80,
+ XE_GUC_RESPONSE_PENDING_ACTION = 0x90,
+ XE_GUC_RESPONSE_INVALID_SIZE = 0x102,
+ XE_GUC_RESPONSE_MALFORMED_KLV = 0x103,
+ XE_GUC_RESPONSE_INVALID_KLV_KEY = 0x105,
+ XE_GUC_RESPONSE_DATA_TOO_LARGE = 0x106,
+ XE_GUC_RESPONSE_VF_MIGRATED = 0x107,
+ XE_GUC_RESPONSE_NO_ATTRIBUTE_TABLE = 0x201,
+ XE_GUC_RESPONSE_NO_DECRYPTION_KEY = 0x202,
+ XE_GUC_RESPONSE_DECRYPTION_FAILED = 0x204,
+ XE_GUC_RESPONSE_VGT_DISABLED = 0x300,
+ XE_GUC_RESPONSE_CTB_FULL = 0x301,
+ XE_GUC_RESPONSE_VGT_UNAUTHORIZED_REQUEST = 0x302,
+ XE_GUC_RESPONSE_CTB_INVALID = 0x303,
+ XE_GUC_RESPONSE_CTB_NOT_REGISTERED = 0x304,
+ XE_GUC_RESPONSE_CTB_IN_USE = 0x305,
+ XE_GUC_RESPONSE_CTB_INVALID_DESC = 0x306,
+ XE_GUC_RESPONSE_CTB_SOURCE_INVALID_DESCRIPTOR = 0x30D,
+ XE_GUC_RESPONSE_CTB_DESTINATION_INVALID_DESCRIPTOR = 0x30E,
+ XE_GUC_RESPONSE_INVALID_CONFIG_STATE = 0x30F,
+ XE_GUC_RESPONSE_STATUS_GENERIC_FAIL = 0xF000,
};
enum xe_guc_load_status {
@@ -17,6 +52,9 @@ enum xe_guc_load_status {
XE_GUC_LOAD_STATUS_ERROR_DEVID_BUILD_MISMATCH = 0x02,
XE_GUC_LOAD_STATUS_GUC_PREPROD_BUILD_MISMATCH = 0x03,
XE_GUC_LOAD_STATUS_ERROR_DEVID_INVALID_GUCTYPE = 0x04,
+ XE_GUC_LOAD_STATUS_HWCONFIG_START = 0x05,
+ XE_GUC_LOAD_STATUS_HWCONFIG_DONE = 0x06,
+ XE_GUC_LOAD_STATUS_HWCONFIG_ERROR = 0x07,
XE_GUC_LOAD_STATUS_GDT_DONE = 0x10,
XE_GUC_LOAD_STATUS_IDT_DONE = 0x20,
XE_GUC_LOAD_STATUS_LAPIC_DONE = 0x30,
@@ -34,4 +72,19 @@ enum xe_guc_load_status {
XE_GUC_LOAD_STATUS_READY = 0xF0,
};
+enum xe_bootrom_load_status {
+ XE_BOOTROM_STATUS_NO_KEY_FOUND = 0x13,
+ XE_BOOTROM_STATUS_AES_PROD_KEY_FOUND = 0x1A,
+ XE_BOOTROM_STATUS_PROD_KEY_CHECK_FAILURE = 0x2B,
+ XE_BOOTROM_STATUS_RSA_FAILED = 0x50,
+ XE_BOOTROM_STATUS_PAVPC_FAILED = 0x73,
+ XE_BOOTROM_STATUS_WOPCM_FAILED = 0x74,
+ XE_BOOTROM_STATUS_LOADLOC_FAILED = 0x75,
+ XE_BOOTROM_STATUS_JUMP_PASSED = 0x76,
+ XE_BOOTROM_STATUS_JUMP_FAILED = 0x77,
+ XE_BOOTROM_STATUS_RC6CTXCONFIG_FAILED = 0x79,
+ XE_BOOTROM_STATUS_MPUMAP_INCORRECT = 0x7A,
+ XE_BOOTROM_STATUS_EXCEPTION = 0x7E,
+};
+
#endif
diff --git a/drivers/gpu/drm/xe/abi/guc_klvs_abi.h b/drivers/gpu/drm/xe/abi/guc_klvs_abi.h
index 511cf974d585..8f9f60b28306 100644
--- a/drivers/gpu/drm/xe/abi/guc_klvs_abi.h
+++ b/drivers/gpu/drm/xe/abi/guc_klvs_abi.h
@@ -36,6 +36,20 @@
#define GUC_KLV_n_VALUE (0xffffffffu << 0)
/**
+ * DOC: GuC Global Config KLVs
+ *
+ * `GuC KLV`_ keys available for use with HOST2GUC_SELF_CFG_.
+ *
+ * _`GUC_KLV_GLOBAL_CFG_GMD_ID` : 0x3000
+ * 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+.
+ */
+
+#define GUC_KLV_GLOBAL_CFG_GMD_ID_KEY 0x3000u
+#define GUC_KLV_GLOBAL_CFG_GMD_ID_LEN 1u
+
+/**
* DOC: GuC Self Config KLVs
*
* `GuC KLV`_ keys available for use with HOST2GUC_SELF_CFG_.
@@ -194,14 +208,18 @@ enum {
* granularity) since the GPUs clock time runs off a different crystal
* from the CPUs clock. Changing this KLV on a VF that is currently
* running a context wont take effect until a new context is scheduled in.
- * That said, when the PF is changing this value from 0xFFFFFFFF to
- * something else, it might never take effect if the VF is running an
- * inifinitely long compute or shader kernel. In such a scenario, the
+ * That said, when the PF is changing this value from 0x0 to
+ * a non-zero value, it might never take effect if the VF is running an
+ * infinitely long compute or shader kernel. In such a scenario, the
* PF would need to trigger a VM PAUSE and then change the KLV to force
* it to take effect. Such cases might typically happen on a 1PF+1VF
* Virtualization config enabled for heavier workloads like AI/ML.
*
+ * The max value for this KLV is 100 seconds, anything exceeding that
+ * will be clamped to the max.
+ *
* :0: infinite exec quantum (default)
+ * :100000: maximum exec quantum (100000ms == 100s)
*
* _`GUC_KLV_VF_CFG_PREEMPT_TIMEOUT` : 0x8A02
* This config sets the VF-preemption-timeout in microseconds.
@@ -211,15 +229,19 @@ enum {
* different crystal from the CPUs clock. Changing this KLV on a VF
* that is currently running a context wont take effect until a new
* context is scheduled in.
- * That said, when the PF is changing this value from 0xFFFFFFFF to
- * something else, it might never take effect if the VF is running an
- * inifinitely long compute or shader kernel.
+ * That said, when the PF is changing this value from 0x0 to
+ * a non-zero value, it might never take effect if the VF is running an
+ * infinitely long compute or shader kernel.
* In this case, the PF would need to trigger a VM PAUSE and then change
* the KLV to force it to take effect. Such cases might typically happen
* on a 1PF+1VF Virtualization config enabled for heavier workloads like
* AI/ML.
*
+ * The max value for this KLV is 100 seconds, anything exceeding that
+ * will be clamped to the max.
+ *
* :0: no preemption timeout (default)
+ * :100000000: maximum preemption timeout (100000000us == 100s)
*
* _`GUC_KLV_VF_CFG_THRESHOLD_CAT_ERR` : 0x8A03
* This config sets threshold for CAT errors caused by the VF.
@@ -291,9 +313,11 @@ enum {
#define GUC_KLV_VF_CFG_EXEC_QUANTUM_KEY 0x8a01
#define GUC_KLV_VF_CFG_EXEC_QUANTUM_LEN 1u
+#define GUC_KLV_VF_CFG_EXEC_QUANTUM_MAX_VALUE 100000u
-#define GUC_KLV_VF_CFG_PREEMPT_TIMEOUT_KEY 0x8a02
-#define GUC_KLV_VF_CFG_PREEMPT_TIMEOUT_LEN 1u
+#define GUC_KLV_VF_CFG_PREEMPT_TIMEOUT_KEY 0x8a02
+#define GUC_KLV_VF_CFG_PREEMPT_TIMEOUT_LEN 1u
+#define GUC_KLV_VF_CFG_PREEMPT_TIMEOUT_MAX_VALUE 100000000u
#define GUC_KLV_VF_CFG_THRESHOLD_CAT_ERR_KEY 0x8a03
#define GUC_KLV_VF_CFG_THRESHOLD_CAT_ERR_LEN 1u
diff --git a/drivers/gpu/drm/xe/abi/guc_messages_abi.h b/drivers/gpu/drm/xe/abi/guc_messages_abi.h
index 534a39db7772..f6ed4dfd215c 100644
--- a/drivers/gpu/drm/xe/abi/guc_messages_abi.h
+++ b/drivers/gpu/drm/xe/abi/guc_messages_abi.h
@@ -92,6 +92,34 @@
#define GUC_HXG_REQUEST_MSG_n_DATAn GUC_HXG_MSG_n_PAYLOAD
/**
+ * DOC: HXG Fast Request
+ *
+ * The `HXG Request`_ message should be used to initiate asynchronous activity
+ * for which confirmation or return data is not expected.
+ *
+ * If confirmation is required then `HXG Request`_ shall be used instead.
+ *
+ * The recipient of this message may only use `HXG Failure`_ message if it was
+ * unable to accept this request (like invalid data).
+ *
+ * Format of `HXG Fast Request`_ message is same as `HXG Request`_ except @TYPE.
+ *
+ * +---+-------+--------------------------------------------------------------+
+ * | | Bits | Description |
+ * +===+=======+==============================================================+
+ * | 0 | 31 | ORIGIN - see `HXG Message`_ |
+ * | +-------+--------------------------------------------------------------+
+ * | | 30:28 | TYPE = `GUC_HXG_TYPE_FAST_REQUEST`_ |
+ * | +-------+--------------------------------------------------------------+
+ * | | 27:16 | DATA0 - see `HXG Request`_ |
+ * | +-------+--------------------------------------------------------------+
+ * | | 15:0 | ACTION - see `HXG Request`_ |
+ * +---+-------+--------------------------------------------------------------+
+ * |...| | DATAn - see `HXG Request`_ |
+ * +---+-------+--------------------------------------------------------------+
+ */
+
+/**
* DOC: HXG Event
*
* The `HXG Event`_ message should be used to initiate asynchronous activity
@@ -220,17 +248,4 @@
#define GUC_HXG_RESPONSE_MSG_0_DATA0 GUC_HXG_MSG_0_AUX
#define GUC_HXG_RESPONSE_MSG_n_DATAn GUC_HXG_MSG_n_PAYLOAD
-/* deprecated */
-#define INTEL_GUC_MSG_TYPE_SHIFT 28
-#define INTEL_GUC_MSG_TYPE_MASK (0xF << INTEL_GUC_MSG_TYPE_SHIFT)
-#define INTEL_GUC_MSG_DATA_SHIFT 16
-#define INTEL_GUC_MSG_DATA_MASK (0xFFF << INTEL_GUC_MSG_DATA_SHIFT)
-#define INTEL_GUC_MSG_CODE_SHIFT 0
-#define INTEL_GUC_MSG_CODE_MASK (0xFFFF << INTEL_GUC_MSG_CODE_SHIFT)
-
-enum intel_guc_msg_type {
- INTEL_GUC_MSG_TYPE_REQUEST = 0x0,
- INTEL_GUC_MSG_TYPE_RESPONSE = 0xF,
-};
-
#endif
diff --git a/drivers/gpu/drm/xe/abi/guc_relay_actions_abi.h b/drivers/gpu/drm/xe/abi/guc_relay_actions_abi.h
index 747e428de421..6c2834613081 100644
--- a/drivers/gpu/drm/xe/abi/guc_relay_actions_abi.h
+++ b/drivers/gpu/drm/xe/abi/guc_relay_actions_abi.h
@@ -1,11 +1,179 @@
/* SPDX-License-Identifier: MIT */
/*
- * Copyright © 2023 Intel Corporation
+ * Copyright © 2023-2024 Intel Corporation
*/
#ifndef _ABI_GUC_RELAY_ACTIONS_ABI_H_
#define _ABI_GUC_RELAY_ACTIONS_ABI_H_
+#include "abi/guc_relay_communication_abi.h"
+
+/**
+ * DOC: GuC Relay VF/PF ABI Version
+ *
+ * The _`GUC_RELAY_VERSION_BASE` defines minimum VF/PF ABI version that
+ * drivers must support. Currently this is version 1.0.
+ *
+ * The _`GUC_RELAY_VERSION_LATEST` defines latest VF/PF ABI version that
+ * drivers may use. Currently this is version 1.0.
+ *
+ * Some platforms may require different base VF/PF ABI version.
+ * No supported VF/PF ABI version can be 0.0.
+ */
+
+#define GUC_RELAY_VERSION_BASE_MAJOR 1
+#define GUC_RELAY_VERSION_BASE_MINOR 0
+
+#define GUC_RELAY_VERSION_LATEST_MAJOR 1
+#define GUC_RELAY_VERSION_LATEST_MINOR 0
+
+/**
+ * DOC: GuC Relay Actions
+ *
+ * The following actions are supported from VF/PF ABI version 1.0:
+ *
+ * * `VF2PF_HANDSHAKE`_
+ * * `VF2PF_QUERY_RUNTIME`_
+ */
+
+/**
+ * DOC: VF2PF_HANDSHAKE
+ *
+ * This `Relay Message`_ is used by the VF to establish ABI version with the PF.
+ *
+ * Prior to exchanging any other messages, both VF driver and PF driver must
+ * negotiate the VF/PF ABI version that will be used in their communication.
+ *
+ * The VF driver shall use @MAJOR and @MINOR fields to pass requested ABI version.
+ * The VF driver may use special version 0.0 (both @MAJOR and @MINOR set to 0)
+ * to request latest (or any) ABI version that is supported by the PF driver.
+ *
+ * This message definition shall be supported by all future ABI versions.
+ * This message definition shall not be changed by future ABI versions.
+ *
+ * +---+-------+--------------------------------------------------------------+
+ * | | Bits | Description |
+ * +===+=======+==============================================================+
+ * | 0 | 31 | ORIGIN = GUC_HXG_ORIGIN_HOST_ |
+ * | +-------+--------------------------------------------------------------+
+ * | | 30:28 | TYPE = GUC_HXG_TYPE_REQUEST_ |
+ * | +-------+--------------------------------------------------------------+
+ * | | 27:16 | DATA0 = MBZ |
+ * | +-------+--------------------------------------------------------------+
+ * | | 15:0 | ACTION = _`GUC_RELAY_ACTION_VF2PF_HANDSHAKE` = 0x0001 |
+ * +---+-------+--------------------------------------------------------------+
+ * | 1 | 31:16 | **MAJOR** - requested major version of the VFPF interface |
+ * | | | (use MAJOR_ANY to request latest version supported by PF) |
+ * | +-------+--------------------------------------------------------------+
+ * | | 15:0 | **MINOR** - requested minor version of the VFPF interface |
+ * | | | (use MINOR_ANY to request latest version supported by PF) |
+ * +---+-------+--------------------------------------------------------------+
+ *
+ * +---+-------+--------------------------------------------------------------+
+ * | | Bits | Description |
+ * +===+=======+==============================================================+
+ * | 0 | 31 | ORIGIN = GUC_HXG_ORIGIN_HOST_ |
+ * | +-------+--------------------------------------------------------------+
+ * | | 30:28 | TYPE = GUC_HXG_TYPE_RESPONSE_SUCCESS_ |
+ * | +-------+--------------------------------------------------------------+
+ * | | 27:0 | DATA0 = MBZ |
+ * +---+-------+--------------------------------------------------------------+
+ * | 1 | 31:16 | **MAJOR** - agreed major version of the VFPF interface |
+ * | +-------+--------------------------------------------------------------+
+ * | | 15:0 | **MINOR** - agreed minor version of the VFPF interface |
+ * +---+-------+--------------------------------------------------------------+
+ */
+#define GUC_RELAY_ACTION_VF2PF_HANDSHAKE 0x0001u
+
+#define VF2PF_HANDSHAKE_REQUEST_MSG_LEN 2u
+#define VF2PF_HANDSHAKE_REQUEST_MSG_0_MBZ GUC_HXG_REQUEST_MSG_0_DATA0
+#define VF2PF_HANDSHAKE_REQUEST_MSG_1_MAJOR (0xffffu << 16)
+#define VF2PF_HANDSHAKE_MAJOR_ANY 0
+#define VF2PF_HANDSHAKE_REQUEST_MSG_1_MINOR (0xffffu << 0)
+#define VF2PF_HANDSHAKE_MINOR_ANY 0
+
+#define VF2PF_HANDSHAKE_RESPONSE_MSG_LEN 2u
+#define VF2PF_HANDSHAKE_RESPONSE_MSG_0_MBZ GUC_HXG_RESPONSE_MSG_0_DATA0
+#define VF2PF_HANDSHAKE_RESPONSE_MSG_1_MAJOR (0xffffu << 16)
+#define VF2PF_HANDSHAKE_RESPONSE_MSG_1_MINOR (0xffffu << 0)
+
+/**
+ * DOC: VF2PF_QUERY_RUNTIME
+ *
+ * This `Relay Message`_ is used by the VF to query values of runtime registers.
+ *
+ * On some platforms, VF drivers may not have access to the some fuse registers
+ * (referred here as 'runtime registers') and therefore VF drivers need to ask
+ * the PF driver to obtain their values.
+ *
+ * However, the list of such registers, and their values, is fully owned and
+ * maintained by the PF driver and the VF driver may only initiate the query
+ * sequence and indicate in the @START field the starting index of the next
+ * requested register from this predefined list.
+ *
+ * In the response, the PF driver will return tuple of 32-bit register offset and
+ * the 32-bit value of that register (respectively @REG_OFFSET and @REG_VALUE).
+ *
+ * The VF driver can use @LIMIT field to limit number of returned register tuples.
+ * If @LIMIT is unset then PF decides about number of returned register tuples.
+ *
+ * This message definition is supported from ABI version 1.0.
+ *
+ * +---+-------+--------------------------------------------------------------+
+ * | | Bits | Description |
+ * +===+=======+==============================================================+
+ * | 0 | 31 | ORIGIN = GUC_HXG_ORIGIN_HOST_ |
+ * | +-------+--------------------------------------------------------------+
+ * | | 30:28 | TYPE = GUC_HXG_TYPE_REQUEST_ |
+ * | +-------+--------------------------------------------------------------+
+ * | | 27:16 | DATA0 = **LIMIT** - limit number of returned entries |
+ * | | | (use zero to not enforce any limits on the response) |
+ * | +-------+--------------------------------------------------------------+
+ * | | 15:0 | ACTION = _`GUC_RELAY_ACTION_VF2PF_QUERY_RUNTIME` = 0x0101 |
+ * +---+-------+--------------------------------------------------------------+
+ * | 1 | 31:0 | DATA1 = **START** - index of the first requested entry |
+ * +---+-------+--------------------------------------------------------------+
+ *
+ * +---+-------+--------------------------------------------------------------+
+ * | | Bits | Description |
+ * +===+=======+==============================================================+
+ * | 0 | 31 | ORIGIN = GUC_HXG_ORIGIN_HOST_ |
+ * | +-------+--------------------------------------------------------------+
+ * | | 30:28 | TYPE = GUC_HXG_TYPE_RESPONSE_SUCCESS_ |
+ * | +-------+--------------------------------------------------------------+
+ * | | 27:0 | DATA0 = **COUNT** - number of entries included in response |
+ * +---+-------+--------------------------------------------------------------+
+ * | 1 | 31:0 | DATA1 = **REMAINING** - number of remaining entries |
+ * +---+-------+--------------------------------------------------------------+
+ * | 2 | 31:0 | DATA2 = **REG_OFFSET** - offset of register[START] |
+ * +---+-------+--------------------------------------------------------------+
+ * | 3 | 31:0 | DATA3 = **REG_VALUE** - value of register[START] |
+ * +---+-------+--------------------------------------------------------------+
+ * | | | |
+ * +---+-------+--------------------------------------------------------------+
+ * |n-1| 31:0 | REG_OFFSET - offset of register[START + x] |
+ * +---+-------+--------------------------------------------------------------+
+ * | n | 31:0 | REG_VALUE - value of register[START + x] |
+ * +---+-------+--------------------------------------------------------------+
+ */
+#define GUC_RELAY_ACTION_VF2PF_QUERY_RUNTIME 0x0101u
+
+#define VF2PF_QUERY_RUNTIME_REQUEST_MSG_LEN 2u
+#define VF2PF_QUERY_RUNTIME_REQUEST_MSG_0_LIMIT GUC_HXG_REQUEST_MSG_0_DATA0
+#define VF2PF_QUERY_RUNTIME_NO_LIMIT 0u
+#define VF2PF_QUERY_RUNTIME_REQUEST_MSG_1_START GUC_HXG_REQUEST_MSG_n_DATAn
+
+#define VF2PF_QUERY_RUNTIME_RESPONSE_MSG_MIN_LEN (GUC_HXG_MSG_MIN_LEN + 1u)
+#define VF2PF_QUERY_RUNTIME_RESPONSE_MSG_MAX_LEN \
+ (VF2PF_QUERY_RUNTIME_RESPONSE_MSG_MIN_LEN + VF2PF_QUERY_RUNTIME_MAX_COUNT * 2)
+#define VF2PF_QUERY_RUNTIME_RESPONSE_MSG_0_COUNT GUC_HXG_RESPONSE_MSG_0_DATA0
+#define VF2PF_QUERY_RUNTIME_MIN_COUNT 0
+#define VF2PF_QUERY_RUNTIME_MAX_COUNT \
+ ((GUC_RELAY_MSG_MAX_LEN - VF2PF_QUERY_RUNTIME_RESPONSE_MSG_MIN_LEN) / 2)
+#define VF2PF_QUERY_RUNTIME_RESPONSE_MSG_1_REMAINING GUC_HXG_RESPONSE_MSG_n_DATAn
+#define VF2PF_QUERY_RUNTIME_RESPONSE_DATAn_REG_OFFSETx GUC_HXG_RESPONSE_MSG_n_DATAn
+#define VF2PF_QUERY_RUNTIME_RESPONSE_DATAn_REG_VALUEx GUC_HXG_RESPONSE_MSG_n_DATAn
+
/**
* DOC: GuC Relay Debug Actions
*
diff --git a/drivers/gpu/drm/xe/compat-i915-headers/gem/i915_gem_object_types.h b/drivers/gpu/drm/xe/compat-i915-headers/gem/i915_gem_object_types.h
new file mode 100644
index 000000000000..7d6bb1abab73
--- /dev/null
+++ b/drivers/gpu/drm/xe/compat-i915-headers/gem/i915_gem_object_types.h
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: MIT */
+/* Copyright © 2024 Intel Corporation */
+
+#ifndef __I915_GEM_OBJECT_TYPES_H__
+#define __I915_GEM_OBJECT_TYPES_H__
+
+#include "xe_bo.h"
+
+#define to_intel_bo(x) gem_to_xe_bo((x))
+
+#endif
diff --git a/drivers/gpu/drm/xe/compat-i915-headers/i915_gem_stolen.h b/drivers/gpu/drm/xe/compat-i915-headers/gem/i915_gem_stolen.h
index cb6c7598824b..cb6c7598824b 100644
--- a/drivers/gpu/drm/xe/compat-i915-headers/i915_gem_stolen.h
+++ b/drivers/gpu/drm/xe/compat-i915-headers/gem/i915_gem_stolen.h
diff --git a/drivers/gpu/drm/xe/compat-i915-headers/intel_gt_types.h b/drivers/gpu/drm/xe/compat-i915-headers/gt/intel_gt_types.h
index c15806d6c4f7..c15806d6c4f7 100644
--- a/drivers/gpu/drm/xe/compat-i915-headers/intel_gt_types.h
+++ b/drivers/gpu/drm/xe/compat-i915-headers/gt/intel_gt_types.h
diff --git a/drivers/gpu/drm/xe/compat-i915-headers/i915_drv.h b/drivers/gpu/drm/xe/compat-i915-headers/i915_drv.h
index cd4632276141..2feedddf1e40 100644
--- a/drivers/gpu/drm/xe/compat-i915-headers/i915_drv.h
+++ b/drivers/gpu/drm/xe/compat-i915-headers/i915_drv.h
@@ -12,22 +12,9 @@
#include <drm/drm_drv.h>
-#include "gem/i915_gem_object.h"
-
-#include "soc/intel_pch.h"
-#include "xe_device.h"
-#include "xe_bo.h"
-#include "xe_pm.h"
-#include "xe_step.h"
-#include "i915_gem_stolen.h"
-#include "i915_gpu_error.h"
-#include "i915_reg_defs.h"
#include "i915_utils.h"
-#include "intel_gt_types.h"
-#include "intel_step.h"
-#include "intel_uncore.h"
#include "intel_runtime_pm.h"
-#include <linux/pm_runtime.h>
+#include "xe_device_types.h"
static inline struct drm_i915_private *to_i915(const struct drm_device *dev)
{
@@ -119,67 +106,15 @@ static inline struct drm_i915_private *kdev_to_i915(struct device *kdev)
#define IS_RAPTORLAKE_U(xe) ((xe)->info.subplatform == XE_SUBPLATFORM_ALDERLAKE_P_RPLU)
#define IS_ICL_WITH_PORT_F(xe) (xe && 0)
#define HAS_FLAT_CCS(xe) (xe_device_has_flat_ccs(xe))
-#define to_intel_bo(x) gem_to_xe_bo((x))
#define HAS_128_BYTE_Y_TILING(xe) (xe || 1)
-#include "intel_wakeref.h"
-
-static inline intel_wakeref_t intel_runtime_pm_get(struct xe_runtime_pm *pm)
-{
- struct xe_device *xe = container_of(pm, struct xe_device, runtime_pm);
-
- return xe_pm_runtime_resume_and_get(xe);
-}
-
-static inline intel_wakeref_t intel_runtime_pm_get_if_in_use(struct xe_runtime_pm *pm)
-{
- struct xe_device *xe = container_of(pm, struct xe_device, runtime_pm);
-
- return xe_pm_runtime_get_if_in_use(xe);
-}
-
-static inline intel_wakeref_t intel_runtime_pm_get_noresume(struct xe_runtime_pm *pm)
-{
- struct xe_device *xe = container_of(pm, struct xe_device, runtime_pm);
-
- xe_pm_runtime_get_noresume(xe);
- return true;
-}
-
-static inline void intel_runtime_pm_put_unchecked(struct xe_runtime_pm *pm)
-{
- struct xe_device *xe = container_of(pm, struct xe_device, runtime_pm);
-
- xe_pm_runtime_put(xe);
-}
-
-static inline void intel_runtime_pm_put(struct xe_runtime_pm *pm, intel_wakeref_t wakeref)
-{
- if (wakeref)
- intel_runtime_pm_put_unchecked(pm);
-}
-
-#define intel_runtime_pm_get_raw intel_runtime_pm_get
-#define intel_runtime_pm_put_raw intel_runtime_pm_put
-#define assert_rpm_wakelock_held(x) do { } while (0)
-#define assert_rpm_raw_wakeref_held(x) do { } while (0)
-
-#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)
-
#define I915_PRIORITY_DISPLAY 0
struct i915_sched_attr {
int priority;
};
#define i915_gem_fence_wait_priority(fence, attr) do { (void) attr; } while (0)
-#define with_intel_runtime_pm(rpm, wf) \
- for ((wf) = intel_runtime_pm_get(rpm); (wf); \
- intel_runtime_pm_put((rpm), (wf)), (wf) = 0)
-
#define pdev_to_i915 pdev_to_xe_device
#define RUNTIME_INFO(xe) (&(xe)->info.i915_runtime)
diff --git a/drivers/gpu/drm/xe/compat-i915-headers/intel_runtime_pm.h b/drivers/gpu/drm/xe/compat-i915-headers/intel_runtime_pm.h
index 89da3cc62f39..8c7b315aa8ac 100644
--- a/drivers/gpu/drm/xe/compat-i915-headers/intel_runtime_pm.h
+++ b/drivers/gpu/drm/xe/compat-i915-headers/intel_runtime_pm.h
@@ -3,7 +3,12 @@
* Copyright © 2023 Intel Corporation
*/
+#ifndef __INTEL_RUNTIME_PM_H__
+#define __INTEL_RUNTIME_PM_H__
+
#include "intel_wakeref.h"
+#include "xe_device_types.h"
+#include "xe_pm.h"
#define intel_runtime_pm xe_runtime_pm
@@ -14,3 +19,49 @@ static inline void disable_rpm_wakeref_asserts(void *rpm)
static inline void enable_rpm_wakeref_asserts(void *rpm)
{
}
+
+static inline intel_wakeref_t intel_runtime_pm_get(struct xe_runtime_pm *pm)
+{
+ struct xe_device *xe = container_of(pm, struct xe_device, runtime_pm);
+
+ return xe_pm_runtime_resume_and_get(xe);
+}
+
+static inline intel_wakeref_t intel_runtime_pm_get_if_in_use(struct xe_runtime_pm *pm)
+{
+ struct xe_device *xe = container_of(pm, struct xe_device, runtime_pm);
+
+ return xe_pm_runtime_get_if_in_use(xe);
+}
+
+static inline intel_wakeref_t intel_runtime_pm_get_noresume(struct xe_runtime_pm *pm)
+{
+ struct xe_device *xe = container_of(pm, struct xe_device, runtime_pm);
+
+ xe_pm_runtime_get_noresume(xe);
+ return true;
+}
+
+static inline void intel_runtime_pm_put_unchecked(struct xe_runtime_pm *pm)
+{
+ struct xe_device *xe = container_of(pm, struct xe_device, runtime_pm);
+
+ xe_pm_runtime_put(xe);
+}
+
+static inline void intel_runtime_pm_put(struct xe_runtime_pm *pm, intel_wakeref_t wakeref)
+{
+ if (wakeref)
+ intel_runtime_pm_put_unchecked(pm);
+}
+
+#define intel_runtime_pm_get_raw intel_runtime_pm_get
+#define intel_runtime_pm_put_raw intel_runtime_pm_put
+#define assert_rpm_wakelock_held(x) do { } while (0)
+#define assert_rpm_raw_wakeref_held(x) do { } while (0)
+
+#define with_intel_runtime_pm(rpm, wf) \
+ for ((wf) = intel_runtime_pm_get(rpm); (wf); \
+ intel_runtime_pm_put((rpm), (wf)), (wf) = 0)
+
+#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 ef79793caa72..083c4da2ea41 100644
--- a/drivers/gpu/drm/xe/compat-i915-headers/intel_uncore.h
+++ b/drivers/gpu/drm/xe/compat-i915-headers/intel_uncore.h
@@ -172,4 +172,9 @@ static inline void __iomem *intel_uncore_regs(struct intel_uncore *uncore)
#define raw_reg_write(base, reg, value) \
writel(value, base + i915_mmio_reg_offset(reg))
+#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)
+
#endif /* __INTEL_UNCORE_H__ */
diff --git a/drivers/gpu/drm/xe/display/ext/i915_irq.c b/drivers/gpu/drm/xe/display/ext/i915_irq.c
index bee191a4a97d..eb40f1cb44f6 100644
--- a/drivers/gpu/drm/xe/display/ext/i915_irq.c
+++ b/drivers/gpu/drm/xe/display/ext/i915_irq.c
@@ -3,7 +3,6 @@
* Copyright © 2023 Intel Corporation
*/
-#include "i915_drv.h"
#include "i915_irq.h"
#include "i915_reg.h"
#include "intel_uncore.h"
diff --git a/drivers/gpu/drm/xe/display/intel_fb_bo.c b/drivers/gpu/drm/xe/display/intel_fb_bo.c
index e18521acc516..f835492f73fb 100644
--- a/drivers/gpu/drm/xe/display/intel_fb_bo.c
+++ b/drivers/gpu/drm/xe/display/intel_fb_bo.c
@@ -4,10 +4,11 @@
*/
#include <drm/drm_modeset_helper.h>
+#include <drm/ttm/ttm_bo.h>
-#include "i915_drv.h"
#include "intel_display_types.h"
#include "intel_fb_bo.h"
+#include "xe_bo.h"
void intel_fb_bo_framebuffer_fini(struct xe_bo *bo)
{
@@ -24,7 +25,7 @@ int intel_fb_bo_framebuffer_init(struct intel_framebuffer *intel_fb,
struct xe_bo *bo,
struct drm_mode_fb_cmd2 *mode_cmd)
{
- struct drm_i915_private *i915 = to_i915(bo->ttm.base.dev);
+ struct xe_device *xe = to_xe_device(bo->ttm.base.dev);
int ret;
xe_bo_get(bo);
@@ -40,7 +41,7 @@ int intel_fb_bo_framebuffer_init(struct intel_framebuffer *intel_fb,
* mode when the boect is VM_BINDed, so we can only set
* coherency with display when unbound.
*/
- if (XE_IOCTL_DBG(i915, !list_empty(&bo->ttm.base.gpuva.list))) {
+ if (XE_IOCTL_DBG(xe, !list_empty(&bo->ttm.base.gpuva.list))) {
ttm_bo_unreserve(&bo->ttm);
ret = -EINVAL;
goto err;
diff --git a/drivers/gpu/drm/xe/display/intel_fbdev_fb.c b/drivers/gpu/drm/xe/display/intel_fbdev_fb.c
index 9e4bcfdbc7e5..816ad13821a8 100644
--- a/drivers/gpu/drm/xe/display/intel_fbdev_fb.c
+++ b/drivers/gpu/drm/xe/display/intel_fbdev_fb.c
@@ -3,22 +3,20 @@
* Copyright © 2023 Intel Corporation
*/
-#include "intel_fbdev_fb.h"
-
#include <drm/drm_fb_helper.h>
+#include "intel_display_types.h"
+#include "intel_fbdev_fb.h"
+#include "xe_bo.h"
#include "xe_gt.h"
#include "xe_ttm_stolen_mgr.h"
-#include "i915_drv.h"
-#include "intel_display_types.h"
-
-struct drm_framebuffer *intel_fbdev_fb_alloc(struct drm_fb_helper *helper,
- struct drm_fb_helper_surface_size *sizes)
+struct intel_framebuffer *intel_fbdev_fb_alloc(struct drm_fb_helper *helper,
+ struct drm_fb_helper_surface_size *sizes)
{
struct drm_framebuffer *fb;
struct drm_device *dev = helper->dev;
- struct drm_i915_private *dev_priv = to_i915(dev);
+ struct xe_device *xe = to_xe_device(dev);
struct drm_mode_fb_cmd2 mode_cmd = {};
struct drm_i915_gem_object *obj;
int size;
@@ -39,26 +37,26 @@ struct drm_framebuffer *intel_fbdev_fb_alloc(struct drm_fb_helper *helper,
size = PAGE_ALIGN(size);
obj = ERR_PTR(-ENODEV);
- if (!IS_DGFX(dev_priv)) {
- obj = xe_bo_create_pin_map(dev_priv, xe_device_get_root_tile(dev_priv),
+ if (!IS_DGFX(xe)) {
+ obj = xe_bo_create_pin_map(xe, xe_device_get_root_tile(xe),
NULL, size,
ttm_bo_type_kernel, XE_BO_FLAG_SCANOUT |
XE_BO_FLAG_STOLEN |
XE_BO_FLAG_PINNED);
if (!IS_ERR(obj))
- drm_info(&dev_priv->drm, "Allocated fbdev into stolen\n");
+ drm_info(&xe->drm, "Allocated fbdev into stolen\n");
else
- drm_info(&dev_priv->drm, "Allocated fbdev into stolen failed: %li\n", PTR_ERR(obj));
+ drm_info(&xe->drm, "Allocated fbdev into stolen failed: %li\n", PTR_ERR(obj));
}
if (IS_ERR(obj)) {
- obj = xe_bo_create_pin_map(dev_priv, xe_device_get_root_tile(dev_priv), NULL, size,
- ttm_bo_type_kernel, XE_BO_FLAG_SCANOUT |
- XE_BO_FLAG_VRAM_IF_DGFX(xe_device_get_root_tile(dev_priv)) |
- XE_BO_FLAG_PINNED);
+ obj = xe_bo_create_pin_map(xe, xe_device_get_root_tile(xe), NULL, size,
+ ttm_bo_type_kernel, XE_BO_FLAG_SCANOUT |
+ XE_BO_FLAG_VRAM_IF_DGFX(xe_device_get_root_tile(xe)) |
+ XE_BO_FLAG_PINNED);
}
if (IS_ERR(obj)) {
- drm_err(&dev_priv->drm, "failed to allocate framebuffer (%pe)\n", obj);
+ drm_err(&xe->drm, "failed to allocate framebuffer (%pe)\n", obj);
fb = ERR_PTR(-ENOMEM);
goto err;
}
@@ -70,10 +68,11 @@ struct drm_framebuffer *intel_fbdev_fb_alloc(struct drm_fb_helper *helper,
}
drm_gem_object_put(intel_bo_to_drm_bo(obj));
- return fb;
+
+ return to_intel_framebuffer(fb);
err:
- return fb;
+ return ERR_CAST(fb);
}
int intel_fbdev_fb_fill_info(struct drm_i915_private *i915, struct fb_info *info,
diff --git a/drivers/gpu/drm/xe/display/intel_fbdev_fb.h b/drivers/gpu/drm/xe/display/intel_fbdev_fb.h
deleted file mode 100644
index ea186772e0bb..000000000000
--- a/drivers/gpu/drm/xe/display/intel_fbdev_fb.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/*
- * Copyright © 2023 Intel Corporation
- */
-
-#ifndef __INTEL_FBDEV_FB_H__
-#define __INTEL_FBDEV_FB_H__
-
-struct drm_fb_helper;
-struct drm_fb_helper_surface_size;
-struct drm_i915_gem_object;
-struct drm_i915_private;
-struct fb_info;
-struct i915_vma;
-
-struct drm_framebuffer *intel_fbdev_fb_alloc(struct drm_fb_helper *helper,
- struct drm_fb_helper_surface_size *sizes);
-int intel_fbdev_fb_fill_info(struct drm_i915_private *i915, struct fb_info *info,
- struct drm_i915_gem_object *obj, struct i915_vma *vma);
-
-#endif
diff --git a/drivers/gpu/drm/xe/display/xe_display.c b/drivers/gpu/drm/xe/display/xe_display.c
index 0de0566e5b39..8b83dcff72e1 100644
--- a/drivers/gpu/drm/xe/display/xe_display.c
+++ b/drivers/gpu/drm/xe/display/xe_display.c
@@ -23,6 +23,7 @@
#include "intel_display_types.h"
#include "intel_dmc.h"
#include "intel_dp.h"
+#include "intel_encoder.h"
#include "intel_fbdev.h"
#include "intel_hdcp.h"
#include "intel_hotplug.h"
@@ -96,9 +97,6 @@ int xe_display_create(struct xe_device *xe)
xe->display.hotplug.dp_wq = alloc_ordered_workqueue("xe-dp", 0);
- drmm_mutex_init(&xe->drm, &xe->sb_lock);
- xe->enabled_irq_mask = ~0;
-
return drmm_add_action_or_reset(&xe->drm, display_destroy, NULL);
}
@@ -126,15 +124,14 @@ int xe_display_init_nommio(struct xe_device *xe)
return drmm_add_action_or_reset(&xe->drm, xe_display_fini_nommio, xe);
}
-static void xe_display_fini_noirq(struct drm_device *dev, void *dummy)
+static void xe_display_fini_noirq(void *arg)
{
- struct xe_device *xe = to_xe_device(dev);
+ struct xe_device *xe = arg;
if (!xe->info.enable_display)
return;
intel_display_driver_remove_noirq(xe);
- intel_power_domains_driver_remove(xe);
}
int xe_display_init_noirq(struct xe_device *xe)
@@ -163,12 +160,12 @@ int xe_display_init_noirq(struct xe_device *xe)
if (err)
return err;
- return drmm_add_action_or_reset(&xe->drm, xe_display_fini_noirq, NULL);
+ return devm_add_action_or_reset(xe->drm.dev, xe_display_fini_noirq, xe);
}
-static void xe_display_fini_noaccel(struct drm_device *dev, void *dummy)
+static void xe_display_fini_noaccel(void *arg)
{
- struct xe_device *xe = to_xe_device(dev);
+ struct xe_device *xe = arg;
if (!xe->info.enable_display)
return;
@@ -187,7 +184,7 @@ int xe_display_init_noaccel(struct xe_device *xe)
if (err)
return err;
- return drmm_add_action_or_reset(&xe->drm, xe_display_fini_noaccel, NULL);
+ return devm_add_action_or_reset(xe->drm.dev, xe_display_fini_noaccel, xe);
}
int xe_display_init(struct xe_device *xe)
@@ -235,8 +232,6 @@ void xe_display_driver_remove(struct xe_device *xe)
return;
intel_display_driver_remove(xe);
-
- intel_display_device_remove(xe);
}
/* IRQ-related functions */
@@ -276,21 +271,6 @@ void xe_display_irq_postinstall(struct xe_device *xe, struct xe_gt *gt)
gen11_de_irq_postinstall(xe);
}
-static void intel_suspend_encoders(struct xe_device *xe)
-{
- struct drm_device *dev = &xe->drm;
- struct intel_encoder *encoder;
-
- if (has_display(xe))
- return;
-
- drm_modeset_lock_all(dev);
- for_each_intel_encoder(dev, encoder)
- if (encoder->suspend)
- encoder->suspend(encoder);
- drm_modeset_unlock_all(dev);
-}
-
static bool suspend_to_idle(void)
{
#if IS_ENABLED(CONFIG_ACPI_SLEEP)
@@ -300,7 +280,7 @@ static bool suspend_to_idle(void)
return false;
}
-void xe_display_pm_suspend(struct xe_device *xe)
+void xe_display_pm_suspend(struct xe_device *xe, bool runtime)
{
bool s2idle = suspend_to_idle();
if (!xe->info.enable_display)
@@ -314,13 +294,14 @@ void xe_display_pm_suspend(struct xe_device *xe)
if (has_display(xe))
drm_kms_helper_poll_disable(&xe->drm);
- intel_display_driver_suspend(xe);
+ if (!runtime)
+ intel_display_driver_suspend(xe);
intel_dp_mst_suspend(xe);
intel_hpd_cancel_work(xe);
- intel_suspend_encoders(xe);
+ intel_encoder_suspend_all(&xe->display);
intel_opregion_suspend(xe, s2idle ? PCI_D1 : PCI_D3cold);
@@ -350,7 +331,7 @@ void xe_display_pm_resume_early(struct xe_device *xe)
intel_power_domains_resume(xe);
}
-void xe_display_pm_resume(struct xe_device *xe)
+void xe_display_pm_resume(struct xe_device *xe, bool runtime)
{
if (!xe->info.enable_display)
return;
@@ -365,7 +346,8 @@ void xe_display_pm_resume(struct xe_device *xe)
/* MST sideband requires HPD interrupts enabled */
intel_dp_mst_resume(xe);
- intel_display_driver_resume(xe);
+ if (!runtime)
+ intel_display_driver_resume(xe);
intel_hpd_poll_disable(xe);
if (has_display(xe))
@@ -378,17 +360,31 @@ void xe_display_pm_resume(struct xe_device *xe)
intel_power_domains_enable(xe);
}
-void xe_display_probe(struct xe_device *xe)
+static void display_device_remove(struct drm_device *dev, void *arg)
{
+ struct xe_device *xe = arg;
+
+ intel_display_device_remove(xe);
+}
+
+int xe_display_probe(struct xe_device *xe)
+{
+ int err;
+
if (!xe->info.enable_display)
goto no_display;
intel_display_device_probe(xe);
+ err = drmm_add_action_or_reset(&xe->drm, display_device_remove, xe);
+ if (err)
+ return err;
+
if (has_display(xe))
- return;
+ return 0;
no_display:
xe->info.enable_display = false;
unset_display_features(xe);
+ return 0;
}
diff --git a/drivers/gpu/drm/xe/display/xe_display.h b/drivers/gpu/drm/xe/display/xe_display.h
index 710e56180b52..000fb5799df5 100644
--- a/drivers/gpu/drm/xe/display/xe_display.h
+++ b/drivers/gpu/drm/xe/display/xe_display.h
@@ -18,7 +18,7 @@ void xe_display_driver_remove(struct xe_device *xe);
int xe_display_create(struct xe_device *xe);
-void xe_display_probe(struct xe_device *xe);
+int xe_display_probe(struct xe_device *xe);
int xe_display_init_nommio(struct xe_device *xe);
int xe_display_init_noirq(struct xe_device *xe);
@@ -34,10 +34,10 @@ void xe_display_irq_enable(struct xe_device *xe, u32 gu_misc_iir);
void xe_display_irq_reset(struct xe_device *xe);
void xe_display_irq_postinstall(struct xe_device *xe, struct xe_gt *gt);
-void xe_display_pm_suspend(struct xe_device *xe);
+void xe_display_pm_suspend(struct xe_device *xe, bool runtime);
void xe_display_pm_suspend_late(struct xe_device *xe);
void xe_display_pm_resume_early(struct xe_device *xe);
-void xe_display_pm_resume(struct xe_device *xe);
+void xe_display_pm_resume(struct xe_device *xe, bool runtime);
#else
@@ -47,7 +47,7 @@ static inline void xe_display_driver_remove(struct xe_device *xe) {}
static inline int xe_display_create(struct xe_device *xe) { return 0; }
-static inline void xe_display_probe(struct xe_device *xe) { }
+static inline int xe_display_probe(struct xe_device *xe) { return 0; }
static inline int xe_display_init_nommio(struct xe_device *xe) { return 0; }
static inline int xe_display_init_noirq(struct xe_device *xe) { return 0; }
@@ -63,10 +63,10 @@ static inline void xe_display_irq_enable(struct xe_device *xe, u32 gu_misc_iir)
static inline void xe_display_irq_reset(struct xe_device *xe) {}
static inline void xe_display_irq_postinstall(struct xe_device *xe, struct xe_gt *gt) {}
-static inline void xe_display_pm_suspend(struct xe_device *xe) {}
+static inline void xe_display_pm_suspend(struct xe_device *xe, bool runtime) {}
static inline void xe_display_pm_suspend_late(struct xe_device *xe) {}
static inline void xe_display_pm_resume_early(struct xe_device *xe) {}
-static inline void xe_display_pm_resume(struct xe_device *xe) {}
+static inline void xe_display_pm_resume(struct xe_device *xe, bool runtime) {}
#endif /* CONFIG_DRM_XE_DISPLAY */
#endif /* _XE_DISPLAY_H_ */
diff --git a/drivers/gpu/drm/xe/display/xe_dsb_buffer.c b/drivers/gpu/drm/xe/display/xe_dsb_buffer.c
index 44c9fd2143cc..9e860c61f4b3 100644
--- a/drivers/gpu/drm/xe/display/xe_dsb_buffer.c
+++ b/drivers/gpu/drm/xe/display/xe_dsb_buffer.c
@@ -3,7 +3,6 @@
* Copyright 2023, Intel Corporation.
*/
-#include "i915_drv.h"
#include "i915_vma.h"
#include "intel_display_types.h"
#include "intel_dsb_buffer.h"
@@ -34,18 +33,18 @@ void intel_dsb_buffer_memset(struct intel_dsb_buffer *dsb_buf, u32 idx, u32 val,
bool intel_dsb_buffer_create(struct intel_crtc *crtc, struct intel_dsb_buffer *dsb_buf, size_t size)
{
- struct drm_i915_private *i915 = to_i915(crtc->base.dev);
- struct drm_i915_gem_object *obj;
+ struct xe_device *xe = to_xe_device(crtc->base.dev);
+ struct xe_bo *obj;
struct i915_vma *vma;
vma = kzalloc(sizeof(*vma), GFP_KERNEL);
if (!vma)
return false;
- obj = xe_bo_create_pin_map(i915, xe_device_get_root_tile(i915),
+ obj = xe_bo_create_pin_map(xe, xe_device_get_root_tile(xe),
NULL, PAGE_ALIGN(size),
ttm_bo_type_kernel,
- XE_BO_FLAG_VRAM_IF_DGFX(xe_device_get_root_tile(i915)) |
+ XE_BO_FLAG_VRAM_IF_DGFX(xe_device_get_root_tile(xe)) |
XE_BO_FLAG_GGTT);
if (IS_ERR(obj)) {
kfree(vma);
diff --git a/drivers/gpu/drm/xe/display/xe_fb_pin.c b/drivers/gpu/drm/xe/display/xe_fb_pin.c
index 3e1ae37c4c8b..423f367c7065 100644
--- a/drivers/gpu/drm/xe/display/xe_fb_pin.c
+++ b/drivers/gpu/drm/xe/display/xe_fb_pin.c
@@ -3,17 +3,17 @@
* Copyright © 2021 Intel Corporation
*/
-#include "i915_drv.h"
+#include <drm/ttm/ttm_bo.h>
+
#include "intel_display_types.h"
#include "intel_dpt.h"
#include "intel_fb.h"
#include "intel_fb_pin.h"
+#include "xe_bo.h"
#include "xe_ggtt.h"
#include "xe_gt.h"
#include "xe_pm.h"
-#include <drm/ttm/ttm_bo.h>
-
static void
write_dpt_rotated(struct xe_bo *bo, struct iosys_map *map, u32 *dpt_ofs, u32 bo_ofs,
u32 width, u32 height, u32 src_stride, u32 dst_stride)
@@ -77,7 +77,7 @@ write_dpt_remapped(struct xe_bo *bo, struct iosys_map *map, u32 *dpt_ofs,
*dpt_ofs = ALIGN(*dpt_ofs, 4096);
}
-static int __xe_pin_fb_vma_dpt(struct intel_framebuffer *fb,
+static int __xe_pin_fb_vma_dpt(const struct intel_framebuffer *fb,
const struct i915_gtt_view *view,
struct i915_vma *vma)
{
@@ -171,7 +171,7 @@ write_ggtt_rotated(struct xe_bo *bo, struct xe_ggtt *ggtt, u32 *ggtt_ofs, u32 bo
u64 pte = ggtt->pt_ops->pte_encode_bo(bo, src_idx * XE_PAGE_SIZE,
xe->pat.idx[XE_CACHE_NONE]);
- xe_ggtt_set_pte(ggtt, *ggtt_ofs, pte);
+ ggtt->pt_ops->ggtt_set_pte(ggtt, *ggtt_ofs, pte);
*ggtt_ofs += XE_PAGE_SIZE;
src_idx -= src_stride;
}
@@ -181,7 +181,7 @@ write_ggtt_rotated(struct xe_bo *bo, struct xe_ggtt *ggtt, u32 *ggtt_ofs, u32 bo
}
}
-static int __xe_pin_fb_vma_ggtt(struct intel_framebuffer *fb,
+static int __xe_pin_fb_vma_ggtt(const struct intel_framebuffer *fb,
const struct i915_gtt_view *view,
struct i915_vma *vma)
{
@@ -217,7 +217,7 @@ static int __xe_pin_fb_vma_ggtt(struct intel_framebuffer *fb,
u64 pte = ggtt->pt_ops->pte_encode_bo(bo, x,
xe->pat.idx[XE_CACHE_NONE]);
- xe_ggtt_set_pte(ggtt, vma->node.start + x, pte);
+ ggtt->pt_ops->ggtt_set_pte(ggtt, vma->node.start + x, pte);
}
} else {
u32 i, ggtt_ofs;
@@ -249,7 +249,7 @@ out:
return ret;
}
-static struct i915_vma *__xe_pin_fb_vma(struct intel_framebuffer *fb,
+static struct i915_vma *__xe_pin_fb_vma(const struct intel_framebuffer *fb,
const struct i915_gtt_view *view)
{
struct drm_device *dev = fb->base.dev;
@@ -333,18 +333,19 @@ static void __xe_unpin_fb_vma(struct i915_vma *vma)
}
struct i915_vma *
-intel_pin_and_fence_fb_obj(struct drm_framebuffer *fb,
- bool phys_cursor,
- const struct i915_gtt_view *view,
- bool uses_fence,
- unsigned long *out_flags)
+intel_fb_pin_to_ggtt(const struct drm_framebuffer *fb,
+ const struct i915_gtt_view *view,
+ unsigned int alignment,
+ unsigned int phys_alignment,
+ bool uses_fence,
+ unsigned long *out_flags)
{
*out_flags = 0;
return __xe_pin_fb_vma(to_intel_framebuffer(fb), view);
}
-void intel_unpin_fb_vma(struct i915_vma *vma, unsigned long flags)
+void intel_fb_unpin_vma(struct i915_vma *vma, unsigned long flags)
{
__xe_unpin_fb_vma(vma);
}
diff --git a/drivers/gpu/drm/xe/display/xe_hdcp_gsc.c b/drivers/gpu/drm/xe/display/xe_hdcp_gsc.c
index d46f87a039f2..14b8b4278317 100644
--- a/drivers/gpu/drm/xe/display/xe_hdcp_gsc.c
+++ b/drivers/gpu/drm/xe/display/xe_hdcp_gsc.c
@@ -4,7 +4,7 @@
*/
#include <drm/drm_print.h>
-#include <drm/i915_hdcp_interface.h>
+#include <drm/intel/i915_hdcp_interface.h>
#include <linux/delay.h>
#include "abi/gsc_command_header_abi.h"
@@ -13,6 +13,7 @@
#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_gt.h"
diff --git a/drivers/gpu/drm/xe/display/xe_plane_initial.c b/drivers/gpu/drm/xe/display/xe_plane_initial.c
index 9693c56d386b..5eccd6abb3ef 100644
--- a/drivers/gpu/drm/xe/display/xe_plane_initial.c
+++ b/drivers/gpu/drm/xe/display/xe_plane_initial.c
@@ -9,7 +9,6 @@
#include "regs/xe_gtt_defs.h"
#include "xe_ggtt.h"
-#include "i915_drv.h"
#include "intel_atomic_plane.h"
#include "intel_crtc.h"
#include "intel_display.h"
@@ -18,16 +17,17 @@
#include "intel_fb_pin.h"
#include "intel_frontbuffer.h"
#include "intel_plane_initial.h"
+#include "xe_bo.h"
static bool
intel_reuse_initial_plane_obj(struct intel_crtc *this,
const struct intel_initial_plane_config plane_configs[],
struct drm_framebuffer **fb)
{
- struct drm_i915_private *i915 = to_i915(this->base.dev);
+ struct xe_device *xe = to_xe_device(this->base.dev);
struct intel_crtc *crtc;
- for_each_intel_crtc(&i915->drm, 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 =
@@ -134,8 +134,7 @@ static bool
intel_alloc_initial_plane_obj(struct intel_crtc *crtc,
struct intel_initial_plane_config *plane_config)
{
- struct drm_device *dev = crtc->base.dev;
- struct drm_i915_private *dev_priv = to_i915(dev);
+ 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;
@@ -147,9 +146,9 @@ intel_alloc_initial_plane_obj(struct intel_crtc *crtc,
case I915_FORMAT_MOD_4_TILED:
break;
default:
- drm_dbg(&dev_priv->drm,
- "Unsupported modifier for initial FB: 0x%llx\n",
- fb->modifier);
+ drm_dbg_kms(&xe->drm,
+ "Unsupported modifier for initial FB: 0x%llx\n",
+ fb->modifier);
return false;
}
@@ -160,13 +159,13 @@ intel_alloc_initial_plane_obj(struct intel_crtc *crtc,
mode_cmd.modifier[0] = fb->modifier;
mode_cmd.flags = DRM_MODE_FB_MODIFIERS;
- bo = initial_plane_bo(dev_priv, plane_config);
+ bo = initial_plane_bo(xe, plane_config);
if (!bo)
return false;
if (intel_framebuffer_init(to_intel_framebuffer(fb),
bo, &mode_cmd)) {
- drm_dbg_kms(&dev_priv->drm, "intel fb init failed\n");
+ drm_dbg_kms(&xe->drm, "intel fb init failed\n");
goto err_bo;
}
/* Reference handed over to fb */
@@ -211,8 +210,8 @@ intel_find_initial_plane_obj(struct intel_crtc *crtc,
intel_fb_fill_view(to_intel_framebuffer(fb),
plane_state->uapi.rotation, &plane_state->view);
- vma = intel_pin_and_fence_fb_obj(fb, false, &plane_state->view.gtt,
- false, &plane_state->flags);
+ vma = intel_fb_pin_to_ggtt(fb, &plane_state->view.gtt,
+ 0, 0, false, &plane_state->flags);
if (IS_ERR(vma))
goto nofb;
diff --git a/drivers/gpu/drm/xe/display/xe_tdf.c b/drivers/gpu/drm/xe/display/xe_tdf.c
new file mode 100644
index 000000000000..2c0d4e144e09
--- /dev/null
+++ b/drivers/gpu/drm/xe/display/xe_tdf.c
@@ -0,0 +1,13 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2024 Intel Corporation
+ */
+
+#include "xe_device.h"
+#include "intel_display_types.h"
+#include "intel_tdf.h"
+
+void intel_td_flush(struct drm_i915_private *i915)
+{
+ xe_device_td_flush(i915);
+}
diff --git a/drivers/gpu/drm/xe/regs/xe_gpu_commands.h b/drivers/gpu/drm/xe/instructions/xe_gpu_commands.h
index a255946b6f77..a255946b6f77 100644
--- a/drivers/gpu/drm/xe/regs/xe_gpu_commands.h
+++ b/drivers/gpu/drm/xe/instructions/xe_gpu_commands.h
diff --git a/drivers/gpu/drm/xe/instructions/xe_mi_commands.h b/drivers/gpu/drm/xe/instructions/xe_mi_commands.h
index c74ceb550dce..10ec2920d31b 100644
--- a/drivers/gpu/drm/xe/instructions/xe_mi_commands.h
+++ b/drivers/gpu/drm/xe/instructions/xe_mi_commands.h
@@ -45,6 +45,7 @@
#define MI_LRI_MMIO_REMAP_EN REG_BIT(17)
#define MI_LRI_NUM_REGS(x) XE_INSTR_NUM_DW(2 * (x) + 1)
#define MI_LRI_FORCE_POSTED REG_BIT(12)
+#define MI_LRI_LEN(x) (((x) & 0xff) + 1)
#define MI_FLUSH_DW __MI_INSTR(0x26)
#define MI_FLUSH_DW_STORE_INDEX REG_BIT(21)
@@ -59,6 +60,10 @@
#define MI_LOAD_REGISTER_MEM (__MI_INSTR(0x29) | XE_INSTR_NUM_DW(4))
#define MI_LRM_USE_GGTT REG_BIT(22)
+#define MI_COPY_MEM_MEM (__MI_INSTR(0x2e) | XE_INSTR_NUM_DW(5))
+#define MI_COPY_MEM_MEM_SRC_GGTT REG_BIT(22)
+#define MI_COPY_MEM_MEM_DST_GGTT REG_BIT(21)
+
#define MI_BATCH_BUFFER_START __MI_INSTR(0x31)
#endif
diff --git a/drivers/gpu/drm/xe/regs/xe_bars.h b/drivers/gpu/drm/xe/regs/xe_bars.h
new file mode 100644
index 000000000000..ce05b6ae832f
--- /dev/null
+++ b/drivers/gpu/drm/xe/regs/xe_bars.h
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2024 Intel Corporation
+ */
+#ifndef _XE_BARS_H_
+#define _XE_BARS_H_
+
+#define GTTMMADR_BAR 0 /* MMIO + GTT */
+#define LMEM_BAR 2 /* VRAM */
+
+#endif
diff --git a/drivers/gpu/drm/xe/regs/xe_engine_regs.h b/drivers/gpu/drm/xe/regs/xe_engine_regs.h
index af71b87d8030..c38db2a74614 100644
--- a/drivers/gpu/drm/xe/regs/xe_engine_regs.h
+++ b/drivers/gpu/drm/xe/regs/xe_engine_regs.h
@@ -44,9 +44,10 @@
#define GSCCS_RING_BASE 0x11a000
#define RING_TAIL(base) XE_REG((base) + 0x30)
+#define TAIL_ADDR REG_GENMASK(20, 3)
#define RING_HEAD(base) XE_REG((base) + 0x34)
-#define HEAD_ADDR 0x001FFFFC
+#define HEAD_ADDR REG_GENMASK(20, 2)
#define RING_START(base) XE_REG((base) + 0x38)
@@ -54,6 +55,8 @@
#define RING_CTL_SIZE(size) ((size) - PAGE_SIZE) /* in bytes -> pages */
#define RING_CTL_SIZE(size) ((size) - PAGE_SIZE) /* in bytes -> pages */
+#define RING_START_UDW(base) XE_REG((base) + 0x48)
+
#define RING_PSMI_CTL(base) XE_REG((base) + 0x50, XE_REG_OPTION_MASKED)
#define RC_SEMA_IDLE_MSG_DISABLE REG_BIT(12)
#define WAIT_FOR_EVENT_POWER_DOWN_DISABLE REG_BIT(7)
@@ -65,6 +68,7 @@
#define RING_ACTHD_UDW(base) XE_REG((base) + 0x5c)
#define RING_DMA_FADD_UDW(base) XE_REG((base) + 0x60)
#define RING_IPEHR(base) XE_REG((base) + 0x68)
+#define RING_INSTDONE(base) XE_REG((base) + 0x6c)
#define RING_ACTHD(base) XE_REG((base) + 0x74)
#define RING_DMA_FADD(base) XE_REG((base) + 0x78)
#define RING_HWS_PGA(base) XE_REG((base) + 0x80)
@@ -108,6 +112,8 @@
#define FF_DOP_CLOCK_GATE_DISABLE REG_BIT(1)
#define REPLAY_MODE_GRANULARITY REG_BIT(0)
+#define INDIRECT_RING_STATE(base) XE_REG((base) + 0x108)
+
#define RING_BBADDR(base) XE_REG((base) + 0x140)
#define RING_BBADDR_UDW(base) XE_REG((base) + 0x168)
@@ -123,6 +129,9 @@
#define RING_EXECLIST_STATUS_HI(base) XE_REG((base) + 0x234 + 4)
#define RING_CONTEXT_CONTROL(base) XE_REG((base) + 0x244, XE_REG_OPTION_MASKED)
+#define CTX_CTRL_OAC_CONTEXT_ENABLE REG_BIT(8)
+#define CTX_CTRL_RUN_ALONE REG_BIT(7)
+#define CTX_CTRL_INDIRECT_RING_STATE_ENABLE REG_BIT(4)
#define CTX_CTRL_INHIBIT_SYN_CTX_SWITCH REG_BIT(3)
#define CTX_CTRL_ENGINE_CTX_RESTORE_INHIBIT REG_BIT(0)
@@ -135,7 +144,6 @@
#define RING_VALID_MASK 0x00000001
#define RING_VALID 0x00000001
#define STOP_RING REG_BIT(8)
-#define TAIL_ADDR 0x001FFFF8
#define RING_CTX_TIMESTAMP(base) XE_REG((base) + 0x3a8)
#define CSBE_DEBUG_STATUS(base) XE_REG((base) + 0x3fc)
diff --git a/drivers/gpu/drm/xe/regs/xe_gt_regs.h b/drivers/gpu/drm/xe/regs/xe_gt_regs.h
index 94445810ccc9..d44564bad009 100644
--- a/drivers/gpu/drm/xe/regs/xe_gt_regs.h
+++ b/drivers/gpu/drm/xe/regs/xe_gt_regs.h
@@ -59,6 +59,27 @@
#define XELP_GLOBAL_MOCS(i) XE_REG(0x4000 + (i) * 4)
#define XEHP_GLOBAL_MOCS(i) XE_REG_MCR(0x4000 + (i) * 4)
+#define LE_SSE_MASK REG_GENMASK(18, 17)
+#define LE_SSE(value) REG_FIELD_PREP(LE_SSE_MASK, value)
+#define LE_COS_MASK REG_GENMASK(16, 15)
+#define LE_COS(value) REG_FIELD_PREP(LE_COS_MASK)
+#define LE_SCF_MASK REG_BIT(14)
+#define LE_SCF(value) REG_FIELD_PREP(LE_SCF_MASK, value)
+#define LE_PFM_MASK REG_GENMASK(13, 11)
+#define LE_PFM(value) REG_FIELD_PREP(LE_PFM_MASK, value)
+#define LE_SCC_MASK REG_GENMASK(10, 8)
+#define LE_SCC(value) REG_FIELD_PREP(LE_SCC_MASK, value)
+#define LE_RSC_MASK REG_BIT(7)
+#define LE_RSC(value) REG_FIELD_PREP(LE_RSC_MASK, value)
+#define LE_AOM_MASK REG_BIT(6)
+#define LE_AOM(value) REG_FIELD_PREP(LE_AOM_MASK, value)
+#define LE_LRUM_MASK REG_GENMASK(5, 4)
+#define LE_LRUM(value) REG_FIELD_PREP(LE_LRUM_MASK, value)
+#define LE_TGT_CACHE_MASK REG_GENMASK(3, 2)
+#define LE_TGT_CACHE(value) REG_FIELD_PREP(LE_TGT_CACHE_MASK, value)
+#define LE_CACHEABILITY_MASK REG_GENMASK(1, 0)
+#define LE_CACHEABILITY(value) REG_FIELD_PREP(LE_CACHEABILITY_MASK, value)
+
#define CCS_AUX_INV XE_REG(0x4208)
#define VD0_AUX_INV XE_REG(0x4218)
@@ -98,6 +119,8 @@
#define FF_MODE2_TDS_TIMER_MASK REG_GENMASK(23, 16)
#define FF_MODE2_TDS_TIMER_128 REG_FIELD_PREP(FF_MODE2_TDS_TIMER_MASK, 4)
+#define XEHPG_INSTDONE_GEOM_SVGUNIT XE_REG_MCR(0x666c)
+
#define CACHE_MODE_1 XE_REG(0x7004, XE_REG_OPTION_MASKED)
#define MSAA_OPTIMIZATION_REDUC_DISABLE REG_BIT(11)
@@ -115,6 +138,14 @@
#define FLSH_IGNORES_PSD REG_BIT(10)
#define FD_END_COLLECT REG_BIT(5)
+#define SC_INSTDONE XE_REG(0x7100)
+#define SC_INSTDONE_EXTRA XE_REG(0x7104)
+#define SC_INSTDONE_EXTRA2 XE_REG(0x7108)
+
+#define XEHPG_SC_INSTDONE XE_REG_MCR(0x7100)
+#define XEHPG_SC_INSTDONE_EXTRA XE_REG_MCR(0x7104)
+#define XEHPG_SC_INSTDONE_EXTRA2 XE_REG_MCR(0x7108)
+
#define COMMON_SLICE_CHICKEN4 XE_REG(0x7300, XE_REG_OPTION_MASKED)
#define DISABLE_TDC_LOAD_BALANCING_CALC REG_BIT(6)
@@ -139,6 +170,8 @@
#define SQCNT1 XE_REG_MCR(0x8718)
#define XELPMP_SQCNT1 XE_REG(0x8718)
+#define SQCNT1_PMON_ENABLE REG_BIT(30)
+#define SQCNT1_OABPC REG_BIT(29)
#define ENFORCE_RAR REG_BIT(23)
#define XEHP_SQCM XE_REG_MCR(0x8724)
@@ -173,8 +206,11 @@
#define MAX_MSLICES 4
#define MEML3_EN_MASK REG_GENMASK(3, 0)
+#define MIRROR_FUSE1 XE_REG(0x911c)
+
#define XELP_EU_ENABLE XE_REG(0x9134) /* "_DISABLE" on Xe_LP */
#define XELP_EU_MASK REG_GENMASK(7, 0)
+#define XELP_GT_SLICE_ENABLE XE_REG(0x9138)
#define XELP_GT_GEOMETRY_DSS_ENABLE XE_REG(0x913c)
#define GT_VEBOX_VDBOX_DISABLE XE_REG(0x9140)
@@ -275,6 +311,8 @@
#define RC_CTL_RC6_ENABLE REG_BIT(18)
#define RC_STATE XE_REG(0xa094)
#define RC_IDLE_HYSTERSIS XE_REG(0xa0ac)
+#define MEDIA_POWERGATE_IDLE_HYSTERESIS XE_REG(0xa0c4)
+#define RENDER_POWERGATE_IDLE_HYSTERESIS XE_REG(0xa0c8)
#define PMINTRMSK XE_REG(0xa168)
#define PMINTR_DISABLE_REDIRECT_TO_GUC REG_BIT(31)
@@ -282,11 +320,11 @@
#define FORCEWAKE_GT XE_REG(0xa188)
-#define PG_ENABLE XE_REG(0xa210)
-#define VD2_MFXVDENC_POWERGATE_ENABLE REG_BIT(8)
-#define VD2_HCP_POWERGATE_ENABLE REG_BIT(7)
-#define VD0_MFXVDENC_POWERGATE_ENABLE REG_BIT(4)
-#define VD0_HCP_POWERGATE_ENABLE REG_BIT(3)
+#define POWERGATE_ENABLE XE_REG(0xa210)
+#define RENDER_POWERGATE_ENABLE REG_BIT(0)
+#define MEDIA_POWERGATE_ENABLE REG_BIT(1)
+#define VDN_HCP_POWERGATE_ENABLE(n) REG_BIT(3 + 2 * (n))
+#define VDN_MFXVDENC_POWERGATE_ENABLE(n) REG_BIT(4 + 2 * (n))
#define CTC_MODE XE_REG(0xa26c)
#define CTC_SHIFT_PARAMETER_MASK REG_GENMASK(2, 1)
@@ -301,9 +339,24 @@
#define XEHPC_OVRLSCCC REG_BIT(0)
/* L3 Cache Control */
+#define LNCFCMOCS_REG_COUNT 32
#define XELP_LNCFCMOCS(i) XE_REG(0xb020 + (i) * 4)
#define XEHP_LNCFCMOCS(i) XE_REG_MCR(0xb020 + (i) * 4)
-#define LNCFCMOCS_REG_COUNT 32
+#define L3_UPPER_LKUP_MASK REG_BIT(23)
+#define L3_UPPER_GLBGO_MASK REG_BIT(22)
+#define L3_UPPER_IDX_CACHEABILITY_MASK REG_GENMASK(21, 20)
+#define L3_UPPER_IDX_SCC_MASK REG_GENMASK(19, 17)
+#define L3_UPPER_IDX_ESC_MASK REG_BIT(16)
+#define L3_LKUP_MASK REG_BIT(7)
+#define L3_LKUP(value) REG_FIELD_PREP(L3_LKUP_MASK, value)
+#define L3_GLBGO_MASK REG_BIT(6)
+#define L3_GLBGO(value) REG_FIELD_PREP(L3_GLBGO_MASK, value)
+#define L3_CACHEABILITY_MASK REG_GENMASK(5, 4)
+#define L3_CACHEABILITY(value) REG_FIELD_PREP(L3_CACHEABILITY_MASK, value)
+#define L3_SCC_MASK REG_GENMASK(3, 1)
+#define L3_SCC(value) REG_FIELD_PREP(L3_SCC_MASK, value)
+#define L3_ESC_MASK REG_BIT(0)
+#define L3_ESC(value) REG_FIELD_PREP(L3_ESC_MASK, value)
#define XEHP_L3NODEARBCFG XE_REG_MCR(0xb0b4)
#define XEHP_LNESPARE REG_BIT(19)
@@ -321,6 +374,9 @@
#define XE2LPM_L3SQCREG5 XE_REG_MCR(0xb658)
+#define XE2_TDF_CTRL XE_REG(0xb418)
+#define TRANSIENT_FLUSH_REQUEST REG_BIT(0)
+
#define XEHP_MERT_MOD_CTRL XE_REG_MCR(0xcf28)
#define RENDER_MOD_CTRL XE_REG_MCR(0xcf2c)
#define COMP_MOD_CTRL XE_REG_MCR(0xcf30)
@@ -342,6 +398,9 @@
#define HALF_SLICE_CHICKEN5 XE_REG_MCR(0xe188, XE_REG_OPTION_MASKED)
#define DISABLE_SAMPLE_G_PERFORMANCE REG_BIT(0)
+#define SAMPLER_INSTDONE XE_REG_MCR(0xe160)
+#define ROW_INSTDONE XE_REG_MCR(0xe164)
+
#define SAMPLER_MODE XE_REG_MCR(0xe18c, XE_REG_OPTION_MASKED)
#define ENABLE_SMALLPL REG_BIT(15)
#define SC_DISABLE_POWER_OPTIMIZATION_EBB REG_BIT(9)
@@ -350,6 +409,7 @@
#define HALF_SLICE_CHICKEN7 XE_REG_MCR(0xe194, XE_REG_OPTION_MASKED)
#define DG2_DISABLE_ROUND_ENABLE_ALLOW_FOR_SSLA REG_BIT(15)
+#define CLEAR_OPTIMIZATION_DISABLE REG_BIT(6)
#define CACHE_MODE_SS XE_REG_MCR(0xe420, XE_REG_OPTION_MASKED)
#define DISABLE_ECC REG_BIT(5)
@@ -374,6 +434,7 @@
#define ROW_CHICKEN XE_REG_MCR(0xe4f0, XE_REG_OPTION_MASKED)
#define UGM_BACKUP_MODE REG_BIT(13)
#define MDQ_ARBITRATION_MODE REG_BIT(12)
+#define STALL_DOP_GATING_DISABLE REG_BIT(5)
#define EARLY_EOT_DIS REG_BIT(1)
#define ROW_CHICKEN2 XE_REG_MCR(0xe4f4, XE_REG_OPTION_MASKED)
@@ -432,9 +493,11 @@
((ccs) << ((cslice) * CCS_MODE_CSLICE_WIDTH))
#define FORCEWAKE_ACK_GT XE_REG(0x130044)
-#define FORCEWAKE_KERNEL BIT(0)
-#define FORCEWAKE_USER BIT(1)
-#define FORCEWAKE_KERNEL_FALLBACK BIT(15)
+
+/* Applicable for all FORCEWAKE_DOMAIN and FORCEWAKE_ACK_DOMAIN regs */
+#define FORCEWAKE_KERNEL 0
+#define FORCEWAKE_MT(bit) BIT(bit)
+#define FORCEWAKE_MT_MASK(bit) BIT((bit) + 16)
#define MTL_MEDIA_PERF_LIMIT_REASONS XE_REG(0x138030)
#define MTL_MEDIA_MC6 XE_REG(0x138048)
diff --git a/drivers/gpu/drm/xe/regs/xe_guc_regs.h b/drivers/gpu/drm/xe/regs/xe_guc_regs.h
index 11682e675e0f..a5fd14307f94 100644
--- a/drivers/gpu/drm/xe/regs/xe_guc_regs.h
+++ b/drivers/gpu/drm/xe/regs/xe_guc_regs.h
@@ -40,6 +40,8 @@
#define GS_BOOTROM_JUMP_PASSED REG_FIELD_PREP(GS_BOOTROM_MASK, 0x76)
#define GS_MIA_IN_RESET REG_BIT(0)
+#define GUC_HEADER_INFO XE_REG(0xc014)
+
#define GUC_WOPCM_SIZE XE_REG(0xc050)
#define GUC_WOPCM_SIZE_MASK REG_GENMASK(31, 12)
#define GUC_WOPCM_SIZE_LOCKED REG_BIT(0)
diff --git a/drivers/gpu/drm/xe/regs/xe_lrc_layout.h b/drivers/gpu/drm/xe/regs/xe_lrc_layout.h
index 1825d8f79db6..045dfd09db99 100644
--- a/drivers/gpu/drm/xe/regs/xe_lrc_layout.h
+++ b/drivers/gpu/drm/xe/regs/xe_lrc_layout.h
@@ -11,6 +11,8 @@
#define CTX_RING_TAIL (0x06 + 1)
#define CTX_RING_START (0x08 + 1)
#define CTX_RING_CTL (0x0a + 1)
+#define CTX_TIMESTAMP (0x22 + 1)
+#define CTX_INDIRECT_RING_STATE (0x26 + 1)
#define CTX_PDP0_UDW (0x30 + 1)
#define CTX_PDP0_LDW (0x32 + 1)
@@ -23,4 +25,10 @@
#define CTX_INT_SRC_REPORT_REG (CTX_LRI_INT_REPORT_PTR + 3)
#define CTX_INT_SRC_REPORT_PTR (CTX_LRI_INT_REPORT_PTR + 4)
+#define INDIRECT_CTX_RING_HEAD (0x02 + 1)
+#define INDIRECT_CTX_RING_TAIL (0x04 + 1)
+#define INDIRECT_CTX_RING_START (0x06 + 1)
+#define INDIRECT_CTX_RING_START_UDW (0x08 + 1)
+#define INDIRECT_CTX_RING_CTL (0x0a + 1)
+
#endif
diff --git a/drivers/gpu/drm/xe/regs/xe_oa_regs.h b/drivers/gpu/drm/xe/regs/xe_oa_regs.h
new file mode 100644
index 000000000000..1189f5a540a8
--- /dev/null
+++ b/drivers/gpu/drm/xe/regs/xe_oa_regs.h
@@ -0,0 +1,100 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2023 Intel Corporation
+ */
+
+#ifndef __XE_OA_REGS__
+#define __XE_OA_REGS__
+
+#define RPM_CONFIG1 XE_REG(0xd04)
+#define GT_NOA_ENABLE REG_BIT(9)
+
+#define EU_PERF_CNTL0 XE_REG(0xe458)
+#define EU_PERF_CNTL4 XE_REG(0xe45c)
+#define EU_PERF_CNTL1 XE_REG(0xe558)
+#define EU_PERF_CNTL5 XE_REG(0xe55c)
+#define EU_PERF_CNTL2 XE_REG(0xe658)
+#define EU_PERF_CNTL6 XE_REG(0xe65c)
+#define EU_PERF_CNTL3 XE_REG(0xe758)
+
+#define OA_TLB_INV_CR XE_REG(0xceec)
+
+/* OAR unit */
+#define OAR_OACONTROL XE_REG(0x2960)
+#define OAR_OACONTROL_COUNTER_SEL_MASK REG_GENMASK(3, 1)
+#define OAR_OACONTROL_COUNTER_ENABLE REG_BIT(0)
+
+#define OACTXCONTROL(base) XE_REG((base) + 0x360)
+#define OAR_OASTATUS XE_REG(0x2968)
+#define OA_COUNTER_RESUME REG_BIT(0)
+
+/* OAG unit */
+#define OAG_OAGLBCTXCTRL XE_REG(0x2b28)
+#define OAG_OAGLBCTXCTRL_TIMER_PERIOD_MASK REG_GENMASK(7, 2)
+#define OAG_OAGLBCTXCTRL_TIMER_ENABLE REG_BIT(1)
+#define OAG_OAGLBCTXCTRL_COUNTER_RESUME REG_BIT(0)
+
+#define OAG_OAHEADPTR XE_REG(0xdb00)
+#define OAG_OAHEADPTR_MASK REG_GENMASK(31, 6)
+#define OAG_OATAILPTR XE_REG(0xdb04)
+#define OAG_OATAILPTR_MASK REG_GENMASK(31, 6)
+
+#define OAG_OABUFFER XE_REG(0xdb08)
+#define OABUFFER_SIZE_MASK REG_GENMASK(5, 3)
+#define OABUFFER_SIZE_128K REG_FIELD_PREP(OABUFFER_SIZE_MASK, 0)
+#define OABUFFER_SIZE_256K REG_FIELD_PREP(OABUFFER_SIZE_MASK, 1)
+#define OABUFFER_SIZE_512K REG_FIELD_PREP(OABUFFER_SIZE_MASK, 2)
+#define OABUFFER_SIZE_1M REG_FIELD_PREP(OABUFFER_SIZE_MASK, 3)
+#define OABUFFER_SIZE_2M REG_FIELD_PREP(OABUFFER_SIZE_MASK, 4)
+#define OABUFFER_SIZE_4M REG_FIELD_PREP(OABUFFER_SIZE_MASK, 5)
+#define OABUFFER_SIZE_8M REG_FIELD_PREP(OABUFFER_SIZE_MASK, 6)
+#define OABUFFER_SIZE_16M REG_FIELD_PREP(OABUFFER_SIZE_MASK, 7)
+#define OAG_OABUFFER_MEMORY_SELECT REG_BIT(0) /* 0: PPGTT, 1: GGTT */
+
+#define OAG_OACONTROL XE_REG(0xdaf4)
+#define OAG_OACONTROL_OA_CCS_SELECT_MASK REG_GENMASK(18, 16)
+#define OAG_OACONTROL_OA_COUNTER_SEL_MASK REG_GENMASK(4, 2)
+#define OAG_OACONTROL_OA_COUNTER_ENABLE REG_BIT(0)
+/* Common to all OA units */
+#define OA_OACONTROL_REPORT_BC_MASK REG_GENMASK(9, 9)
+#define OA_OACONTROL_COUNTER_SIZE_MASK REG_GENMASK(8, 8)
+
+#define OAG_OA_DEBUG XE_REG(0xdaf8, XE_REG_OPTION_MASKED)
+#define OAG_OA_DEBUG_DISABLE_MMIO_TRG REG_BIT(14)
+#define OAG_OA_DEBUG_START_TRIGGER_SCOPE_CONTROL REG_BIT(13)
+#define OAG_OA_DEBUG_DISABLE_START_TRG_2_COUNT_QUAL REG_BIT(8)
+#define OAG_OA_DEBUG_DISABLE_START_TRG_1_COUNT_QUAL REG_BIT(7)
+#define OAG_OA_DEBUG_INCLUDE_CLK_RATIO REG_BIT(6)
+#define OAG_OA_DEBUG_DISABLE_CLK_RATIO_REPORTS REG_BIT(5)
+#define OAG_OA_DEBUG_DISABLE_CTX_SWITCH_REPORTS REG_BIT(1)
+
+#define OAG_OASTATUS XE_REG(0xdafc)
+#define OASTATUS_MMIO_TRG_Q_FULL REG_BIT(6)
+#define OASTATUS_COUNTER_OVERFLOW REG_BIT(2)
+#define OASTATUS_BUFFER_OVERFLOW REG_BIT(1)
+#define OASTATUS_REPORT_LOST REG_BIT(0)
+#define OAG_MMIOTRIGGER XE_REG(0xdb1c)
+/* OAC unit */
+#define OAC_OACONTROL XE_REG(0x15114)
+
+/* OAM unit */
+#define OAM_HEAD_POINTER_OFFSET (0x1a0)
+#define OAM_TAIL_POINTER_OFFSET (0x1a4)
+#define OAM_BUFFER_OFFSET (0x1a8)
+#define OAM_CONTEXT_CONTROL_OFFSET (0x1bc)
+#define OAM_CONTROL_OFFSET (0x194)
+#define OAM_CONTROL_COUNTER_SEL_MASK REG_GENMASK(3, 1)
+#define OAM_DEBUG_OFFSET (0x198)
+#define OAM_STATUS_OFFSET (0x19c)
+#define OAM_MMIO_TRG_OFFSET (0x1d0)
+
+#define OAM_HEAD_POINTER(base) XE_REG((base) + OAM_HEAD_POINTER_OFFSET)
+#define OAM_TAIL_POINTER(base) XE_REG((base) + OAM_TAIL_POINTER_OFFSET)
+#define OAM_BUFFER(base) XE_REG((base) + OAM_BUFFER_OFFSET)
+#define OAM_CONTEXT_CONTROL(base) XE_REG((base) + OAM_CONTEXT_CONTROL_OFFSET)
+#define OAM_CONTROL(base) XE_REG((base) + OAM_CONTROL_OFFSET)
+#define OAM_DEBUG(base) XE_REG((base) + OAM_DEBUG_OFFSET)
+#define OAM_STATUS(base) XE_REG((base) + OAM_STATUS_OFFSET)
+#define OAM_MMIO_TRG(base) XE_REG((base) + OAM_MMIO_TRG_OFFSET)
+
+#endif
diff --git a/drivers/gpu/drm/xe/regs/xe_pcode_regs.h b/drivers/gpu/drm/xe/regs/xe_pcode_regs.h
index 3dae858508c8..0b0b49d850ae 100644
--- a/drivers/gpu/drm/xe/regs/xe_pcode_regs.h
+++ b/drivers/gpu/drm/xe/regs/xe_pcode_regs.h
@@ -18,4 +18,11 @@
#define PVC_GT0_PLATFORM_ENERGY_STATUS XE_REG(0x28106c)
#define PVC_GT0_PACKAGE_POWER_SKU XE_REG(0x281080)
+#define BMG_PACKAGE_POWER_SKU XE_REG(0x138098)
+#define BMG_PACKAGE_POWER_SKU_UNIT XE_REG(0x1380dc)
+#define BMG_PACKAGE_ENERGY_STATUS XE_REG(0x138120)
+#define BMG_PACKAGE_RAPL_LIMIT XE_REG(0x138440)
+#define BMG_PLATFORM_ENERGY_STATUS XE_REG(0x138458)
+#define BMG_PLATFORM_POWER_LIMIT XE_REG(0x138460)
+
#endif /* _XE_PCODE_REGS_H_ */
diff --git a/drivers/gpu/drm/xe/regs/xe_regs.h b/drivers/gpu/drm/xe/regs/xe_regs.h
index 722fb6dbb72e..23e33ec84902 100644
--- a/drivers/gpu/drm/xe/regs/xe_regs.h
+++ b/drivers/gpu/drm/xe/regs/xe_regs.h
@@ -30,6 +30,9 @@
#define XEHP_CLOCK_GATE_DIS XE_REG(0x101014)
#define SGSI_SIDECLK_DIS REG_BIT(17)
+#define XEHP_MTCFG_ADDR XE_REG(0x101800)
+#define TILE_COUNT REG_GENMASK(15, 8)
+
#define GGC XE_REG(0x108040)
#define GMS_MASK REG_GENMASK(15, 8)
#define GGMS_MASK REG_GENMASK(7, 6)
diff --git a/drivers/gpu/drm/xe/regs/xe_sriov_regs.h b/drivers/gpu/drm/xe/regs/xe_sriov_regs.h
index 617ddb84b7fa..017b4ddd1ecf 100644
--- a/drivers/gpu/drm/xe/regs/xe_sriov_regs.h
+++ b/drivers/gpu/drm/xe/regs/xe_sriov_regs.h
@@ -14,6 +14,9 @@
#define LMEM_EN REG_BIT(31)
#define LMTT_DIR_PTR REG_GENMASK(30, 0) /* in multiples of 64KB */
+#define VIRTUAL_CTRL_REG XE_REG(0x10108c)
+#define GUEST_GTT_UPDATE_EN REG_BIT(8)
+
#define VF_CAP_REG XE_REG(0x1901f8, XE_REG_OPTION_VF)
#define VF_CAP REG_BIT(0)
diff --git a/drivers/gpu/drm/xe/tests/Makefile b/drivers/gpu/drm/xe/tests/Makefile
index 8cf2367449d8..6e58931fddd4 100644
--- a/drivers/gpu/drm/xe/tests/Makefile
+++ b/drivers/gpu/drm/xe/tests/Makefile
@@ -11,6 +11,7 @@ xe_live_test-y = xe_live_test_mod.o \
# Normal kunit tests
obj-$(CONFIG_DRM_XE_KUNIT_TEST) += xe_test.o
xe_test-y = xe_test_mod.o \
+ xe_args_test.o \
xe_pci_test.o \
xe_rtp_test.o \
xe_wa_test.o
diff --git a/drivers/gpu/drm/xe/tests/xe_args_test.c b/drivers/gpu/drm/xe/tests/xe_args_test.c
new file mode 100644
index 000000000000..f3fb23aa5d2e
--- /dev/null
+++ b/drivers/gpu/drm/xe/tests/xe_args_test.c
@@ -0,0 +1,221 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright © 2024 Intel Corporation
+ */
+
+#include <kunit/test.h>
+
+#include "xe_args.h"
+
+static void call_args_example(struct kunit *test)
+{
+#define foo X, Y, Z, Q
+#define bar COUNT_ARGS(foo)
+#define buz CALL_ARGS(COUNT_ARGS, foo)
+
+ KUNIT_EXPECT_EQ(test, bar, 1);
+ KUNIT_EXPECT_EQ(test, buz, 4);
+
+#undef foo
+#undef bar
+#undef buz
+}
+
+static void drop_first_arg_example(struct kunit *test)
+{
+#define foo X, Y, Z, Q
+#define bar CALL_ARGS(COUNT_ARGS, DROP_FIRST_ARG(foo))
+
+ KUNIT_EXPECT_EQ(test, bar, 3);
+
+#undef foo
+#undef bar
+}
+
+static void first_arg_example(struct kunit *test)
+{
+ int X = 1;
+
+#define foo X, Y, Z, Q
+#define bar FIRST_ARG(foo)
+
+ KUNIT_EXPECT_EQ(test, bar, X);
+ KUNIT_EXPECT_STREQ(test, __stringify(bar), "X");
+
+#undef foo
+#undef bar
+}
+
+static void last_arg_example(struct kunit *test)
+{
+ int Q = 1;
+
+#define foo X, Y, Z, Q
+#define bar LAST_ARG(foo)
+
+ KUNIT_EXPECT_EQ(test, bar, Q);
+ KUNIT_EXPECT_STREQ(test, __stringify(bar), "Q");
+
+#undef foo
+#undef bar
+}
+
+static void pick_arg_example(struct kunit *test)
+{
+ int Y = 1, Z = 2;
+
+#define foo X, Y, Z, Q
+#define bar PICK_ARG(2, foo)
+#define buz PICK_ARG3(foo)
+
+ KUNIT_EXPECT_EQ(test, bar, Y);
+ KUNIT_EXPECT_STREQ(test, __stringify(bar), "Y");
+ KUNIT_EXPECT_EQ(test, buz, Z);
+ KUNIT_EXPECT_STREQ(test, __stringify(buz), "Z");
+
+#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)
+#define bar DROP_FIRST_ARG(foo(ARGS_SEP_COMMA __stringify))
+#define buz CALL_ARGS(COUNT_ARGS, DROP_FIRST_ARG(foo(ARGS_SEP_COMMA)))
+
+ static const char * const a[] = { bar };
+
+ KUNIT_EXPECT_STREQ(test, a[0], "X");
+ KUNIT_EXPECT_STREQ(test, a[1], "Y");
+ KUNIT_EXPECT_STREQ(test, a[2], "Z");
+ KUNIT_EXPECT_STREQ(test, a[3], "Q");
+
+ KUNIT_EXPECT_EQ(test, buz, 4);
+
+#undef foo
+#undef bar
+#undef buz
+}
+
+#define NO_ARGS
+#define FOO_ARGS X, Y, Z, Q
+#define MAX_ARGS -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12
+
+static void count_args_test(struct kunit *test)
+{
+ int count;
+
+ /* COUNT_ARGS() counts to 12 */
+
+ count = COUNT_ARGS();
+ KUNIT_EXPECT_EQ(test, count, 0);
+
+ count = COUNT_ARGS(1);
+ KUNIT_EXPECT_EQ(test, count, 1);
+
+ count = COUNT_ARGS(a, b, c, d, e);
+ KUNIT_EXPECT_EQ(test, count, 5);
+
+ count = COUNT_ARGS(a, b, c, d, e, f, g, h, i, j, k, l);
+ KUNIT_EXPECT_EQ(test, count, 12);
+
+ /* COUNT_ARGS() does not expand params */
+
+ count = COUNT_ARGS(NO_ARGS);
+ KUNIT_EXPECT_EQ(test, count, 1);
+
+ count = COUNT_ARGS(FOO_ARGS);
+ KUNIT_EXPECT_EQ(test, count, 1);
+}
+
+static void call_args_test(struct kunit *test)
+{
+ int count;
+
+ count = CALL_ARGS(COUNT_ARGS, NO_ARGS);
+ KUNIT_EXPECT_EQ(test, count, 0);
+ KUNIT_EXPECT_EQ(test, CALL_ARGS(COUNT_ARGS, NO_ARGS), 0);
+ KUNIT_EXPECT_EQ(test, CALL_ARGS(COUNT_ARGS, FOO_ARGS), 4);
+ KUNIT_EXPECT_EQ(test, CALL_ARGS(COUNT_ARGS, FOO_ARGS, FOO_ARGS), 8);
+ KUNIT_EXPECT_EQ(test, CALL_ARGS(COUNT_ARGS, MAX_ARGS), 12);
+}
+
+static void drop_first_arg_test(struct kunit *test)
+{
+ int Y = -2, Z = -3, Q = -4;
+ int a[] = { DROP_FIRST_ARG(FOO_ARGS) };
+
+ KUNIT_EXPECT_EQ(test, DROP_FIRST_ARG(0, -1), -1);
+ KUNIT_EXPECT_EQ(test, DROP_FIRST_ARG(DROP_FIRST_ARG(0, -1, -2)), -2);
+
+ KUNIT_EXPECT_EQ(test, CALL_ARGS(COUNT_ARGS, DROP_FIRST_ARG(FOO_ARGS)), 3);
+ KUNIT_EXPECT_EQ(test, DROP_FIRST_ARG(DROP_FIRST_ARG(DROP_FIRST_ARG(FOO_ARGS))), -4);
+ KUNIT_EXPECT_EQ(test, a[0], -2);
+ KUNIT_EXPECT_EQ(test, a[1], -3);
+ KUNIT_EXPECT_EQ(test, a[2], -4);
+
+#define foo DROP_FIRST_ARG(FOO_ARGS)
+#define bar DROP_FIRST_ARG(DROP_FIRST_ARG(FOO_ARGS))
+#define buz DROP_FIRST_ARG(DROP_FIRST_ARG(DROP_FIRST_ARG(FOO_ARGS)))
+
+ KUNIT_EXPECT_EQ(test, CALL_ARGS(COUNT_ARGS, foo), 3);
+ KUNIT_EXPECT_EQ(test, CALL_ARGS(COUNT_ARGS, bar), 2);
+ KUNIT_EXPECT_EQ(test, CALL_ARGS(COUNT_ARGS, buz), 1);
+ KUNIT_EXPECT_STREQ(test, __stringify(buz), "Q");
+
+#undef foo
+#undef bar
+#undef buz
+}
+
+static void first_arg_test(struct kunit *test)
+{
+ int X = -1;
+ int a[] = { FIRST_ARG(FOO_ARGS) };
+
+ KUNIT_EXPECT_EQ(test, FIRST_ARG(-1, -2), -1);
+
+ KUNIT_EXPECT_EQ(test, CALL_ARGS(COUNT_ARGS, FIRST_ARG(FOO_ARGS)), 1);
+ KUNIT_EXPECT_EQ(test, FIRST_ARG(FOO_ARGS), -1);
+ KUNIT_EXPECT_EQ(test, a[0], -1);
+ KUNIT_EXPECT_STREQ(test, __stringify(FIRST_ARG(FOO_ARGS)), "X");
+}
+
+static void last_arg_test(struct kunit *test)
+{
+ int Q = -4;
+ int a[] = { LAST_ARG(FOO_ARGS) };
+
+ KUNIT_EXPECT_EQ(test, LAST_ARG(-1, -2), -2);
+
+ KUNIT_EXPECT_EQ(test, CALL_ARGS(COUNT_ARGS, LAST_ARG(FOO_ARGS)), 1);
+ KUNIT_EXPECT_EQ(test, LAST_ARG(FOO_ARGS), -4);
+ KUNIT_EXPECT_EQ(test, a[0], -4);
+ KUNIT_EXPECT_STREQ(test, __stringify(LAST_ARG(FOO_ARGS)), "Q");
+
+ KUNIT_EXPECT_EQ(test, LAST_ARG(MAX_ARGS), -12);
+ KUNIT_EXPECT_STREQ(test, __stringify(LAST_ARG(MAX_ARGS)), "-12");
+}
+
+static struct kunit_case args_tests[] = {
+ KUNIT_CASE(count_args_test),
+ KUNIT_CASE(call_args_example),
+ KUNIT_CASE(call_args_test),
+ KUNIT_CASE(drop_first_arg_example),
+ KUNIT_CASE(drop_first_arg_test),
+ KUNIT_CASE(first_arg_example),
+ KUNIT_CASE(first_arg_test),
+ KUNIT_CASE(last_arg_example),
+ KUNIT_CASE(last_arg_test),
+ KUNIT_CASE(pick_arg_example),
+ KUNIT_CASE(sep_comma_example),
+ {}
+};
+
+static struct kunit_suite args_test_suite = {
+ .name = "args",
+ .test_cases = args_tests,
+};
+
+kunit_test_suite(args_test_suite);
diff --git a/drivers/gpu/drm/xe/tests/xe_gt_sriov_pf_service_test.c b/drivers/gpu/drm/xe/tests/xe_gt_sriov_pf_service_test.c
new file mode 100644
index 000000000000..b683585db852
--- /dev/null
+++ b/drivers/gpu/drm/xe/tests/xe_gt_sriov_pf_service_test.c
@@ -0,0 +1,232 @@
+// SPDX-License-Identifier: GPL-2.0 AND MIT
+/*
+ * Copyright © 2024 Intel Corporation
+ */
+
+#include <kunit/test.h>
+
+#include "xe_device.h"
+#include "xe_kunit_helpers.h"
+#include "xe_pci_test.h"
+
+static int pf_service_test_init(struct kunit *test)
+{
+ struct xe_pci_fake_data fake = {
+ .sriov_mode = XE_SRIOV_MODE_PF,
+ .platform = XE_TIGERLAKE, /* some random platform */
+ .subplatform = XE_SUBPLATFORM_NONE,
+ };
+ struct xe_device *xe;
+ struct xe_gt *gt;
+
+ test->priv = &fake;
+ xe_kunit_helper_xe_device_test_init(test);
+
+ xe = test->priv;
+ KUNIT_ASSERT_EQ(test, xe_sriov_init(xe), 0);
+
+ gt = xe_device_get_gt(xe, 0);
+ pf_init_versions(gt);
+
+ /*
+ * sanity check:
+ * - all supported platforms VF/PF ABI versions must be defined
+ * - base version can't be newer than latest
+ */
+ KUNIT_ASSERT_NE(test, 0, gt->sriov.pf.service.version.base.major);
+ KUNIT_ASSERT_NE(test, 0, gt->sriov.pf.service.version.latest.major);
+ KUNIT_ASSERT_LE(test, gt->sriov.pf.service.version.base.major,
+ gt->sriov.pf.service.version.latest.major);
+ if (gt->sriov.pf.service.version.base.major == gt->sriov.pf.service.version.latest.major)
+ KUNIT_ASSERT_LE(test, gt->sriov.pf.service.version.base.minor,
+ gt->sriov.pf.service.version.latest.minor);
+
+ test->priv = gt;
+ return 0;
+}
+
+static void pf_negotiate_any(struct kunit *test)
+{
+ struct xe_gt *gt = test->priv;
+ u32 major, minor;
+
+ KUNIT_ASSERT_EQ(test, 0,
+ pf_negotiate_version(gt, VF2PF_HANDSHAKE_MAJOR_ANY,
+ VF2PF_HANDSHAKE_MINOR_ANY,
+ &major, &minor));
+ KUNIT_ASSERT_EQ(test, major, gt->sriov.pf.service.version.latest.major);
+ KUNIT_ASSERT_EQ(test, minor, gt->sriov.pf.service.version.latest.minor);
+}
+
+static void pf_negotiate_base_match(struct kunit *test)
+{
+ struct xe_gt *gt = test->priv;
+ u32 major, minor;
+
+ KUNIT_ASSERT_EQ(test, 0,
+ pf_negotiate_version(gt,
+ gt->sriov.pf.service.version.base.major,
+ gt->sriov.pf.service.version.base.minor,
+ &major, &minor));
+ KUNIT_ASSERT_EQ(test, major, gt->sriov.pf.service.version.base.major);
+ KUNIT_ASSERT_EQ(test, minor, gt->sriov.pf.service.version.base.minor);
+}
+
+static void pf_negotiate_base_newer(struct kunit *test)
+{
+ struct xe_gt *gt = test->priv;
+ u32 major, minor;
+
+ KUNIT_ASSERT_EQ(test, 0,
+ pf_negotiate_version(gt,
+ gt->sriov.pf.service.version.base.major,
+ gt->sriov.pf.service.version.base.minor + 1,
+ &major, &minor));
+ KUNIT_ASSERT_EQ(test, major, gt->sriov.pf.service.version.base.major);
+ KUNIT_ASSERT_GE(test, minor, gt->sriov.pf.service.version.base.minor);
+ if (gt->sriov.pf.service.version.base.major == gt->sriov.pf.service.version.latest.major)
+ KUNIT_ASSERT_LE(test, minor, gt->sriov.pf.service.version.latest.minor);
+ else
+ KUNIT_FAIL(test, "FIXME: don't know how to test multi-version yet!\n");
+}
+
+static void pf_negotiate_base_next(struct kunit *test)
+{
+ struct xe_gt *gt = test->priv;
+ u32 major, minor;
+
+ KUNIT_ASSERT_EQ(test, 0,
+ pf_negotiate_version(gt,
+ gt->sriov.pf.service.version.base.major + 1, 0,
+ &major, &minor));
+ KUNIT_ASSERT_GE(test, major, gt->sriov.pf.service.version.base.major);
+ KUNIT_ASSERT_LE(test, major, gt->sriov.pf.service.version.latest.major);
+ if (major == gt->sriov.pf.service.version.latest.major)
+ KUNIT_ASSERT_LE(test, minor, gt->sriov.pf.service.version.latest.minor);
+ else
+ KUNIT_FAIL(test, "FIXME: don't know how to test multi-version yet!\n");
+}
+
+static void pf_negotiate_base_older(struct kunit *test)
+{
+ struct xe_gt *gt = test->priv;
+ u32 major, minor;
+
+ if (!gt->sriov.pf.service.version.base.minor)
+ kunit_skip(test, "no older minor\n");
+
+ KUNIT_ASSERT_NE(test, 0,
+ pf_negotiate_version(gt,
+ gt->sriov.pf.service.version.base.major,
+ gt->sriov.pf.service.version.base.minor - 1,
+ &major, &minor));
+}
+
+static void pf_negotiate_base_prev(struct kunit *test)
+{
+ struct xe_gt *gt = test->priv;
+ u32 major, minor;
+
+ KUNIT_ASSERT_NE(test, 0,
+ pf_negotiate_version(gt,
+ gt->sriov.pf.service.version.base.major - 1, 1,
+ &major, &minor));
+}
+
+static void pf_negotiate_latest_match(struct kunit *test)
+{
+ struct xe_gt *gt = test->priv;
+ u32 major, minor;
+
+ KUNIT_ASSERT_EQ(test, 0,
+ pf_negotiate_version(gt,
+ gt->sriov.pf.service.version.latest.major,
+ gt->sriov.pf.service.version.latest.minor,
+ &major, &minor));
+ KUNIT_ASSERT_EQ(test, major, gt->sriov.pf.service.version.latest.major);
+ KUNIT_ASSERT_EQ(test, minor, gt->sriov.pf.service.version.latest.minor);
+}
+
+static void pf_negotiate_latest_newer(struct kunit *test)
+{
+ struct xe_gt *gt = test->priv;
+ u32 major, minor;
+
+ KUNIT_ASSERT_EQ(test, 0,
+ pf_negotiate_version(gt,
+ gt->sriov.pf.service.version.latest.major,
+ gt->sriov.pf.service.version.latest.minor + 1,
+ &major, &minor));
+ KUNIT_ASSERT_EQ(test, major, gt->sriov.pf.service.version.latest.major);
+ KUNIT_ASSERT_EQ(test, minor, gt->sriov.pf.service.version.latest.minor);
+}
+
+static void pf_negotiate_latest_next(struct kunit *test)
+{
+ struct xe_gt *gt = test->priv;
+ u32 major, minor;
+
+ KUNIT_ASSERT_EQ(test, 0,
+ pf_negotiate_version(gt,
+ gt->sriov.pf.service.version.latest.major + 1, 0,
+ &major, &minor));
+ KUNIT_ASSERT_EQ(test, major, gt->sriov.pf.service.version.latest.major);
+ KUNIT_ASSERT_EQ(test, minor, gt->sriov.pf.service.version.latest.minor);
+}
+
+static void pf_negotiate_latest_older(struct kunit *test)
+{
+ struct xe_gt *gt = test->priv;
+ u32 major, minor;
+
+ if (!gt->sriov.pf.service.version.latest.minor)
+ kunit_skip(test, "no older minor\n");
+
+ KUNIT_ASSERT_EQ(test, 0,
+ pf_negotiate_version(gt,
+ gt->sriov.pf.service.version.latest.major,
+ gt->sriov.pf.service.version.latest.minor - 1,
+ &major, &minor));
+ KUNIT_ASSERT_EQ(test, major, gt->sriov.pf.service.version.latest.major);
+ KUNIT_ASSERT_EQ(test, minor, gt->sriov.pf.service.version.latest.minor - 1);
+}
+
+static void pf_negotiate_latest_prev(struct kunit *test)
+{
+ struct xe_gt *gt = test->priv;
+ u32 major, minor;
+
+ if (gt->sriov.pf.service.version.base.major == gt->sriov.pf.service.version.latest.major)
+ kunit_skip(test, "no prev major");
+
+ KUNIT_ASSERT_EQ(test, 0,
+ pf_negotiate_version(gt,
+ gt->sriov.pf.service.version.latest.major - 1,
+ gt->sriov.pf.service.version.base.minor + 1,
+ &major, &minor));
+ KUNIT_ASSERT_EQ(test, major, gt->sriov.pf.service.version.latest.major - 1);
+ KUNIT_ASSERT_GE(test, major, gt->sriov.pf.service.version.base.major);
+}
+
+static struct kunit_case pf_service_test_cases[] = {
+ KUNIT_CASE(pf_negotiate_any),
+ KUNIT_CASE(pf_negotiate_base_match),
+ KUNIT_CASE(pf_negotiate_base_newer),
+ KUNIT_CASE(pf_negotiate_base_next),
+ KUNIT_CASE(pf_negotiate_base_older),
+ KUNIT_CASE(pf_negotiate_base_prev),
+ KUNIT_CASE(pf_negotiate_latest_match),
+ KUNIT_CASE(pf_negotiate_latest_newer),
+ KUNIT_CASE(pf_negotiate_latest_next),
+ KUNIT_CASE(pf_negotiate_latest_older),
+ KUNIT_CASE(pf_negotiate_latest_prev),
+ {}
+};
+
+static struct kunit_suite pf_service_suite = {
+ .name = "pf_service",
+ .test_cases = pf_service_test_cases,
+ .init = pf_service_test_init,
+};
+
+kunit_test_suite(pf_service_suite);
diff --git a/drivers/gpu/drm/xe/tests/xe_migrate.c b/drivers/gpu/drm/xe/tests/xe_migrate.c
index 977d5f4e4490..962f6438e219 100644
--- a/drivers/gpu/drm/xe/tests/xe_migrate.c
+++ b/drivers/gpu/drm/xe/tests/xe_migrate.c
@@ -62,36 +62,6 @@ static int run_sanity_job(struct xe_migrate *m, struct xe_device *xe,
return 0;
}
-static void
-sanity_populate_cb(struct xe_migrate_pt_update *pt_update,
- struct xe_tile *tile, struct iosys_map *map, void *dst,
- u32 qword_ofs, u32 num_qwords,
- const struct xe_vm_pgtable_update *update)
-{
- struct migrate_test_params *p =
- to_migrate_test_params(xe_cur_kunit_priv(XE_TEST_LIVE_MIGRATE));
- int i;
- u64 *ptr = dst;
- u64 value;
-
- for (i = 0; i < num_qwords; i++) {
- value = (qword_ofs + i - update->ofs) * 0x1111111111111111ULL;
- if (map)
- xe_map_wr(tile_to_xe(tile), map, (qword_ofs + i) *
- sizeof(u64), u64, value);
- else
- ptr[i] = value;
- }
-
- kunit_info(xe_cur_kunit(), "Used %s.\n", map ? "CPU" : "GPU");
- if (p->force_gpu && map)
- KUNIT_FAIL(xe_cur_kunit(), "GPU pagetable update used CPU.\n");
-}
-
-static const struct xe_migrate_pt_update_ops sanity_ops = {
- .populate = sanity_populate_cb,
-};
-
#define check(_retval, _expected, str, _test) \
do { if ((_retval) != (_expected)) { \
KUNIT_FAIL(_test, "Sanity check failed: " str \
@@ -209,57 +179,6 @@ static void test_copy_vram(struct xe_migrate *m, struct xe_bo *bo,
test_copy(m, bo, test, region);
}
-static void test_pt_update(struct xe_migrate *m, struct xe_bo *pt,
- struct kunit *test, bool force_gpu)
-{
- struct xe_device *xe = tile_to_xe(m->tile);
- struct dma_fence *fence;
- u64 retval, expected;
- ktime_t then, now;
- int i;
-
- struct xe_vm_pgtable_update update = {
- .ofs = 1,
- .qwords = 0x10,
- .pt_bo = pt,
- };
- struct xe_migrate_pt_update pt_update = {
- .ops = &sanity_ops,
- };
- struct migrate_test_params p = {
- .base.id = XE_TEST_LIVE_MIGRATE,
- .force_gpu = force_gpu,
- };
-
- test->priv = &p;
- /* Test xe_migrate_update_pgtables() updates the pagetable as expected */
- expected = 0xf0f0f0f0f0f0f0f0ULL;
- xe_map_memset(xe, &pt->vmap, 0, (u8)expected, pt->size);
-
- then = ktime_get();
- fence = xe_migrate_update_pgtables(m, m->q->vm, NULL, m->q, &update, 1,
- NULL, 0, &pt_update);
- now = ktime_get();
- if (sanity_fence_failed(xe, fence, "Migration pagetable update", test))
- return;
-
- kunit_info(test, "Updating without syncing took %llu us,\n",
- (unsigned long long)ktime_to_us(ktime_sub(now, then)));
-
- dma_fence_put(fence);
- retval = xe_map_rd(xe, &pt->vmap, 0, u64);
- check(retval, expected, "PTE[0] must stay untouched", test);
-
- for (i = 0; i < update.qwords; i++) {
- retval = xe_map_rd(xe, &pt->vmap, (update.ofs + i) * 8, u64);
- check(retval, i * 0x1111111111111111ULL, "PTE update", test);
- }
-
- retval = xe_map_rd(xe, &pt->vmap, 8 * (update.ofs + update.qwords),
- u64);
- check(retval, expected, "PTE[0x11] must stay untouched", test);
-}
-
static void xe_migrate_sanity_test(struct xe_migrate *m, struct kunit *test)
{
struct xe_tile *tile = m->tile;
@@ -398,11 +317,6 @@ static void xe_migrate_sanity_test(struct xe_migrate *m, struct kunit *test)
test_copy_vram(m, big, test);
}
- kunit_info(test, "Testing page table update using CPU if GPU idle.\n");
- test_pt_update(m, pt, test, false);
- kunit_info(test, "Testing page table update using GPU\n");
- test_pt_update(m, pt, test, true);
-
out:
xe_bb_free(bb, NULL);
free_tiny:
@@ -430,7 +344,7 @@ static int migrate_test_run_device(struct xe_device *xe)
struct xe_migrate *m = tile->migrate;
kunit_info(test, "Testing tile id %d.\n", id);
- xe_vm_lock(m->q->vm, true);
+ xe_vm_lock(m->q->vm, false);
xe_migrate_sanity_test(m, test);
xe_vm_unlock(m->q->vm);
}
diff --git a/drivers/gpu/drm/xe/tests/xe_mocs.c b/drivers/gpu/drm/xe/tests/xe_mocs.c
index 1b8617075b37..67c65e88c384 100644
--- a/drivers/gpu/drm/xe/tests/xe_mocs.c
+++ b/drivers/gpu/drm/xe/tests/xe_mocs.c
@@ -31,9 +31,9 @@ static int live_mocs_init(struct live_mocs *arg, struct xe_gt *gt)
kunit_info(test, "gt %d", gt->info.id);
kunit_info(test, "gt type %d", gt->info.type);
- kunit_info(test, "table size %d", arg->table.size);
+ kunit_info(test, "table size %d", arg->table.table_size);
kunit_info(test, "table uc_index %d", arg->table.uc_index);
- kunit_info(test, "table n_entries %d", arg->table.n_entries);
+ kunit_info(test, "table num_mocs_regs %d", arg->table.num_mocs_regs);
return flags;
}
@@ -50,7 +50,7 @@ static void read_l3cc_table(struct xe_gt *gt,
ret = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
KUNIT_ASSERT_EQ_MSG(test, ret, 0, "Forcewake Failed.\n");
- for (i = 0; i < info->n_entries; i++) {
+ for (i = 0; i < info->num_mocs_regs; i++) {
if (!(i & 1)) {
if (regs_are_mcr(gt))
reg_val = xe_gt_mcr_unicast_read_any(gt, XEHP_LNCFCMOCS(i >> 1));
@@ -90,7 +90,7 @@ static void read_mocs_table(struct xe_gt *gt,
ret = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
KUNIT_ASSERT_EQ_MSG(test, ret, 0, "Forcewake Failed.\n");
- for (i = 0; i < info->n_entries; i++) {
+ for (i = 0; i < info->num_mocs_regs; i++) {
if (regs_are_mcr(gt))
reg_val = xe_gt_mcr_unicast_read_any(gt, XEHP_GLOBAL_MOCS(i));
else
diff --git a/drivers/gpu/drm/xe/tests/xe_rtp_test.c b/drivers/gpu/drm/xe/tests/xe_rtp_test.c
index 06759d754783..f217445c246a 100644
--- a/drivers/gpu/drm/xe/tests/xe_rtp_test.c
+++ b/drivers/gpu/drm/xe/tests/xe_rtp_test.c
@@ -91,6 +91,59 @@ static const struct rtp_test_case cases[] = {
},
},
{
+ .name = "match-or",
+ .expected_reg = REGULAR_REG1,
+ .expected_set_bits = REG_BIT(0) | REG_BIT(1) | REG_BIT(2),
+ .expected_clr_bits = REG_BIT(0) | REG_BIT(1) | REG_BIT(2),
+ .expected_count = 1,
+ .entries = (const struct xe_rtp_entry_sr[]) {
+ { XE_RTP_NAME("first"),
+ XE_RTP_RULES(FUNC(match_yes), OR, FUNC(match_no)),
+ XE_RTP_ACTIONS(SET(REGULAR_REG1, REG_BIT(0)))
+ },
+ { XE_RTP_NAME("middle"),
+ XE_RTP_RULES(FUNC(match_no), FUNC(match_no), OR,
+ FUNC(match_yes), OR,
+ FUNC(match_no)),
+ XE_RTP_ACTIONS(SET(REGULAR_REG1, REG_BIT(1)))
+ },
+ { XE_RTP_NAME("last"),
+ XE_RTP_RULES(FUNC(match_no), OR, FUNC(match_yes)),
+ XE_RTP_ACTIONS(SET(REGULAR_REG1, REG_BIT(2)))
+ },
+ { XE_RTP_NAME("no-match"),
+ XE_RTP_RULES(FUNC(match_no), OR, FUNC(match_no)),
+ XE_RTP_ACTIONS(SET(REGULAR_REG1, REG_BIT(3)))
+ },
+ {}
+ },
+ },
+ {
+ .name = "match-or-xfail",
+ .expected_reg = REGULAR_REG1,
+ .expected_count = 0,
+ .entries = (const struct xe_rtp_entry_sr[]) {
+ { XE_RTP_NAME("leading-or"),
+ XE_RTP_RULES(OR, FUNC(match_yes)),
+ XE_RTP_ACTIONS(SET(REGULAR_REG1, REG_BIT(0)))
+ },
+ { XE_RTP_NAME("trailing-or"),
+ /*
+ * First condition is match_no, otherwise the failure
+ * wouldn't really trigger as RTP stops processing as
+ * soon as it has a matching set of rules
+ */
+ XE_RTP_RULES(FUNC(match_no), OR),
+ XE_RTP_ACTIONS(SET(REGULAR_REG1, REG_BIT(1)))
+ },
+ { XE_RTP_NAME("no-or-or-yes"),
+ XE_RTP_RULES(FUNC(match_no), OR, OR, FUNC(match_yes)),
+ XE_RTP_ACTIONS(SET(REGULAR_REG1, REG_BIT(2)))
+ },
+ {}
+ },
+ },
+ {
.name = "no-match-no-add-multiple-rules",
.expected_reg = REGULAR_REG1,
.expected_set_bits = REG_BIT(0),
@@ -255,9 +308,14 @@ static void xe_rtp_process_tests(struct kunit *test)
}
KUNIT_EXPECT_EQ(test, count, param->expected_count);
- KUNIT_EXPECT_EQ(test, sr_entry->clr_bits, param->expected_clr_bits);
- KUNIT_EXPECT_EQ(test, sr_entry->set_bits, param->expected_set_bits);
- KUNIT_EXPECT_EQ(test, sr_entry->reg.raw, param->expected_reg.raw);
+ if (count) {
+ KUNIT_EXPECT_EQ(test, sr_entry->clr_bits, param->expected_clr_bits);
+ KUNIT_EXPECT_EQ(test, sr_entry->set_bits, param->expected_set_bits);
+ KUNIT_EXPECT_EQ(test, sr_entry->reg.raw, param->expected_reg.raw);
+ } else {
+ KUNIT_EXPECT_NULL(test, sr_entry);
+ }
+
KUNIT_EXPECT_EQ(test, reg_sr->errors, param->expected_sr_errors);
}
diff --git a/drivers/gpu/drm/xe/xe_args.h b/drivers/gpu/drm/xe/xe_args.h
new file mode 100644
index 000000000000..4dbc7e53c624
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_args.h
@@ -0,0 +1,143 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2024 Intel Corporation
+ */
+
+#ifndef _XE_ARGS_H_
+#define _XE_ARGS_H_
+
+#include <linux/args.h>
+
+/*
+ * Why don't the following macros have the XE prefix?
+ *
+ * Once we find more potential users outside of the Xe driver, we plan to move
+ * all of the following macros unchanged to linux/args.h.
+ */
+
+/**
+ * CALL_ARGS - Invoke a macro, but allow parameters to be expanded beforehand.
+ * @f: name of the macro to invoke
+ * @args: arguments for the macro
+ *
+ * This macro allows calling macros which names might generated or we want to
+ * make sure it's arguments will be correctly expanded.
+ *
+ * Example:
+ *
+ * #define foo X,Y,Z,Q
+ * #define bar COUNT_ARGS(foo)
+ * #define buz CALL_ARGS(COUNT_ARGS, foo)
+ *
+ * With above definitions bar expands to 1 while buz expands to 4.
+ */
+#define CALL_ARGS(f, args...) __CALL_ARGS(f, args)
+#define __CALL_ARGS(f, args...) f(args)
+
+/**
+ * DROP_FIRST_ARG - Returns all arguments except the first one.
+ * @args: arguments
+ *
+ * This helper macro allows manipulation the argument list before passing it
+ * to the next level macro.
+ *
+ * Example:
+ *
+ * #define foo X,Y,Z,Q
+ * #define bar CALL_ARGS(COUNT_ARGS, DROP_FIRST_ARG(foo))
+ *
+ * With above definitions bar expands to 3.
+ */
+#define DROP_FIRST_ARG(args...) __DROP_FIRST_ARG(args)
+#define __DROP_FIRST_ARG(a, b...) b
+
+/**
+ * FIRST_ARG - Returns the first argument.
+ * @args: arguments
+ *
+ * This helper macro allows manipulation the argument list before passing it
+ * to the next level macro.
+ *
+ * Example:
+ *
+ * #define foo X,Y,Z,Q
+ * #define bar FIRST_ARG(foo)
+ *
+ * With above definitions bar expands to X.
+ */
+#define FIRST_ARG(args...) __FIRST_ARG(args)
+#define __FIRST_ARG(a, b...) a
+
+/**
+ * LAST_ARG - Returns the last argument.
+ * @args: arguments
+ *
+ * This helper macro allows manipulation the argument list before passing it
+ * to the next level macro.
+ *
+ * Like COUNT_ARGS() this macro works up to 12 arguments.
+ *
+ * Example:
+ *
+ * #define foo X,Y,Z,Q
+ * #define bar LAST_ARG(foo)
+ *
+ * With above definitions bar expands to Q.
+ */
+#define LAST_ARG(args...) __LAST_ARG(args)
+#define __LAST_ARG(args...) PICK_ARG(COUNT_ARGS(args), args)
+
+/**
+ * PICK_ARG - Returns the n-th argument.
+ * @n: argument number to be returned
+ * @args: arguments
+ *
+ * This helper macro allows manipulation the argument list before passing it
+ * to the next level macro.
+ *
+ * Like COUNT_ARGS() this macro supports n up to 12.
+ * Specialized macros PICK_ARG1() to PICK_ARG12() are also available.
+ *
+ * Example:
+ *
+ * #define foo X,Y,Z,Q
+ * #define bar PICK_ARG(2, foo)
+ * #define buz PICK_ARG3(foo)
+ *
+ * With above definitions bar expands to Y and buz expands to Z.
+ */
+#define PICK_ARG(n, args...) __PICK_ARG(n, args)
+#define __PICK_ARG(n, args...) CALL_ARGS(CONCATENATE(PICK_ARG, n), args)
+#define PICK_ARG1(args...) FIRST_ARG(args)
+#define PICK_ARG2(args...) PICK_ARG1(DROP_FIRST_ARG(args))
+#define PICK_ARG3(args...) PICK_ARG2(DROP_FIRST_ARG(args))
+#define PICK_ARG4(args...) PICK_ARG3(DROP_FIRST_ARG(args))
+#define PICK_ARG5(args...) PICK_ARG4(DROP_FIRST_ARG(args))
+#define PICK_ARG6(args...) PICK_ARG5(DROP_FIRST_ARG(args))
+#define PICK_ARG7(args...) PICK_ARG6(DROP_FIRST_ARG(args))
+#define PICK_ARG8(args...) PICK_ARG7(DROP_FIRST_ARG(args))
+#define PICK_ARG9(args...) PICK_ARG8(DROP_FIRST_ARG(args))
+#define PICK_ARG10(args...) PICK_ARG9(DROP_FIRST_ARG(args))
+#define PICK_ARG11(args...) PICK_ARG10(DROP_FIRST_ARG(args))
+#define PICK_ARG12(args...) PICK_ARG11(DROP_FIRST_ARG(args))
+
+/**
+ * ARGS_SEP_COMMA - Definition of a comma character.
+ *
+ * This definition can be used in cases where any intermediate macro expects
+ * fixed number of arguments, but we want to pass more arguments which can
+ * be properly evaluated only by the next level macro.
+ *
+ * Example:
+ *
+ * #define foo(f) f(X) f(Y) f(Z) f(Q)
+ * #define bar DROP_FIRST_ARG(foo(ARGS_SEP_COMMA __stringify))
+ * #define buz CALL_ARGS(COUNT_ARGS, DROP_FIRST_ARG(foo(ARGS_SEP_COMMA)))
+ *
+ * With above definitions bar expands to
+ * "X", "Y", "Z", "Q"
+ * and buz expands to 4.
+ */
+#define ARGS_SEP_COMMA ,
+
+#endif
diff --git a/drivers/gpu/drm/xe/xe_assert.h b/drivers/gpu/drm/xe/xe_assert.h
index 34c142e6cfb0..8b0cc1bc9327 100644
--- a/drivers/gpu/drm/xe/xe_assert.h
+++ b/drivers/gpu/drm/xe/xe_assert.h
@@ -109,11 +109,11 @@
#define xe_assert_msg(xe, condition, msg, arg...) ({ \
const struct xe_device *__xe = (xe); \
__xe_assert_msg(__xe, condition, \
- "platform: %d subplatform: %d\n" \
+ "platform: %s subplatform: %d\n" \
"graphics: %s %u.%02u step %s\n" \
"media: %s %u.%02u step %s\n" \
msg, \
- __xe->info.platform, __xe->info.subplatform, \
+ __xe->info.platform_name, __xe->info.subplatform, \
__xe->info.graphics_name, \
__xe->info.graphics_verx100 / 100, \
__xe->info.graphics_verx100 % 100, \
diff --git a/drivers/gpu/drm/xe/xe_bb.c b/drivers/gpu/drm/xe/xe_bb.c
index 541361caff3b..a13e0b3a169e 100644
--- a/drivers/gpu/drm/xe/xe_bb.c
+++ b/drivers/gpu/drm/xe/xe_bb.c
@@ -6,7 +6,7 @@
#include "xe_bb.h"
#include "instructions/xe_mi_commands.h"
-#include "regs/xe_gpu_commands.h"
+#include "xe_assert.h"
#include "xe_device.h"
#include "xe_exec_queue_types.h"
#include "xe_gt.h"
diff --git a/drivers/gpu/drm/xe/xe_bo.c b/drivers/gpu/drm/xe/xe_bo.c
index bc1f794e3e61..65c696966e96 100644
--- a/drivers/gpu/drm/xe/xe_bo.c
+++ b/drivers/gpu/drm/xe/xe_bo.c
@@ -25,7 +25,7 @@
#include "xe_pm.h"
#include "xe_preempt_fence.h"
#include "xe_res_cursor.h"
-#include "xe_trace.h"
+#include "xe_trace_bo.h"
#include "xe_ttm_stolen_mgr.h"
#include "xe_vm.h"
@@ -96,6 +96,20 @@ bool xe_bo_is_stolen(struct xe_bo *bo)
}
/**
+ * xe_bo_has_single_placement - check if BO is placed only in one memory location
+ * @bo: The BO
+ *
+ * This function checks whether a given BO is placed in only one memory location.
+ *
+ * Returns: true if the BO is placed in a single memory location, false otherwise.
+ *
+ */
+bool xe_bo_has_single_placement(struct xe_bo *bo)
+{
+ return bo->placement.num_placement == 1;
+}
+
+/**
* xe_bo_is_stolen_devmem - check if BO is of stolen type accessed via PCI BAR
* @bo: The BO
*
@@ -302,6 +316,18 @@ static int xe_tt_map_sg(struct ttm_tt *tt)
return 0;
}
+static void xe_tt_unmap_sg(struct ttm_tt *tt)
+{
+ struct xe_ttm_tt *xe_tt = container_of(tt, struct xe_ttm_tt, ttm);
+
+ if (xe_tt->sg) {
+ dma_unmap_sgtable(xe_tt->dev, xe_tt->sg,
+ DMA_BIDIRECTIONAL, 0);
+ sg_free_table(xe_tt->sg);
+ xe_tt->sg = NULL;
+ }
+}
+
struct sg_table *xe_bo_sg(struct xe_bo *bo)
{
struct ttm_tt *tt = bo->ttm.ttm;
@@ -352,6 +378,15 @@ static struct ttm_tt *xe_ttm_tt_create(struct ttm_buffer_object *ttm_bo,
(xe->info.graphics_verx100 >= 1270 && bo->flags & XE_BO_FLAG_PAGETABLE))
caching = ttm_write_combined;
+ if (bo->flags & XE_BO_FLAG_NEEDS_UC) {
+ /*
+ * Valid only for internally-created buffers only, for
+ * which cpu_caching is never initialized.
+ */
+ xe_assert(xe, bo->cpu_caching == 0);
+ caching = ttm_uncached;
+ }
+
err = ttm_tt_init(&tt->ttm, &bo->ttm, page_flags, caching, extra_pages);
if (err) {
kfree(tt);
@@ -377,27 +412,15 @@ static int xe_ttm_tt_populate(struct ttm_device *ttm_dev, struct ttm_tt *tt,
if (err)
return err;
- /* A follow up may move this xe_bo_move when BO is moved to XE_PL_TT */
- err = xe_tt_map_sg(tt);
- if (err)
- ttm_pool_free(&ttm_dev->pool, tt);
-
return err;
}
static void xe_ttm_tt_unpopulate(struct ttm_device *ttm_dev, struct ttm_tt *tt)
{
- struct xe_ttm_tt *xe_tt = container_of(tt, struct xe_ttm_tt, ttm);
-
if (tt->page_flags & TTM_TT_FLAG_EXTERNAL)
return;
- if (xe_tt->sg) {
- dma_unmap_sgtable(xe_tt->dev, xe_tt->sg,
- DMA_BIDIRECTIONAL, 0);
- sg_free_table(xe_tt->sg);
- xe_tt->sg = NULL;
- }
+ xe_tt_unmap_sg(tt);
return ttm_pool_free(&ttm_dev->pool, tt);
}
@@ -628,17 +651,21 @@ static int xe_bo_move(struct ttm_buffer_object *ttm_bo, bool evict,
bool handle_system_ccs = (!IS_DGFX(xe) && xe_bo_needs_ccs_pages(bo) &&
ttm && ttm_tt_is_populated(ttm)) ? true : false;
int ret = 0;
+
/* Bo creation path, moving to system or TT. */
if ((!old_mem && ttm) && !handle_system_ccs) {
- ttm_bo_move_null(ttm_bo, new_mem);
- return 0;
+ if (new_mem->mem_type == XE_PL_TT)
+ ret = xe_tt_map_sg(ttm);
+ if (!ret)
+ ttm_bo_move_null(ttm_bo, new_mem);
+ goto out;
}
if (ttm_bo->type == ttm_bo_type_sg) {
ret = xe_bo_move_notify(bo, ctx);
if (!ret)
ret = xe_bo_move_dmabuf(ttm_bo, new_mem);
- goto out;
+ return ret;
}
tt_has_data = ttm && (ttm_tt_is_populated(ttm) ||
@@ -650,6 +677,12 @@ static int xe_bo_move(struct ttm_buffer_object *ttm_bo, bool evict,
needs_clear = (ttm && ttm->page_flags & TTM_TT_FLAG_ZERO_ALLOC) ||
(!ttm && ttm_bo->type == ttm_bo_type_device);
+ if (new_mem->mem_type == XE_PL_TT) {
+ ret = xe_tt_map_sg(ttm);
+ if (ret)
+ goto out;
+ }
+
if ((move_lacks_source && !needs_clear)) {
ttm_bo_move_null(ttm_bo, new_mem);
goto out;
@@ -786,8 +819,11 @@ static int xe_bo_move(struct ttm_buffer_object *ttm_bo, bool evict,
xe_pm_runtime_put(xe);
out:
- return ret;
+ if ((!ttm_bo->resource || ttm_bo->resource->mem_type == XE_PL_SYSTEM) &&
+ ttm_bo->ttm)
+ xe_tt_unmap_sg(ttm_bo->ttm);
+ return ret;
}
/**
@@ -1731,11 +1767,10 @@ void xe_bo_unpin_external(struct xe_bo *bo)
xe_assert(xe, xe_bo_is_pinned(bo));
xe_assert(xe, xe_bo_is_user(bo));
- if (bo->ttm.pin_count == 1 && !list_empty(&bo->pinned_link)) {
- spin_lock(&xe->pinned.lock);
+ spin_lock(&xe->pinned.lock);
+ if (bo->ttm.pin_count == 1 && !list_empty(&bo->pinned_link))
list_del_init(&bo->pinned_link);
- spin_unlock(&xe->pinned.lock);
- }
+ spin_unlock(&xe->pinned.lock);
ttm_bo_unpin(&bo->ttm);
@@ -1758,9 +1793,8 @@ void xe_bo_unpin(struct xe_bo *bo)
struct ttm_place *place = &(bo->placements[0]);
if (mem_type_is_vram(place->mem_type)) {
- xe_assert(xe, !list_empty(&bo->pinned_link));
-
spin_lock(&xe->pinned.lock);
+ xe_assert(xe, !list_empty(&bo->pinned_link));
list_del_init(&bo->pinned_link);
spin_unlock(&xe->pinned.lock);
}
diff --git a/drivers/gpu/drm/xe/xe_bo.h b/drivers/gpu/drm/xe/xe_bo.h
index a885b14bf595..6de894c728f5 100644
--- a/drivers/gpu/drm/xe/xe_bo.h
+++ b/drivers/gpu/drm/xe/xe_bo.h
@@ -206,6 +206,7 @@ bool mem_type_is_vram(u32 mem_type);
bool xe_bo_is_vram(struct xe_bo *bo);
bool xe_bo_is_stolen(struct xe_bo *bo);
bool xe_bo_is_stolen_devmem(struct xe_bo *bo);
+bool xe_bo_has_single_placement(struct xe_bo *bo);
uint64_t vram_region_gpu_offset(struct ttm_resource *res);
bool xe_bo_can_migrate(struct xe_bo *bo, u32 mem_type);
diff --git a/drivers/gpu/drm/xe/xe_debugfs.c b/drivers/gpu/drm/xe/xe_debugfs.c
index 0b7aebaae843..1011e5d281fa 100644
--- a/drivers/gpu/drm/xe/xe_debugfs.c
+++ b/drivers/gpu/drm/xe/xe_debugfs.c
@@ -12,7 +12,10 @@
#include "xe_bo.h"
#include "xe_device.h"
+#include "xe_force_wake.h"
#include "xe_gt_debugfs.h"
+#include "xe_gt_printk.h"
+#include "xe_guc_ads.h"
#include "xe_pm.h"
#include "xe_sriov.h"
#include "xe_step.h"
@@ -118,6 +121,58 @@ static const struct file_operations forcewake_all_fops = {
.release = forcewake_release,
};
+static ssize_t wedged_mode_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->wedged.mode);
+
+ return simple_read_from_buffer(ubuf, size, pos, buf, len);
+}
+
+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;
+
+ if (xe->wedged.mode == wedged_mode)
+ return 0;
+
+ 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");
+ return -EIO;
+ }
+ }
+ xe_pm_runtime_put(xe);
+
+ return size;
+}
+
+static const struct file_operations wedged_mode_fops = {
+ .owner = THIS_MODULE,
+ .read = wedged_mode_show,
+ .write = wedged_mode_set,
+};
+
void xe_debugfs_register(struct xe_device *xe)
{
struct ttm_device *bdev = &xe->ttm;
@@ -135,6 +190,9 @@ void xe_debugfs_register(struct xe_device *xe)
debugfs_create_file("forcewake_all", 0400, root, xe,
&forcewake_all_fops);
+ debugfs_create_file("wedged_mode", 0400, root, xe,
+ &wedged_mode_fops);
+
for (mem_type = XE_PL_VRAM0; mem_type <= XE_PL_VRAM1; ++mem_type) {
man = ttm_manager_type(bdev, mem_type);
diff --git a/drivers/gpu/drm/xe/xe_devcoredump.c b/drivers/gpu/drm/xe/xe_devcoredump.c
index 3d7980232be1..62c2b10fbf1d 100644
--- a/drivers/gpu/drm/xe/xe_devcoredump.c
+++ b/drivers/gpu/drm/xe/xe_devcoredump.c
@@ -53,6 +53,9 @@
#ifdef CONFIG_DEV_COREDUMP
+/* 1 hour timeout */
+#define XE_COREDUMP_TIMEOUT_JIFFIES (60 * 60 * HZ)
+
static struct xe_device *coredump_to_xe(const struct xe_devcoredump *coredump)
{
return container_of(coredump, struct xe_device, devcoredump);
@@ -110,6 +113,7 @@ static ssize_t xe_devcoredump_read(char *buffer, loff_t offset,
drm_printf(&p, "Snapshot time: %lld.%09ld\n", ts.tv_sec, ts.tv_nsec);
ts = ktime_to_timespec64(ss->boot_time);
drm_printf(&p, "Uptime: %lld.%09ld\n", ts.tv_sec, ts.tv_nsec);
+ drm_printf(&p, "Process: %s\n", ss->process_name);
xe_device_snapshot_print(xe, &p);
drm_printf(&p, "\n**** GuC CT ****\n");
@@ -166,12 +170,24 @@ static void devcoredump_snapshot(struct xe_devcoredump *coredump,
enum xe_hw_engine_id id;
u32 adj_logical_mask = q->logical_mask;
u32 width_mask = (0x1 << q->width) - 1;
+ const char *process_name = "no process";
+ struct task_struct *task = NULL;
+
int i;
bool cookie;
ss->snapshot_time = ktime_get_real();
ss->boot_time = ktime_get_boottime();
+ if (q->vm && q->vm->xef) {
+ task = get_pid_task(q->vm->xef->drm->pid, PIDTYPE_PID);
+ if (task)
+ process_name = task->comm;
+ }
+ strscpy(ss->process_name, process_name);
+ if (task)
+ put_task_struct(task);
+
ss->gt = q->gt;
INIT_WORK(&ss->work, xe_devcoredump_deferred_snap_work);
@@ -234,17 +250,20 @@ void xe_devcoredump(struct xe_sched_job *job)
drm_info(&xe->drm, "Check your /sys/class/drm/card%d/device/devcoredump/data\n",
xe->drm.primary->index);
- dev_coredumpm(xe->drm.dev, THIS_MODULE, coredump, 0, GFP_KERNEL,
- xe_devcoredump_read, xe_devcoredump_free);
+ dev_coredumpm_timeout(xe->drm.dev, THIS_MODULE, coredump, 0, GFP_KERNEL,
+ xe_devcoredump_read, xe_devcoredump_free,
+ XE_COREDUMP_TIMEOUT_JIFFIES);
}
-static void xe_driver_devcoredump_fini(struct drm_device *drm, void *arg)
+static void xe_driver_devcoredump_fini(void *arg)
{
+ struct drm_device *drm = arg;
+
dev_coredump_put(drm->dev);
}
int xe_devcoredump_init(struct xe_device *xe)
{
- return drmm_add_action_or_reset(&xe->drm, xe_driver_devcoredump_fini, xe);
+ return devm_add_action_or_reset(xe->drm.dev, xe_driver_devcoredump_fini, &xe->drm);
}
#endif
diff --git a/drivers/gpu/drm/xe/xe_devcoredump_types.h b/drivers/gpu/drm/xe/xe_devcoredump_types.h
index 6f654b63c7f1..923cdf72a816 100644
--- a/drivers/gpu/drm/xe/xe_devcoredump_types.h
+++ b/drivers/gpu/drm/xe/xe_devcoredump_types.h
@@ -26,6 +26,8 @@ struct xe_devcoredump_snapshot {
ktime_t snapshot_time;
/** @boot_time: Relative boot time so the uptime can be calculated. */
ktime_t boot_time;
+ /** @process_name: Name of process that triggered this gpu hang */
+ char process_name[TASK_COMM_LEN];
/** @gt: Affected GT, used by forcewake for delayed capture */
struct xe_gt *gt;
diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c
index 5ef9b50a20d0..76109415eba6 100644
--- a/drivers/gpu/drm/xe/xe_device.c
+++ b/drivers/gpu/drm/xe/xe_device.c
@@ -5,6 +5,7 @@
#include "xe_device.h"
+#include <linux/delay.h>
#include <linux/units.h>
#include <drm/drm_aperture.h>
@@ -17,6 +18,7 @@
#include <drm/xe_drm.h>
#include "display/xe_display.h"
+#include "instructions/xe_gpu_commands.h"
#include "regs/xe_gt_regs.h"
#include "regs/xe_regs.h"
#include "xe_bo.h"
@@ -27,15 +29,20 @@
#include "xe_drv.h"
#include "xe_exec.h"
#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"
+#include "xe_gt_sriov_vf.h"
+#include "xe_guc.h"
#include "xe_hwmon.h"
#include "xe_irq.h"
#include "xe_memirq.h"
#include "xe_mmio.h"
#include "xe_module.h"
+#include "xe_observation.h"
#include "xe_pat.h"
#include "xe_pcode.h"
#include "xe_pm.h"
@@ -45,6 +52,7 @@
#include "xe_ttm_stolen_mgr.h"
#include "xe_ttm_sys_mgr.h"
#include "xe_vm.h"
+#include "xe_vram.h"
#include "xe_wait_user_fence.h"
static int xe_file_open(struct drm_device *dev, struct drm_file *file)
@@ -90,12 +98,16 @@ static void xe_file_close(struct drm_device *dev, struct drm_file *file)
struct xe_exec_queue *q;
unsigned long idx;
- mutex_lock(&xef->exec_queue.lock);
+ /*
+ * No need for exec_queue.lock here as there is no contention for it
+ * when FD is closing as IOCTLs presumably can't be modifying the
+ * xarray. Taking exec_queue.lock here causes undue dependency on
+ * vm->lock taken during xe_exec_queue_kill().
+ */
xa_for_each(&xef->exec_queue.xa, idx, q) {
xe_exec_queue_kill(q);
xe_exec_queue_put(q);
}
- mutex_unlock(&xef->exec_queue.lock);
xa_destroy(&xef->exec_queue.xa);
mutex_destroy(&xef->exec_queue.lock);
mutex_lock(&xef->vm.lock);
@@ -130,6 +142,7 @@ static const struct drm_ioctl_desc xe_ioctls[] = {
DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(XE_WAIT_USER_FENCE, xe_wait_user_fence_ioctl,
DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF_DRV(XE_OBSERVATION, xe_observation_ioctl, DRM_RENDER_ALLOW),
};
static long xe_drm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
@@ -138,6 +151,9 @@ static long xe_drm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
struct xe_device *xe = to_xe_device(file_priv->minor->dev);
long ret;
+ if (xe_device_wedged(xe))
+ return -ECANCELED;
+
ret = xe_pm_runtime_get_ioctl(xe);
if (ret >= 0)
ret = drm_ioctl(file, cmd, arg);
@@ -153,6 +169,9 @@ static long xe_drm_compat_ioctl(struct file *file, unsigned int cmd, unsigned lo
struct xe_device *xe = to_xe_device(file_priv->minor->dev);
long ret;
+ if (xe_device_wedged(xe))
+ return -ECANCELED;
+
ret = xe_pm_runtime_get_ioctl(xe);
if (ret >= 0)
ret = drm_compat_ioctl(file, cmd, arg);
@@ -180,13 +199,6 @@ static const struct file_operations xe_driver_fops = {
#endif
};
-static void xe_driver_release(struct drm_device *dev)
-{
- struct xe_device *xe = to_xe_device(dev);
-
- pci_set_drvdata(to_pci_dev(xe->drm.dev), NULL);
-}
-
static struct drm_driver driver = {
/* Don't use MTRRs here; the Xserver or userspace app should
* deal with them for Intel hardware.
@@ -205,8 +217,6 @@ static struct drm_driver driver = {
#ifdef CONFIG_PROC_FS
.show_fdinfo = xe_drm_client_fdinfo,
#endif
- .release = &xe_driver_release,
-
.ioctls = xe_ioctls,
.num_ioctls = ARRAY_SIZE(xe_ioctls),
.fops = &xe_driver_fops,
@@ -269,7 +279,10 @@ struct xe_device *xe_device_create(struct pci_dev *pdev,
init_waitqueue_head(&xe->ufence_wq);
- drmm_mutex_init(&xe->drm, &xe->usm.lock);
+ err = drmm_mutex_init(&xe->drm, &xe->usm.lock);
+ if (err)
+ goto err;
+
xa_init_flags(&xe->usm.asid_to_vm, XA_FLAGS_ALLOC);
if (IS_ENABLED(CONFIG_DRM_XE_DEBUG)) {
@@ -378,7 +391,7 @@ static void xe_driver_flr(struct xe_device *xe)
xe_mmio_write32(gt, GU_DEBUG, DRIVERFLR_STATUS);
}
-static void xe_driver_flr_fini(struct drm_device *drm, void *arg)
+static void xe_driver_flr_fini(void *arg)
{
struct xe_device *xe = arg;
@@ -386,7 +399,7 @@ static void xe_driver_flr_fini(struct drm_device *drm, void *arg)
xe_driver_flr(xe);
}
-static void xe_device_sanitize(struct drm_device *drm, void *arg)
+static void xe_device_sanitize(void *arg)
{
struct xe_device *xe = arg;
struct xe_gt *gt;
@@ -473,6 +486,17 @@ static int wait_for_lmem_ready(struct xe_device *xe)
return 0;
}
+static void update_device_info(struct xe_device *xe)
+{
+ /* disable features that are not available/applicable to VFs */
+ if (IS_SRIOV_VF(xe)) {
+ xe->info.enable_display = 0;
+ xe->info.has_heci_gscfi = 0;
+ xe->info.skip_guc_pc = 1;
+ xe->info.skip_pcode = 1;
+ }
+}
+
/**
* xe_device_probe_early: Device early probe
* @xe: xe device instance
@@ -493,6 +517,8 @@ int xe_device_probe_early(struct xe_device *xe)
xe_sriov_probe_early(xe);
+ update_device_info(xe);
+
err = xe_pcode_probe_early(xe);
if (err)
return err;
@@ -501,6 +527,8 @@ int xe_device_probe_early(struct xe_device *xe)
if (err)
return err;
+ xe->wedged.mode = xe_modparam.wedged_mode;
+
return 0;
}
@@ -551,14 +579,28 @@ int xe_device_probe(struct xe_device *xe)
if (err)
return err;
- xe_mmio_probe_tiles(xe);
+ err = xe_mmio_probe_tiles(xe);
+ if (err)
+ return err;
xe_ttm_sys_mgr_init(xe);
- for_each_gt(gt, xe, id)
- xe_force_wake_init_gt(gt, gt_to_fw(gt));
+ for_each_gt(gt, xe, id) {
+ err = xe_gt_init_early(gt);
+ if (err)
+ return err;
+ }
for_each_tile(tile, xe, id) {
+ if (IS_SRIOV_VF(xe)) {
+ xe_guc_comm_init_early(&tile->primary_gt->uc.guc);
+ err = xe_gt_sriov_vf_bootstrap(tile->primary_gt);
+ if (err)
+ return err;
+ err = xe_gt_sriov_vf_query_config(tile->primary_gt);
+ if (err)
+ return err;
+ }
err = xe_ggtt_init_early(tile->mem.ggtt);
if (err)
return err;
@@ -578,13 +620,10 @@ int xe_device_probe(struct xe_device *xe)
err = xe_devcoredump_init(xe);
if (err)
return err;
- err = drmm_add_action_or_reset(&xe->drm, xe_driver_flr_fini, xe);
+ err = devm_add_action_or_reset(xe->drm.dev, xe_driver_flr_fini, xe);
if (err)
return err;
- for_each_gt(gt, xe, id)
- xe_pcode_init(gt);
-
err = xe_display_init_noirq(xe);
if (err)
return err;
@@ -593,24 +632,18 @@ int xe_device_probe(struct xe_device *xe)
if (err)
goto err;
- for_each_gt(gt, xe, id) {
- err = xe_gt_init_early(gt);
- if (err)
- goto err_irq_shutdown;
- }
-
err = xe_device_set_has_flat_ccs(xe);
if (err)
- goto err_irq_shutdown;
+ goto err;
- err = xe_mmio_probe_vram(xe);
+ err = xe_vram_probe(xe);
if (err)
- goto err_irq_shutdown;
+ goto err;
for_each_tile(tile, xe, id) {
err = xe_tile_init_noalloc(tile);
if (err)
- goto err_irq_shutdown;
+ goto err;
}
/* Allocate and map stolen after potential VRAM resize */
@@ -624,7 +657,7 @@ int xe_device_probe(struct xe_device *xe)
*/
err = xe_display_init_noaccel(xe);
if (err)
- goto err_irq_shutdown;
+ goto err;
for_each_gt(gt, xe, id) {
last_gt = id;
@@ -636,25 +669,37 @@ int xe_device_probe(struct xe_device *xe)
xe_heci_gsc_init(xe);
- err = xe_display_init(xe);
+ err = xe_oa_init(xe);
if (err)
goto err_fini_gt;
+ err = xe_display_init(xe);
+ if (err)
+ goto err_fini_oa;
+
err = drm_dev_register(&xe->drm, 0);
if (err)
goto err_fini_display;
xe_display_register(xe);
+ xe_oa_register(xe);
+
xe_debugfs_register(xe);
xe_hwmon_register(xe);
- return drmm_add_action_or_reset(&xe->drm, xe_device_sanitize, xe);
+ for_each_gt(gt, xe, id)
+ xe_gt_sanitize_freq(gt);
+
+ return devm_add_action_or_reset(xe->drm.dev, xe_device_sanitize, xe);
err_fini_display:
xe_display_driver_remove(xe);
+err_fini_oa:
+ xe_oa_fini(xe);
+
err_fini_gt:
for_each_gt(gt, xe, id) {
if (id < last_gt)
@@ -663,8 +708,6 @@ err_fini_gt:
break;
}
-err_irq_shutdown:
- xe_irq_shutdown(xe);
err:
xe_display_fini(xe);
return err;
@@ -683,16 +726,18 @@ void xe_device_remove(struct xe_device *xe)
struct xe_gt *gt;
u8 id;
+ xe_oa_unregister(xe);
+
xe_device_remove_display(xe);
xe_display_fini(xe);
+ xe_oa_fini(xe);
+
xe_heci_gsc_fini(xe);
for_each_gt(gt, xe, id)
xe_gt_remove(gt);
-
- xe_irq_shutdown(xe);
}
void xe_device_shutdown(struct xe_device *xe)
@@ -708,6 +753,55 @@ void xe_device_wmb(struct xe_device *xe)
xe_mmio_write32(gt, SOFTWARE_FLAGS_SPR33, 0);
}
+/**
+ * xe_device_td_flush() - Flush transient L3 cache entries
+ * @xe: The device
+ *
+ * Display engine has direct access to memory and is never coherent with L3/L4
+ * caches (or CPU caches), however KMD is responsible for specifically flushing
+ * transient L3 GPU cache entries prior to the flip sequence to ensure scanout
+ * can happen from such a surface without seeing corruption.
+ *
+ * Display surfaces can be tagged as transient by mapping it using one of the
+ * various L3:XD PAT index modes on Xe2.
+ *
+ * Note: On non-discrete xe2 platforms, like LNL, the entire L3 cache is flushed
+ * at the end of each submission via PIPE_CONTROL for compute/render, since SA
+ * Media is not coherent with L3 and we want to support render-vs-media
+ * usescases. For other engines like copy/blt the HW internally forces uncached
+ * behaviour, hence why we can skip the TDF on such platforms.
+ */
+void xe_device_td_flush(struct xe_device *xe)
+{
+ struct xe_gt *gt;
+ u8 id;
+
+ if (!IS_DGFX(xe) || GRAPHICS_VER(xe) < 20)
+ return;
+
+ for_each_gt(gt, xe, id) {
+ if (xe_gt_is_media_type(gt))
+ continue;
+
+ if (xe_force_wake_get(gt_to_fw(gt), XE_FW_GT))
+ return;
+
+ xe_mmio_write32(gt, XE2_TDF_CTRL, TRANSIENT_FLUSH_REQUEST);
+ /*
+ * FIXME: We can likely do better here with our choice of
+ * timeout. Currently we just assume the worst case, i.e. 150us,
+ * which is believed to be sufficient to cover the worst case
+ * scenario on current platforms if all cache entries are
+ * transient and need to be flushed..
+ */
+ if (xe_mmio_wait32(gt, XE2_TDF_CTRL, TRANSIENT_FLUSH_REQUEST, 0,
+ 150, NULL, false))
+ xe_gt_err_once(gt, "TD flush timeout\n");
+
+ xe_force_wake_put(gt_to_fw(gt), XE_FW_GT);
+ }
+}
+
u32 xe_device_ccs_bytes(struct xe_device *xe, u64 size)
{
return xe_device_has_flat_ccs(xe) ?
@@ -759,3 +853,54 @@ u64 xe_device_uncanonicalize_addr(struct xe_device *xe, u64 address)
{
return address & GENMASK_ULL(xe->info.va_bits - 1, 0);
}
+
+static void xe_device_wedged_fini(struct drm_device *drm, void *arg)
+{
+ struct xe_device *xe = arg;
+
+ xe_pm_runtime_put(xe);
+}
+
+/**
+ * xe_device_declare_wedged - Declare device wedged
+ * @xe: xe device instance
+ *
+ * This is a final state that can only be cleared with a mudule
+ * re-probe (unbind + bind).
+ * In this state every IOCTL will be blocked so the GT cannot be used.
+ * In general it will be called upon any critical error such as gt reset
+ * failure or guc loading failure.
+ * If xe.wedged module parameter is set to 2, this function will be called
+ * on every single execution timeout (a.k.a. GPU hang) right after devcoredump
+ * snapshot capture. In this mode, GT reset won't be attempted so the state of
+ * the issue is preserved for further debugging.
+ */
+void xe_device_declare_wedged(struct xe_device *xe)
+{
+ struct xe_gt *gt;
+ u8 id;
+
+ if (xe->wedged.mode == 0) {
+ drm_dbg(&xe->drm, "Wedged mode is forcibly disabled\n");
+ return;
+ }
+
+ if (drmm_add_action_or_reset(&xe->drm, xe_device_wedged_fini, xe)) {
+ drm_err(&xe->drm, "Failed to register xe_device_wedged_fini clean-up. Although device is wedged.\n");
+ return;
+ }
+
+ xe_pm_runtime_get_noresume(xe);
+
+ if (!atomic_xchg(&xe->wedged.flag, 1)) {
+ xe->needs_flr_on_fini = true;
+ drm_err(&xe->drm,
+ "CRITICAL: Xe has declared device %s as wedged.\n"
+ "IOCTLs and executions are blocked. Only a rebind may clear the failure\n"
+ "Please file a _new_ bug report at https://gitlab.freedesktop.org/drm/xe/kernel/issues/new\n",
+ dev_name(xe->drm.dev));
+ }
+
+ for_each_gt(gt, xe, id)
+ xe_gt_declare_wedged(gt);
+}
diff --git a/drivers/gpu/drm/xe/xe_device.h b/drivers/gpu/drm/xe/xe_device.h
index 36d4434ebccc..bb07f5669dbb 100644
--- a/drivers/gpu/drm/xe/xe_device.h
+++ b/drivers/gpu/drm/xe/xe_device.h
@@ -6,15 +6,9 @@
#ifndef _XE_DEVICE_H_
#define _XE_DEVICE_H_
-struct xe_exec_queue;
-struct xe_file;
-
#include <drm/drm_util.h>
-#include "regs/xe_gpu_commands.h"
#include "xe_device_types.h"
-#include "xe_force_wake.h"
-#include "xe_macros.h"
static inline struct xe_device *to_xe_device(const struct drm_device *dev)
{
@@ -167,4 +161,13 @@ void xe_device_snapshot_print(struct xe_device *xe, struct drm_printer *p);
u64 xe_device_canonicalize_addr(struct xe_device *xe, u64 address);
u64 xe_device_uncanonicalize_addr(struct xe_device *xe, u64 address);
+void xe_device_td_flush(struct xe_device *xe);
+
+static inline bool xe_device_wedged(struct xe_device *xe)
+{
+ return atomic_read(&xe->wedged.flag);
+}
+
+void xe_device_declare_wedged(struct xe_device *xe);
+
#endif
diff --git a/drivers/gpu/drm/xe/xe_device_sysfs.c b/drivers/gpu/drm/xe/xe_device_sysfs.c
index 21677b8cd977..7375937934fa 100644
--- a/drivers/gpu/drm/xe/xe_device_sysfs.c
+++ b/drivers/gpu/drm/xe/xe_device_sysfs.c
@@ -69,7 +69,7 @@ vram_d3cold_threshold_store(struct device *dev, struct device_attribute *attr,
static DEVICE_ATTR_RW(vram_d3cold_threshold);
-static void xe_device_sysfs_fini(struct drm_device *drm, void *arg)
+static void xe_device_sysfs_fini(void *arg)
{
struct xe_device *xe = arg;
@@ -85,5 +85,5 @@ int xe_device_sysfs_init(struct xe_device *xe)
if (ret)
return ret;
- return drmm_add_action_or_reset(&xe->drm, xe_device_sysfs_fini, xe);
+ return devm_add_action_or_reset(dev, xe_device_sysfs_fini, xe);
}
diff --git a/drivers/gpu/drm/xe/xe_device_types.h b/drivers/gpu/drm/xe/xe_device_types.h
index 2e62450d86e1..3bca6d344744 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_gt_types.h"
#include "xe_lmtt_types.h"
#include "xe_memirq_types.h"
+#include "xe_oa.h"
#include "xe_platform_types.h"
#include "xe_pt_types.h"
#include "xe_sriov_types.h"
@@ -196,6 +197,9 @@ struct xe_tile {
struct {
/** @sriov.vf.memirq: Memory Based Interrupts. */
struct xe_memirq memirq;
+
+ /** @sriov.vf.ggtt_balloon: GGTT regions excluded from use. */
+ struct drm_mm_node ggtt_balloon[2];
} vf;
} sriov;
@@ -218,6 +222,8 @@ struct xe_device {
/** @info: device info */
struct intel_device_info {
+ /** @info.platform_name: platform name */
+ const char *platform_name;
/** @info.graphics_name: graphics IP name */
const char *graphics_name;
/** @info.media_name: media IP name */
@@ -281,6 +287,10 @@ struct xe_device {
u8 has_heci_gscfi:1;
/** @info.skip_guc_pc: Skip GuC based PM feature init */
u8 skip_guc_pc:1;
+ /** @info.has_atomic_enable_pte_bit: Device has atomic enable PTE bit */
+ u8 has_atomic_enable_pte_bit:1;
+ /** @info.has_device_atomics_on_smem: Supports device atomics on SMEM */
+ u8 has_device_atomics_on_smem:1;
#if IS_ENABLED(CONFIG_DRM_XE_DISPLAY)
struct {
@@ -427,9 +437,6 @@ struct xe_device {
/** @d3cold.allowed: Indicates if d3cold is a valid device state */
bool allowed;
- /** @d3cold.power_lost: Indicates if card has really lost power. */
- bool power_lost;
-
/**
* @d3cold.vram_threshold:
*
@@ -456,9 +463,20 @@ struct xe_device {
/** @heci_gsc: graphics security controller */
struct xe_heci_gsc heci_gsc;
+ /** @oa: oa observation subsystem */
+ struct xe_oa oa;
+
/** @needs_flr_on_fini: requests function-reset on fini */
bool needs_flr_on_fini;
+ /** @wedged: Struct to control Wedged States and mode */
+ struct {
+ /** @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;
+ } wedged;
+
/* private: */
#if IS_ENABLED(CONFIG_DRM_XE_DISPLAY)
@@ -484,6 +502,7 @@ struct xe_device {
INTEL_DRAM_LPDDR4,
INTEL_DRAM_DDR5,
INTEL_DRAM_LPDDR5,
+ INTEL_DRAM_GDDR,
} type;
u8 num_qgv_points;
u8 num_psf_gv_points;
@@ -498,14 +517,9 @@ struct xe_device {
/* To shut up runtime pm macros.. */
struct xe_runtime_pm {} runtime_pm;
- /* For pcode */
- struct mutex sb_lock;
-
/* only to allow build, not used functionally */
u32 irq_mask;
- u32 enabled_irq_mask;
-
struct intel_uncore {
spinlock_t lock;
} uncore;
@@ -547,6 +561,9 @@ struct xe_file {
struct mutex lock;
} exec_queue;
+ /** @run_ticks: hw engine class run time in ticks for this drm client */
+ u64 run_ticks[XE_ENGINE_CLASS_MAX];
+
/** @client: drm client */
struct xe_drm_client *client;
};
diff --git a/drivers/gpu/drm/xe/xe_drm_client.c b/drivers/gpu/drm/xe/xe_drm_client.c
index 08f0b7c95901..6a26923fa10e 100644
--- a/drivers/gpu/drm/xe/xe_drm_client.c
+++ b/drivers/gpu/drm/xe/xe_drm_client.c
@@ -2,6 +2,7 @@
/*
* Copyright © 2023 Intel Corporation
*/
+#include "xe_drm_client.h"
#include <drm/drm_print.h>
#include <drm/xe_drm.h>
@@ -12,10 +13,67 @@
#include "xe_bo.h"
#include "xe_bo_types.h"
#include "xe_device_types.h"
-#include "xe_drm_client.h"
+#include "xe_exec_queue.h"
+#include "xe_force_wake.h"
+#include "xe_gt.h"
+#include "xe_hw_engine.h"
+#include "xe_pm.h"
#include "xe_trace.h"
/**
+ * DOC: DRM Client usage stats
+ *
+ * The drm/xe driver implements the DRM client usage stats specification as
+ * documented in :ref:`drm-client-usage-stats`.
+ *
+ * Example of the output showing the implemented key value pairs and entirety of
+ * the currently possible format options:
+ *
+ * ::
+ *
+ * pos: 0
+ * flags: 0100002
+ * mnt_id: 26
+ * ino: 685
+ * drm-driver: xe
+ * drm-client-id: 3
+ * drm-pdev: 0000:03:00.0
+ * drm-total-system: 0
+ * drm-shared-system: 0
+ * drm-active-system: 0
+ * drm-resident-system: 0
+ * drm-purgeable-system: 0
+ * drm-total-gtt: 192 KiB
+ * drm-shared-gtt: 0
+ * drm-active-gtt: 0
+ * drm-resident-gtt: 192 KiB
+ * drm-total-vram0: 23992 KiB
+ * drm-shared-vram0: 16 MiB
+ * drm-active-vram0: 0
+ * drm-resident-vram0: 23992 KiB
+ * drm-total-stolen: 0
+ * drm-shared-stolen: 0
+ * drm-active-stolen: 0
+ * drm-resident-stolen: 0
+ * drm-cycles-rcs: 28257900
+ * drm-total-cycles-rcs: 7655183225
+ * drm-cycles-bcs: 0
+ * drm-total-cycles-bcs: 7655183225
+ * drm-cycles-vcs: 0
+ * drm-total-cycles-vcs: 7655183225
+ * drm-engine-capacity-vcs: 2
+ * drm-cycles-vecs: 0
+ * drm-total-cycles-vecs: 7655183225
+ * drm-engine-capacity-vecs: 2
+ * drm-cycles-ccs: 0
+ * drm-total-cycles-ccs: 7655183225
+ * drm-engine-capacity-ccs: 4
+ *
+ * Possible `drm-cycles-` key names are: `rcs`, `ccs`, `bcs`, `vcs`, `vecs` and
+ * "other".
+ */
+
+/**
* xe_drm_client_alloc() - Allocate drm client
* @void: No arg
*
@@ -179,6 +237,76 @@ static void show_meminfo(struct drm_printer *p, struct drm_file *file)
}
}
+static void show_run_ticks(struct drm_printer *p, struct drm_file *file)
+{
+ unsigned long class, i, gt_id, capacity[XE_ENGINE_CLASS_MAX] = { };
+ struct xe_file *xef = file->driver_priv;
+ struct xe_device *xe = xef->xe;
+ struct xe_gt *gt;
+ struct xe_hw_engine *hwe;
+ struct xe_exec_queue *q;
+ u64 gpu_timestamp;
+
+ xe_pm_runtime_get(xe);
+
+ /* 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_update_run_ticks(q);
+ xef->run_ticks[q->class] += q->run_ticks - q->old_run_ticks;
+ q->old_run_ticks = q->run_ticks;
+ }
+ mutex_unlock(&xef->exec_queue.lock);
+
+ /* Get the total GPU cycles */
+ for_each_gt(gt, xe, gt_id) {
+ enum xe_force_wake_domains fw;
+
+ hwe = xe_gt_any_hw_engine(gt);
+ if (!hwe)
+ continue;
+
+ fw = xe_hw_engine_to_fw_domain(hwe);
+ if (xe_force_wake_get(gt_to_fw(gt), fw)) {
+ hwe = NULL;
+ break;
+ }
+
+ gpu_timestamp = xe_hw_engine_read_timestamp(hwe);
+ XE_WARN_ON(xe_force_wake_put(gt_to_fw(gt), fw));
+ break;
+ }
+
+ xe_pm_runtime_put(xe);
+
+ if (unlikely(!hwe))
+ return;
+
+ for (class = 0; class < XE_ENGINE_CLASS_MAX; class++) {
+ const char *class_name;
+
+ for_each_gt(gt, xe, gt_id)
+ capacity[class] += gt->user_engines.instances_per_class[class];
+
+ /*
+ * Engines may be fused off or not exposed to userspace. Don't
+ * return anything if this entire class is not available
+ */
+ if (!capacity[class])
+ continue;
+
+ class_name = xe_hw_engine_class_to_str(class);
+ drm_printf(p, "drm-cycles-%s:\t%llu\n",
+ class_name, xef->run_ticks[class]);
+ drm_printf(p, "drm-total-cycles-%s:\t%llu\n",
+ class_name, gpu_timestamp);
+
+ if (capacity[class] > 1)
+ drm_printf(p, "drm-engine-capacity-%s:\t%lu\n",
+ class_name, capacity[class]);
+ }
+}
+
/**
* xe_drm_client_fdinfo() - Callback for fdinfo interface
* @p: The drm_printer ptr
@@ -192,5 +320,6 @@ static void show_meminfo(struct drm_printer *p, struct drm_file *file)
void xe_drm_client_fdinfo(struct drm_printer *p, struct drm_file *file)
{
show_meminfo(p, file);
+ show_run_ticks(p, file);
}
#endif
diff --git a/drivers/gpu/drm/xe/xe_exec.c b/drivers/gpu/drm/xe/xe_exec.c
index 97eeb973e897..f36980aa26e6 100644
--- a/drivers/gpu/drm/xe/xe_exec.c
+++ b/drivers/gpu/drm/xe/xe_exec.c
@@ -118,7 +118,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 = 0, num_ufence = 0;
+ u32 i, num_syncs, num_ufence = 0;
struct xe_sched_job *job;
struct xe_vm *vm;
bool write_locked, skip_retry = false;
@@ -141,7 +141,7 @@ int xe_exec_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
q->width != args->num_batch_buffer))
return -EINVAL;
- if (XE_IOCTL_DBG(xe, q->flags & EXEC_QUEUE_FLAG_BANNED)) {
+ if (XE_IOCTL_DBG(xe, q->ops->reset_status(q))) {
err = -ECANCELED;
goto err_exec_queue;
}
@@ -156,15 +156,15 @@ int xe_exec_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
vm = q->vm;
- for (i = 0; i < args->num_syncs; i++) {
- err = xe_sync_entry_parse(xe, xef, &syncs[num_syncs++],
- &syncs_user[i], SYNC_PARSE_FLAG_EXEC |
+ for (num_syncs = 0; num_syncs < args->num_syncs; num_syncs++) {
+ err = xe_sync_entry_parse(xe, xef, &syncs[num_syncs],
+ &syncs_user[num_syncs], SYNC_PARSE_FLAG_EXEC |
(xe_vm_in_lr_mode(vm) ?
SYNC_PARSE_FLAG_LR_MODE : 0));
if (err)
goto err_syncs;
- if (xe_sync_is_ufence(&syncs[i]))
+ if (xe_sync_is_ufence(&syncs[num_syncs]))
num_ufence++;
}
@@ -259,9 +259,9 @@ retry:
/* Wait behind rebinds */
if (!xe_vm_in_lr_mode(vm)) {
- err = drm_sched_job_add_resv_dependencies(&job->drm,
- xe_vm_resv(vm),
- DMA_RESV_USAGE_KERNEL);
+ err = xe_sched_job_add_deps(job,
+ xe_vm_resv(vm),
+ DMA_RESV_USAGE_KERNEL);
if (err)
goto err_put_job;
}
@@ -325,8 +325,8 @@ err_unlock_list:
if (err == -EAGAIN && !skip_retry)
goto retry;
err_syncs:
- for (i = 0; i < num_syncs; i++)
- xe_sync_entry_cleanup(&syncs[i]);
+ while (num_syncs--)
+ xe_sync_entry_cleanup(&syncs[num_syncs]);
kfree(syncs);
err_exec_queue:
xe_exec_queue_put(q);
diff --git a/drivers/gpu/drm/xe/xe_exec_queue.c b/drivers/gpu/drm/xe/xe_exec_queue.c
index 395de93579fa..0ba37835849b 100644
--- a/drivers/gpu/drm/xe/xe_exec_queue.c
+++ b/drivers/gpu/drm/xe/xe_exec_queue.c
@@ -67,7 +67,7 @@ static struct xe_exec_queue *__xe_exec_queue_alloc(struct xe_device *xe,
q->fence_irq = &gt->fence_irq[hwe->class];
q->ring_ops = gt->ring_ops[hwe->class];
q->ops = gt->exec_queue_ops;
- INIT_LIST_HEAD(&q->compute.link);
+ INIT_LIST_HEAD(&q->lr.link);
INIT_LIST_HEAD(&q->multi_gt_link);
q->sched_props.timeslice_us = hwe->eclass->sched_props.timeslice_us;
@@ -86,7 +86,7 @@ static struct xe_exec_queue *__xe_exec_queue_alloc(struct xe_device *xe,
if (extensions) {
/*
- * may set q->usm, must come before xe_lrc_init(),
+ * 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);
@@ -96,45 +96,30 @@ static struct xe_exec_queue *__xe_exec_queue_alloc(struct xe_device *xe,
}
}
- if (xe_exec_queue_is_parallel(q)) {
- q->parallel.composite_fence_ctx = dma_fence_context_alloc(1);
- q->parallel.composite_fence_seqno = XE_FENCE_INITIAL_SEQNO;
- }
-
return q;
}
static int __xe_exec_queue_init(struct xe_exec_queue *q)
{
- struct xe_device *xe = gt_to_xe(q->gt);
int i, err;
for (i = 0; i < q->width; ++i) {
- err = xe_lrc_init(q->lrc + i, q->hwe, q, q->vm, SZ_16K);
- if (err)
+ q->lrc[i] = xe_lrc_create(q->hwe, q->vm, SZ_16K);
+ if (IS_ERR(q->lrc[i])) {
+ err = PTR_ERR(q->lrc[i]);
goto err_lrc;
+ }
}
err = q->ops->init(q);
if (err)
goto err_lrc;
- /*
- * Normally the user vm holds an rpm ref to keep the device
- * awake, and the context holds a ref for the vm, however for
- * some engines we use the kernels migrate vm underneath which offers no
- * such rpm ref, or we lack a vm. Make sure we keep a ref here, so we
- * can perform GuC CT actions when needed. Caller is expected to have
- * already grabbed the rpm ref outside any sensitive locks.
- */
- if (!(q->flags & EXEC_QUEUE_FLAG_PERMANENT) && (q->flags & EXEC_QUEUE_FLAG_VM || !q->vm))
- xe_pm_runtime_get_noresume(xe);
-
return 0;
err_lrc:
for (i = i - 1; i >= 0; --i)
- xe_lrc_finish(q->lrc + i);
+ xe_lrc_put(q->lrc[i]);
return err;
}
@@ -215,9 +200,7 @@ void xe_exec_queue_fini(struct xe_exec_queue *q)
int i;
for (i = 0; i < q->width; ++i)
- xe_lrc_finish(q->lrc + i);
- if (!(q->flags & EXEC_QUEUE_FLAG_PERMANENT) && (q->flags & EXEC_QUEUE_FLAG_VM || !q->vm))
- xe_pm_runtime_put(gt_to_xe(q->gt));
+ xe_lrc_put(q->lrc[i]);
__xe_exec_queue_free(q);
}
@@ -650,8 +633,8 @@ int xe_exec_queue_create_ioctl(struct drm_device *dev, void *data,
return PTR_ERR(q);
if (xe_vm_in_preempt_fence_mode(vm)) {
- q->compute.context = dma_fence_context_alloc(1);
- spin_lock_init(&q->compute.lock);
+ q->lr.context = dma_fence_context_alloc(1);
+ spin_lock_init(&q->lr.lock);
err = xe_vm_add_compute_exec_queue(vm, q);
if (XE_IOCTL_DBG(xe, err))
@@ -694,7 +677,7 @@ int xe_exec_queue_get_property_ioctl(struct drm_device *dev, void *data,
switch (args->property) {
case DRM_XE_EXEC_QUEUE_GET_PROPERTY_BAN:
- args->value = !!(q->flags & EXEC_QUEUE_FLAG_BANNED);
+ args->value = q->ops->reset_status(q);
ret = 0;
break;
default:
@@ -720,7 +703,7 @@ bool xe_exec_queue_is_lr(struct xe_exec_queue *q)
static s32 xe_exec_queue_num_job_inflight(struct xe_exec_queue *q)
{
- return q->lrc->fence_ctx.next_seqno - xe_lrc_seqno(q->lrc) - 1;
+ return q->lrc[0]->fence_ctx.next_seqno - xe_lrc_seqno(q->lrc[0]) - 1;
}
/**
@@ -731,7 +714,7 @@ static s32 xe_exec_queue_num_job_inflight(struct xe_exec_queue *q)
*/
bool xe_exec_queue_ring_full(struct xe_exec_queue *q)
{
- struct xe_lrc *lrc = q->lrc;
+ struct xe_lrc *lrc = q->lrc[0];
s32 max_job = lrc->ring.size / MAX_JOB_SIZE_BYTES;
return xe_exec_queue_num_job_inflight(q) >= max_job;
@@ -757,16 +740,50 @@ bool xe_exec_queue_is_idle(struct xe_exec_queue *q)
int i;
for (i = 0; i < q->width; ++i) {
- if (xe_lrc_seqno(&q->lrc[i]) !=
- q->lrc[i].fence_ctx.next_seqno - 1)
+ if (xe_lrc_seqno(q->lrc[i]) !=
+ q->lrc[i]->fence_ctx.next_seqno - 1)
return false;
}
return true;
}
- return xe_lrc_seqno(&q->lrc[0]) ==
- q->lrc[0].fence_ctx.next_seqno - 1;
+ return xe_lrc_seqno(q->lrc[0]) ==
+ q->lrc[0]->fence_ctx.next_seqno - 1;
+}
+
+/**
+ * xe_exec_queue_update_run_ticks() - Update run time in ticks for this exec queue
+ * from hw
+ * @q: The exec queue
+ *
+ * Update the timestamp saved by HW for this exec queue and save run ticks
+ * calculated by using the delta from last update.
+ */
+void xe_exec_queue_update_run_ticks(struct xe_exec_queue *q)
+{
+ struct xe_lrc *lrc;
+ u32 old_ts, new_ts;
+
+ /*
+ * Jobs that are run during driver load may use an exec_queue, but are
+ * not associated with a user xe file, so avoid accumulating busyness
+ * for kernel specific work.
+ */
+ if (!q->vm || !q->vm->xef)
+ return;
+
+ /*
+ * Only sample the first LRC. For parallel submission, all of them are
+ * scheduled together and we compensate that below by multiplying by
+ * width - this may introduce errors if that premise is not true and
+ * they don't exit 100% aligned. On the other hand, looping through
+ * the LRCs and reading them in different time could also introduce
+ * errors.
+ */
+ lrc = q->lrc[0];
+ new_ts = xe_lrc_update_timestamp(lrc, &old_ts);
+ q->run_ticks += (new_ts - old_ts) * q->width;
}
void xe_exec_queue_kill(struct xe_exec_queue *q)
diff --git a/drivers/gpu/drm/xe/xe_exec_queue.h b/drivers/gpu/drm/xe/xe_exec_queue.h
index 02ce8d204622..289a3a51d2a2 100644
--- a/drivers/gpu/drm/xe/xe_exec_queue.h
+++ b/drivers/gpu/drm/xe/xe_exec_queue.h
@@ -26,6 +26,15 @@ void xe_exec_queue_fini(struct xe_exec_queue *q);
void xe_exec_queue_destroy(struct kref *ref);
void xe_exec_queue_assign_name(struct xe_exec_queue *q, u32 instance);
+static inline struct xe_exec_queue *
+xe_exec_queue_get_unless_zero(struct xe_exec_queue *q)
+{
+ if (kref_get_unless_zero(&q->refcount))
+ return q;
+
+ return NULL;
+}
+
struct xe_exec_queue *xe_exec_queue_lookup(struct xe_file *xef, u32 id);
static inline struct xe_exec_queue *xe_exec_queue_get(struct xe_exec_queue *q)
@@ -66,5 +75,6 @@ struct dma_fence *xe_exec_queue_last_fence_get(struct xe_exec_queue *e,
struct xe_vm *vm);
void xe_exec_queue_last_fence_set(struct xe_exec_queue *e, struct xe_vm *vm,
struct dma_fence *fence);
+void xe_exec_queue_update_run_ticks(struct xe_exec_queue *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 ee78d497d838..201588ec33c3 100644
--- a/drivers/gpu/drm/xe/xe_exec_queue_types.h
+++ b/drivers/gpu/drm/xe/xe_exec_queue_types.h
@@ -70,18 +70,16 @@ struct xe_exec_queue {
*/
struct dma_fence *last_fence;
-/* queue no longer allowed to submit */
-#define EXEC_QUEUE_FLAG_BANNED BIT(0)
/* queue used for kernel submission only */
-#define EXEC_QUEUE_FLAG_KERNEL BIT(1)
+#define EXEC_QUEUE_FLAG_KERNEL BIT(0)
/* kernel engine only destroyed at driver unload */
-#define EXEC_QUEUE_FLAG_PERMANENT BIT(2)
+#define EXEC_QUEUE_FLAG_PERMANENT BIT(1)
/* for VM jobs. Caller needs to hold rpm ref when creating queue with this flag */
-#define EXEC_QUEUE_FLAG_VM BIT(3)
+#define EXEC_QUEUE_FLAG_VM BIT(2)
/* child of VM queue for multi-tile VM jobs */
-#define EXEC_QUEUE_FLAG_BIND_ENGINE_CHILD BIT(4)
+#define EXEC_QUEUE_FLAG_BIND_ENGINE_CHILD BIT(3)
/* kernel exec_queue only, set priority to highest level */
-#define EXEC_QUEUE_FLAG_HIGH_PRIORITY BIT(5)
+#define EXEC_QUEUE_FLAG_HIGH_PRIORITY BIT(4)
/**
* @flags: flags for this exec queue, should statically setup aside from ban
@@ -103,16 +101,6 @@ struct xe_exec_queue {
struct xe_guc_exec_queue *guc;
};
- /**
- * @parallel: parallel submission state
- */
- struct {
- /** @parallel.composite_fence_ctx: context composite fence */
- u64 composite_fence_ctx;
- /** @parallel.composite_fence_seqno: seqno for composite fence */
- u32 composite_fence_seqno;
- } parallel;
-
/** @sched_props: scheduling properties */
struct {
/** @sched_props.timeslice_us: timeslice period in micro-seconds */
@@ -125,19 +113,19 @@ struct xe_exec_queue {
enum xe_exec_queue_priority priority;
} sched_props;
- /** @compute: compute exec queue state */
+ /** @lr: long-running exec queue state */
struct {
- /** @compute.pfence: preemption fence */
+ /** @lr.pfence: preemption fence */
struct dma_fence *pfence;
- /** @compute.context: preemption fence context */
+ /** @lr.context: preemption fence context */
u64 context;
- /** @compute.seqno: preemption fence seqno */
+ /** @lr.seqno: preemption fence seqno */
u32 seqno;
- /** @compute.link: link into VM's list of exec queues */
+ /** @lr.link: link into VM's list of exec queues */
struct list_head link;
- /** @compute.lock: preemption fences lock */
+ /** @lr.lock: preemption fences lock */
spinlock_t lock;
- } compute;
+ } lr;
/** @ops: submission backend exec queue operations */
const struct xe_exec_queue_ops *ops;
@@ -151,8 +139,12 @@ struct xe_exec_queue {
* Protected by @vm's resv. Unused if @vm == NULL.
*/
u64 tlb_flush_seqno;
+ /** @old_run_ticks: prior hw engine class run time in ticks for this exec queue */
+ u64 old_run_ticks;
+ /** @run_ticks: hw engine class run time in ticks for this exec queue */
+ u64 run_ticks;
/** @lrc: logical ring context for this exec queue */
- struct xe_lrc lrc[];
+ struct xe_lrc *lrc[];
};
/**
diff --git a/drivers/gpu/drm/xe/xe_execlist.c b/drivers/gpu/drm/xe/xe_execlist.c
index dece2785933c..db906117db6d 100644
--- a/drivers/gpu/drm/xe/xe_execlist.c
+++ b/drivers/gpu/drm/xe/xe_execlist.c
@@ -9,7 +9,6 @@
#include "instructions/xe_mi_commands.h"
#include "regs/xe_engine_regs.h"
-#include "regs/xe_gpu_commands.h"
#include "regs/xe_gt_regs.h"
#include "regs/xe_lrc_layout.h"
#include "xe_assert.h"
@@ -110,7 +109,7 @@ static void __xe_execlist_port_start(struct xe_execlist_port *port,
port->last_ctx_id = 1;
}
- __start_lrc(port->hwe, exl->q->lrc, port->last_ctx_id);
+ __start_lrc(port->hwe, exl->q->lrc[0], port->last_ctx_id);
port->running_exl = exl;
exl->has_run = true;
}
@@ -124,14 +123,14 @@ static void __xe_execlist_port_idle(struct xe_execlist_port *port)
if (!port->running_exl)
return;
- xe_lrc_write_ring(&port->hwe->kernel_lrc, noop, sizeof(noop));
- __start_lrc(port->hwe, &port->hwe->kernel_lrc, 0);
+ xe_lrc_write_ring(port->hwe->kernel_lrc, noop, sizeof(noop));
+ __start_lrc(port->hwe, port->hwe->kernel_lrc, 0);
port->running_exl = NULL;
}
static bool xe_execlist_is_idle(struct xe_execlist_exec_queue *exl)
{
- struct xe_lrc *lrc = exl->q->lrc;
+ struct xe_lrc *lrc = exl->q->lrc[0];
return lrc->ring.tail == lrc->ring.old_tail;
}
@@ -307,6 +306,7 @@ static void execlist_job_free(struct drm_sched_job *drm_job)
{
struct xe_sched_job *job = to_xe_sched_job(drm_job);
+ xe_exec_queue_update_run_ticks(job->q);
xe_sched_job_put(job);
}
@@ -333,7 +333,7 @@ static int execlist_exec_queue_init(struct xe_exec_queue *q)
exl->q = q;
err = drm_sched_init(&exl->sched, &drm_sched_ops, NULL, 1,
- q->lrc[0].ring.size / MAX_JOB_SIZE_BYTES,
+ q->lrc[0]->ring.size / MAX_JOB_SIZE_BYTES,
XE_SCHED_HANG_LIMIT, XE_SCHED_JOB_TIMEOUT,
NULL, NULL, q->hwe->name,
gt_to_xe(q->gt)->drm.dev);
diff --git a/drivers/gpu/drm/xe/xe_force_wake.c b/drivers/gpu/drm/xe/xe_force_wake.c
index 9bbe8a5040da..b263fff15273 100644
--- a/drivers/gpu/drm/xe/xe_force_wake.c
+++ b/drivers/gpu/drm/xe/xe_force_wake.c
@@ -10,31 +10,26 @@
#include "regs/xe_gt_regs.h"
#include "regs/xe_reg_defs.h"
#include "xe_gt.h"
+#include "xe_gt_printk.h"
#include "xe_mmio.h"
+#include "xe_sriov.h"
#define XE_FORCE_WAKE_ACK_TIMEOUT_MS 50
-static struct xe_gt *
-fw_to_gt(struct xe_force_wake *fw)
+static const char *str_wake_sleep(bool wake)
{
- return fw->gt;
-}
-
-static struct xe_device *
-fw_to_xe(struct xe_force_wake *fw)
-{
- return gt_to_xe(fw_to_gt(fw));
+ return wake ? "wake" : "sleep";
}
static void domain_init(struct xe_force_wake_domain *domain,
enum xe_force_wake_domain_id id,
- struct xe_reg reg, struct xe_reg ack, u32 val, u32 mask)
+ struct xe_reg reg, struct xe_reg ack)
{
domain->id = id;
domain->reg_ctl = reg;
domain->reg_ack = ack;
- domain->val = val;
- domain->mask = mask;
+ domain->val = FORCEWAKE_MT(FORCEWAKE_KERNEL);
+ domain->mask = FORCEWAKE_MT_MASK(FORCEWAKE_KERNEL);
}
void xe_force_wake_init_gt(struct xe_gt *gt, struct xe_force_wake *fw)
@@ -51,14 +46,12 @@ void xe_force_wake_init_gt(struct xe_gt *gt, struct xe_force_wake *fw)
domain_init(&fw->domains[XE_FW_DOMAIN_ID_GT],
XE_FW_DOMAIN_ID_GT,
FORCEWAKE_GT,
- FORCEWAKE_ACK_GT_MTL,
- BIT(0), BIT(16));
+ FORCEWAKE_ACK_GT_MTL);
} else {
domain_init(&fw->domains[XE_FW_DOMAIN_ID_GT],
XE_FW_DOMAIN_ID_GT,
FORCEWAKE_GT,
- FORCEWAKE_ACK_GT,
- BIT(0), BIT(16));
+ FORCEWAKE_ACK_GT);
}
}
@@ -73,8 +66,7 @@ void xe_force_wake_init_engines(struct xe_gt *gt, struct xe_force_wake *fw)
domain_init(&fw->domains[XE_FW_DOMAIN_ID_RENDER],
XE_FW_DOMAIN_ID_RENDER,
FORCEWAKE_RENDER,
- FORCEWAKE_ACK_RENDER,
- BIT(0), BIT(16));
+ FORCEWAKE_ACK_RENDER);
for (i = XE_HW_ENGINE_VCS0, j = 0; i <= XE_HW_ENGINE_VCS7; ++i, ++j) {
if (!(gt->info.engine_mask & BIT(i)))
@@ -83,8 +75,7 @@ void xe_force_wake_init_engines(struct xe_gt *gt, struct xe_force_wake *fw)
domain_init(&fw->domains[XE_FW_DOMAIN_ID_MEDIA_VDBOX0 + j],
XE_FW_DOMAIN_ID_MEDIA_VDBOX0 + j,
FORCEWAKE_MEDIA_VDBOX(j),
- FORCEWAKE_ACK_MEDIA_VDBOX(j),
- BIT(0), BIT(16));
+ FORCEWAKE_ACK_MEDIA_VDBOX(j));
}
for (i = XE_HW_ENGINE_VECS0, j = 0; i <= XE_HW_ENGINE_VECS3; ++i, ++j) {
@@ -94,42 +85,63 @@ void xe_force_wake_init_engines(struct xe_gt *gt, struct xe_force_wake *fw)
domain_init(&fw->domains[XE_FW_DOMAIN_ID_MEDIA_VEBOX0 + j],
XE_FW_DOMAIN_ID_MEDIA_VEBOX0 + j,
FORCEWAKE_MEDIA_VEBOX(j),
- FORCEWAKE_ACK_MEDIA_VEBOX(j),
- BIT(0), BIT(16));
+ FORCEWAKE_ACK_MEDIA_VEBOX(j));
}
if (gt->info.engine_mask & BIT(XE_HW_ENGINE_GSCCS0))
domain_init(&fw->domains[XE_FW_DOMAIN_ID_GSC],
XE_FW_DOMAIN_ID_GSC,
FORCEWAKE_GSC,
- FORCEWAKE_ACK_GSC,
- BIT(0), BIT(16));
+ FORCEWAKE_ACK_GSC);
+}
+
+static void __domain_ctl(struct xe_gt *gt, struct xe_force_wake_domain *domain, bool wake)
+{
+ if (IS_SRIOV_VF(gt_to_xe(gt)))
+ return;
+
+ xe_mmio_write32(gt, domain->reg_ctl, domain->mask | (wake ? domain->val : 0));
+}
+
+static int __domain_wait(struct xe_gt *gt, struct xe_force_wake_domain *domain, bool wake)
+{
+ u32 value;
+ int ret;
+
+ if (IS_SRIOV_VF(gt_to_xe(gt)))
+ return 0;
+
+ ret = xe_mmio_wait32(gt, domain->reg_ack, domain->val, wake ? domain->val : 0,
+ XE_FORCE_WAKE_ACK_TIMEOUT_MS * USEC_PER_MSEC,
+ &value, true);
+ if (ret)
+ xe_gt_notice(gt, "Force wake domain %d failed to ack %s (%pe) reg[%#x] = %#x\n",
+ domain->id, str_wake_sleep(wake), ERR_PTR(ret),
+ domain->reg_ack.addr, value);
+
+ return ret;
}
static void domain_wake(struct xe_gt *gt, struct xe_force_wake_domain *domain)
{
- xe_mmio_write32(gt, domain->reg_ctl, domain->mask | domain->val);
+ __domain_ctl(gt, domain, true);
}
static int domain_wake_wait(struct xe_gt *gt,
struct xe_force_wake_domain *domain)
{
- return xe_mmio_wait32(gt, domain->reg_ack, domain->val, domain->val,
- XE_FORCE_WAKE_ACK_TIMEOUT_MS * USEC_PER_MSEC,
- NULL, true);
+ return __domain_wait(gt, domain, true);
}
static void domain_sleep(struct xe_gt *gt, struct xe_force_wake_domain *domain)
{
- xe_mmio_write32(gt, domain->reg_ctl, domain->mask);
+ __domain_ctl(gt, domain, false);
}
static int domain_sleep_wait(struct xe_gt *gt,
struct xe_force_wake_domain *domain)
{
- return xe_mmio_wait32(gt, domain->reg_ack, domain->val, 0,
- XE_FORCE_WAKE_ACK_TIMEOUT_MS * USEC_PER_MSEC,
- NULL, true);
+ return __domain_wait(gt, domain, false);
}
#define for_each_fw_domain_masked(domain__, mask__, fw__, tmp__) \
@@ -141,12 +153,11 @@ static int domain_sleep_wait(struct xe_gt *gt,
int xe_force_wake_get(struct xe_force_wake *fw,
enum xe_force_wake_domains domains)
{
- struct xe_device *xe = fw_to_xe(fw);
- struct xe_gt *gt = fw_to_gt(fw);
+ struct xe_gt *gt = fw->gt;
struct xe_force_wake_domain *domain;
enum xe_force_wake_domains tmp, woken = 0;
unsigned long flags;
- int ret, ret2 = 0;
+ int ret = 0;
spin_lock_irqsave(&fw->lock, flags);
for_each_fw_domain_masked(domain, domains, fw, tmp) {
@@ -156,27 +167,22 @@ int xe_force_wake_get(struct xe_force_wake *fw,
}
}
for_each_fw_domain_masked(domain, woken, fw, tmp) {
- ret = domain_wake_wait(gt, domain);
- ret2 |= ret;
- if (ret)
- drm_notice(&xe->drm, "Force wake domain (%d) failed to ack wake, ret=%d\n",
- domain->id, ret);
+ ret |= domain_wake_wait(gt, domain);
}
fw->awake_domains |= woken;
spin_unlock_irqrestore(&fw->lock, flags);
- return ret2;
+ return ret;
}
int xe_force_wake_put(struct xe_force_wake *fw,
enum xe_force_wake_domains domains)
{
- struct xe_device *xe = fw_to_xe(fw);
- struct xe_gt *gt = fw_to_gt(fw);
+ struct xe_gt *gt = fw->gt;
struct xe_force_wake_domain *domain;
enum xe_force_wake_domains tmp, sleep = 0;
unsigned long flags;
- int ret, ret2 = 0;
+ int ret = 0;
spin_lock_irqsave(&fw->lock, flags);
for_each_fw_domain_masked(domain, domains, fw, tmp) {
@@ -186,14 +192,10 @@ int xe_force_wake_put(struct xe_force_wake *fw,
}
}
for_each_fw_domain_masked(domain, sleep, fw, tmp) {
- ret = domain_sleep_wait(gt, domain);
- ret2 |= ret;
- if (ret)
- drm_notice(&xe->drm, "Force wake domain (%d) failed to ack sleep, ret=%d\n",
- domain->id, ret);
+ ret |= domain_sleep_wait(gt, domain);
}
fw->awake_domains &= ~sleep;
spin_unlock_irqrestore(&fw->lock, flags);
- return ret2;
+ return ret;
}
diff --git a/drivers/gpu/drm/xe/xe_force_wake.h b/drivers/gpu/drm/xe/xe_force_wake.h
index 83cb157da7cc..a2577672f4e3 100644
--- a/drivers/gpu/drm/xe/xe_force_wake.h
+++ b/drivers/gpu/drm/xe/xe_force_wake.h
@@ -24,14 +24,25 @@ static inline int
xe_force_wake_ref(struct xe_force_wake *fw,
enum xe_force_wake_domains domain)
{
- xe_gt_assert(fw->gt, domain);
+ xe_gt_assert(fw->gt, domain != XE_FORCEWAKE_ALL);
return fw->domains[ffs(domain) - 1].ref;
}
+/**
+ * xe_force_wake_assert_held - asserts domain is awake
+ * @fw : xe_force_wake structure
+ * @domain: xe_force_wake_domains apart from XE_FORCEWAKE_ALL
+ *
+ * xe_force_wake_assert_held() is designed to confirm a particular
+ * forcewake domain's wakefulness; it doesn't verify the wakefulness of
+ * multiple domains. Make sure the caller doesn't input multiple
+ * domains(XE_FORCEWAKE_ALL) as a parameter.
+ */
static inline void
xe_force_wake_assert_held(struct xe_force_wake *fw,
enum xe_force_wake_domains domain)
{
+ xe_gt_assert(fw->gt, domain != XE_FORCEWAKE_ALL);
xe_gt_assert(fw->gt, fw->awake_domains & domain);
}
diff --git a/drivers/gpu/drm/xe/xe_ggtt.c b/drivers/gpu/drm/xe/xe_ggtt.c
index 0d541f55b4fc..0cdbc1296e88 100644
--- a/drivers/gpu/drm/xe/xe_ggtt.c
+++ b/drivers/gpu/drm/xe/xe_ggtt.c
@@ -8,8 +8,10 @@
#include <linux/io-64-nonatomic-lo-hi.h>
#include <linux/sizes.h>
+#include <drm/drm_drv.h>
#include <drm/drm_managed.h>
-#include <drm/i915_drm.h>
+#include <drm/intel/i915_drm.h>
+#include <generated/xe_wa_oob.h>
#include "regs/xe_gt_regs.h"
#include "regs/xe_gtt_defs.h"
@@ -19,10 +21,13 @@
#include "xe_device.h"
#include "xe_gt.h"
#include "xe_gt_printk.h"
+#include "xe_gt_sriov_vf.h"
#include "xe_gt_tlb_invalidation.h"
#include "xe_map.h"
+#include "xe_mmio.h"
#include "xe_pm.h"
#include "xe_sriov.h"
+#include "xe_wa.h"
#include "xe_wopcm.h"
static u64 xelp_ggtt_pte_encode_bo(struct xe_bo *bo, u64 bo_offset,
@@ -67,7 +72,25 @@ static unsigned int probe_gsm_size(struct pci_dev *pdev)
return ggms ? SZ_1M << ggms : 0;
}
-void xe_ggtt_set_pte(struct xe_ggtt *ggtt, u64 addr, u64 pte)
+static void ggtt_update_access_counter(struct xe_ggtt *ggtt)
+{
+ struct xe_gt *gt = XE_WA(ggtt->tile->primary_gt, 22019338487) ? ggtt->tile->primary_gt :
+ ggtt->tile->media_gt;
+ u32 max_gtt_writes = XE_WA(ggtt->tile->primary_gt, 22019338487) ? 1100 : 63;
+ /*
+ * Wa_22019338487: GMD_ID is a RO register, a dummy write forces gunit
+ * to wait for completion of prior GTT writes before letting this through.
+ * This needs to be done for all GGTT writes originating from the CPU.
+ */
+ lockdep_assert_held(&ggtt->lock);
+
+ if ((++ggtt->access_count % max_gtt_writes) == 0) {
+ xe_mmio_write32(gt, GMD_ID, 0x0);
+ ggtt->access_count = 0;
+ }
+}
+
+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);
@@ -75,6 +98,12 @@ void xe_ggtt_set_pte(struct xe_ggtt *ggtt, u64 addr, u64 pte)
writeq(pte, &ggtt->gsm[addr >> XE_PTE_SHIFT]);
}
+static void xe_ggtt_set_pte_and_flush(struct xe_ggtt *ggtt, u64 addr, u64 pte)
+{
+ xe_ggtt_set_pte(ggtt, addr, pte);
+ ggtt_update_access_counter(ggtt);
+}
+
static void xe_ggtt_clear(struct xe_ggtt *ggtt, u64 start, u64 size)
{
u16 pat_index = tile_to_xe(ggtt->tile)->pat.idx[XE_CACHE_WB];
@@ -90,7 +119,7 @@ static void xe_ggtt_clear(struct xe_ggtt *ggtt, u64 start, u64 size)
scratch_pte = 0;
while (start < end) {
- xe_ggtt_set_pte(ggtt, start, scratch_pte);
+ ggtt->pt_ops->ggtt_set_pte(ggtt, start, scratch_pte);
start += XE_PAGE_SIZE;
}
}
@@ -122,10 +151,17 @@ static void primelockdep(struct xe_ggtt *ggtt)
static const struct xe_ggtt_pt_ops xelp_pt_ops = {
.pte_encode_bo = xelp_ggtt_pte_encode_bo,
+ .ggtt_set_pte = xe_ggtt_set_pte,
};
static const struct xe_ggtt_pt_ops xelpg_pt_ops = {
.pte_encode_bo = xelpg_ggtt_pte_encode_bo,
+ .ggtt_set_pte = xe_ggtt_set_pte,
+};
+
+static const struct xe_ggtt_pt_ops xelpg_pt_wa_ops = {
+ .pte_encode_bo = xelpg_ggtt_pte_encode_bo,
+ .ggtt_set_pte = xe_ggtt_set_pte_and_flush,
};
/*
@@ -140,6 +176,7 @@ 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;
+ int err;
if (IS_SRIOV_VF(xe))
gsm_size = SZ_8M; /* GGTT is expected to be 4GiB */
@@ -184,7 +221,10 @@ int xe_ggtt_init_early(struct xe_ggtt *ggtt)
ggtt->size = GUC_GGTT_TOP;
if (GRAPHICS_VERx100(xe) >= 1270)
- ggtt->pt_ops = &xelpg_pt_ops;
+ ggtt->pt_ops = (ggtt->tile->media_gt &&
+ XE_WA(ggtt->tile->media_gt, 22019338487)) ||
+ XE_WA(ggtt->tile->primary_gt, 22019338487) ?
+ &xelpg_pt_wa_ops : &xelpg_pt_ops;
else
ggtt->pt_ops = &xelp_pt_ops;
@@ -193,7 +233,17 @@ int xe_ggtt_init_early(struct xe_ggtt *ggtt)
mutex_init(&ggtt->lock);
primelockdep(ggtt);
- return drmm_add_action_or_reset(&xe->drm, ggtt_fini_early, ggtt);
+ err = drmm_add_action_or_reset(&xe->drm, ggtt_fini_early, ggtt);
+ if (err)
+ return err;
+
+ if (IS_SRIOV_VF(xe)) {
+ err = xe_gt_sriov_vf_prepare_ggtt(xe_tile_get_gt(ggtt->tile, 0));
+ if (err)
+ return err;
+ }
+
+ return 0;
}
static void xe_ggtt_invalidate(struct xe_ggtt *ggtt);
@@ -381,7 +431,7 @@ void xe_ggtt_map_bo(struct xe_ggtt *ggtt, struct xe_bo *bo)
for (offset = 0; offset < bo->size; offset += XE_PAGE_SIZE) {
pte = ggtt->pt_ops->pte_encode_bo(bo, offset, pat_index);
- xe_ggtt_set_pte(ggtt, start + offset, pte);
+ ggtt->pt_ops->ggtt_set_pte(ggtt, start + offset, pte);
}
}
@@ -433,18 +483,29 @@ int xe_ggtt_insert_bo(struct xe_ggtt *ggtt, struct xe_bo *bo)
void xe_ggtt_remove_node(struct xe_ggtt *ggtt, struct drm_mm_node *node,
bool invalidate)
{
- xe_pm_runtime_get_noresume(tile_to_xe(ggtt->tile));
+ struct xe_device *xe = tile_to_xe(ggtt->tile);
+ bool bound;
+ int idx;
+
+ bound = drm_dev_enter(&xe->drm, &idx);
+ if (bound)
+ xe_pm_runtime_get_noresume(xe);
mutex_lock(&ggtt->lock);
- xe_ggtt_clear(ggtt, node->start, node->size);
+ if (bound)
+ xe_ggtt_clear(ggtt, node->start, node->size);
drm_mm_remove_node(node);
node->size = 0;
mutex_unlock(&ggtt->lock);
+ if (!bound)
+ return;
+
if (invalidate)
xe_ggtt_invalidate(ggtt);
- xe_pm_runtime_put(tile_to_xe(ggtt->tile));
+ xe_pm_runtime_put(xe);
+ drm_dev_exit(idx);
}
void xe_ggtt_remove_bo(struct xe_ggtt *ggtt, struct xe_bo *bo)
@@ -478,7 +539,7 @@ static void xe_ggtt_assign_locked(struct xe_ggtt *ggtt, const struct drm_mm_node
return;
while (start < end) {
- xe_ggtt_set_pte(ggtt, start, pte);
+ ggtt->pt_ops->ggtt_set_pte(ggtt, start, pte);
start += XE_PAGE_SIZE;
}
diff --git a/drivers/gpu/drm/xe/xe_ggtt.h b/drivers/gpu/drm/xe/xe_ggtt.h
index 4a41a1762358..6a96fd54bf60 100644
--- a/drivers/gpu/drm/xe/xe_ggtt.h
+++ b/drivers/gpu/drm/xe/xe_ggtt.h
@@ -10,7 +10,6 @@
struct drm_printer;
-void xe_ggtt_set_pte(struct xe_ggtt *ggtt, u64 addr, u64 pte);
int xe_ggtt_init_early(struct xe_ggtt *ggtt);
int xe_ggtt_init(struct xe_ggtt *ggtt);
void xe_ggtt_printk(struct xe_ggtt *ggtt, const char *prefix);
diff --git a/drivers/gpu/drm/xe/xe_ggtt_types.h b/drivers/gpu/drm/xe/xe_ggtt_types.h
index d8c584d9a8c3..2245d88d8f39 100644
--- a/drivers/gpu/drm/xe/xe_ggtt_types.h
+++ b/drivers/gpu/drm/xe/xe_ggtt_types.h
@@ -13,10 +13,6 @@
struct xe_bo;
struct xe_gt;
-struct xe_ggtt_pt_ops {
- u64 (*pte_encode_bo)(struct xe_bo *bo, u64 bo_offset, u16 pat_index);
-};
-
struct xe_ggtt {
struct xe_tile *tile;
@@ -34,6 +30,14 @@ struct xe_ggtt {
const struct xe_ggtt_pt_ops *pt_ops;
struct drm_mm mm;
+
+ /** @access_count: counts GGTT writes */
+ unsigned int access_count;
+};
+
+struct xe_ggtt_pt_ops {
+ u64 (*pte_encode_bo)(struct xe_bo *bo, u64 bo_offset, u16 pat_index);
+ void (*ggtt_set_pte)(struct xe_ggtt *ggtt, u64 addr, u64 pte);
};
#endif
diff --git a/drivers/gpu/drm/xe/xe_gsc.c b/drivers/gpu/drm/xe/xe_gsc.c
index 60202b903687..f8239a13fa2b 100644
--- a/drivers/gpu/drm/xe/xe_gsc.c
+++ b/drivers/gpu/drm/xe/xe_gsc.c
@@ -5,6 +5,8 @@
#include "xe_gsc.h"
+#include <linux/delay.h>
+
#include <drm/drm_managed.h>
#include <generated/xe_wa_oob.h>
@@ -14,11 +16,13 @@
#include "xe_bo.h"
#include "xe_device.h"
#include "xe_exec_queue.h"
+#include "xe_force_wake.h"
#include "xe_gsc_proxy.h"
#include "xe_gsc_submit.h"
#include "xe_gt.h"
#include "xe_gt_mcr.h"
#include "xe_gt_printk.h"
+#include "xe_guc_pc.h"
#include "xe_huc.h"
#include "xe_map.h"
#include "xe_mmio.h"
@@ -281,6 +285,10 @@ static int gsc_upload_and_init(struct xe_gsc *gsc)
return ret;
xe_uc_fw_change_status(&gsc->fw, XE_UC_FIRMWARE_TRANSFERRED);
+
+ /* GSC load is done, restore expected GT frequencies */
+ xe_gt_sanitize_freq(gt);
+
xe_gt_dbg(gt, "GSC FW async load completed\n");
/* HuC auth failure is not fatal */
diff --git a/drivers/gpu/drm/xe/xe_gsc.h b/drivers/gpu/drm/xe/xe_gsc.h
index dd16e9b8b894..1c7a623faf11 100644
--- a/drivers/gpu/drm/xe/xe_gsc.h
+++ b/drivers/gpu/drm/xe/xe_gsc.h
@@ -6,8 +6,9 @@
#ifndef _XE_GSC_H_
#define _XE_GSC_H_
-#include "xe_gsc_types.h"
+#include <linux/types.h>
+struct xe_gsc;
struct xe_gt;
struct xe_hw_engine;
diff --git a/drivers/gpu/drm/xe/xe_gsc_proxy.c b/drivers/gpu/drm/xe/xe_gsc_proxy.c
index 1b908d238bd1..aa812a2bc3ed 100644
--- a/drivers/gpu/drm/xe/xe_gsc_proxy.c
+++ b/drivers/gpu/drm/xe/xe_gsc_proxy.c
@@ -9,12 +9,13 @@
#include <linux/delay.h>
#include <drm/drm_managed.h>
-#include <drm/i915_component.h>
-#include <drm/i915_gsc_proxy_mei_interface.h>
+#include <drm/intel/i915_component.h>
+#include <drm/intel/i915_gsc_proxy_mei_interface.h>
#include "abi/gsc_proxy_commands_abi.h"
#include "regs/xe_gsc_regs.h"
#include "xe_bo.h"
+#include "xe_force_wake.h"
#include "xe_gsc.h"
#include "xe_gsc_submit.h"
#include "xe_gt.h"
diff --git a/drivers/gpu/drm/xe/xe_gsc_submit.c b/drivers/gpu/drm/xe/xe_gsc_submit.c
index d34d03248843..9ede483d37ef 100644
--- a/drivers/gpu/drm/xe/xe_gsc_submit.c
+++ b/drivers/gpu/drm/xe/xe_gsc_submit.c
@@ -8,6 +8,7 @@
#include <linux/poison.h>
#include "abi/gsc_command_header_abi.h"
+#include "xe_assert.h"
#include "xe_bb.h"
#include "xe_exec_queue.h"
#include "xe_gt_printk.h"
diff --git a/drivers/gpu/drm/xe/xe_gt.c b/drivers/gpu/drm/xe/xe_gt.c
index 491d0413de15..31b2e64c70c6 100644
--- a/drivers/gpu/drm/xe/xe_gt.c
+++ b/drivers/gpu/drm/xe/xe_gt.c
@@ -9,6 +9,7 @@
#include <drm/drm_managed.h>
#include <drm/xe_drm.h>
+#include <generated/xe_wa_oob.h>
#include "instructions/xe_gfxpipe_commands.h"
#include "instructions/xe_mi_commands.h"
@@ -44,6 +45,7 @@
#include "xe_migrate.h"
#include "xe_mmio.h"
#include "xe_pat.h"
+#include "xe_pcode.h"
#include "xe_pm.h"
#include "xe_mocs.h"
#include "xe_reg_sr.h"
@@ -53,13 +55,22 @@
#include "xe_sriov.h"
#include "xe_tuning.h"
#include "xe_uc.h"
+#include "xe_uc_fw.h"
#include "xe_vm.h"
#include "xe_wa.h"
#include "xe_wopcm.h"
+static void gt_fini(struct drm_device *drm, void *arg)
+{
+ struct xe_gt *gt = arg;
+
+ destroy_workqueue(gt->ordered_wq);
+}
+
struct xe_gt *xe_gt_alloc(struct xe_tile *tile)
{
struct xe_gt *gt;
+ int err;
gt = drmm_kzalloc(&tile_to_xe(tile)->drm, sizeof(*gt), GFP_KERNEL);
if (!gt)
@@ -68,6 +79,10 @@ struct xe_gt *xe_gt_alloc(struct xe_tile *tile)
gt->tile = tile;
gt->ordered_wq = alloc_ordered_workqueue("gt-ordered-wq", 0);
+ err = drmm_add_action_or_reset(&gt_to_xe(gt)->drm, gt_fini, gt);
+ if (err)
+ return ERR_PTR(err);
+
return gt;
}
@@ -90,15 +105,9 @@ void xe_gt_sanitize(struct xe_gt *gt)
*/
void xe_gt_remove(struct xe_gt *gt)
{
- xe_uc_remove(&gt->uc);
-}
-
-static void gt_fini(struct drm_device *drm, void *arg)
-{
- struct xe_gt *gt = arg;
int i;
- destroy_workqueue(gt->ordered_wq);
+ xe_uc_remove(&gt->uc);
for (i = 0; i < XE_ENGINE_CLASS_MAX; ++i)
xe_hw_fence_irq_finish(&gt->fence_irq[i]);
@@ -160,7 +169,7 @@ static int emit_wa_job(struct xe_gt *gt, struct xe_exec_queue *q)
if (q->hwe->class == XE_ENGINE_CLASS_RENDER)
/* Big enough to emit all of the context's 3DSTATE */
- bb = xe_bb_new(gt, xe_lrc_size(gt_to_xe(gt), q->hwe->class), false);
+ bb = xe_bb_new(gt, xe_gt_lrc_size(gt, q->hwe->class), false);
else
/* Just pick a large BB size */
bb = xe_bb_new(gt, SZ_4K, false);
@@ -244,7 +253,7 @@ int xe_gt_record_default_lrcs(struct xe_gt *gt)
xe_tuning_process_lrc(hwe);
default_lrc = drmm_kzalloc(&xe->drm,
- xe_lrc_size(xe, hwe->class),
+ xe_gt_lrc_size(gt, hwe->class),
GFP_KERNEL);
if (!default_lrc)
return -ENOMEM;
@@ -292,9 +301,9 @@ int xe_gt_record_default_lrcs(struct xe_gt *gt)
}
xe_map_memcpy_from(xe, default_lrc,
- &q->lrc[0].bo->vmap,
- xe_lrc_pphwsp_offset(&q->lrc[0]),
- xe_lrc_size(xe, hwe->class));
+ &q->lrc[0]->bo->vmap,
+ xe_lrc_pphwsp_offset(q->lrc[0]),
+ xe_gt_lrc_size(gt, hwe->class));
gt->default_lrc[hwe->class] = default_lrc;
put_nop_q:
@@ -318,14 +327,6 @@ int xe_gt_init_early(struct xe_gt *gt)
return err;
}
- err = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
- if (err)
- return err;
-
- err = xe_force_wake_put(gt_to_fw(gt), XE_FW_GT);
- if (err)
- return err;
-
xe_reg_sr_init(&gt->reg_sr, "GT", gt_to_xe(gt));
err = xe_wa_init(gt);
@@ -336,6 +337,9 @@ int xe_gt_init_early(struct xe_gt *gt)
xe_wa_process_oob(gt);
xe_tuning_process_gt(gt);
+ xe_force_wake_init_gt(gt, gt_to_fw(gt));
+ xe_pcode_init(gt);
+
return 0;
}
@@ -366,10 +370,6 @@ static int gt_fw_domain_init(struct xe_gt *gt)
xe_lmtt_init(&gt_to_tile(gt)->sriov.pf.lmtt);
}
- err = xe_gt_idle_sysfs_init(&gt->gtidle);
- if (err)
- goto err_force_wake;
-
/* Enable per hw engine IRQs */
xe_irq_enable_hwe(gt);
@@ -434,6 +434,10 @@ static int all_fw_domain_init(struct xe_gt *gt)
if (err)
goto err_force_wake;
+ err = xe_uc_init_post_hwconfig(&gt->uc);
+ if (err)
+ goto err_force_wake;
+
if (!xe_gt_is_media_type(gt)) {
/*
* USM has its only SA pool to non-block behind user operations
@@ -460,10 +464,6 @@ static int all_fw_domain_init(struct xe_gt *gt)
}
}
- err = xe_uc_init_post_hwconfig(&gt->uc);
- if (err)
- goto err_force_wake;
-
err = xe_uc_init_hw(&gt->uc);
if (err)
goto err_force_wake;
@@ -477,6 +477,9 @@ static int all_fw_domain_init(struct xe_gt *gt)
if (IS_SRIOV_PF(gt_to_xe(gt)) && !xe_gt_is_media_type(gt))
xe_lmtt_init_hw(&gt_to_tile(gt)->sriov.pf.lmtt);
+ if (IS_SRIOV_PF(gt_to_xe(gt)))
+ xe_gt_sriov_pf_init_hw(gt);
+
err = xe_force_wake_put(gt_to_fw(gt), XE_FORCEWAKE_ALL);
XE_WARN_ON(err);
@@ -503,8 +506,7 @@ int xe_gt_init_hwconfig(struct xe_gt *gt)
if (err)
goto out;
- xe_gt_topology_init(gt);
- xe_gt_mcr_init(gt);
+ xe_gt_mcr_init_early(gt);
xe_pat_init(gt);
err = xe_uc_init(&gt->uc);
@@ -515,8 +517,8 @@ int xe_gt_init_hwconfig(struct xe_gt *gt)
if (err)
goto out_fw;
- /* XXX: Fake that we pull the engine mask from hwconfig blob */
- gt->info.engine_mask = gt->info.__engine_mask;
+ xe_gt_topology_init(gt);
+ xe_gt_mcr_init(gt);
out_fw:
xe_force_wake_put(gt_to_fw(gt), XE_FW_GT);
@@ -554,6 +556,10 @@ int xe_gt_init(struct xe_gt *gt)
if (err)
return err;
+ err = xe_gt_idle_init(&gt->gtidle);
+ if (err)
+ return err;
+
err = xe_gt_freq_init(gt);
if (err)
return err;
@@ -564,7 +570,30 @@ int xe_gt_init(struct xe_gt *gt)
if (err)
return err;
- return drmm_add_action_or_reset(&gt_to_xe(gt)->drm, gt_fini, gt);
+ xe_gt_record_user_engines(gt);
+
+ return 0;
+}
+
+void xe_gt_record_user_engines(struct xe_gt *gt)
+{
+ struct xe_hw_engine *hwe;
+ enum xe_hw_engine_id id;
+
+ gt->user_engines.mask = 0;
+ memset(gt->user_engines.instances_per_class, 0,
+ sizeof(gt->user_engines.instances_per_class));
+
+ for_each_hw_engine(hwe, gt, id) {
+ if (xe_hw_engine_is_reserved(hwe))
+ continue;
+
+ gt->user_engines.mask |= BIT_ULL(id);
+ gt->user_engines.instances_per_class[hwe->class]++;
+ }
+
+ xe_gt_assert(gt, (gt->user_engines.mask | gt->info.engine_mask)
+ == gt->info.engine_mask);
}
static int do_gt_reset(struct xe_gt *gt)
@@ -584,12 +613,34 @@ static int do_gt_reset(struct xe_gt *gt)
return err;
}
+static int vf_gt_restart(struct xe_gt *gt)
+{
+ int err;
+
+ err = xe_uc_sanitize_reset(&gt->uc);
+ if (err)
+ return err;
+
+ err = xe_uc_init_hw(&gt->uc);
+ if (err)
+ return err;
+
+ err = xe_uc_start(&gt->uc);
+ if (err)
+ return err;
+
+ return 0;
+}
+
static int do_gt_restart(struct xe_gt *gt)
{
struct xe_hw_engine *hwe;
enum xe_hw_engine_id id;
int err;
+ if (IS_SRIOV_VF(gt_to_xe(gt)))
+ return vf_gt_restart(gt);
+
xe_pat_init(gt);
xe_gt_mcr_set_implicit_defaults(gt);
@@ -613,6 +664,9 @@ static int do_gt_restart(struct xe_gt *gt)
if (IS_SRIOV_PF(gt_to_xe(gt)) && !xe_gt_is_media_type(gt))
xe_lmtt_init_hw(&gt_to_tile(gt)->sriov.pf.lmtt);
+ if (IS_SRIOV_PF(gt_to_xe(gt)))
+ xe_gt_sriov_pf_init_hw(gt);
+
xe_mocs_init(gt);
err = xe_uc_start(&gt->uc);
if (err)
@@ -626,6 +680,12 @@ static int do_gt_restart(struct xe_gt *gt)
/* Get CCS mode in sync between sw/hw */
xe_gt_apply_ccs_mode(gt);
+ /* Restore GT freq to expected values */
+ xe_gt_sanitize_freq(gt);
+
+ if (IS_SRIOV_PF(gt_to_xe(gt)))
+ xe_gt_sriov_pf_restart(gt);
+
return 0;
}
@@ -633,6 +693,9 @@ static int gt_reset(struct xe_gt *gt)
{
int err;
+ if (xe_device_wedged(gt_to_xe(gt)))
+ return -ECANCELED;
+
/* We only support GT resets with GuC submission */
if (!xe_device_uc_enabled(gt_to_xe(gt)))
return -ENODEV;
@@ -655,9 +718,7 @@ static int gt_reset(struct xe_gt *gt)
xe_uc_stop_prepare(&gt->uc);
xe_gt_pagefault_reset(gt);
- err = xe_uc_stop(&gt->uc);
- if (err)
- goto err_out;
+ xe_uc_stop(&gt->uc);
xe_gt_tlb_invalidation_reset(gt);
@@ -685,7 +746,7 @@ err_msg:
err_fail:
xe_gt_err(gt, "reset failed (%pe)\n", ERR_PTR(err));
- gt_to_xe(gt)->needs_flr_on_fini = true;
+ xe_device_declare_wedged(gt_to_xe(gt));
return err;
}
@@ -733,6 +794,8 @@ int xe_gt_suspend(struct xe_gt *gt)
if (err)
goto err_force_wake;
+ xe_gt_idle_disable_pg(gt);
+
XE_WARN_ON(xe_force_wake_put(gt_to_fw(gt), XE_FORCEWAKE_ALL));
xe_gt_dbg(gt, "suspended\n");
@@ -746,6 +809,24 @@ err_msg:
return err;
}
+/**
+ * xe_gt_sanitize_freq() - Restore saved frequencies if necessary.
+ * @gt: the GT object
+ *
+ * Called after driver init/GSC load completes to restore GT frequencies if we
+ * limited them for any WAs.
+ */
+int xe_gt_sanitize_freq(struct xe_gt *gt)
+{
+ int ret = 0;
+
+ if ((!xe_uc_fw_is_available(&gt->uc.gsc.fw) ||
+ xe_uc_fw_is_loaded(&gt->uc.gsc.fw)) && XE_WA(gt, 22019338487))
+ ret = xe_guc_pc_restore_stashed_freq(&gt->uc.guc.pc);
+
+ return ret;
+}
+
int xe_gt_resume(struct xe_gt *gt)
{
int err;
@@ -759,6 +840,8 @@ int xe_gt_resume(struct xe_gt *gt)
if (err)
goto err_force_wake;
+ xe_gt_idle_enable_pg(gt);
+
XE_WARN_ON(xe_force_wake_put(gt_to_fw(gt), XE_FORCEWAKE_ALL));
xe_gt_dbg(gt, "resumed\n");
@@ -810,3 +893,29 @@ struct xe_hw_engine *xe_gt_any_hw_engine_by_reset_domain(struct xe_gt *gt,
return NULL;
}
+
+struct xe_hw_engine *xe_gt_any_hw_engine(struct xe_gt *gt)
+{
+ struct xe_hw_engine *hwe;
+ enum xe_hw_engine_id id;
+
+ for_each_hw_engine(hwe, gt, id)
+ return hwe;
+
+ return NULL;
+}
+
+/**
+ * xe_gt_declare_wedged() - Declare GT wedged
+ * @gt: the GT object
+ *
+ * Wedge the GT which stops all submission, saves desired debug state, and
+ * cleans up anything which could timeout.
+ */
+void xe_gt_declare_wedged(struct xe_gt *gt)
+{
+ xe_gt_assert(gt, gt_to_xe(gt)->wedged.mode);
+
+ xe_uc_declare_wedged(&gt->uc);
+ xe_gt_tlb_invalidation_reset(gt);
+}
diff --git a/drivers/gpu/drm/xe/xe_gt.h b/drivers/gpu/drm/xe/xe_gt.h
index ed6ea8057e35..8b1a5027dcf2 100644
--- a/drivers/gpu/drm/xe/xe_gt.h
+++ b/drivers/gpu/drm/xe/xe_gt.h
@@ -8,6 +8,7 @@
#include <drm/drm_util.h>
+#include "xe_device.h"
#include "xe_device_types.h"
#include "xe_hw_engine.h"
@@ -36,12 +37,27 @@ struct xe_gt *xe_gt_alloc(struct xe_tile *tile);
int xe_gt_init_hwconfig(struct xe_gt *gt);
int xe_gt_init_early(struct xe_gt *gt);
int xe_gt_init(struct xe_gt *gt);
+void xe_gt_declare_wedged(struct xe_gt *gt);
int xe_gt_record_default_lrcs(struct xe_gt *gt);
+
+/**
+ * xe_gt_record_user_engines - save data related to engines available to
+ * usersapce
+ * @gt: GT structure
+ *
+ * Walk the available HW engines from gt->info.engine_mask and calculate data
+ * related to those engines that may be used by userspace. To be used whenever
+ * available engines change in runtime (e.g. with ccs_mode) or during
+ * initialization
+ */
+void xe_gt_record_user_engines(struct xe_gt *gt);
+
void xe_gt_suspend_prepare(struct xe_gt *gt);
int xe_gt_suspend(struct xe_gt *gt);
int xe_gt_resume(struct xe_gt *gt);
void xe_gt_reset_async(struct xe_gt *gt);
void xe_gt_sanitize(struct xe_gt *gt);
+int xe_gt_sanitize_freq(struct xe_gt *gt);
void xe_gt_remove(struct xe_gt *gt);
/**
@@ -53,11 +69,24 @@ void xe_gt_remove(struct xe_gt *gt);
struct xe_hw_engine *
xe_gt_any_hw_engine_by_reset_domain(struct xe_gt *gt, enum xe_engine_class class);
+/**
+ * xe_gt_any_hw_engine - scan the list of engines and return the
+ * first available
+ * @gt: GT structure
+ */
+struct xe_hw_engine *xe_gt_any_hw_engine(struct xe_gt *gt);
+
struct xe_hw_engine *xe_gt_hw_engine(struct xe_gt *gt,
enum xe_engine_class class,
u16 instance,
bool logical);
+static inline bool xe_gt_has_indirect_ring_state(struct xe_gt *gt)
+{
+ return gt->info.has_indirect_ring_state &&
+ xe_device_uc_enabled(gt_to_xe(gt));
+}
+
static inline bool xe_gt_is_media_type(struct xe_gt *gt)
{
return gt->info.type == XE_GT_TYPE_MEDIA;
diff --git a/drivers/gpu/drm/xe/xe_gt_ccs_mode.c b/drivers/gpu/drm/xe/xe_gt_ccs_mode.c
index 396aeb5b9924..d2e4dc3aaf61 100644
--- a/drivers/gpu/drm/xe/xe_gt_ccs_mode.c
+++ b/drivers/gpu/drm/xe/xe_gt_ccs_mode.c
@@ -9,8 +9,10 @@
#include "xe_assert.h"
#include "xe_gt.h"
#include "xe_gt_ccs_mode.h"
+#include "xe_gt_printk.h"
#include "xe_gt_sysfs.h"
#include "xe_mmio.h"
+#include "xe_sriov.h"
static void __xe_gt_apply_ccs_mode(struct xe_gt *gt, u32 num_engines)
{
@@ -68,13 +70,13 @@ static void __xe_gt_apply_ccs_mode(struct xe_gt *gt, u32 num_engines)
xe_mmio_write32(gt, CCS_MODE, mode);
- xe_gt_info(gt, "CCS_MODE=%x config:%08x, num_engines:%d, num_slices:%d\n",
- mode, config, num_engines, num_slices);
+ xe_gt_dbg(gt, "CCS_MODE=%x config:%08x, num_engines:%d, num_slices:%d\n",
+ mode, config, num_engines, num_slices);
}
void xe_gt_apply_ccs_mode(struct xe_gt *gt)
{
- if (!gt->ccs_mode)
+ if (!gt->ccs_mode || IS_SRIOV_VF(gt_to_xe(gt)))
return;
__xe_gt_apply_ccs_mode(gt, gt->ccs_mode);
@@ -109,6 +111,12 @@ ccs_mode_store(struct device *kdev, struct device_attribute *attr,
u32 num_engines, num_slices;
int ret;
+ if (IS_SRIOV(xe)) {
+ xe_gt_dbg(gt, "Can't change compute mode when running as %s\n",
+ xe_sriov_mode_to_string(xe_device_sriov_mode(xe)));
+ return -EOPNOTSUPP;
+ }
+
ret = kstrtou32(buff, 0, &num_engines);
if (ret)
return ret;
@@ -134,6 +142,7 @@ ccs_mode_store(struct device *kdev, struct device_attribute *attr,
if (gt->ccs_mode != num_engines) {
xe_gt_info(gt, "Setting compute mode to %d\n", num_engines);
gt->ccs_mode = num_engines;
+ xe_gt_record_user_engines(gt);
xe_gt_reset_async(gt);
}
@@ -150,7 +159,7 @@ static const struct attribute *gt_ccs_mode_attrs[] = {
NULL,
};
-static void xe_gt_ccs_mode_sysfs_fini(struct drm_device *drm, void *arg)
+static void xe_gt_ccs_mode_sysfs_fini(void *arg)
{
struct xe_gt *gt = arg;
@@ -182,5 +191,5 @@ int xe_gt_ccs_mode_sysfs_init(struct xe_gt *gt)
if (err)
return err;
- return drmm_add_action_or_reset(&xe->drm, xe_gt_ccs_mode_sysfs_fini, gt);
+ return devm_add_action_or_reset(xe->drm.dev, xe_gt_ccs_mode_sysfs_fini, gt);
}
diff --git a/drivers/gpu/drm/xe/xe_gt_clock.c b/drivers/gpu/drm/xe/xe_gt_clock.c
index c7bca20f6b65..86c2d62b4bdc 100644
--- a/drivers/gpu/drm/xe/xe_gt_clock.c
+++ b/drivers/gpu/drm/xe/xe_gt_clock.c
@@ -3,10 +3,13 @@
* Copyright © 2022 Intel Corporation
*/
+#include <linux/math64.h>
+
#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_macros.h"
@@ -78,3 +81,21 @@ int xe_gt_clock_init(struct xe_gt *gt)
gt->info.reference_clock = freq;
return 0;
}
+
+static u64 div_u64_roundup(u64 n, u32 d)
+{
+ return div_u64(n + d - 1, d);
+}
+
+/**
+ * xe_gt_clock_interval_to_ms - Convert sampled GT clock ticks to msec
+ *
+ * @gt: the &xe_gt
+ * @count: count of GT clock ticks
+ *
+ * Returns: time in msec
+ */
+u64 xe_gt_clock_interval_to_ms(struct xe_gt *gt, u64 count)
+{
+ return div_u64_roundup(count * MSEC_PER_SEC, gt->info.reference_clock);
+}
diff --git a/drivers/gpu/drm/xe/xe_gt_clock.h b/drivers/gpu/drm/xe/xe_gt_clock.h
index 44fa0371b973..3adeb7baaca4 100644
--- a/drivers/gpu/drm/xe/xe_gt_clock.h
+++ b/drivers/gpu/drm/xe/xe_gt_clock.h
@@ -11,5 +11,6 @@
struct xe_gt;
int xe_gt_clock_init(struct xe_gt *gt);
+u64 xe_gt_clock_interval_to_ms(struct xe_gt *gt, u64 count);
#endif
diff --git a/drivers/gpu/drm/xe/xe_gt_debugfs.c b/drivers/gpu/drm/xe/xe_gt_debugfs.c
index 8cf0b2625efc..5e7fd937917a 100644
--- a/drivers/gpu/drm/xe/xe_gt_debugfs.c
+++ b/drivers/gpu/drm/xe/xe_gt_debugfs.c
@@ -15,14 +15,18 @@
#include "xe_ggtt.h"
#include "xe_gt.h"
#include "xe_gt_mcr.h"
+#include "xe_gt_sriov_pf_debugfs.h"
+#include "xe_gt_sriov_vf_debugfs.h"
#include "xe_gt_topology.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"
#include "xe_reg_sr.h"
#include "xe_reg_whitelist.h"
+#include "xe_sriov.h"
#include "xe_uc_debugfs.h"
#include "xe_wa.h"
@@ -112,6 +116,17 @@ static int force_reset(struct xe_gt *gt, struct drm_printer *p)
return 0;
}
+static int force_reset_sync(struct xe_gt *gt, struct drm_printer *p)
+{
+ xe_pm_runtime_get(gt_to_xe(gt));
+ xe_gt_reset_async(gt);
+ xe_pm_runtime_put(gt_to_xe(gt));
+
+ flush_work(&gt->reset.worker);
+
+ return 0;
+}
+
static int sa_info(struct xe_gt *gt, struct drm_printer *p)
{
struct xe_tile *tile = gt_to_tile(gt);
@@ -200,6 +215,15 @@ static int pat(struct xe_gt *gt, struct drm_printer *p)
return 0;
}
+static int mocs(struct xe_gt *gt, struct drm_printer *p)
+{
+ xe_pm_runtime_get(gt_to_xe(gt));
+ xe_mocs_dump(gt, p);
+ xe_pm_runtime_put(gt_to_xe(gt));
+
+ return 0;
+}
+
static int rcs_default_lrc(struct xe_gt *gt, struct drm_printer *p)
{
xe_pm_runtime_get(gt_to_xe(gt));
@@ -248,6 +272,7 @@ static int vecs_default_lrc(struct xe_gt *gt, struct drm_printer *p)
static const struct drm_info_list debugfs_list[] = {
{"hw_engines", .show = xe_gt_debugfs_simple_show, .data = hw_engines},
{"force_reset", .show = xe_gt_debugfs_simple_show, .data = force_reset},
+ {"force_reset_sync", .show = xe_gt_debugfs_simple_show, .data = force_reset_sync},
{"sa_info", .show = xe_gt_debugfs_simple_show, .data = sa_info},
{"topology", .show = xe_gt_debugfs_simple_show, .data = topology},
{"steering", .show = xe_gt_debugfs_simple_show, .data = steering},
@@ -255,6 +280,7 @@ static const struct drm_info_list debugfs_list[] = {
{"register-save-restore", .show = xe_gt_debugfs_simple_show, .data = register_save_restore},
{"workarounds", .show = xe_gt_debugfs_simple_show, .data = workarounds},
{"pat", .show = xe_gt_debugfs_simple_show, .data = pat},
+ {"mocs", .show = xe_gt_debugfs_simple_show, .data = mocs},
{"default_lrc_rcs", .show = xe_gt_debugfs_simple_show, .data = rcs_default_lrc},
{"default_lrc_ccs", .show = xe_gt_debugfs_simple_show, .data = ccs_default_lrc},
{"default_lrc_bcs", .show = xe_gt_debugfs_simple_show, .data = bcs_default_lrc},
@@ -290,4 +316,9 @@ void xe_gt_debugfs_register(struct xe_gt *gt)
root, minor);
xe_uc_debugfs_register(&gt->uc, root);
+
+ if (IS_SRIOV_PF(xe))
+ xe_gt_sriov_pf_debugfs_register(gt, root);
+ else if (IS_SRIOV_VF(xe))
+ xe_gt_sriov_vf_debugfs_register(gt, root);
}
diff --git a/drivers/gpu/drm/xe/xe_gt_freq.c b/drivers/gpu/drm/xe/xe_gt_freq.c
index 855de40e40ea..68a5778b4319 100644
--- a/drivers/gpu/drm/xe/xe_gt_freq.c
+++ b/drivers/gpu/drm/xe/xe_gt_freq.c
@@ -13,7 +13,7 @@
#include "xe_device_types.h"
#include "xe_gt_sysfs.h"
-#include "xe_gt_throttle_sysfs.h"
+#include "xe_gt_throttle.h"
#include "xe_guc_pc.h"
#include "xe_pm.h"
@@ -209,7 +209,7 @@ static const struct attribute *freq_attrs[] = {
NULL
};
-static void freq_fini(struct drm_device *drm, void *arg)
+static void freq_fini(void *arg)
{
struct kobject *kobj = arg;
@@ -237,7 +237,7 @@ int xe_gt_freq_init(struct xe_gt *gt)
if (!gt->freq)
return -ENOMEM;
- err = drmm_add_action_or_reset(&xe->drm, freq_fini, gt->freq);
+ err = devm_add_action(xe->drm.dev, freq_fini, gt->freq);
if (err)
return err;
@@ -245,5 +245,5 @@ int xe_gt_freq_init(struct xe_gt *gt)
if (err)
return err;
- return xe_gt_throttle_sysfs_init(gt);
+ return xe_gt_throttle_init(gt);
}
diff --git a/drivers/gpu/drm/xe/xe_gt_idle.c b/drivers/gpu/drm/xe/xe_gt_idle.c
index 8fc0f3f6ecc5..67aba4140510 100644
--- a/drivers/gpu/drm/xe/xe_gt_idle.c
+++ b/drivers/gpu/drm/xe/xe_gt_idle.c
@@ -5,14 +5,17 @@
#include <drm/drm_managed.h>
+#include "xe_force_wake.h"
#include "xe_device.h"
#include "xe_gt.h"
#include "xe_gt_idle.h"
#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"
/**
* DOC: Xe GT Idle
@@ -92,6 +95,56 @@ static u64 get_residency_ms(struct xe_gt_idle *gtidle, u64 cur_residency)
return cur_residency;
}
+void xe_gt_idle_enable_pg(struct xe_gt *gt)
+{
+ struct xe_device *xe = gt_to_xe(gt);
+ u32 pg_enable;
+ int i, j;
+
+ if (IS_SRIOV_VF(xe))
+ return;
+
+ /* Disable CPG for PVC */
+ if (xe->info.platform == XE_PVC)
+ return;
+
+ xe_device_assert_mem_access(gt_to_xe(gt));
+
+ pg_enable = RENDER_POWERGATE_ENABLE | MEDIA_POWERGATE_ENABLE;
+
+ for (i = XE_HW_ENGINE_VCS0, j = 0; i <= XE_HW_ENGINE_VCS7; ++i, ++j) {
+ if ((gt->info.engine_mask & BIT(i)))
+ pg_enable |= (VDN_HCP_POWERGATE_ENABLE(j) |
+ VDN_MFXVDENC_POWERGATE_ENABLE(j));
+ }
+
+ XE_WARN_ON(xe_force_wake_get(gt_to_fw(gt), XE_FW_GT));
+ if (xe->info.skip_guc_pc) {
+ /*
+ * GuC sets the hysteresis value when GuC PC is enabled
+ * else set it to 25 (25 * 1.28us)
+ */
+ xe_mmio_write32(gt, MEDIA_POWERGATE_IDLE_HYSTERESIS, 25);
+ xe_mmio_write32(gt, RENDER_POWERGATE_IDLE_HYSTERESIS, 25);
+ }
+
+ xe_mmio_write32(gt, POWERGATE_ENABLE, pg_enable);
+ XE_WARN_ON(xe_force_wake_put(gt_to_fw(gt), XE_FW_GT));
+}
+
+void xe_gt_idle_disable_pg(struct xe_gt *gt)
+{
+ if (IS_SRIOV_VF(gt_to_xe(gt)))
+ return;
+
+ xe_device_assert_mem_access(gt_to_xe(gt));
+ XE_WARN_ON(xe_force_wake_get(gt_to_fw(gt), XE_FW_GT));
+
+ xe_mmio_write32(gt, POWERGATE_ENABLE, 0);
+
+ XE_WARN_ON(xe_force_wake_put(gt_to_fw(gt), XE_FW_GT));
+}
+
static ssize_t name_show(struct device *dev,
struct device_attribute *attr, char *buff)
{
@@ -144,21 +197,33 @@ static const struct attribute *gt_idle_attrs[] = {
NULL,
};
-static void gt_idle_sysfs_fini(struct drm_device *drm, void *arg)
+static void gt_idle_fini(void *arg)
{
struct kobject *kobj = arg;
+ struct xe_gt *gt = kobj_to_gt(kobj->parent);
+
+ xe_gt_idle_disable_pg(gt);
+
+ if (gt_to_xe(gt)->info.skip_guc_pc) {
+ XE_WARN_ON(xe_force_wake_get(gt_to_fw(gt), XE_FW_GT));
+ xe_gt_idle_disable_c6(gt);
+ xe_force_wake_put(gt_to_fw(gt), XE_FW_GT);
+ }
sysfs_remove_files(kobj, gt_idle_attrs);
kobject_put(kobj);
}
-int xe_gt_idle_sysfs_init(struct xe_gt_idle *gtidle)
+int xe_gt_idle_init(struct xe_gt_idle *gtidle)
{
struct xe_gt *gt = gtidle_to_gt(gtidle);
struct xe_device *xe = gt_to_xe(gt);
struct kobject *kobj;
int err;
+ if (IS_SRIOV_VF(xe))
+ return 0;
+
kobj = kobject_create_and_add("gtidle", gt->sysfs);
if (!kobj)
return -ENOMEM;
@@ -181,7 +246,9 @@ int xe_gt_idle_sysfs_init(struct xe_gt_idle *gtidle)
return err;
}
- return drmm_add_action_or_reset(&xe->drm, gt_idle_sysfs_fini, kobj);
+ xe_gt_idle_enable_pg(gt);
+
+ return devm_add_action_or_reset(xe->drm.dev, gt_idle_fini, kobj);
}
void xe_gt_idle_enable_c6(struct xe_gt *gt)
@@ -189,6 +256,9 @@ void xe_gt_idle_enable_c6(struct xe_gt *gt)
xe_device_assert_mem_access(gt_to_xe(gt));
xe_force_wake_assert_held(gt_to_fw(gt), XE_FW_GT);
+ if (IS_SRIOV_VF(gt_to_xe(gt)))
+ return;
+
/* Units of 1280 ns for a total of 5s */
xe_mmio_write32(gt, RC_IDLE_HYSTERSIS, 0x3B9ACA);
/* Enable RC6 */
@@ -199,9 +269,11 @@ void xe_gt_idle_enable_c6(struct xe_gt *gt)
void xe_gt_idle_disable_c6(struct xe_gt *gt)
{
xe_device_assert_mem_access(gt_to_xe(gt));
- xe_force_wake_assert_held(gt_to_fw(gt), XE_FORCEWAKE_ALL);
+ xe_force_wake_assert_held(gt_to_fw(gt), XE_FW_GT);
+
+ if (IS_SRIOV_VF(gt_to_xe(gt)))
+ return;
- xe_mmio_write32(gt, PG_ENABLE, 0);
xe_mmio_write32(gt, RC_CONTROL, 0);
xe_mmio_write32(gt, RC_STATE, 0);
}
diff --git a/drivers/gpu/drm/xe/xe_gt_idle.h b/drivers/gpu/drm/xe/xe_gt_idle.h
index 75bd99659b1b..554447b5d46d 100644
--- a/drivers/gpu/drm/xe/xe_gt_idle.h
+++ b/drivers/gpu/drm/xe/xe_gt_idle.h
@@ -10,8 +10,10 @@
struct xe_gt;
-int xe_gt_idle_sysfs_init(struct xe_gt_idle *gtidle);
+int xe_gt_idle_init(struct xe_gt_idle *gtidle);
void xe_gt_idle_enable_c6(struct xe_gt *gt);
void xe_gt_idle_disable_c6(struct xe_gt *gt);
+void xe_gt_idle_enable_pg(struct xe_gt *gt);
+void xe_gt_idle_disable_pg(struct xe_gt *gt);
#endif /* _XE_GT_IDLE_H_ */
diff --git a/drivers/gpu/drm/xe/xe_gt_mcr.c b/drivers/gpu/drm/xe/xe_gt_mcr.c
index 577bd7043740..6d948a469126 100644
--- a/drivers/gpu/drm/xe/xe_gt_mcr.c
+++ b/drivers/gpu/drm/xe/xe_gt_mcr.c
@@ -342,7 +342,7 @@ static void init_steering_oaddrm(struct xe_gt *gt)
else
gt->steering[OADDRM].group_target = 1;
- gt->steering[DSS].instance_target = 0; /* unused */
+ gt->steering[OADDRM].instance_target = 0; /* unused */
}
static void init_steering_sqidi_psmi(struct xe_gt *gt)
@@ -357,8 +357,8 @@ static void init_steering_sqidi_psmi(struct xe_gt *gt)
static void init_steering_inst0(struct xe_gt *gt)
{
- gt->steering[DSS].group_target = 0; /* unused */
- gt->steering[DSS].instance_target = 0; /* unused */
+ gt->steering[INSTANCE0].group_target = 0; /* unused */
+ gt->steering[INSTANCE0].instance_target = 0; /* unused */
}
static const struct {
@@ -375,18 +375,35 @@ static const struct {
[IMPLICIT_STEERING] = { "IMPLICIT", NULL },
};
-void xe_gt_mcr_init(struct xe_gt *gt)
+/**
+ * xe_gt_mcr_init_early - Early initialization of the MCR support
+ * @gt: GT structure
+ *
+ * Perform early software only initialization of the MCR lock to allow
+ * the synchronization on accessing the STEER_SEMAPHORE register and
+ * use the xe_gt_mcr_multicast_write() function.
+ */
+void xe_gt_mcr_init_early(struct xe_gt *gt)
{
- struct xe_device *xe = gt_to_xe(gt);
-
BUILD_BUG_ON(IMPLICIT_STEERING + 1 != NUM_STEERING_TYPES);
BUILD_BUG_ON(ARRAY_SIZE(xe_steering_types) != NUM_STEERING_TYPES);
+ spin_lock_init(&gt->mcr_lock);
+}
+
+/**
+ * xe_gt_mcr_init - Normal initialization of the MCR support
+ * @gt: GT structure
+ *
+ * Perform normal initialization of the MCR for all usages.
+ */
+void xe_gt_mcr_init(struct xe_gt *gt)
+{
+ struct xe_device *xe = gt_to_xe(gt);
+
if (IS_SRIOV_VF(xe))
return;
- spin_lock_init(&gt->mcr_lock);
-
if (gt->info.type == XE_GT_TYPE_MEDIA) {
drm_WARN_ON(&xe->drm, MEDIA_VER(xe) < 13);
diff --git a/drivers/gpu/drm/xe/xe_gt_mcr.h b/drivers/gpu/drm/xe/xe_gt_mcr.h
index a7f4ab1aa584..8d119a0d5493 100644
--- a/drivers/gpu/drm/xe/xe_gt_mcr.h
+++ b/drivers/gpu/drm/xe/xe_gt_mcr.h
@@ -12,6 +12,7 @@
struct drm_printer;
struct xe_gt;
+void xe_gt_mcr_init_early(struct xe_gt *gt);
void xe_gt_mcr_init(struct xe_gt *gt);
void xe_gt_mcr_set_implicit_defaults(struct xe_gt *gt);
@@ -40,4 +41,28 @@ void xe_gt_mcr_get_dss_steering(struct xe_gt *gt, unsigned int dss, u16 *group,
for_each_dss((dss), (gt)) \
for_each_if((xe_gt_mcr_get_dss_steering((gt), (dss), &(group), &(instance)), true))
+/*
+ * Loop over each DSS available for geometry and determine the group and
+ * instance IDs that should be used to steer MCR accesses toward this DSS.
+ * @dss: DSS ID to obtain steering for
+ * @gt: GT structure
+ * @group: steering group ID, data type: u16
+ * @instance: steering instance ID, data type: u16
+ */
+#define for_each_geometry_dss(dss, gt, group, instance) \
+ for_each_dss_steering(dss, gt, group, instance) \
+ if (xe_gt_has_geometry_dss(gt, dss))
+
+/*
+ * Loop over each DSS available for compute and determine the group and
+ * instance IDs that should be used to steer MCR accesses toward this DSS.
+ * @dss: DSS ID to obtain steering for
+ * @gt: GT structure
+ * @group: steering group ID, data type: u16
+ * @instance: steering instance ID, data type: u16
+ */
+#define for_each_compute_dss(dss, gt, group, instance) \
+ for_each_dss_steering(dss, gt, group, instance) \
+ if (xe_gt_has_compute_dss(gt, dss))
+
#endif /* _XE_GT_MCR_H_ */
diff --git a/drivers/gpu/drm/xe/xe_gt_pagefault.c b/drivers/gpu/drm/xe/xe_gt_pagefault.c
index fa9e9853c53b..9292d5468868 100644
--- a/drivers/gpu/drm/xe/xe_gt_pagefault.c
+++ b/drivers/gpu/drm/xe/xe_gt_pagefault.c
@@ -19,8 +19,7 @@
#include "xe_guc.h"
#include "xe_guc_ct.h"
#include "xe_migrate.h"
-#include "xe_pt.h"
-#include "xe_trace.h"
+#include "xe_trace_bo.h"
#include "xe_vm.h"
struct pagefault {
@@ -126,17 +125,73 @@ static int xe_pf_begin(struct drm_exec *exec, struct xe_vma *vma,
return 0;
}
+static int handle_vma_pagefault(struct xe_tile *tile, struct pagefault *pf,
+ struct xe_vma *vma)
+{
+ struct xe_vm *vm = xe_vma_vm(vma);
+ struct drm_exec exec;
+ struct dma_fence *fence;
+ ktime_t end = 0;
+ int err;
+ bool atomic;
+
+ trace_xe_vma_pagefault(vma);
+ atomic = access_is_atomic(pf->access_type);
+
+ /* Check if VMA is valid */
+ if (vma_is_valid(tile, vma) && !atomic)
+ return 0;
+
+retry_userptr:
+ if (xe_vma_is_userptr(vma) &&
+ xe_vma_userptr_check_repin(to_userptr_vma(vma))) {
+ struct xe_userptr_vma *uvma = to_userptr_vma(vma);
+
+ err = xe_vma_userptr_pin_pages(uvma);
+ if (err)
+ return err;
+ }
+
+ /* Lock VM and BOs dma-resv */
+ drm_exec_init(&exec, 0, 0);
+ drm_exec_until_all_locked(&exec) {
+ err = xe_pf_begin(&exec, vma, atomic, tile->id);
+ drm_exec_retry_on_contention(&exec);
+ if (xe_vm_validate_should_retry(&exec, err, &end))
+ err = -EAGAIN;
+ if (err)
+ goto unlock_dma_resv;
+
+ /* Bind VMA only to the GT that has faulted */
+ trace_xe_vma_pf_bind(vma);
+ fence = xe_vma_rebind(vm, vma, BIT(tile->id));
+ if (IS_ERR(fence)) {
+ err = PTR_ERR(fence);
+ if (xe_vm_validate_should_retry(&exec, err, &end))
+ err = -EAGAIN;
+ goto unlock_dma_resv;
+ }
+ }
+
+ dma_fence_wait(fence, false);
+ dma_fence_put(fence);
+ vma->tile_invalidated &= ~BIT(tile->id);
+
+unlock_dma_resv:
+ drm_exec_fini(&exec);
+ if (err == -EAGAIN)
+ goto retry_userptr;
+
+ return err;
+}
+
static int handle_pagefault(struct xe_gt *gt, struct pagefault *pf)
{
struct xe_device *xe = gt_to_xe(gt);
struct xe_tile *tile = gt_to_tile(gt);
- struct drm_exec exec;
struct xe_vm *vm;
struct xe_vma *vma = NULL;
- struct dma_fence *fence;
- bool write_locked;
- int ret = 0;
- bool atomic;
+ int err;
/* SW isn't expected to handle TRTT faults */
if (pf->trva_fault)
@@ -153,100 +208,25 @@ static int handle_pagefault(struct xe_gt *gt, struct pagefault *pf)
if (!vm)
return -EINVAL;
-retry_userptr:
/*
- * TODO: Avoid exclusive lock if VM doesn't have userptrs, or
- * start out read-locked?
+ * TODO: Change to read lock? Using write lock for simplicity.
*/
down_write(&vm->lock);
- write_locked = true;
vma = lookup_vma(vm, pf->page_addr);
if (!vma) {
- ret = -EINVAL;
+ err = -EINVAL;
goto unlock_vm;
}
- if (!xe_vma_is_userptr(vma) ||
- !xe_vma_userptr_check_repin(to_userptr_vma(vma))) {
- downgrade_write(&vm->lock);
- write_locked = false;
- }
-
- trace_xe_vma_pagefault(vma);
-
- atomic = access_is_atomic(pf->access_type);
+ err = handle_vma_pagefault(tile, pf, vma);
- /* Check if VMA is valid */
- if (vma_is_valid(tile, vma) && !atomic)
- goto unlock_vm;
-
- /* TODO: Validate fault */
-
- if (xe_vma_is_userptr(vma) && write_locked) {
- struct xe_userptr_vma *uvma = to_userptr_vma(vma);
-
- spin_lock(&vm->userptr.invalidated_lock);
- list_del_init(&uvma->userptr.invalidate_link);
- spin_unlock(&vm->userptr.invalidated_lock);
-
- ret = xe_vma_userptr_pin_pages(uvma);
- if (ret)
- goto unlock_vm;
-
- downgrade_write(&vm->lock);
- write_locked = false;
- }
-
- /* Lock VM and BOs dma-resv */
- drm_exec_init(&exec, 0, 0);
- drm_exec_until_all_locked(&exec) {
- ret = xe_pf_begin(&exec, vma, atomic, tile->id);
- drm_exec_retry_on_contention(&exec);
- if (ret)
- goto unlock_dma_resv;
- }
-
- /* Bind VMA only to the GT that has faulted */
- trace_xe_vma_pf_bind(vma);
- fence = __xe_pt_bind_vma(tile, vma, xe_tile_migrate_engine(tile), NULL, 0,
- vma->tile_present & BIT(tile->id));
- if (IS_ERR(fence)) {
- ret = PTR_ERR(fence);
- goto unlock_dma_resv;
- }
-
- /*
- * XXX: Should we drop the lock before waiting? This only helps if doing
- * GPU binds which is currently only done if we have to wait for more
- * than 10ms on a move.
- */
- dma_fence_wait(fence, false);
- dma_fence_put(fence);
-
- if (xe_vma_is_userptr(vma))
- ret = xe_vma_userptr_check_repin(to_userptr_vma(vma));
- vma->tile_invalidated &= ~BIT(tile->id);
-
-unlock_dma_resv:
- drm_exec_fini(&exec);
unlock_vm:
- if (!ret)
+ if (!err)
vm->usm.last_fault_vma = vma;
- if (write_locked)
- up_write(&vm->lock);
- else
- up_read(&vm->lock);
- if (ret == -EAGAIN)
- goto retry_userptr;
-
- if (!ret) {
- ret = xe_gt_tlb_invalidation_vma(gt, NULL, vma);
- if (ret >= 0)
- ret = 0;
- }
+ up_write(&vm->lock);
xe_vm_put(vm);
- return ret;
+ return err;
}
static int send_pagefault_reply(struct xe_guc *guc,
diff --git a/drivers/gpu/drm/xe/xe_gt_printk.h b/drivers/gpu/drm/xe/xe_gt_printk.h
index c2b004d3f48e..d6228baaff1e 100644
--- a/drivers/gpu/drm/xe/xe_gt_printk.h
+++ b/drivers/gpu/drm/xe/xe_gt_printk.h
@@ -13,6 +13,9 @@
#define xe_gt_printk(_gt, _level, _fmt, ...) \
drm_##_level(&gt_to_xe(_gt)->drm, "GT%u: " _fmt, (_gt)->info.id, ##__VA_ARGS__)
+#define xe_gt_err_once(_gt, _fmt, ...) \
+ xe_gt_printk((_gt), err_once, _fmt, ##__VA_ARGS__)
+
#define xe_gt_err(_gt, _fmt, ...) \
xe_gt_printk((_gt), err, _fmt, ##__VA_ARGS__)
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf.c b/drivers/gpu/drm/xe/xe_gt_sriov_pf.c
index 791dcdd767e2..9dbba9ab7a9a 100644
--- a/drivers/gpu/drm/xe/xe_gt_sriov_pf.c
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf.c
@@ -5,8 +5,13 @@
#include <drm/drm_managed.h>
+#include "regs/xe_sriov_regs.h"
+
#include "xe_gt_sriov_pf.h"
+#include "xe_gt_sriov_pf_config.h"
#include "xe_gt_sriov_pf_helpers.h"
+#include "xe_gt_sriov_pf_service.h"
+#include "xe_mmio.h"
/*
* VF's metadata is maintained in the flexible array where:
@@ -48,5 +53,44 @@ int xe_gt_sriov_pf_init_early(struct xe_gt *gt)
if (err)
return err;
+ err = xe_gt_sriov_pf_service_init(gt);
+ if (err)
+ return err;
+
return 0;
}
+
+static bool pf_needs_enable_ggtt_guest_update(struct xe_device *xe)
+{
+ return GRAPHICS_VERx100(xe) == 1200;
+}
+
+static void pf_enable_ggtt_guest_update(struct xe_gt *gt)
+{
+ xe_mmio_write32(gt, VIRTUAL_CTRL_REG, GUEST_GTT_UPDATE_EN);
+}
+
+/**
+ * xe_gt_sriov_pf_init_hw - Initialize SR-IOV hardware support.
+ * @gt: the &xe_gt to initialize
+ *
+ * On some platforms the PF must explicitly enable VF's access to the GGTT.
+ */
+void xe_gt_sriov_pf_init_hw(struct xe_gt *gt)
+{
+ if (pf_needs_enable_ggtt_guest_update(gt_to_xe(gt)))
+ pf_enable_ggtt_guest_update(gt);
+
+ xe_gt_sriov_pf_service_update(gt);
+}
+
+/**
+ * xe_gt_sriov_pf_restart - Restart SR-IOV support after a GT reset.
+ * @gt: the &xe_gt
+ *
+ * This function can only be called on PF.
+ */
+void xe_gt_sriov_pf_restart(struct xe_gt *gt)
+{
+ xe_gt_sriov_pf_config_restart(gt);
+}
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf.h b/drivers/gpu/drm/xe/xe_gt_sriov_pf.h
index 05142ffc4319..f0cb726a6919 100644
--- a/drivers/gpu/drm/xe/xe_gt_sriov_pf.h
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf.h
@@ -10,11 +10,21 @@ struct xe_gt;
#ifdef CONFIG_PCI_IOV
int xe_gt_sriov_pf_init_early(struct xe_gt *gt);
+void xe_gt_sriov_pf_init_hw(struct xe_gt *gt);
+void xe_gt_sriov_pf_restart(struct xe_gt *gt);
#else
static inline int xe_gt_sriov_pf_init_early(struct xe_gt *gt)
{
return 0;
}
+
+static inline void xe_gt_sriov_pf_init_hw(struct xe_gt *gt)
+{
+}
+
+static inline void xe_gt_sriov_pf_restart(struct xe_gt *gt)
+{
+}
#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 79116ad58620..4699b7836001 100644
--- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_config.c
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_config.c
@@ -25,6 +25,7 @@
#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"
@@ -187,14 +188,20 @@ 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_cfg_exec_quantum(struct xe_gt *gt, unsigned int vfid, u32 exec_quantum)
+static int pf_push_vf_cfg_exec_quantum(struct xe_gt *gt, unsigned int vfid, u32 *exec_quantum)
{
- return pf_push_vf_cfg_u32(gt, vfid, GUC_KLV_VF_CFG_EXEC_QUANTUM_KEY, exec_quantum);
+ /* GuC will silently clamp values exceeding max */
+ *exec_quantum = min_t(u32, *exec_quantum, GUC_KLV_VF_CFG_EXEC_QUANTUM_MAX_VALUE);
+
+ return pf_push_vf_cfg_u32(gt, vfid, GUC_KLV_VF_CFG_EXEC_QUANTUM_KEY, *exec_quantum);
}
-static int pf_push_vf_cfg_preempt_timeout(struct xe_gt *gt, unsigned int vfid, u32 preempt_timeout)
+static int pf_push_vf_cfg_preempt_timeout(struct xe_gt *gt, unsigned int vfid, u32 *preempt_timeout)
{
- return pf_push_vf_cfg_u32(gt, vfid, GUC_KLV_VF_CFG_PREEMPT_TIMEOUT_KEY, preempt_timeout);
+ /* GuC will silently clamp values exceeding max */
+ *preempt_timeout = min_t(u32, *preempt_timeout, GUC_KLV_VF_CFG_PREEMPT_TIMEOUT_MAX_VALUE);
+
+ return pf_push_vf_cfg_u32(gt, vfid, GUC_KLV_VF_CFG_PREEMPT_TIMEOUT_KEY, *preempt_timeout);
}
static int pf_push_vf_cfg_lmem(struct xe_gt *gt, unsigned int vfid, u64 size)
@@ -202,6 +209,15 @@ static int pf_push_vf_cfg_lmem(struct xe_gt *gt, unsigned int vfid, u64 size)
return pf_push_vf_cfg_u64(gt, vfid, GUC_KLV_VF_CFG_LMEM_SIZE_KEY, size);
}
+static int pf_push_vf_cfg_threshold(struct xe_gt *gt, unsigned int vfid,
+ enum xe_guc_klv_threshold_index index, u32 value)
+{
+ u32 key = xe_guc_klv_threshold_index_to_key(index);
+
+ xe_gt_assert(gt, key);
+ return pf_push_vf_cfg_u32(gt, vfid, key, value);
+}
+
static struct xe_gt_sriov_config *pf_pick_vf_config(struct xe_gt *gt, unsigned int vfid)
{
xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
@@ -1274,6 +1290,9 @@ static void pf_reset_vf_lmtt(struct xe_device *xe, unsigned int vfid)
struct xe_tile *tile;
unsigned int tid;
+ xe_assert(xe, IS_DGFX(xe));
+ xe_assert(xe, IS_SRIOV_PF(xe));
+
for_each_tile(tile, xe, tid) {
lmtt = &tile->sriov.pf.lmtt;
xe_lmtt_drop_pages(lmtt, vfid);
@@ -1292,6 +1311,9 @@ static int pf_update_vf_lmtt(struct xe_device *xe, unsigned int vfid)
unsigned int tid;
int err;
+ xe_assert(xe, IS_DGFX(xe));
+ xe_assert(xe, IS_SRIOV_PF(xe));
+
total = 0;
for_each_tile(tile, xe, tid)
total += pf_get_vf_config_lmem(tile->primary_gt, vfid);
@@ -1337,6 +1359,7 @@ fail:
static void pf_release_vf_config_lmem(struct xe_gt *gt, struct xe_gt_sriov_config *config)
{
+ xe_gt_assert(gt, IS_DGFX(gt_to_xe(gt)));
xe_gt_assert(gt, !xe_gt_is_media_type(gt));
lockdep_assert_held(xe_gt_sriov_pf_master_mutex(gt));
@@ -1355,6 +1378,7 @@ static int pf_provision_vf_lmem(struct xe_gt *gt, unsigned int vfid, u64 size)
int err;
xe_gt_assert(gt, vfid);
+ xe_gt_assert(gt, IS_DGFX(xe));
xe_gt_assert(gt, !xe_gt_is_media_type(gt));
size = round_up(size, pf_get_lmem_alignment(gt));
@@ -1519,6 +1543,7 @@ static u64 pf_estimate_fair_lmem(struct xe_gt *gt, unsigned int num_vfs)
u64 fair;
fair = div_u64(available, num_vfs);
+ fair = rounddown_pow_of_two(fair); /* XXX: ttm_vram_mgr & drm_buddy limitation */
fair = ALIGN_DOWN(fair, alignment);
#ifdef MAX_FAIR_LMEM
fair = min_t(u64, MAX_FAIR_LMEM, fair);
@@ -1604,7 +1629,7 @@ 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;
- err = pf_push_vf_cfg_exec_quantum(gt, vfid, exec_quantum);
+ err = pf_push_vf_cfg_exec_quantum(gt, vfid, &exec_quantum);
if (unlikely(err))
return err;
@@ -1674,7 +1699,7 @@ 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;
- err = pf_push_vf_cfg_preempt_timeout(gt, vfid, preempt_timeout);
+ err = pf_push_vf_cfg_preempt_timeout(gt, vfid, &preempt_timeout);
if (unlikely(err))
return err;
@@ -1742,13 +1767,94 @@ static void pf_reset_config_sched(struct xe_gt *gt, struct xe_gt_sriov_config *c
config->preempt_timeout = 0;
}
+static int pf_provision_threshold(struct xe_gt *gt, unsigned int vfid,
+ enum xe_guc_klv_threshold_index index, u32 value)
+{
+ struct xe_gt_sriov_config *config = pf_pick_vf_config(gt, vfid);
+ int err;
+
+ err = pf_push_vf_cfg_threshold(gt, vfid, index, value);
+ if (unlikely(err))
+ return err;
+
+ config->thresholds[index] = value;
+
+ return 0;
+}
+
+static int pf_get_threshold(struct xe_gt *gt, unsigned int vfid,
+ enum xe_guc_klv_threshold_index index)
+{
+ struct xe_gt_sriov_config *config = pf_pick_vf_config(gt, vfid);
+
+ return config->thresholds[index];
+}
+
+static const char *threshold_unit(u32 threshold)
+{
+ return threshold ? "" : "(disabled)";
+}
+
+/**
+ * xe_gt_sriov_pf_config_set_threshold - Configure threshold for the VF.
+ * @gt: the &xe_gt
+ * @vfid: the VF identifier
+ * @index: the threshold index
+ * @value: requested value (0 means disabled)
+ *
+ * This function can only be called on PF.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_gt_sriov_pf_config_set_threshold(struct xe_gt *gt, unsigned int vfid,
+ enum xe_guc_klv_threshold_index index, u32 value)
+{
+ u32 key = xe_guc_klv_threshold_index_to_key(index);
+ const char *name = xe_guc_klv_key_to_string(key);
+ int err;
+
+ mutex_lock(xe_gt_sriov_pf_master_mutex(gt));
+ err = pf_provision_threshold(gt, vfid, index, value);
+ mutex_unlock(xe_gt_sriov_pf_master_mutex(gt));
+
+ return pf_config_set_u32_done(gt, vfid, value,
+ xe_gt_sriov_pf_config_get_threshold(gt, vfid, index),
+ name, threshold_unit, err);
+}
+
+/**
+ * xe_gt_sriov_pf_config_get_threshold - Get VF's threshold.
+ * @gt: the &xe_gt
+ * @vfid: the VF identifier
+ * @index: the threshold index
+ *
+ * This function can only be called on PF.
+ *
+ * Return: value of VF's (or PF's) threshold.
+ */
+u32 xe_gt_sriov_pf_config_get_threshold(struct xe_gt *gt, unsigned int vfid,
+ enum xe_guc_klv_threshold_index index)
+{
+ u32 value;
+
+ mutex_lock(xe_gt_sriov_pf_master_mutex(gt));
+ value = pf_get_threshold(gt, vfid, index);
+ mutex_unlock(xe_gt_sriov_pf_master_mutex(gt));
+
+ return value;
+}
+
static void pf_release_vf_config(struct xe_gt *gt, unsigned int vfid)
{
struct xe_gt_sriov_config *config = pf_pick_vf_config(gt, vfid);
+ struct xe_device *xe = gt_to_xe(gt);
if (!xe_gt_is_media_type(gt)) {
pf_release_vf_config_ggtt(gt, config);
- pf_release_vf_config_lmem(gt, config);
+ if (IS_DGFX(xe)) {
+ pf_release_vf_config_lmem(gt, config);
+ pf_update_vf_lmtt(xe, vfid);
+ }
}
pf_release_config_ctxs(gt, config);
pf_release_config_dbs(gt, config);
@@ -1817,6 +1923,84 @@ int xe_gt_sriov_pf_config_push(struct xe_gt *gt, unsigned int vfid, bool refresh
return err;
}
+static int pf_validate_vf_config(struct xe_gt *gt, unsigned int vfid)
+{
+ struct xe_gt *primary_gt = gt_to_tile(gt)->primary_gt;
+ struct xe_device *xe = gt_to_xe(gt);
+ bool valid_ggtt, valid_ctxs, valid_dbs;
+ bool valid_any, valid_all;
+
+ valid_ggtt = pf_get_vf_config_ggtt(primary_gt, vfid);
+ valid_ctxs = pf_get_vf_config_ctxs(gt, vfid);
+ valid_dbs = pf_get_vf_config_dbs(gt, vfid);
+
+ /* note that GuC doorbells are optional */
+ valid_any = valid_ggtt || valid_ctxs || valid_dbs;
+ valid_all = valid_ggtt && valid_ctxs;
+
+ if (IS_DGFX(xe)) {
+ bool valid_lmem = pf_get_vf_config_ggtt(primary_gt, vfid);
+
+ valid_any = valid_any || valid_lmem;
+ valid_all = valid_all && valid_lmem;
+ }
+
+ return valid_all ? 1 : valid_any ? -ENOKEY : -ENODATA;
+}
+
+/**
+ * xe_gt_sriov_pf_config_is_empty - Check VF's configuration.
+ * @gt: the &xe_gt
+ * @vfid: the VF identifier (can't be PF)
+ *
+ * This function can only be called on PF.
+ *
+ * Return: true if VF mandatory configuration (GGTT, LMEM, ...) is empty.
+ */
+bool xe_gt_sriov_pf_config_is_empty(struct xe_gt *gt, unsigned int vfid)
+{
+ bool empty;
+
+ xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
+ xe_gt_assert(gt, vfid);
+
+ mutex_lock(xe_gt_sriov_pf_master_mutex(gt));
+ empty = pf_validate_vf_config(gt, vfid) == -ENODATA;
+ mutex_unlock(xe_gt_sriov_pf_master_mutex(gt));
+
+ return empty;
+}
+
+/**
+ * xe_gt_sriov_pf_config_restart - Restart SR-IOV configurations after a GT reset.
+ * @gt: the &xe_gt
+ *
+ * Any prior configurations pushed to GuC are lost when the GT is reset.
+ * Push again all non-empty VF configurations to the GuC.
+ *
+ * This function can only be called on PF.
+ */
+void xe_gt_sriov_pf_config_restart(struct xe_gt *gt)
+{
+ unsigned int n, total_vfs = xe_sriov_pf_get_totalvfs(gt_to_xe(gt));
+ unsigned int fail = 0, skip = 0;
+
+ for (n = 1; n <= total_vfs; n++) {
+ if (xe_gt_sriov_pf_config_is_empty(gt, n))
+ skip++;
+ else if (xe_gt_sriov_pf_config_push(gt, n, false))
+ fail++;
+ }
+
+ if (fail)
+ xe_gt_sriov_notice(gt, "Failed to push %u of %u VF%s configurations\n",
+ fail, total_vfs - skip, str_plural(total_vfs));
+
+ if (fail != total_vfs)
+ xe_gt_sriov_dbg(gt, "pushed %u skip %u of %u VF%s configurations\n",
+ total_vfs - skip - fail, skip, total_vfs, str_plural(total_vfs));
+}
+
/**
* xe_gt_sriov_pf_config_print_ggtt - Print GGTT configurations.
* @gt: the &xe_gt
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 5e6b36f00b5b..c0e6e4743dc2 100644
--- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_config.h
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_config.h
@@ -8,6 +8,7 @@
#include <linux/types.h>
+enum xe_guc_klv_threshold_index;
struct drm_printer;
struct xe_gt;
@@ -43,10 +44,19 @@ u32 xe_gt_sriov_pf_config_get_preempt_timeout(struct xe_gt *gt, unsigned int vfi
int xe_gt_sriov_pf_config_set_preempt_timeout(struct xe_gt *gt, unsigned int vfid,
u32 preempt_timeout);
+u32 xe_gt_sriov_pf_config_get_threshold(struct xe_gt *gt, unsigned int vfid,
+ enum xe_guc_klv_threshold_index index);
+int xe_gt_sriov_pf_config_set_threshold(struct xe_gt *gt, unsigned int vfid,
+ enum xe_guc_klv_threshold_index index, u32 value);
+
int xe_gt_sriov_pf_config_set_fair(struct xe_gt *gt, unsigned int vfid, unsigned int num_vfs);
int xe_gt_sriov_pf_config_release(struct xe_gt *gt, unsigned int vfid, bool force);
int xe_gt_sriov_pf_config_push(struct xe_gt *gt, unsigned int vfid, bool refresh);
+bool xe_gt_sriov_pf_config_is_empty(struct xe_gt *gt, unsigned int vfid);
+
+void xe_gt_sriov_pf_config_restart(struct xe_gt *gt);
+
int xe_gt_sriov_pf_config_print_ggtt(struct xe_gt *gt, struct drm_printer *p);
int xe_gt_sriov_pf_config_print_ctxs(struct xe_gt *gt, struct drm_printer *p);
int xe_gt_sriov_pf_config_print_dbs(struct xe_gt *gt, struct drm_printer *p);
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 d3745c355957..7bc66656fcc7 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
@@ -8,6 +8,8 @@
#include <drm/drm_mm.h>
+#include "xe_guc_klv_thresholds_set_types.h"
+
struct xe_bo;
/**
@@ -32,6 +34,8 @@ struct xe_gt_sriov_config {
u32 exec_quantum;
/** @preempt_timeout: preemption timeout in microseconds. */
u32 preempt_timeout;
+ /** @thresholds: GuC thresholds for adverse events notifications. */
+ u32 thresholds[XE_GUC_KLV_NUM_THRESHOLDS];
};
/**
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_control.c b/drivers/gpu/drm/xe/xe_gt_sriov_pf_control.c
index 40b8f881fe04..ebf06e037750 100644
--- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_control.c
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_control.c
@@ -130,6 +130,27 @@ int xe_gt_sriov_pf_control_stop_vf(struct xe_gt *gt, unsigned int vfid)
}
/**
+ * xe_gt_sriov_pf_control_trigger_flr - Start a VF FLR sequence.
+ * @gt: the &xe_gt
+ * @vfid: the VF identifier
+ *
+ * This function is for PF only.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_gt_sriov_pf_control_trigger_flr(struct xe_gt *gt, unsigned int vfid)
+{
+ int err;
+
+ /* XXX pf_send_vf_flr_start() expects ct->lock */
+ mutex_lock(&gt->uc.guc.ct.lock);
+ err = pf_send_vf_flr_start(gt, vfid);
+ mutex_unlock(&gt->uc.guc.ct.lock);
+
+ return err;
+}
+
+/**
* DOC: The VF FLR Flow with GuC
*
* PF GUC PCI
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_control.h b/drivers/gpu/drm/xe/xe_gt_sriov_pf_control.h
index 850a3e37661f..405d1586f991 100644
--- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_control.h
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_control.h
@@ -14,6 +14,7 @@ struct xe_gt;
int xe_gt_sriov_pf_control_pause_vf(struct xe_gt *gt, unsigned int vfid);
int xe_gt_sriov_pf_control_resume_vf(struct xe_gt *gt, unsigned int vfid);
int xe_gt_sriov_pf_control_stop_vf(struct xe_gt *gt, unsigned int vfid);
+int xe_gt_sriov_pf_control_trigger_flr(struct xe_gt *gt, unsigned int vfid);
#ifdef CONFIG_PCI_IOV
int xe_gt_sriov_pf_control_process_guc2pf(struct xe_gt *gt, const u32 *msg, u32 len);
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_debugfs.c b/drivers/gpu/drm/xe/xe_gt_sriov_pf_debugfs.c
new file mode 100644
index 000000000000..2290ddaf9594
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_debugfs.c
@@ -0,0 +1,427 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2023-2024 Intel Corporation
+ */
+
+#include <linux/debugfs.h>
+
+#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"
+#include "xe_gt_debugfs.h"
+#include "xe_gt_sriov_pf_config.h"
+#include "xe_gt_sriov_pf_control.h"
+#include "xe_gt_sriov_pf_debugfs.h"
+#include "xe_gt_sriov_pf_helpers.h"
+#include "xe_gt_sriov_pf_monitor.h"
+#include "xe_gt_sriov_pf_policy.h"
+#include "xe_gt_sriov_pf_service.h"
+#include "xe_pm.h"
+
+/*
+ * /sys/kernel/debug/dri/0/
+ * ├── gt0 # d_inode->i_private = gt
+ * │   ├── pf # d_inode->i_private = gt
+ * │   ├── vf1 # d_inode->i_private = VFID(1)
+ * :   :
+ * │   ├── vfN # d_inode->i_private = VFID(N)
+ */
+
+static void *extract_priv(struct dentry *d)
+{
+ return d->d_inode->i_private;
+}
+
+static struct xe_gt *extract_gt(struct dentry *d)
+{
+ return extract_priv(d->d_parent);
+}
+
+static unsigned int extract_vfid(struct dentry *d)
+{
+ return extract_priv(d) == extract_gt(d) ? PFID : (uintptr_t)extract_priv(d);
+}
+
+/*
+ * /sys/kernel/debug/dri/0/
+ * ├── gt0
+ * │   ├── pf
+ * │   │   ├── ggtt_available
+ * │   │   ├── ggtt_provisioned
+ * │   │   ├── contexts_provisioned
+ * │   │   ├── doorbells_provisioned
+ * │   │   ├── runtime_registers
+ * │   │   ├── negotiated_versions
+ * │   │   ├── adverse_events
+ */
+
+static const struct drm_info_list pf_info[] = {
+ {
+ "ggtt_available",
+ .show = xe_gt_debugfs_simple_show,
+ .data = xe_gt_sriov_pf_config_print_available_ggtt,
+ },
+ {
+ "ggtt_provisioned",
+ .show = xe_gt_debugfs_simple_show,
+ .data = xe_gt_sriov_pf_config_print_ggtt,
+ },
+ {
+ "contexts_provisioned",
+ .show = xe_gt_debugfs_simple_show,
+ .data = xe_gt_sriov_pf_config_print_ctxs,
+ },
+ {
+ "doorbells_provisioned",
+ .show = xe_gt_debugfs_simple_show,
+ .data = xe_gt_sriov_pf_config_print_dbs,
+ },
+ {
+ "runtime_registers",
+ .show = xe_gt_debugfs_simple_show,
+ .data = xe_gt_sriov_pf_service_print_runtime,
+ },
+ {
+ "negotiated_versions",
+ .show = xe_gt_debugfs_simple_show,
+ .data = xe_gt_sriov_pf_service_print_version,
+ },
+ {
+ "adverse_events",
+ .show = xe_gt_debugfs_simple_show,
+ .data = xe_gt_sriov_pf_monitor_print_events,
+ },
+};
+
+/*
+ * /sys/kernel/debug/dri/0/
+ * ├── gt0
+ * │   ├── pf
+ * │   │   ├── reset_engine
+ * │   │   ├── sample_period
+ * │   │   ├── sched_if_idle
+ */
+
+#define DEFINE_SRIOV_GT_POLICY_DEBUGFS_ATTRIBUTE(POLICY, TYPE, FORMAT) \
+ \
+static int POLICY##_set(void *data, u64 val) \
+{ \
+ struct xe_gt *gt = extract_gt(data); \
+ struct xe_device *xe = gt_to_xe(gt); \
+ int err; \
+ \
+ if (val > (TYPE)~0ull) \
+ return -EOVERFLOW; \
+ \
+ xe_pm_runtime_get(xe); \
+ err = xe_gt_sriov_pf_policy_set_##POLICY(gt, val); \
+ xe_pm_runtime_put(xe); \
+ \
+ return err; \
+} \
+ \
+static int POLICY##_get(void *data, u64 *val) \
+{ \
+ struct xe_gt *gt = extract_gt(data); \
+ \
+ *val = xe_gt_sriov_pf_policy_get_##POLICY(gt); \
+ return 0; \
+} \
+ \
+DEFINE_DEBUGFS_ATTRIBUTE(POLICY##_fops, POLICY##_get, POLICY##_set, FORMAT)
+
+DEFINE_SRIOV_GT_POLICY_DEBUGFS_ATTRIBUTE(reset_engine, bool, "%llu\n");
+DEFINE_SRIOV_GT_POLICY_DEBUGFS_ATTRIBUTE(sched_if_idle, bool, "%llu\n");
+DEFINE_SRIOV_GT_POLICY_DEBUGFS_ATTRIBUTE(sample_period, u32, "%llu\n");
+
+static void pf_add_policy_attrs(struct xe_gt *gt, struct dentry *parent)
+{
+ xe_gt_assert(gt, gt == extract_gt(parent));
+ xe_gt_assert(gt, PFID == extract_vfid(parent));
+
+ debugfs_create_file_unsafe("reset_engine", 0644, parent, parent, &reset_engine_fops);
+ debugfs_create_file_unsafe("sched_if_idle", 0644, parent, parent, &sched_if_idle_fops);
+ debugfs_create_file_unsafe("sample_period_ms", 0644, parent, parent, &sample_period_fops);
+}
+
+/*
+ * /sys/kernel/debug/dri/0/
+ * ├── gt0
+ * │   ├── pf
+ * │   │   ├── ggtt_spare
+ * │   │   ├── lmem_spare
+ * │   │   ├── doorbells_spare
+ * │   │   ├── contexts_spare
+ * │   │   ├── exec_quantum_ms
+ * │   │   ├── preempt_timeout_us
+ * │   ├── vf1
+ * │   │   ├── ggtt_quota
+ * │   │   ├── lmem_quota
+ * │   │   ├── doorbells_quota
+ * │   │   ├── contexts_quota
+ * │   │   ├── exec_quantum_ms
+ * │   │   ├── preempt_timeout_us
+ */
+
+#define DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(CONFIG, TYPE, FORMAT) \
+ \
+static int CONFIG##_set(void *data, u64 val) \
+{ \
+ struct xe_gt *gt = extract_gt(data); \
+ unsigned int vfid = extract_vfid(data); \
+ struct xe_device *xe = gt_to_xe(gt); \
+ int err; \
+ \
+ if (val > (TYPE)~0ull) \
+ return -EOVERFLOW; \
+ \
+ xe_pm_runtime_get(xe); \
+ err = xe_gt_sriov_pf_config_set_##CONFIG(gt, vfid, val); \
+ xe_pm_runtime_put(xe); \
+ \
+ return err; \
+} \
+ \
+static int CONFIG##_get(void *data, u64 *val) \
+{ \
+ struct xe_gt *gt = extract_gt(data); \
+ unsigned int vfid = extract_vfid(data); \
+ \
+ *val = xe_gt_sriov_pf_config_get_##CONFIG(gt, vfid); \
+ return 0; \
+} \
+ \
+DEFINE_DEBUGFS_ATTRIBUTE(CONFIG##_fops, CONFIG##_get, CONFIG##_set, FORMAT)
+
+DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(ggtt, u64, "%llu\n");
+DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(lmem, u64, "%llu\n");
+DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(ctxs, u32, "%llu\n");
+DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(dbs, u32, "%llu\n");
+DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(exec_quantum, u32, "%llu\n");
+DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(preempt_timeout, u32, "%llu\n");
+
+/*
+ * /sys/kernel/debug/dri/0/
+ * ├── gt0
+ * │   ├── pf
+ * │   │   ├── threshold_cat_error_count
+ * │   │   ├── threshold_doorbell_time_us
+ * │   │   ├── threshold_engine_reset_count
+ * │   │   ├── threshold_guc_time_us
+ * │   │   ├── threshold_irq_time_us
+ * │   │   ├── threshold_page_fault_count
+ * │   ├── vf1
+ * │   │   ├── threshold_cat_error_count
+ * │   │   ├── threshold_doorbell_time_us
+ * │   │   ├── threshold_engine_reset_count
+ * │   │   ├── threshold_guc_time_us
+ * │   │   ├── threshold_irq_time_us
+ * │   │   ├── threshold_page_fault_count
+ */
+
+static int set_threshold(void *data, u64 val, enum xe_guc_klv_threshold_index index)
+{
+ struct xe_gt *gt = extract_gt(data);
+ unsigned int vfid = extract_vfid(data);
+ struct xe_device *xe = gt_to_xe(gt);
+ int err;
+
+ if (val > (u32)~0ull)
+ return -EOVERFLOW;
+
+ xe_pm_runtime_get(xe);
+ err = xe_gt_sriov_pf_config_set_threshold(gt, vfid, index, val);
+ xe_pm_runtime_put(xe);
+
+ return err;
+}
+
+static int get_threshold(void *data, u64 *val, enum xe_guc_klv_threshold_index index)
+{
+ struct xe_gt *gt = extract_gt(data);
+ unsigned int vfid = extract_vfid(data);
+
+ *val = xe_gt_sriov_pf_config_get_threshold(gt, vfid, index);
+ return 0;
+}
+
+#define DEFINE_SRIOV_GT_THRESHOLD_DEBUGFS_ATTRIBUTE(THRESHOLD, INDEX) \
+ \
+static int THRESHOLD##_set(void *data, u64 val) \
+{ \
+ return set_threshold(data, val, INDEX); \
+} \
+ \
+static int THRESHOLD##_get(void *data, u64 *val) \
+{ \
+ return get_threshold(data, val, INDEX); \
+} \
+ \
+DEFINE_DEBUGFS_ATTRIBUTE(THRESHOLD##_fops, THRESHOLD##_get, THRESHOLD##_set, "%llu\n")
+
+/* generate all threshold attributes */
+#define define_threshold_attribute(TAG, NAME, ...) \
+ DEFINE_SRIOV_GT_THRESHOLD_DEBUGFS_ATTRIBUTE(NAME, MAKE_XE_GUC_KLV_THRESHOLD_INDEX(TAG));
+MAKE_XE_GUC_KLV_THRESHOLDS_SET(define_threshold_attribute)
+#undef define_threshold_attribute
+
+static void pf_add_config_attrs(struct xe_gt *gt, struct dentry *parent, unsigned int vfid)
+{
+ xe_gt_assert(gt, gt == extract_gt(parent));
+ xe_gt_assert(gt, vfid == extract_vfid(parent));
+
+ if (!xe_gt_is_media_type(gt)) {
+ debugfs_create_file_unsafe(vfid ? "ggtt_quota" : "ggtt_spare",
+ 0644, parent, parent, &ggtt_fops);
+ if (IS_DGFX(gt_to_xe(gt)))
+ debugfs_create_file_unsafe(vfid ? "lmem_quota" : "lmem_spare",
+ 0644, parent, parent, &lmem_fops);
+ }
+ debugfs_create_file_unsafe(vfid ? "doorbells_quota" : "doorbells_spare",
+ 0644, parent, parent, &dbs_fops);
+ debugfs_create_file_unsafe(vfid ? "contexts_quota" : "contexts_spare",
+ 0644, parent, parent, &ctxs_fops);
+ debugfs_create_file_unsafe("exec_quantum_ms", 0644, parent, parent,
+ &exec_quantum_fops);
+ debugfs_create_file_unsafe("preempt_timeout_us", 0644, parent, parent,
+ &preempt_timeout_fops);
+
+ /* register all threshold attributes */
+#define register_threshold_attribute(TAG, NAME, ...) \
+ debugfs_create_file_unsafe("threshold_" #NAME, 0644, parent, parent, \
+ &NAME##_fops);
+ MAKE_XE_GUC_KLV_THRESHOLDS_SET(register_threshold_attribute)
+#undef register_threshold_attribute
+}
+
+/*
+ * /sys/kernel/debug/dri/0/
+ * ├── gt0
+ * │   ├── vf1
+ * │   │   ├── control { stop, pause, resume }
+ */
+
+static const struct {
+ const char *cmd;
+ int (*fn)(struct xe_gt *gt, unsigned int vfid);
+} control_cmds[] = {
+ { "stop", xe_gt_sriov_pf_control_stop_vf },
+ { "pause", xe_gt_sriov_pf_control_pause_vf },
+ { "resume", xe_gt_sriov_pf_control_resume_vf },
+};
+
+static ssize_t control_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)
+{
+ struct dentry *dent = file_dentry(file);
+ struct dentry *parent = dent->d_parent;
+ struct xe_gt *gt = extract_gt(parent);
+ struct xe_device *xe = gt_to_xe(gt);
+ unsigned int vfid = extract_vfid(parent);
+ int ret = -EINVAL;
+ char cmd[32];
+ size_t n;
+
+ xe_gt_assert(gt, vfid);
+ xe_gt_sriov_pf_assert_vfid(gt, vfid);
+
+ if (*pos)
+ return -ESPIPE;
+
+ if (count > sizeof(cmd) - 1)
+ return -EINVAL;
+
+ ret = simple_write_to_buffer(cmd, sizeof(cmd) - 1, pos, buf, count);
+ if (ret < 0)
+ return ret;
+ cmd[ret] = '\0';
+
+ for (n = 0; n < ARRAY_SIZE(control_cmds); n++) {
+ xe_gt_assert(gt, sizeof(cmd) > strlen(control_cmds[n].cmd));
+
+ if (sysfs_streq(cmd, control_cmds[n].cmd)) {
+ xe_pm_runtime_get(xe);
+ ret = control_cmds[n].fn ? (*control_cmds[n].fn)(gt, vfid) : 0;
+ xe_pm_runtime_put(xe);
+ break;
+ }
+ }
+
+ return (ret < 0) ? ret : count;
+}
+
+static ssize_t control_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
+{
+ char help[128];
+ size_t n;
+
+ help[0] = '\0';
+ for (n = 0; n < ARRAY_SIZE(control_cmds); n++) {
+ strlcat(help, control_cmds[n].cmd, sizeof(help));
+ strlcat(help, "\n", sizeof(help));
+ }
+
+ return simple_read_from_buffer(buf, count, ppos, help, strlen(help));
+}
+
+static const struct file_operations control_ops = {
+ .owner = THIS_MODULE,
+ .open = simple_open,
+ .write = control_write,
+ .read = control_read,
+ .llseek = default_llseek,
+};
+
+/**
+ * xe_gt_sriov_pf_debugfs_register - Register SR-IOV PF specific entries in GT debugfs.
+ * @gt: the &xe_gt to register
+ * @root: the &dentry that represents the GT directory
+ *
+ * Register SR-IOV PF entries that are GT related and must be shown under GT debugfs.
+ */
+void xe_gt_sriov_pf_debugfs_register(struct xe_gt *gt, struct dentry *root)
+{
+ struct xe_device *xe = gt_to_xe(gt);
+ struct drm_minor *minor = xe->drm.primary;
+ int n, totalvfs = xe_sriov_pf_get_totalvfs(xe);
+ struct dentry *pfdentry;
+ struct dentry *vfdentry;
+ char buf[14]; /* should be enough up to "vf%u\0" for 2^32 - 1 */
+
+ xe_gt_assert(gt, IS_SRIOV_PF(xe));
+ xe_gt_assert(gt, root->d_inode->i_private == gt);
+
+ /*
+ * /sys/kernel/debug/dri/0/
+ * ├── gt0
+ * │   ├── pf
+ */
+ pfdentry = debugfs_create_dir("pf", root);
+ if (IS_ERR(pfdentry))
+ return;
+ pfdentry->d_inode->i_private = gt;
+
+ drm_debugfs_create_files(pf_info, ARRAY_SIZE(pf_info), pfdentry, minor);
+ pf_add_policy_attrs(gt, pfdentry);
+ pf_add_config_attrs(gt, pfdentry, PFID);
+
+ for (n = 1; n <= totalvfs; n++) {
+ /*
+ * /sys/kernel/debug/dri/0/
+ * ├── gt0
+ * │   ├── vf1
+ * │   ├── vf2
+ */
+ snprintf(buf, sizeof(buf), "vf%u", n);
+ vfdentry = debugfs_create_dir(buf, root);
+ if (IS_ERR(vfdentry))
+ break;
+ vfdentry->d_inode->i_private = (void *)(uintptr_t)n;
+
+ pf_add_config_attrs(gt, vfdentry, VFID(n));
+ debugfs_create_file("control", 0600, vfdentry, NULL, &control_ops);
+ }
+}
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_debugfs.h b/drivers/gpu/drm/xe/xe_gt_sriov_pf_debugfs.h
new file mode 100644
index 000000000000..038cc8ddc244
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_debugfs.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2023-2024 Intel Corporation
+ */
+
+#ifndef _XE_GT_SRIOV_PF_DEBUGFS_H_
+#define _XE_GT_SRIOV_PF_DEBUGFS_H_
+
+struct xe_gt;
+struct dentry;
+
+#ifdef CONFIG_PCI_IOV
+void xe_gt_sriov_pf_debugfs_register(struct xe_gt *gt, struct dentry *root);
+#else
+static inline void xe_gt_sriov_pf_debugfs_register(struct xe_gt *gt, struct dentry *root) { }
+#endif
+
+#endif
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_monitor.c b/drivers/gpu/drm/xe/xe_gt_sriov_pf_monitor.c
new file mode 100644
index 000000000000..7d532bded02a
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_monitor.c
@@ -0,0 +1,147 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2023-2024 Intel Corporation
+ */
+
+#include "abi/guc_actions_sriov_abi.h"
+#include "abi/guc_messages_abi.h"
+
+#include "xe_gt_sriov_pf_config.h"
+#include "xe_gt_sriov_pf_helpers.h"
+#include "xe_gt_sriov_pf_monitor.h"
+#include "xe_gt_sriov_printk.h"
+#include "xe_guc_klv_helpers.h"
+#include "xe_guc_klv_thresholds_set.h"
+
+/**
+ * xe_gt_sriov_pf_monitor_flr - Cleanup VF data after VF FLR.
+ * @gt: the &xe_gt
+ * @vfid: the VF identifier
+ *
+ * On FLR this function will reset all event data related to the VF.
+ * This function is for PF only.
+ */
+void xe_gt_sriov_pf_monitor_flr(struct xe_gt *gt, u32 vfid)
+{
+ int e;
+
+ xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
+ xe_gt_sriov_pf_assert_vfid(gt, vfid);
+
+ for (e = 0; e < XE_GUC_KLV_NUM_THRESHOLDS; e++)
+ gt->sriov.pf.vfs[vfid].monitor.guc.events[e] = 0;
+}
+
+static void pf_update_event_counter(struct xe_gt *gt, u32 vfid,
+ enum xe_guc_klv_threshold_index e)
+{
+ xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
+ xe_gt_assert(gt, e < XE_GUC_KLV_NUM_THRESHOLDS);
+
+ gt->sriov.pf.vfs[vfid].monitor.guc.events[e]++;
+}
+
+static int pf_handle_vf_threshold_event(struct xe_gt *gt, u32 vfid, u32 threshold)
+{
+ char origin[8];
+ int e;
+
+ e = xe_guc_klv_threshold_key_to_index(threshold);
+ xe_sriov_function_name(vfid, origin, sizeof(origin));
+
+ /* was there a new KEY added that we missed? */
+ if (unlikely(e < 0)) {
+ xe_gt_sriov_notice(gt, "unknown threshold key %#x reported for %s\n",
+ threshold, origin);
+ return -ENOTCONN;
+ }
+
+ xe_gt_sriov_dbg(gt, "%s exceeded threshold %u %s\n",
+ origin, xe_gt_sriov_pf_config_get_threshold(gt, vfid, e),
+ xe_guc_klv_key_to_string(threshold));
+
+ pf_update_event_counter(gt, vfid, e);
+
+ return 0;
+}
+
+/**
+ * xe_gt_sriov_pf_monitor_process_guc2pf - Handle adverse event notification from the GuC.
+ * @gt: the &xe_gt
+ * @msg: G2H event message
+ * @len: length of the message
+ *
+ * This function is intended for PF only.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_gt_sriov_pf_monitor_process_guc2pf(struct xe_gt *gt, const u32 *msg, u32 len)
+{
+ struct xe_device *xe = gt_to_xe(gt);
+ u32 vfid;
+ u32 threshold;
+
+ xe_gt_assert(gt, len >= GUC_HXG_MSG_MIN_LEN);
+ xe_gt_assert(gt, FIELD_GET(GUC_HXG_MSG_0_ORIGIN, msg[0]) == GUC_HXG_ORIGIN_GUC);
+ xe_gt_assert(gt, FIELD_GET(GUC_HXG_MSG_0_TYPE, msg[0]) == GUC_HXG_TYPE_EVENT);
+ xe_gt_assert(gt, FIELD_GET(GUC_HXG_EVENT_MSG_0_ACTION, msg[0]) ==
+ GUC_ACTION_GUC2PF_ADVERSE_EVENT);
+
+ if (unlikely(!IS_SRIOV_PF(xe)))
+ return -EPROTO;
+
+ if (unlikely(FIELD_GET(GUC2PF_ADVERSE_EVENT_EVENT_MSG_0_MBZ, msg[0])))
+ return -EPFNOSUPPORT;
+
+ if (unlikely(len < GUC2PF_ADVERSE_EVENT_EVENT_MSG_LEN))
+ return -EPROTO;
+
+ vfid = FIELD_GET(GUC2PF_ADVERSE_EVENT_EVENT_MSG_1_VFID, msg[1]);
+ threshold = FIELD_GET(GUC2PF_ADVERSE_EVENT_EVENT_MSG_2_THRESHOLD, msg[2]);
+
+ if (unlikely(vfid > xe_gt_sriov_pf_get_totalvfs(gt)))
+ return -EINVAL;
+
+ return pf_handle_vf_threshold_event(gt, vfid, threshold);
+}
+
+/**
+ * xe_gt_sriov_pf_monitor_print_events - Print adverse events counters.
+ * @gt: the &xe_gt to print events from
+ * @p: the &drm_printer
+ *
+ * Print adverse events counters for all VFs.
+ * VFs with no events are not printed.
+ *
+ * This function can only be called on PF.
+ */
+void xe_gt_sriov_pf_monitor_print_events(struct xe_gt *gt, struct drm_printer *p)
+{
+ unsigned int n, total_vfs = xe_gt_sriov_pf_get_totalvfs(gt);
+ const struct xe_gt_sriov_monitor *data;
+ int e;
+
+ xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
+
+ for (n = 1; n <= total_vfs; n++) {
+ data = &gt->sriov.pf.vfs[n].monitor;
+
+ for (e = 0; e < XE_GUC_KLV_NUM_THRESHOLDS; e++)
+ if (data->guc.events[e])
+ break;
+
+ /* skip empty unless in debug mode */
+ if (e >= XE_GUC_KLV_NUM_THRESHOLDS &&
+ !IS_ENABLED(CONFIG_DRM_XE_DEBUG_SRIOV))
+ continue;
+
+#define __format(...) "%s:%u "
+#define __value(TAG, NAME, ...) , #NAME, data->guc.events[MAKE_XE_GUC_KLV_THRESHOLD_INDEX(TAG)]
+
+ drm_printf(p, "VF%u:\t" MAKE_XE_GUC_KLV_THRESHOLDS_SET(__format) "\n",
+ n MAKE_XE_GUC_KLV_THRESHOLDS_SET(__value));
+
+#undef __format
+#undef __value
+ }
+}
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_monitor.h b/drivers/gpu/drm/xe/xe_gt_sriov_pf_monitor.h
new file mode 100644
index 000000000000..7ca9351a271b
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_monitor.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2023-2024 Intel Corporation
+ */
+
+#ifndef _XE_GT_SRIOV_PF_MONITOR_H_
+#define _XE_GT_SRIOV_PF_MONITOR_H_
+
+#include <linux/errno.h>
+#include <linux/types.h>
+
+struct drm_printer;
+struct xe_gt;
+
+void xe_gt_sriov_pf_monitor_flr(struct xe_gt *gt, u32 vfid);
+void xe_gt_sriov_pf_monitor_print_events(struct xe_gt *gt, struct drm_printer *p);
+
+#ifdef CONFIG_PCI_IOV
+int xe_gt_sriov_pf_monitor_process_guc2pf(struct xe_gt *gt, const u32 *msg, u32 len);
+#else
+static inline int xe_gt_sriov_pf_monitor_process_guc2pf(struct xe_gt *gt, const u32 *msg, u32 len)
+{
+ return -EPROTO;
+}
+#endif
+
+#endif
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_monitor_types.h b/drivers/gpu/drm/xe/xe_gt_sriov_pf_monitor_types.h
new file mode 100644
index 000000000000..e27c0308c5db
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_monitor_types.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2023-2024 Intel Corporation
+ */
+
+#ifndef _XE_GT_SRIOV_PF_MONITOR_TYPES_H_
+#define _XE_GT_SRIOV_PF_MONITOR_TYPES_H_
+
+#include "xe_guc_klv_thresholds_set_types.h"
+
+/**
+ * struct xe_gt_sriov_monitor - GT level per-VF monitoring data.
+ */
+struct xe_gt_sriov_monitor {
+ /** @guc: monitoring data related to the GuC. */
+ struct {
+ /** @guc.events: number of adverse events reported by the GuC. */
+ unsigned int events[XE_GUC_KLV_NUM_THRESHOLDS];
+ } guc;
+};
+
+#endif
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_service.c b/drivers/gpu/drm/xe/xe_gt_sriov_pf_service.c
new file mode 100644
index 000000000000..0e23b7ea4f3e
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_service.c
@@ -0,0 +1,550 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2023-2024 Intel Corporation
+ */
+
+#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_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"
+
+static void pf_init_versions(struct xe_gt *gt)
+{
+ BUILD_BUG_ON(!GUC_RELAY_VERSION_BASE_MAJOR && !GUC_RELAY_VERSION_BASE_MINOR);
+ BUILD_BUG_ON(GUC_RELAY_VERSION_BASE_MAJOR > GUC_RELAY_VERSION_LATEST_MAJOR);
+
+ /* base versions may differ between platforms */
+ gt->sriov.pf.service.version.base.major = GUC_RELAY_VERSION_BASE_MAJOR;
+ gt->sriov.pf.service.version.base.minor = GUC_RELAY_VERSION_BASE_MINOR;
+
+ /* latest version is same for all platforms */
+ gt->sriov.pf.service.version.latest.major = GUC_RELAY_VERSION_LATEST_MAJOR;
+ gt->sriov.pf.service.version.latest.minor = GUC_RELAY_VERSION_LATEST_MINOR;
+}
+
+/* Return: 0 on success or a negative error code on failure. */
+static int pf_negotiate_version(struct xe_gt *gt,
+ u32 wanted_major, u32 wanted_minor,
+ u32 *major, u32 *minor)
+{
+ struct xe_gt_sriov_pf_service_version base = gt->sriov.pf.service.version.base;
+ struct xe_gt_sriov_pf_service_version latest = gt->sriov.pf.service.version.latest;
+
+ xe_gt_assert(gt, base.major);
+ xe_gt_assert(gt, base.major <= latest.major);
+ xe_gt_assert(gt, (base.major < latest.major) || (base.minor <= latest.minor));
+
+ /* VF doesn't care - return our latest */
+ if (wanted_major == VF2PF_HANDSHAKE_MAJOR_ANY &&
+ wanted_minor == VF2PF_HANDSHAKE_MINOR_ANY) {
+ *major = latest.major;
+ *minor = latest.minor;
+ return 0;
+ }
+
+ /* VF wants newer than our - return our latest */
+ if (wanted_major > latest.major) {
+ *major = latest.major;
+ *minor = latest.minor;
+ return 0;
+ }
+
+ /* VF wants older than min required - reject */
+ if (wanted_major < base.major ||
+ (wanted_major == base.major && wanted_minor < base.minor)) {
+ return -EPERM;
+ }
+
+ /* previous major - return wanted, as we should still support it */
+ if (wanted_major < latest.major) {
+ /* XXX: we are not prepared for multi-versions yet */
+ xe_gt_assert(gt, base.major == latest.major);
+ return -ENOPKG;
+ }
+
+ /* same major - return common minor */
+ *major = wanted_major;
+ *minor = min_t(u32, latest.minor, wanted_minor);
+ return 0;
+}
+
+static void pf_connect(struct xe_gt *gt, u32 vfid, u32 major, u32 minor)
+{
+ xe_gt_sriov_pf_assert_vfid(gt, vfid);
+ xe_gt_assert(gt, major || minor);
+
+ gt->sriov.pf.vfs[vfid].version.major = major;
+ gt->sriov.pf.vfs[vfid].version.minor = minor;
+}
+
+static void pf_disconnect(struct xe_gt *gt, u32 vfid)
+{
+ xe_gt_sriov_pf_assert_vfid(gt, vfid);
+
+ gt->sriov.pf.vfs[vfid].version.major = 0;
+ gt->sriov.pf.vfs[vfid].version.minor = 0;
+}
+
+static bool pf_is_negotiated(struct xe_gt *gt, u32 vfid, u32 major, u32 minor)
+{
+ xe_gt_sriov_pf_assert_vfid(gt, vfid);
+
+ return major == gt->sriov.pf.vfs[vfid].version.major &&
+ minor <= gt->sriov.pf.vfs[vfid].version.minor;
+}
+
+static const struct xe_reg tgl_runtime_regs[] = {
+ RPM_CONFIG0, /* _MMIO(0x0d00) */
+ MIRROR_FUSE3, /* _MMIO(0x9118) */
+ XELP_EU_ENABLE, /* _MMIO(0x9134) */
+ XELP_GT_SLICE_ENABLE, /* _MMIO(0x9138) */
+ XELP_GT_GEOMETRY_DSS_ENABLE, /* _MMIO(0x913c) */
+ GT_VEBOX_VDBOX_DISABLE, /* _MMIO(0x9140) */
+ CTC_MODE, /* _MMIO(0xa26c) */
+ HUC_KERNEL_LOAD_INFO, /* _MMIO(0xc1dc) */
+ TIMESTAMP_OVERRIDE, /* _MMIO(0x44074) */
+};
+
+static const struct xe_reg ats_m_runtime_regs[] = {
+ RPM_CONFIG0, /* _MMIO(0x0d00) */
+ MIRROR_FUSE3, /* _MMIO(0x9118) */
+ MIRROR_FUSE1, /* _MMIO(0x911c) */
+ XELP_EU_ENABLE, /* _MMIO(0x9134) */
+ XELP_GT_GEOMETRY_DSS_ENABLE, /* _MMIO(0x913c) */
+ GT_VEBOX_VDBOX_DISABLE, /* _MMIO(0x9140) */
+ XEHP_GT_COMPUTE_DSS_ENABLE, /* _MMIO(0x9144) */
+ CTC_MODE, /* _MMIO(0xa26c) */
+ HUC_KERNEL_LOAD_INFO, /* _MMIO(0xc1dc) */
+ TIMESTAMP_OVERRIDE, /* _MMIO(0x44074) */
+};
+
+static const struct xe_reg pvc_runtime_regs[] = {
+ RPM_CONFIG0, /* _MMIO(0x0d00) */
+ MIRROR_FUSE3, /* _MMIO(0x9118) */
+ XELP_EU_ENABLE, /* _MMIO(0x9134) */
+ XELP_GT_GEOMETRY_DSS_ENABLE, /* _MMIO(0x913c) */
+ GT_VEBOX_VDBOX_DISABLE, /* _MMIO(0x9140) */
+ XEHP_GT_COMPUTE_DSS_ENABLE, /* _MMIO(0x9144) */
+ XEHPC_GT_COMPUTE_DSS_ENABLE_EXT,/* _MMIO(0x9148) */
+ CTC_MODE, /* _MMIO(0xA26C) */
+ HUC_KERNEL_LOAD_INFO, /* _MMIO(0xc1dc) */
+ TIMESTAMP_OVERRIDE, /* _MMIO(0x44074) */
+};
+
+static const struct xe_reg ver_1270_runtime_regs[] = {
+ RPM_CONFIG0, /* _MMIO(0x0d00) */
+ XEHP_FUSE4, /* _MMIO(0x9114) */
+ MIRROR_FUSE3, /* _MMIO(0x9118) */
+ MIRROR_FUSE1, /* _MMIO(0x911c) */
+ XELP_EU_ENABLE, /* _MMIO(0x9134) */
+ XELP_GT_GEOMETRY_DSS_ENABLE, /* _MMIO(0x913c) */
+ GT_VEBOX_VDBOX_DISABLE, /* _MMIO(0x9140) */
+ XEHP_GT_COMPUTE_DSS_ENABLE, /* _MMIO(0x9144) */
+ XEHPC_GT_COMPUTE_DSS_ENABLE_EXT,/* _MMIO(0x9148) */
+ CTC_MODE, /* _MMIO(0xa26c) */
+ HUC_KERNEL_LOAD_INFO, /* _MMIO(0xc1dc) */
+ TIMESTAMP_OVERRIDE, /* _MMIO(0x44074) */
+};
+
+static const struct xe_reg ver_2000_runtime_regs[] = {
+ RPM_CONFIG0, /* _MMIO(0x0d00) */
+ XEHP_FUSE4, /* _MMIO(0x9114) */
+ MIRROR_FUSE3, /* _MMIO(0x9118) */
+ MIRROR_FUSE1, /* _MMIO(0x911c) */
+ XELP_EU_ENABLE, /* _MMIO(0x9134) */
+ XELP_GT_GEOMETRY_DSS_ENABLE, /* _MMIO(0x913c) */
+ GT_VEBOX_VDBOX_DISABLE, /* _MMIO(0x9140) */
+ XEHP_GT_COMPUTE_DSS_ENABLE, /* _MMIO(0x9144) */
+ XEHPC_GT_COMPUTE_DSS_ENABLE_EXT,/* _MMIO(0x9148) */
+ XE2_GT_COMPUTE_DSS_2, /* _MMIO(0x914c) */
+ XE2_GT_GEOMETRY_DSS_1, /* _MMIO(0x9150) */
+ XE2_GT_GEOMETRY_DSS_2, /* _MMIO(0x9154) */
+ CTC_MODE, /* _MMIO(0xa26c) */
+ HUC_KERNEL_LOAD_INFO, /* _MMIO(0xc1dc) */
+ TIMESTAMP_OVERRIDE, /* _MMIO(0x44074) */
+};
+
+static const struct xe_reg *pick_runtime_regs(struct xe_device *xe, unsigned int *count)
+{
+ const struct xe_reg *regs;
+
+ if (GRAPHICS_VERx100(xe) >= 2000) {
+ *count = ARRAY_SIZE(ver_2000_runtime_regs);
+ regs = ver_2000_runtime_regs;
+ } else if (GRAPHICS_VERx100(xe) >= 1270) {
+ *count = ARRAY_SIZE(ver_1270_runtime_regs);
+ regs = ver_1270_runtime_regs;
+ } else if (GRAPHICS_VERx100(xe) == 1260) {
+ *count = ARRAY_SIZE(pvc_runtime_regs);
+ regs = pvc_runtime_regs;
+ } else if (GRAPHICS_VERx100(xe) == 1255) {
+ *count = ARRAY_SIZE(ats_m_runtime_regs);
+ regs = ats_m_runtime_regs;
+ } else if (GRAPHICS_VERx100(xe) == 1200) {
+ *count = ARRAY_SIZE(tgl_runtime_regs);
+ regs = tgl_runtime_regs;
+ } else {
+ regs = ERR_PTR(-ENOPKG);
+ *count = 0;
+ }
+
+ return regs;
+}
+
+static int pf_alloc_runtime_info(struct xe_gt *gt)
+{
+ struct xe_device *xe = gt_to_xe(gt);
+ const struct xe_reg *regs;
+ unsigned int size;
+ u32 *values;
+
+ xe_gt_assert(gt, IS_SRIOV_PF(xe));
+ xe_gt_assert(gt, !gt->sriov.pf.service.runtime.size);
+ xe_gt_assert(gt, !gt->sriov.pf.service.runtime.regs);
+ xe_gt_assert(gt, !gt->sriov.pf.service.runtime.values);
+
+ regs = pick_runtime_regs(xe, &size);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ if (unlikely(!size))
+ return 0;
+
+ values = drmm_kcalloc(&xe->drm, size, sizeof(u32), GFP_KERNEL);
+ if (!values)
+ return -ENOMEM;
+
+ gt->sriov.pf.service.runtime.size = size;
+ gt->sriov.pf.service.runtime.regs = regs;
+ gt->sriov.pf.service.runtime.values = values;
+
+ return 0;
+}
+
+static void read_many(struct xe_gt *gt, unsigned int count,
+ const struct xe_reg *regs, u32 *values)
+{
+ while (count--)
+ *values++ = xe_mmio_read32(gt, *regs++);
+}
+
+static void pf_prepare_runtime_info(struct xe_gt *gt)
+{
+ const struct xe_reg *regs;
+ unsigned int size;
+ u32 *values;
+
+ if (!gt->sriov.pf.service.runtime.size)
+ return;
+
+ size = gt->sriov.pf.service.runtime.size;
+ regs = gt->sriov.pf.service.runtime.regs;
+ values = gt->sriov.pf.service.runtime.values;
+
+ read_many(gt, size, regs, values);
+
+ if (IS_ENABLED(CONFIG_DRM_XE_DEBUG_SRIOV)) {
+ struct drm_printer p = xe_gt_info_printer(gt);
+
+ xe_gt_sriov_pf_service_print_runtime(gt, &p);
+ }
+}
+
+/**
+ * xe_gt_sriov_pf_service_init - Early initialization of the GT SR-IOV PF services.
+ * @gt: the &xe_gt to initialize
+ *
+ * Performs early initialization of the GT SR-IOV PF services, including preparation
+ * of the runtime info that will be shared with VFs.
+ *
+ * This function can only be called on PF.
+ */
+int xe_gt_sriov_pf_service_init(struct xe_gt *gt)
+{
+ int err;
+
+ pf_init_versions(gt);
+
+ err = pf_alloc_runtime_info(gt);
+ if (unlikely(err))
+ goto failed;
+
+ return 0;
+failed:
+ xe_gt_sriov_err(gt, "Failed to initialize service (%pe)\n", ERR_PTR(err));
+ return err;
+}
+
+/**
+ * xe_gt_sriov_pf_service_update - Update PF SR-IOV services.
+ * @gt: the &xe_gt to update
+ *
+ * Updates runtime data shared with VFs.
+ *
+ * This function can be called more than once.
+ * This function can only be called on PF.
+ */
+void xe_gt_sriov_pf_service_update(struct xe_gt *gt)
+{
+ pf_prepare_runtime_info(gt);
+}
+
+/**
+ * xe_gt_sriov_pf_service_reset - Reset a connection with the VF.
+ * @gt: the &xe_gt
+ * @vfid: the VF identifier
+ *
+ * Reset a VF driver negotiated VF/PF ABI version.
+ * After that point, the VF driver will have to perform new version handshake
+ * to continue use of the PF services again.
+ *
+ * This function can only be called on PF.
+ */
+void xe_gt_sriov_pf_service_reset(struct xe_gt *gt, unsigned int vfid)
+{
+ pf_disconnect(gt, vfid);
+}
+
+/* Return: 0 on success or a negative error code on failure. */
+static int pf_process_handshake(struct xe_gt *gt, u32 vfid,
+ u32 wanted_major, u32 wanted_minor,
+ u32 *major, u32 *minor)
+{
+ int err;
+
+ xe_gt_sriov_dbg_verbose(gt, "VF%u wants ABI version %u.%u\n",
+ vfid, wanted_major, wanted_minor);
+
+ err = pf_negotiate_version(gt, wanted_major, wanted_minor, major, minor);
+
+ if (err < 0) {
+ xe_gt_sriov_notice(gt, "VF%u failed to negotiate ABI %u.%u (%pe)\n",
+ vfid, wanted_major, wanted_minor, ERR_PTR(err));
+ pf_disconnect(gt, vfid);
+ } else {
+ xe_gt_sriov_dbg(gt, "VF%u negotiated ABI version %u.%u\n",
+ vfid, *major, *minor);
+ pf_connect(gt, vfid, *major, *minor);
+ }
+
+ return 0;
+}
+
+/* Return: length of the response message or a negative error code on failure. */
+static int pf_process_handshake_msg(struct xe_gt *gt, u32 origin,
+ const u32 *request, u32 len, u32 *response, u32 size)
+{
+ u32 wanted_major, wanted_minor;
+ u32 major, minor;
+ u32 mbz;
+ int err;
+
+ if (unlikely(len != VF2PF_HANDSHAKE_REQUEST_MSG_LEN))
+ return -EMSGSIZE;
+
+ mbz = FIELD_GET(VF2PF_HANDSHAKE_REQUEST_MSG_0_MBZ, request[0]);
+ if (unlikely(mbz))
+ return -EPFNOSUPPORT;
+
+ wanted_major = FIELD_GET(VF2PF_HANDSHAKE_REQUEST_MSG_1_MAJOR, request[1]);
+ wanted_minor = FIELD_GET(VF2PF_HANDSHAKE_REQUEST_MSG_1_MINOR, request[1]);
+
+ err = pf_process_handshake(gt, origin, wanted_major, wanted_minor, &major, &minor);
+ if (err < 0)
+ return err;
+
+ xe_gt_assert(gt, major || minor);
+ xe_gt_assert(gt, size >= VF2PF_HANDSHAKE_RESPONSE_MSG_LEN);
+
+ response[0] = FIELD_PREP(GUC_HXG_MSG_0_ORIGIN, GUC_HXG_ORIGIN_HOST) |
+ FIELD_PREP(GUC_HXG_MSG_0_TYPE, GUC_HXG_TYPE_RESPONSE_SUCCESS) |
+ FIELD_PREP(GUC_HXG_RESPONSE_MSG_0_DATA0, 0);
+ response[1] = FIELD_PREP(VF2PF_HANDSHAKE_RESPONSE_MSG_1_MAJOR, major) |
+ FIELD_PREP(VF2PF_HANDSHAKE_RESPONSE_MSG_1_MINOR, minor);
+
+ return VF2PF_HANDSHAKE_RESPONSE_MSG_LEN;
+}
+
+struct reg_data {
+ u32 offset;
+ u32 value;
+} __packed;
+static_assert(hxg_sizeof(struct reg_data) == 2);
+
+/* Return: number of entries copied or negative error code on failure. */
+static int pf_service_runtime_query(struct xe_gt *gt, u32 start, u32 limit,
+ struct reg_data *data, u32 *remaining)
+{
+ struct xe_gt_sriov_pf_service_runtime_regs *runtime;
+ unsigned int count, i;
+ u32 addr;
+
+ xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
+
+ runtime = &gt->sriov.pf.service.runtime;
+
+ if (start > runtime->size)
+ return -ERANGE;
+
+ count = min_t(u32, runtime->size - start, limit);
+
+ for (i = 0; i < count; ++i, ++data) {
+ addr = runtime->regs[start + i].addr;
+ data->offset = xe_mmio_adjusted_addr(gt, addr);
+ data->value = runtime->values[start + i];
+ }
+
+ *remaining = runtime->size - start - count;
+ return count;
+}
+
+/* Return: length of the response message or a negative error code on failure. */
+static int pf_process_runtime_query_msg(struct xe_gt *gt, u32 origin,
+ const u32 *msg, u32 msg_len, u32 *response, u32 resp_size)
+{
+ const u32 chunk_size = hxg_sizeof(struct reg_data);
+ struct reg_data *reg_data_buf;
+ u32 limit, start, max_chunks;
+ u32 remaining = 0;
+ int ret;
+
+ if (!pf_is_negotiated(gt, origin, 1, 0))
+ return -EACCES;
+ if (unlikely(msg_len > VF2PF_QUERY_RUNTIME_REQUEST_MSG_LEN))
+ return -EMSGSIZE;
+ if (unlikely(msg_len < VF2PF_QUERY_RUNTIME_REQUEST_MSG_LEN))
+ return -EPROTO;
+ if (unlikely(resp_size < VF2PF_QUERY_RUNTIME_RESPONSE_MSG_MIN_LEN))
+ return -EINVAL;
+
+ limit = FIELD_GET(VF2PF_QUERY_RUNTIME_REQUEST_MSG_0_LIMIT, msg[0]);
+ start = FIELD_GET(VF2PF_QUERY_RUNTIME_REQUEST_MSG_1_START, msg[1]);
+
+ resp_size = min_t(u32, resp_size, VF2PF_QUERY_RUNTIME_RESPONSE_MSG_MAX_LEN);
+ max_chunks = (resp_size - VF2PF_QUERY_RUNTIME_RESPONSE_MSG_MIN_LEN) / chunk_size;
+ limit = limit == VF2PF_QUERY_RUNTIME_NO_LIMIT ? max_chunks : min_t(u32, max_chunks, limit);
+ reg_data_buf = (void *)(response + VF2PF_QUERY_RUNTIME_RESPONSE_MSG_MIN_LEN);
+
+ ret = pf_service_runtime_query(gt, start, limit, reg_data_buf, &remaining);
+ if (ret < 0)
+ return ret;
+
+ response[0] = FIELD_PREP(GUC_HXG_MSG_0_ORIGIN, GUC_HXG_ORIGIN_HOST) |
+ FIELD_PREP(GUC_HXG_MSG_0_TYPE, GUC_HXG_TYPE_RESPONSE_SUCCESS) |
+ FIELD_PREP(VF2PF_QUERY_RUNTIME_RESPONSE_MSG_0_COUNT, ret);
+ response[1] = FIELD_PREP(VF2PF_QUERY_RUNTIME_RESPONSE_MSG_1_REMAINING, remaining);
+
+ return VF2PF_QUERY_RUNTIME_RESPONSE_MSG_MIN_LEN + ret * hxg_sizeof(struct reg_data);
+}
+
+/**
+ * xe_gt_sriov_pf_service_process_request - Service GT level SR-IOV request message from the VF.
+ * @gt: the &xe_gt that provides the service
+ * @origin: VF number that is requesting the service
+ * @msg: request message
+ * @msg_len: length of the request message (in dwords)
+ * @response: placeholder for the response message
+ * @resp_size: length of the response message buffer (in dwords)
+ *
+ * This function processes `Relay Message`_ request from the VF.
+ *
+ * Return: length of the response message or a negative error code on failure.
+ */
+int xe_gt_sriov_pf_service_process_request(struct xe_gt *gt, u32 origin,
+ const u32 *msg, u32 msg_len,
+ u32 *response, u32 resp_size)
+{
+ u32 action, data __maybe_unused;
+ int ret;
+
+ xe_gt_assert(gt, msg_len >= GUC_HXG_MSG_MIN_LEN);
+ xe_gt_assert(gt, FIELD_GET(GUC_HXG_MSG_0_TYPE, msg[0]) == GUC_HXG_TYPE_REQUEST);
+
+ action = FIELD_GET(GUC_HXG_REQUEST_MSG_0_ACTION, msg[0]);
+ data = FIELD_GET(GUC_HXG_REQUEST_MSG_0_DATA0, msg[0]);
+ xe_gt_sriov_dbg_verbose(gt, "service action %#x:%u from VF%u\n",
+ action, data, origin);
+
+ switch (action) {
+ case GUC_RELAY_ACTION_VF2PF_HANDSHAKE:
+ ret = pf_process_handshake_msg(gt, origin, msg, msg_len, response, resp_size);
+ break;
+ case GUC_RELAY_ACTION_VF2PF_QUERY_RUNTIME:
+ ret = pf_process_runtime_query_msg(gt, origin, msg, msg_len, response, resp_size);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ return ret;
+}
+
+/**
+ * xe_gt_sriov_pf_service_print_runtime - Print PF runtime data shared with VFs.
+ * @gt: the &xe_gt
+ * @p: the &drm_printer
+ *
+ * This function is for PF use only.
+ */
+int xe_gt_sriov_pf_service_print_runtime(struct xe_gt *gt, struct drm_printer *p)
+{
+ const struct xe_reg *regs;
+ unsigned int size;
+ u32 *values;
+
+ xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
+
+ size = gt->sriov.pf.service.runtime.size;
+ regs = gt->sriov.pf.service.runtime.regs;
+ values = gt->sriov.pf.service.runtime.values;
+
+ for (; size--; regs++, values++) {
+ drm_printf(p, "reg[%#x] = %#x\n",
+ xe_mmio_adjusted_addr(gt, regs->addr), *values);
+ }
+
+ return 0;
+}
+
+/**
+ * xe_gt_sriov_pf_service_print_version - Print ABI versions negotiated with VFs.
+ * @gt: the &xe_gt
+ * @p: the &drm_printer
+ *
+ * This function is for PF use only.
+ */
+int xe_gt_sriov_pf_service_print_version(struct xe_gt *gt, struct drm_printer *p)
+{
+ struct xe_device *xe = gt_to_xe(gt);
+ unsigned int n, total_vfs = xe_sriov_pf_get_totalvfs(xe);
+ struct xe_gt_sriov_pf_service_version *version;
+
+ xe_gt_assert(gt, IS_SRIOV_PF(xe));
+
+ for (n = 1; n <= total_vfs; n++) {
+ version = &gt->sriov.pf.vfs[n].version;
+ if (!version->major && !version->minor)
+ continue;
+
+ drm_printf(p, "VF%u:\t%u.%u\n", n, version->major, version->minor);
+ }
+
+ return 0;
+}
+
+#if IS_BUILTIN(CONFIG_DRM_XE_KUNIT_TEST)
+#include "tests/xe_gt_sriov_pf_service_test.c"
+#endif
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_service.h b/drivers/gpu/drm/xe/xe_gt_sriov_pf_service.h
new file mode 100644
index 000000000000..56aaadf0360d
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_service.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2023-2024 Intel Corporation
+ */
+
+#ifndef _XE_GT_SRIOV_PF_SERVICE_H_
+#define _XE_GT_SRIOV_PF_SERVICE_H_
+
+#include <linux/errno.h>
+#include <linux/types.h>
+
+struct drm_printer;
+struct xe_gt;
+
+int xe_gt_sriov_pf_service_init(struct xe_gt *gt);
+void xe_gt_sriov_pf_service_update(struct xe_gt *gt);
+void xe_gt_sriov_pf_service_reset(struct xe_gt *gt, unsigned int vfid);
+
+int xe_gt_sriov_pf_service_print_version(struct xe_gt *gt, struct drm_printer *p);
+int xe_gt_sriov_pf_service_print_runtime(struct xe_gt *gt, struct drm_printer *p);
+
+#ifdef CONFIG_PCI_IOV
+int xe_gt_sriov_pf_service_process_request(struct xe_gt *gt, u32 origin,
+ const u32 *msg, u32 msg_len,
+ u32 *response, u32 resp_size);
+#else
+static inline int
+xe_gt_sriov_pf_service_process_request(struct xe_gt *gt, u32 origin,
+ const u32 *msg, u32 msg_len,
+ u32 *response, u32 resp_size)
+{
+ return -EPROTO;
+}
+#endif
+
+#endif
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_service_types.h b/drivers/gpu/drm/xe/xe_gt_sriov_pf_service_types.h
new file mode 100644
index 000000000000..ad6dd75f0056
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_service_types.h
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2023-2024 Intel Corporation
+ */
+
+#ifndef _XE_GT_SRIOV_PF_SERVICE_TYPES_H_
+#define _XE_GT_SRIOV_PF_SERVICE_TYPES_H_
+
+#include <linux/types.h>
+
+struct xe_reg;
+
+/**
+ * struct xe_gt_sriov_pf_service_version - VF/PF ABI Version.
+ * @major: the major version of the VF/PF ABI
+ * @minor: the minor version of the VF/PF ABI
+ *
+ * See `GuC Relay Communication`_.
+ */
+struct xe_gt_sriov_pf_service_version {
+ u16 major;
+ u16 minor;
+};
+
+/**
+ * struct xe_gt_sriov_pf_service_runtime_regs - Runtime data shared with VFs.
+ * @regs: pointer to static array with register offsets.
+ * @values: pointer to array with captured register values.
+ * @size: size of the regs and value arrays.
+ */
+struct xe_gt_sriov_pf_service_runtime_regs {
+ const struct xe_reg *regs;
+ u32 *values;
+ u32 size;
+};
+
+/**
+ * struct xe_gt_sriov_pf_service - Data used by the PF service.
+ * @version: information about VF/PF ABI versions for current platform.
+ * @version.base: lowest VF/PF ABI version that could be negotiated with VF.
+ * @version.latest: latest VF/PF ABI version supported by the PF driver.
+ * @runtime: runtime data shared with VFs.
+ */
+struct xe_gt_sriov_pf_service {
+ struct {
+ struct xe_gt_sriov_pf_service_version base;
+ struct xe_gt_sriov_pf_service_version latest;
+ } version;
+ struct xe_gt_sriov_pf_service_runtime_regs runtime;
+};
+
+#endif
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_types.h b/drivers/gpu/drm/xe/xe_gt_sriov_pf_types.h
index faf9ee8266ce..40cbaea3ef44 100644
--- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_types.h
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_types.h
@@ -9,7 +9,9 @@
#include <linux/types.h>
#include "xe_gt_sriov_pf_config_types.h"
+#include "xe_gt_sriov_pf_monitor_types.h"
#include "xe_gt_sriov_pf_policy_types.h"
+#include "xe_gt_sriov_pf_service_types.h"
/**
* struct xe_gt_sriov_metadata - GT level per-VF metadata.
@@ -17,15 +19,23 @@
struct xe_gt_sriov_metadata {
/** @config: per-VF provisioning data. */
struct xe_gt_sriov_config config;
+
+ /** @monitor: per-VF monitoring data. */
+ struct xe_gt_sriov_monitor monitor;
+
+ /** @version: negotiated VF/PF ABI version */
+ struct xe_gt_sriov_pf_service_version version;
};
/**
* struct xe_gt_sriov_pf - GT level PF virtualization data.
+ * @service: service data.
* @policy: policy data.
* @spare: PF-only provisioning configuration.
* @vfs: metadata for all VFs.
*/
struct xe_gt_sriov_pf {
+ struct xe_gt_sriov_pf_service service;
struct xe_gt_sriov_pf_policy policy;
struct xe_gt_sriov_spare_config spare;
struct xe_gt_sriov_metadata *vfs;
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_vf.c b/drivers/gpu/drm/xe/xe_gt_sriov_vf.c
new file mode 100644
index 000000000000..41e46a00c01e
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_vf.c
@@ -0,0 +1,979 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2023-2024 Intel Corporation
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bsearch.h>
+
+#include <drm/drm_managed.h>
+#include <drm/drm_print.h>
+
+#include "abi/guc_actions_sriov_abi.h"
+#include "abi/guc_communication_mmio_abi.h"
+#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"
+#include "xe_ggtt.h"
+#include "xe_gt_sriov_printk.h"
+#include "xe_gt_sriov_vf.h"
+#include "xe_gt_sriov_vf_types.h"
+#include "xe_guc.h"
+#include "xe_guc_hxg_helpers.h"
+#include "xe_guc_relay.h"
+#include "xe_mmio.h"
+#include "xe_sriov.h"
+#include "xe_uc_fw.h"
+#include "xe_wopcm.h"
+
+#define make_u64_from_u32(hi, lo) ((u64)((u64)(u32)(hi) << 32 | (u32)(lo)))
+
+static int guc_action_vf_reset(struct xe_guc *guc)
+{
+ 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_VF_RESET),
+ };
+ int ret;
+
+ ret = xe_guc_mmio_send(guc, request, ARRAY_SIZE(request));
+
+ return ret > 0 ? -EPROTO : ret;
+}
+
+static int vf_reset_guc_state(struct xe_gt *gt)
+{
+ struct xe_guc *guc = &gt->uc.guc;
+ int err;
+
+ err = guc_action_vf_reset(guc);
+ if (unlikely(err))
+ xe_gt_sriov_err(gt, "Failed to reset GuC state (%pe)\n", ERR_PTR(err));
+ return err;
+}
+
+static int guc_action_match_version(struct xe_guc *guc,
+ u32 wanted_branch, u32 wanted_major, u32 wanted_minor,
+ u32 *branch, u32 *major, u32 *minor, u32 *patch)
+{
+ u32 request[VF2GUC_MATCH_VERSION_REQUEST_MSG_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_MATCH_VERSION),
+ FIELD_PREP(VF2GUC_MATCH_VERSION_REQUEST_MSG_1_BRANCH, wanted_branch) |
+ FIELD_PREP(VF2GUC_MATCH_VERSION_REQUEST_MSG_1_MAJOR, wanted_major) |
+ FIELD_PREP(VF2GUC_MATCH_VERSION_REQUEST_MSG_1_MINOR, wanted_minor),
+ };
+ u32 response[GUC_MAX_MMIO_MSG_LEN];
+ int ret;
+
+ BUILD_BUG_ON(VF2GUC_MATCH_VERSION_RESPONSE_MSG_LEN > GUC_MAX_MMIO_MSG_LEN);
+
+ ret = xe_guc_mmio_send_recv(guc, request, ARRAY_SIZE(request), response);
+ if (unlikely(ret < 0))
+ return ret;
+
+ if (unlikely(FIELD_GET(VF2GUC_MATCH_VERSION_RESPONSE_MSG_0_MBZ, response[0])))
+ return -EPROTO;
+
+ *branch = FIELD_GET(VF2GUC_MATCH_VERSION_RESPONSE_MSG_1_BRANCH, response[1]);
+ *major = FIELD_GET(VF2GUC_MATCH_VERSION_RESPONSE_MSG_1_MAJOR, response[1]);
+ *minor = FIELD_GET(VF2GUC_MATCH_VERSION_RESPONSE_MSG_1_MINOR, response[1]);
+ *patch = FIELD_GET(VF2GUC_MATCH_VERSION_RESPONSE_MSG_1_PATCH, response[1]);
+
+ return 0;
+}
+
+static void vf_minimum_guc_version(struct xe_gt *gt, u32 *branch, u32 *major, u32 *minor)
+{
+ struct xe_device *xe = gt_to_xe(gt);
+
+ switch (xe->info.platform) {
+ case XE_TIGERLAKE ... XE_PVC:
+ /* 1.1 this is current baseline for Xe driver */
+ *branch = 0;
+ *major = 1;
+ *minor = 1;
+ break;
+ default:
+ /* 1.2 has support for the GMD_ID KLV */
+ *branch = 0;
+ *major = 1;
+ *minor = 2;
+ break;
+ }
+}
+
+static void vf_wanted_guc_version(struct xe_gt *gt, u32 *branch, u32 *major, u32 *minor)
+{
+ /* for now it's the same as minimum */
+ return vf_minimum_guc_version(gt, branch, major, minor);
+}
+
+static int vf_handshake_with_guc(struct xe_gt *gt)
+{
+ struct xe_gt_sriov_vf_guc_version *guc_version = &gt->sriov.vf.guc_version;
+ struct xe_guc *guc = &gt->uc.guc;
+ u32 wanted_branch, wanted_major, wanted_minor;
+ u32 branch, major, minor, patch;
+ int err;
+
+ xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
+
+ /* select wanted version - prefer previous (if any) */
+ if (guc_version->major || guc_version->minor) {
+ wanted_branch = guc_version->branch;
+ wanted_major = guc_version->major;
+ wanted_minor = guc_version->minor;
+ } else {
+ vf_wanted_guc_version(gt, &wanted_branch, &wanted_major, &wanted_minor);
+ xe_gt_assert(gt, wanted_major != GUC_VERSION_MAJOR_ANY);
+ }
+
+ err = guc_action_match_version(guc, wanted_branch, wanted_major, wanted_minor,
+ &branch, &major, &minor, &patch);
+ if (unlikely(err))
+ goto fail;
+
+ /* we don't support interface version change */
+ if ((guc_version->major || guc_version->minor) &&
+ (guc_version->branch != branch || guc_version->major != major ||
+ guc_version->minor != minor)) {
+ xe_gt_sriov_err(gt, "New GuC interface version detected: %u.%u.%u.%u\n",
+ branch, major, minor, patch);
+ xe_gt_sriov_info(gt, "Previously used version was: %u.%u.%u.%u\n",
+ guc_version->branch, guc_version->major,
+ guc_version->minor, guc_version->patch);
+ err = -EREMCHG;
+ goto fail;
+ }
+
+ /* illegal */
+ if (major > wanted_major) {
+ err = -EPROTO;
+ goto unsupported;
+ }
+
+ /* there's no fallback on major version. */
+ if (major != wanted_major) {
+ err = -ENOPKG;
+ goto unsupported;
+ }
+
+ /* check against minimum version supported by us */
+ vf_minimum_guc_version(gt, &wanted_branch, &wanted_major, &wanted_minor);
+ xe_gt_assert(gt, major != GUC_VERSION_MAJOR_ANY);
+ if (major < wanted_major || (major == wanted_major && minor < wanted_minor)) {
+ err = -ENOKEY;
+ goto unsupported;
+ }
+
+ xe_gt_sriov_dbg(gt, "using GuC interface version %u.%u.%u.%u\n",
+ branch, major, minor, patch);
+
+ guc_version->branch = branch;
+ guc_version->major = major;
+ guc_version->minor = minor;
+ guc_version->patch = patch;
+ return 0;
+
+unsupported:
+ xe_gt_sriov_err(gt, "Unsupported GuC version %u.%u.%u.%u (%pe)\n",
+ branch, major, minor, patch, ERR_PTR(err));
+fail:
+ xe_gt_sriov_err(gt, "Unable to confirm GuC version %u.%u (%pe)\n",
+ wanted_major, wanted_minor, ERR_PTR(err));
+
+ /* try again with *any* just to query which version is supported */
+ if (!guc_action_match_version(guc, GUC_VERSION_BRANCH_ANY,
+ GUC_VERSION_MAJOR_ANY, GUC_VERSION_MINOR_ANY,
+ &branch, &major, &minor, &patch))
+ xe_gt_sriov_notice(gt, "GuC reports interface version %u.%u.%u.%u\n",
+ branch, major, minor, patch);
+ return err;
+}
+
+/**
+ * xe_gt_sriov_vf_bootstrap - Query and setup GuC ABI interface version.
+ * @gt: the &xe_gt
+ *
+ * This function is for VF use only.
+ * It requires functional `GuC MMIO based communication`_.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_gt_sriov_vf_bootstrap(struct xe_gt *gt)
+{
+ int err;
+
+ err = vf_reset_guc_state(gt);
+ if (unlikely(err))
+ return err;
+
+ err = vf_handshake_with_guc(gt);
+ if (unlikely(err))
+ return err;
+
+ return 0;
+}
+
+static int guc_action_query_single_klv(struct xe_guc *guc, u32 key,
+ u32 *value, u32 value_len)
+{
+ u32 request[VF2GUC_QUERY_SINGLE_KLV_REQUEST_MSG_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_QUERY_SINGLE_KLV),
+ FIELD_PREP(VF2GUC_QUERY_SINGLE_KLV_REQUEST_MSG_1_KEY, key),
+ };
+ u32 response[GUC_MAX_MMIO_MSG_LEN];
+ u32 length;
+ int ret;
+
+ BUILD_BUG_ON(VF2GUC_QUERY_SINGLE_KLV_RESPONSE_MSG_MAX_LEN > GUC_MAX_MMIO_MSG_LEN);
+ ret = xe_guc_mmio_send_recv(guc, request, ARRAY_SIZE(request), response);
+ if (unlikely(ret < 0))
+ return ret;
+
+ if (unlikely(FIELD_GET(VF2GUC_QUERY_SINGLE_KLV_RESPONSE_MSG_0_MBZ, response[0])))
+ return -EPROTO;
+
+ length = FIELD_GET(VF2GUC_QUERY_SINGLE_KLV_RESPONSE_MSG_0_LENGTH, response[0]);
+ if (unlikely(length > value_len))
+ return -EOVERFLOW;
+ if (unlikely(length < value_len))
+ return -ENODATA;
+
+ switch (value_len) {
+ default:
+ xe_gt_WARN_ON(guc_to_gt(guc), value_len > 3);
+ fallthrough;
+ case 3:
+ value[2] = FIELD_GET(VF2GUC_QUERY_SINGLE_KLV_RESPONSE_MSG_3_VALUE96, response[3]);
+ fallthrough;
+ case 2:
+ value[1] = FIELD_GET(VF2GUC_QUERY_SINGLE_KLV_RESPONSE_MSG_2_VALUE64, response[2]);
+ fallthrough;
+ case 1:
+ value[0] = FIELD_GET(VF2GUC_QUERY_SINGLE_KLV_RESPONSE_MSG_1_VALUE32, response[1]);
+ fallthrough;
+ case 0:
+ break;
+ }
+
+ return 0;
+}
+
+static int guc_action_query_single_klv32(struct xe_guc *guc, u32 key, u32 *value32)
+{
+ return guc_action_query_single_klv(guc, key, value32, hxg_sizeof(u32));
+}
+
+static int guc_action_query_single_klv64(struct xe_guc *guc, u32 key, u64 *value64)
+{
+ u32 value[2];
+ int err;
+
+ err = guc_action_query_single_klv(guc, key, value, hxg_sizeof(value));
+ if (unlikely(err))
+ return err;
+
+ *value64 = make_u64_from_u32(value[1], value[0]);
+ return 0;
+}
+
+static bool has_gmdid(struct xe_device *xe)
+{
+ return GRAPHICS_VERx100(xe) >= 1270;
+}
+
+/**
+ * xe_gt_sriov_vf_gmdid - Query GMDID over MMIO.
+ * @gt: the &xe_gt
+ *
+ * This function is for VF use only.
+ *
+ * Return: value of GMDID KLV on success or 0 on failure.
+ */
+u32 xe_gt_sriov_vf_gmdid(struct xe_gt *gt)
+{
+ const char *type = xe_gt_is_media_type(gt) ? "media" : "graphics";
+ struct xe_guc *guc = &gt->uc.guc;
+ u32 value;
+ int err;
+
+ xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
+ xe_gt_assert(gt, !GRAPHICS_VERx100(gt_to_xe(gt)) || has_gmdid(gt_to_xe(gt)));
+ xe_gt_assert(gt, gt->sriov.vf.guc_version.major > 1 || gt->sriov.vf.guc_version.minor >= 2);
+
+ err = guc_action_query_single_klv32(guc, GUC_KLV_GLOBAL_CFG_GMD_ID_KEY, &value);
+ if (unlikely(err)) {
+ xe_gt_sriov_err(gt, "Failed to obtain %s GMDID (%pe)\n",
+ type, ERR_PTR(err));
+ return 0;
+ }
+
+ xe_gt_sriov_dbg(gt, "%s GMDID = %#x\n", type, value);
+ return value;
+}
+
+static int vf_get_ggtt_info(struct xe_gt *gt)
+{
+ struct xe_gt_sriov_vf_selfconfig *config = &gt->sriov.vf.self_config;
+ struct xe_guc *guc = &gt->uc.guc;
+ u64 start, size;
+ int err;
+
+ xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
+
+ err = guc_action_query_single_klv64(guc, GUC_KLV_VF_CFG_GGTT_START_KEY, &start);
+ if (unlikely(err))
+ return err;
+
+ err = guc_action_query_single_klv64(guc, GUC_KLV_VF_CFG_GGTT_SIZE_KEY, &size);
+ if (unlikely(err))
+ return err;
+
+ if (config->ggtt_size && config->ggtt_size != size) {
+ xe_gt_sriov_err(gt, "Unexpected GGTT reassignment: %lluK != %lluK\n",
+ size / SZ_1K, config->ggtt_size / SZ_1K);
+ return -EREMCHG;
+ }
+
+ xe_gt_sriov_dbg_verbose(gt, "GGTT %#llx-%#llx = %lluK\n",
+ start, start + size - 1, size / SZ_1K);
+
+ config->ggtt_base = start;
+ config->ggtt_size = size;
+
+ return config->ggtt_size ? 0 : -ENODATA;
+}
+
+static int vf_get_lmem_info(struct xe_gt *gt)
+{
+ struct xe_gt_sriov_vf_selfconfig *config = &gt->sriov.vf.self_config;
+ struct xe_guc *guc = &gt->uc.guc;
+ char size_str[10];
+ u64 size;
+ int err;
+
+ xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
+
+ err = guc_action_query_single_klv64(guc, GUC_KLV_VF_CFG_LMEM_SIZE_KEY, &size);
+ if (unlikely(err))
+ return err;
+
+ if (config->lmem_size && config->lmem_size != size) {
+ xe_gt_sriov_err(gt, "Unexpected LMEM reassignment: %lluM != %lluM\n",
+ size / SZ_1M, config->lmem_size / SZ_1M);
+ return -EREMCHG;
+ }
+
+ string_get_size(size, 1, STRING_UNITS_2, size_str, sizeof(size_str));
+ xe_gt_sriov_dbg_verbose(gt, "LMEM %lluM %s\n", size / SZ_1M, size_str);
+
+ config->lmem_size = size;
+
+ return config->lmem_size ? 0 : -ENODATA;
+}
+
+static int vf_get_submission_cfg(struct xe_gt *gt)
+{
+ struct xe_gt_sriov_vf_selfconfig *config = &gt->sriov.vf.self_config;
+ struct xe_guc *guc = &gt->uc.guc;
+ u32 num_ctxs, num_dbs;
+ int err;
+
+ xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
+
+ err = guc_action_query_single_klv32(guc, GUC_KLV_VF_CFG_NUM_CONTEXTS_KEY, &num_ctxs);
+ if (unlikely(err))
+ return err;
+
+ err = guc_action_query_single_klv32(guc, GUC_KLV_VF_CFG_NUM_DOORBELLS_KEY, &num_dbs);
+ if (unlikely(err))
+ return err;
+
+ if (config->num_ctxs && config->num_ctxs != num_ctxs) {
+ xe_gt_sriov_err(gt, "Unexpected CTXs reassignment: %u != %u\n",
+ num_ctxs, config->num_ctxs);
+ return -EREMCHG;
+ }
+ if (config->num_dbs && config->num_dbs != num_dbs) {
+ xe_gt_sriov_err(gt, "Unexpected DBs reassignment: %u != %u\n",
+ num_dbs, config->num_dbs);
+ return -EREMCHG;
+ }
+
+ xe_gt_sriov_dbg_verbose(gt, "CTXs %u DBs %u\n", num_ctxs, num_dbs);
+
+ config->num_ctxs = num_ctxs;
+ config->num_dbs = num_dbs;
+
+ return config->num_ctxs ? 0 : -ENODATA;
+}
+
+static void vf_cache_gmdid(struct xe_gt *gt)
+{
+ xe_gt_assert(gt, has_gmdid(gt_to_xe(gt)));
+ xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
+
+ gt->sriov.vf.runtime.gmdid = xe_gt_sriov_vf_gmdid(gt);
+}
+
+/**
+ * xe_gt_sriov_vf_query_config - Query SR-IOV config data over MMIO.
+ * @gt: the &xe_gt
+ *
+ * This function is for VF use only.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_gt_sriov_vf_query_config(struct xe_gt *gt)
+{
+ struct xe_device *xe = gt_to_xe(gt);
+ int err;
+
+ err = vf_get_ggtt_info(gt);
+ if (unlikely(err))
+ return err;
+
+ if (IS_DGFX(xe) && !xe_gt_is_media_type(gt)) {
+ err = vf_get_lmem_info(gt);
+ if (unlikely(err))
+ return err;
+ }
+
+ err = vf_get_submission_cfg(gt);
+ if (unlikely(err))
+ return err;
+
+ if (has_gmdid(xe))
+ vf_cache_gmdid(gt);
+
+ return 0;
+}
+
+/**
+ * xe_gt_sriov_vf_guc_ids - VF GuC context IDs configuration.
+ * @gt: the &xe_gt
+ *
+ * This function is for VF use only.
+ *
+ * Return: number of GuC context IDs assigned to VF.
+ */
+u16 xe_gt_sriov_vf_guc_ids(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);
+ xe_gt_assert(gt, gt->sriov.vf.self_config.num_ctxs);
+
+ return gt->sriov.vf.self_config.num_ctxs;
+}
+
+/**
+ * xe_gt_sriov_vf_lmem - VF LMEM configuration.
+ * @gt: the &xe_gt
+ *
+ * This function is for VF use only.
+ *
+ * Return: size of the LMEM assigned to VF.
+ */
+u64 xe_gt_sriov_vf_lmem(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);
+ xe_gt_assert(gt, gt->sriov.vf.self_config.lmem_size);
+
+ return gt->sriov.vf.self_config.lmem_size;
+}
+
+static int vf_balloon_ggtt(struct xe_gt *gt)
+{
+ struct xe_gt_sriov_vf_selfconfig *config = &gt->sriov.vf.self_config;
+ struct xe_tile *tile = gt_to_tile(gt);
+ struct xe_ggtt *ggtt = tile->mem.ggtt;
+ struct xe_device *xe = gt_to_xe(gt);
+ u64 start, end;
+ int err;
+
+ xe_gt_assert(gt, IS_SRIOV_VF(xe));
+ xe_gt_assert(gt, !xe_gt_is_media_type(gt));
+
+ if (!config->ggtt_size)
+ return -ENODATA;
+
+ /*
+ * VF can only use part of the GGTT as allocated by the PF:
+ *
+ * WOPCM GUC_GGTT_TOP
+ * |<------------ Total GGTT size ------------------>|
+ *
+ * VF GGTT base -->|<- size ->|
+ *
+ * +--------------------+----------+-----------------+
+ * |////////////////////| block |\\\\\\\\\\\\\\\\\|
+ * +--------------------+----------+-----------------+
+ *
+ * |<--- balloon[0] --->|<-- VF -->|<-- balloon[1] ->|
+ */
+
+ start = xe_wopcm_size(xe);
+ end = config->ggtt_base;
+ if (end != start) {
+ err = xe_ggtt_balloon(ggtt, start, end, &tile->sriov.vf.ggtt_balloon[0]);
+ if (err)
+ goto failed;
+ }
+
+ start = config->ggtt_base + config->ggtt_size;
+ end = GUC_GGTT_TOP;
+ if (end != start) {
+ err = xe_ggtt_balloon(ggtt, start, end, &tile->sriov.vf.ggtt_balloon[1]);
+ if (err)
+ goto deballoon;
+ }
+
+ return 0;
+
+deballoon:
+ xe_ggtt_deballoon(ggtt, &tile->sriov.vf.ggtt_balloon[0]);
+failed:
+ return err;
+}
+
+static void deballoon_ggtt(struct drm_device *drm, void *arg)
+{
+ struct xe_tile *tile = arg;
+ struct xe_ggtt *ggtt = tile->mem.ggtt;
+
+ xe_tile_assert(tile, IS_SRIOV_VF(tile_to_xe(tile)));
+ xe_ggtt_deballoon(ggtt, &tile->sriov.vf.ggtt_balloon[1]);
+ xe_ggtt_deballoon(ggtt, &tile->sriov.vf.ggtt_balloon[0]);
+}
+
+/**
+ * xe_gt_sriov_vf_prepare_ggtt - Prepare a VF's GGTT configuration.
+ * @gt: the &xe_gt
+ *
+ * This function is for VF use only.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_gt_sriov_vf_prepare_ggtt(struct xe_gt *gt)
+{
+ struct xe_tile *tile = gt_to_tile(gt);
+ struct xe_device *xe = tile_to_xe(tile);
+ int err;
+
+ if (xe_gt_is_media_type(gt))
+ return 0;
+
+ err = vf_balloon_ggtt(gt);
+ if (err)
+ return err;
+
+ return drmm_add_action_or_reset(&xe->drm, deballoon_ggtt, tile);
+}
+
+static int relay_action_handshake(struct xe_gt *gt, u32 *major, u32 *minor)
+{
+ u32 request[VF2PF_HANDSHAKE_REQUEST_MSG_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_RELAY_ACTION_VF2PF_HANDSHAKE),
+ FIELD_PREP(VF2PF_HANDSHAKE_REQUEST_MSG_1_MAJOR, *major) |
+ FIELD_PREP(VF2PF_HANDSHAKE_REQUEST_MSG_1_MINOR, *minor),
+ };
+ u32 response[VF2PF_HANDSHAKE_RESPONSE_MSG_LEN];
+ int ret;
+
+ xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
+
+ ret = xe_guc_relay_send_to_pf(&gt->uc.guc.relay,
+ request, ARRAY_SIZE(request),
+ response, ARRAY_SIZE(response));
+ if (unlikely(ret < 0))
+ return ret;
+
+ if (unlikely(ret != VF2PF_HANDSHAKE_RESPONSE_MSG_LEN))
+ return -EPROTO;
+
+ if (unlikely(FIELD_GET(VF2PF_HANDSHAKE_RESPONSE_MSG_0_MBZ, response[0])))
+ return -EPROTO;
+
+ *major = FIELD_GET(VF2PF_HANDSHAKE_RESPONSE_MSG_1_MAJOR, response[1]);
+ *minor = FIELD_GET(VF2PF_HANDSHAKE_RESPONSE_MSG_1_MINOR, response[1]);
+
+ return 0;
+}
+
+static void vf_connect_pf(struct xe_gt *gt, u16 major, u16 minor)
+{
+ xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
+
+ gt->sriov.vf.pf_version.major = major;
+ gt->sriov.vf.pf_version.minor = minor;
+}
+
+static void vf_disconnect_pf(struct xe_gt *gt)
+{
+ vf_connect_pf(gt, 0, 0);
+}
+
+static int vf_handshake_with_pf(struct xe_gt *gt)
+{
+ u32 major_wanted = GUC_RELAY_VERSION_LATEST_MAJOR;
+ u32 minor_wanted = GUC_RELAY_VERSION_LATEST_MINOR;
+ u32 major = major_wanted, minor = minor_wanted;
+ int err;
+
+ err = relay_action_handshake(gt, &major, &minor);
+ if (unlikely(err))
+ goto failed;
+
+ if (!major && !minor) {
+ err = -ENODATA;
+ goto failed;
+ }
+
+ xe_gt_sriov_dbg(gt, "using VF/PF ABI %u.%u\n", major, minor);
+ vf_connect_pf(gt, major, minor);
+ return 0;
+
+failed:
+ xe_gt_sriov_err(gt, "Unable to confirm VF/PF ABI version %u.%u (%pe)\n",
+ major, minor, ERR_PTR(err));
+ vf_disconnect_pf(gt);
+ return err;
+}
+
+/**
+ * xe_gt_sriov_vf_connect - Establish connection with the PF driver.
+ * @gt: the &xe_gt
+ *
+ * This function is for VF use only.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_gt_sriov_vf_connect(struct xe_gt *gt)
+{
+ int err;
+
+ err = vf_handshake_with_pf(gt);
+ if (unlikely(err))
+ goto failed;
+
+ return 0;
+
+failed:
+ xe_gt_sriov_err(gt, "Failed to get version info (%pe)\n", ERR_PTR(err));
+ return err;
+}
+
+static bool vf_is_negotiated(struct xe_gt *gt, u16 major, u16 minor)
+{
+ xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
+
+ return major == gt->sriov.vf.pf_version.major &&
+ minor <= gt->sriov.vf.pf_version.minor;
+}
+
+static int vf_prepare_runtime_info(struct xe_gt *gt, unsigned int num_regs)
+{
+ struct vf_runtime_reg *regs = gt->sriov.vf.runtime.regs;
+ unsigned int regs_size = round_up(num_regs, 4);
+ struct xe_device *xe = gt_to_xe(gt);
+
+ xe_gt_assert(gt, IS_SRIOV_VF(xe));
+
+ if (regs) {
+ if (num_regs <= gt->sriov.vf.runtime.regs_size) {
+ memset(regs, 0, num_regs * sizeof(*regs));
+ gt->sriov.vf.runtime.num_regs = num_regs;
+ return 0;
+ }
+
+ drmm_kfree(&xe->drm, regs);
+ gt->sriov.vf.runtime.regs = NULL;
+ gt->sriov.vf.runtime.num_regs = 0;
+ gt->sriov.vf.runtime.regs_size = 0;
+ }
+
+ regs = drmm_kcalloc(&xe->drm, regs_size, sizeof(*regs), GFP_KERNEL);
+ if (unlikely(!regs))
+ return -ENOMEM;
+
+ gt->sriov.vf.runtime.regs = regs;
+ gt->sriov.vf.runtime.num_regs = num_regs;
+ gt->sriov.vf.runtime.regs_size = regs_size;
+ return 0;
+}
+
+static int vf_query_runtime_info(struct xe_gt *gt)
+{
+ u32 request[VF2PF_QUERY_RUNTIME_REQUEST_MSG_LEN];
+ u32 response[VF2PF_QUERY_RUNTIME_RESPONSE_MSG_MIN_LEN + 32]; /* up to 16 regs */
+ u32 limit = (ARRAY_SIZE(response) - VF2PF_QUERY_RUNTIME_RESPONSE_MSG_MIN_LEN) / 2;
+ u32 count, remaining, num, i;
+ u32 start = 0;
+ int ret;
+
+ xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
+ xe_gt_assert(gt, limit);
+
+ /* this is part of the 1.0 PF/VF ABI */
+ if (!vf_is_negotiated(gt, 1, 0))
+ return -ENOPKG;
+
+ request[0] = 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_RELAY_ACTION_VF2PF_QUERY_RUNTIME) |
+ FIELD_PREP(VF2PF_QUERY_RUNTIME_REQUEST_MSG_0_LIMIT, limit);
+
+repeat:
+ request[1] = FIELD_PREP(VF2PF_QUERY_RUNTIME_REQUEST_MSG_1_START, start);
+ ret = xe_guc_relay_send_to_pf(&gt->uc.guc.relay,
+ request, ARRAY_SIZE(request),
+ response, ARRAY_SIZE(response));
+ if (unlikely(ret < 0))
+ goto failed;
+
+ if (unlikely(ret < VF2PF_QUERY_RUNTIME_RESPONSE_MSG_MIN_LEN)) {
+ ret = -EPROTO;
+ goto failed;
+ }
+ if (unlikely((ret - VF2PF_QUERY_RUNTIME_RESPONSE_MSG_MIN_LEN) % 2)) {
+ ret = -EPROTO;
+ goto failed;
+ }
+
+ num = (ret - VF2PF_QUERY_RUNTIME_RESPONSE_MSG_MIN_LEN) / 2;
+ count = FIELD_GET(VF2PF_QUERY_RUNTIME_RESPONSE_MSG_0_COUNT, response[0]);
+ remaining = FIELD_GET(VF2PF_QUERY_RUNTIME_RESPONSE_MSG_1_REMAINING, response[1]);
+
+ xe_gt_sriov_dbg_verbose(gt, "count=%u num=%u ret=%d start=%u remaining=%u\n",
+ count, num, ret, start, remaining);
+
+ if (unlikely(count != num)) {
+ ret = -EPROTO;
+ goto failed;
+ }
+
+ if (start == 0) {
+ ret = vf_prepare_runtime_info(gt, num + remaining);
+ if (unlikely(ret < 0))
+ goto failed;
+ } else if (unlikely(start + num > gt->sriov.vf.runtime.num_regs)) {
+ ret = -EPROTO;
+ goto failed;
+ }
+
+ for (i = 0; i < num; ++i) {
+ struct vf_runtime_reg *reg = &gt->sriov.vf.runtime.regs[start + i];
+
+ reg->offset = response[VF2PF_QUERY_RUNTIME_RESPONSE_MSG_MIN_LEN + 2 * i];
+ reg->value = response[VF2PF_QUERY_RUNTIME_RESPONSE_MSG_MIN_LEN + 2 * i + 1];
+ }
+
+ if (remaining) {
+ start += num;
+ goto repeat;
+ }
+
+ return 0;
+
+failed:
+ vf_prepare_runtime_info(gt, 0);
+ return ret;
+}
+
+static void vf_show_runtime_info(struct xe_gt *gt)
+{
+ struct vf_runtime_reg *vf_regs = gt->sriov.vf.runtime.regs;
+ unsigned int size = gt->sriov.vf.runtime.num_regs;
+
+ xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
+
+ for (; size--; vf_regs++)
+ xe_gt_sriov_dbg(gt, "runtime(%#x) = %#x\n",
+ vf_regs->offset, vf_regs->value);
+}
+
+/**
+ * xe_gt_sriov_vf_query_runtime - Query SR-IOV runtime data.
+ * @gt: the &xe_gt
+ *
+ * This function is for VF use only.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_gt_sriov_vf_query_runtime(struct xe_gt *gt)
+{
+ int err;
+
+ err = vf_query_runtime_info(gt);
+ if (unlikely(err))
+ goto failed;
+
+ if (IS_ENABLED(CONFIG_DRM_XE_DEBUG))
+ vf_show_runtime_info(gt);
+
+ return 0;
+
+failed:
+ xe_gt_sriov_err(gt, "Failed to get runtime info (%pe)\n",
+ ERR_PTR(err));
+ return err;
+}
+
+static int vf_runtime_reg_cmp(const void *a, const void *b)
+{
+ const struct vf_runtime_reg *ra = a;
+ const struct vf_runtime_reg *rb = b;
+
+ return (int)ra->offset - (int)rb->offset;
+}
+
+static struct vf_runtime_reg *vf_lookup_reg(struct xe_gt *gt, u32 addr)
+{
+ struct xe_gt_sriov_vf_runtime *runtime = &gt->sriov.vf.runtime;
+ struct vf_runtime_reg key = { .offset = addr };
+
+ xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
+
+ return bsearch(&key, runtime->regs, runtime->regs_size, sizeof(key),
+ vf_runtime_reg_cmp);
+}
+
+/**
+ * xe_gt_sriov_vf_read32 - Get a register value from the runtime data.
+ * @gt: the &xe_gt
+ * @reg: the register to read
+ *
+ * This function is for VF use only.
+ * This function shall be called after VF has connected to PF.
+ * This function is dedicated for registers that VFs can't read directly.
+ *
+ * Return: register value obtained from the PF or 0 if not found.
+ */
+u32 xe_gt_sriov_vf_read32(struct xe_gt *gt, struct xe_reg reg)
+{
+ u32 addr = xe_mmio_adjusted_addr(gt, reg.addr);
+ struct vf_runtime_reg *rr;
+
+ xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
+ xe_gt_assert(gt, gt->sriov.vf.pf_version.major);
+ xe_gt_assert(gt, !reg.vf);
+
+ if (reg.addr == GMD_ID.addr) {
+ xe_gt_sriov_dbg_verbose(gt, "gmdid(%#x) = %#x\n",
+ addr, gt->sriov.vf.runtime.gmdid);
+ return gt->sriov.vf.runtime.gmdid;
+ }
+
+ rr = vf_lookup_reg(gt, addr);
+ if (!rr) {
+ xe_gt_WARN(gt, IS_ENABLED(CONFIG_DRM_XE_DEBUG),
+ "VF is trying to read an inaccessible register %#x+%#x\n",
+ reg.addr, addr - reg.addr);
+ return 0;
+ }
+
+ xe_gt_sriov_dbg_verbose(gt, "runtime[%#x] = %#x\n", addr, rr->value);
+ return rr->value;
+}
+
+/**
+ * xe_gt_sriov_vf_print_config - Print VF self config.
+ * @gt: the &xe_gt
+ * @p: the &drm_printer
+ *
+ * This function is for VF use only.
+ */
+void xe_gt_sriov_vf_print_config(struct xe_gt *gt, struct drm_printer *p)
+{
+ struct xe_gt_sriov_vf_selfconfig *config = &gt->sriov.vf.self_config;
+ struct xe_device *xe = gt_to_xe(gt);
+ char buf[10];
+
+ xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
+
+ drm_printf(p, "GGTT range:\t%#llx-%#llx\n",
+ config->ggtt_base,
+ config->ggtt_base + config->ggtt_size - 1);
+
+ string_get_size(config->ggtt_size, 1, STRING_UNITS_2, buf, sizeof(buf));
+ drm_printf(p, "GGTT size:\t%llu (%s)\n", config->ggtt_size, buf);
+
+ if (IS_DGFX(xe) && !xe_gt_is_media_type(gt)) {
+ string_get_size(config->lmem_size, 1, STRING_UNITS_2, buf, sizeof(buf));
+ drm_printf(p, "LMEM size:\t%llu (%s)\n", config->lmem_size, buf);
+ }
+
+ drm_printf(p, "GuC contexts:\t%u\n", config->num_ctxs);
+ drm_printf(p, "GuC doorbells:\t%u\n", config->num_dbs);
+}
+
+/**
+ * xe_gt_sriov_vf_print_runtime - Print VF's runtime regs received from PF.
+ * @gt: the &xe_gt
+ * @p: the &drm_printer
+ *
+ * This function is for VF use only.
+ */
+void xe_gt_sriov_vf_print_runtime(struct xe_gt *gt, struct drm_printer *p)
+{
+ struct vf_runtime_reg *vf_regs = gt->sriov.vf.runtime.regs;
+ unsigned int size = gt->sriov.vf.runtime.num_regs;
+
+ xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
+
+ for (; size--; vf_regs++)
+ drm_printf(p, "%#x = %#x\n", vf_regs->offset, vf_regs->value);
+}
+
+/**
+ * xe_gt_sriov_vf_print_version - Print VF ABI versions.
+ * @gt: the &xe_gt
+ * @p: the &drm_printer
+ *
+ * This function is for VF use only.
+ */
+void xe_gt_sriov_vf_print_version(struct xe_gt *gt, struct drm_printer *p)
+{
+ struct xe_gt_sriov_vf_guc_version *guc_version = &gt->sriov.vf.guc_version;
+ struct xe_gt_sriov_vf_relay_version *pf_version = &gt->sriov.vf.pf_version;
+ u32 branch, major, minor;
+
+ xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
+
+ drm_printf(p, "GuC ABI:\n");
+
+ vf_minimum_guc_version(gt, &branch, &major, &minor);
+ drm_printf(p, "\tbase:\t%u.%u.%u.*\n", branch, major, minor);
+
+ vf_wanted_guc_version(gt, &branch, &major, &minor);
+ drm_printf(p, "\twanted:\t%u.%u.%u.*\n", branch, major, minor);
+
+ drm_printf(p, "\thandshake:\t%u.%u.%u.%u\n",
+ guc_version->branch, guc_version->major,
+ guc_version->minor, guc_version->patch);
+
+ drm_printf(p, "PF ABI:\n");
+
+ drm_printf(p, "\tbase:\t%u.%u\n",
+ GUC_RELAY_VERSION_BASE_MAJOR, GUC_RELAY_VERSION_BASE_MINOR);
+ drm_printf(p, "\twanted:\t%u.%u\n",
+ GUC_RELAY_VERSION_LATEST_MAJOR, GUC_RELAY_VERSION_LATEST_MINOR);
+ drm_printf(p, "\thandshake:\t%u.%u\n",
+ pf_version->major, pf_version->minor);
+}
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_vf.h b/drivers/gpu/drm/xe/xe_gt_sriov_vf.h
new file mode 100644
index 000000000000..0de7f8cbcfa6
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_vf.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2023-2024 Intel Corporation
+ */
+
+#ifndef _XE_GT_SRIOV_VF_H_
+#define _XE_GT_SRIOV_VF_H_
+
+#include <linux/types.h>
+
+struct drm_printer;
+struct xe_gt;
+struct xe_reg;
+
+int xe_gt_sriov_vf_bootstrap(struct xe_gt *gt);
+int xe_gt_sriov_vf_query_config(struct xe_gt *gt);
+int xe_gt_sriov_vf_connect(struct xe_gt *gt);
+int xe_gt_sriov_vf_query_runtime(struct xe_gt *gt);
+int xe_gt_sriov_vf_prepare_ggtt(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);
+u32 xe_gt_sriov_vf_read32(struct xe_gt *gt, struct xe_reg reg);
+
+void xe_gt_sriov_vf_print_config(struct xe_gt *gt, struct drm_printer *p);
+void xe_gt_sriov_vf_print_runtime(struct xe_gt *gt, struct drm_printer *p);
+void xe_gt_sriov_vf_print_version(struct xe_gt *gt, struct drm_printer *p);
+
+#endif
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_vf_debugfs.c b/drivers/gpu/drm/xe/xe_gt_sriov_vf_debugfs.c
new file mode 100644
index 000000000000..f3ddcbefc6bc
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_vf_debugfs.c
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2023-2024 Intel Corporation
+ */
+
+#include <linux/debugfs.h>
+
+#include <drm/drm_debugfs.h>
+
+#include "xe_gt_debugfs.h"
+#include "xe_gt_sriov_vf.h"
+#include "xe_gt_sriov_vf_debugfs.h"
+#include "xe_gt_types.h"
+#include "xe_sriov.h"
+
+/*
+ * /sys/kernel/debug/dri/0/
+ * ├── gt0
+ * │   ├── vf
+ * │   │   ├── self_config
+ * │   │   ├── abi_versions
+ * │   │   ├── runtime_regs
+ */
+
+static const struct drm_info_list vf_info[] = {
+ {
+ "self_config",
+ .show = xe_gt_debugfs_simple_show,
+ .data = xe_gt_sriov_vf_print_config,
+ },
+ {
+ "abi_versions",
+ .show = xe_gt_debugfs_simple_show,
+ .data = xe_gt_sriov_vf_print_version,
+ },
+#if defined(CONFIG_DRM_XE_DEBUG) || defined(CONFIG_DRM_XE_DEBUG_SRIOV)
+ {
+ "runtime_regs",
+ .show = xe_gt_debugfs_simple_show,
+ .data = xe_gt_sriov_vf_print_runtime,
+ },
+#endif
+};
+
+/**
+ * xe_gt_sriov_vf_debugfs_register - Register SR-IOV VF specific entries in GT debugfs.
+ * @gt: the &xe_gt to register
+ * @root: the &dentry that represents the GT directory
+ *
+ * Register SR-IOV VF entries that are GT related and must be shown under GT debugfs.
+ */
+void xe_gt_sriov_vf_debugfs_register(struct xe_gt *gt, struct dentry *root)
+{
+ struct xe_device *xe = gt_to_xe(gt);
+ struct drm_minor *minor = xe->drm.primary;
+ struct dentry *vfdentry;
+
+ xe_assert(xe, IS_SRIOV_VF(xe));
+ xe_assert(xe, root->d_inode->i_private == gt);
+
+ /*
+ * /sys/kernel/debug/dri/0/
+ * ├── gt0
+ * │   ├── vf
+ */
+ vfdentry = debugfs_create_dir("vf", root);
+ if (IS_ERR(vfdentry))
+ return;
+ vfdentry->d_inode->i_private = gt;
+
+ drm_debugfs_create_files(vf_info, ARRAY_SIZE(vf_info), vfdentry, minor);
+}
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_vf_debugfs.h b/drivers/gpu/drm/xe/xe_gt_sriov_vf_debugfs.h
new file mode 100644
index 000000000000..b2cff7ef5c78
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_vf_debugfs.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2023-2024 Intel Corporation
+ */
+
+#ifndef _XE_GT_SRIOV_VF_DEBUGFS_H_
+#define _XE_GT_SRIOV_VF_DEBUGFS_H_
+
+struct xe_gt;
+struct dentry;
+
+void xe_gt_sriov_vf_debugfs_register(struct xe_gt *gt, struct dentry *root);
+
+#endif
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_vf_types.h b/drivers/gpu/drm/xe/xe_gt_sriov_vf_types.h
new file mode 100644
index 000000000000..a57f13b5afcd
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_vf_types.h
@@ -0,0 +1,84 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2023-2024 Intel Corporation
+ */
+
+#ifndef _XE_GT_SRIOV_VF_TYPES_H_
+#define _XE_GT_SRIOV_VF_TYPES_H_
+
+#include <linux/types.h>
+
+/**
+ * struct xe_gt_sriov_vf_guc_version - GuC ABI version details.
+ */
+struct xe_gt_sriov_vf_guc_version {
+ /** @branch: branch version. */
+ u8 branch;
+ /** @major: major version. */
+ u8 major;
+ /** @minor: minor version. */
+ u8 minor;
+ /** @patch: patch version. */
+ u8 patch;
+};
+
+/**
+ * struct xe_gt_sriov_vf_relay_version - PF ABI version details.
+ */
+struct xe_gt_sriov_vf_relay_version {
+ /** @major: major version. */
+ u16 major;
+ /** @minor: minor version. */
+ u16 minor;
+};
+
+/**
+ * struct xe_gt_sriov_vf_selfconfig - VF configuration data.
+ */
+struct xe_gt_sriov_vf_selfconfig {
+ /** @ggtt_base: assigned base offset of the GGTT region. */
+ u64 ggtt_base;
+ /** @ggtt_size: assigned size of the GGTT region. */
+ u64 ggtt_size;
+ /** @lmem_size: assigned size of the LMEM. */
+ u64 lmem_size;
+ /** @num_ctxs: assigned number of GuC submission context IDs. */
+ u16 num_ctxs;
+ /** @num_dbs: assigned number of GuC doorbells IDs. */
+ u16 num_dbs;
+};
+
+/**
+ * struct xe_gt_sriov_vf_runtime - VF runtime data.
+ */
+struct xe_gt_sriov_vf_runtime {
+ /** @gmdid: cached value of the GDMID register. */
+ u32 gmdid;
+ /** @regs_size: size of runtime register array. */
+ u32 regs_size;
+ /** @num_regs: number of runtime registers in the array. */
+ u32 num_regs;
+ /** @regs: pointer to array of register offset/value pairs. */
+ struct vf_runtime_reg {
+ /** @regs.offset: register offset. */
+ u32 offset;
+ /** @regs.value: register value. */
+ u32 value;
+ } *regs;
+};
+
+/**
+ * struct xe_gt_sriov_vf - GT level VF virtualization data.
+ */
+struct xe_gt_sriov_vf {
+ /** @guc_version: negotiated GuC ABI version. */
+ struct xe_gt_sriov_vf_guc_version guc_version;
+ /** @self_config: resource configurations. */
+ struct xe_gt_sriov_vf_selfconfig self_config;
+ /** @pf_version: negotiated VF/PF ABI version. */
+ struct xe_gt_sriov_vf_relay_version pf_version;
+ /** @runtime: runtime data retrieved from the PF. */
+ struct xe_gt_sriov_vf_runtime runtime;
+};
+
+#endif
diff --git a/drivers/gpu/drm/xe/xe_gt_sysfs.c b/drivers/gpu/drm/xe/xe_gt_sysfs.c
index 1e5971072bc8..a05c3699e8b9 100644
--- a/drivers/gpu/drm/xe/xe_gt_sysfs.c
+++ b/drivers/gpu/drm/xe/xe_gt_sysfs.c
@@ -22,7 +22,7 @@ static const struct kobj_type xe_gt_sysfs_kobj_type = {
.sysfs_ops = &kobj_sysfs_ops,
};
-static void gt_sysfs_fini(struct drm_device *drm, void *arg)
+static void gt_sysfs_fini(void *arg)
{
struct xe_gt *gt = arg;
@@ -51,5 +51,5 @@ int xe_gt_sysfs_init(struct xe_gt *gt)
gt->sysfs = &kg->base;
- return drmm_add_action_or_reset(&xe->drm, gt_sysfs_fini, gt);
+ return devm_add_action(xe->drm.dev, gt_sysfs_fini, gt);
}
diff --git a/drivers/gpu/drm/xe/xe_gt_throttle_sysfs.c b/drivers/gpu/drm/xe/xe_gt_throttle.c
index fbe21a8599ca..25963e33a383 100644
--- a/drivers/gpu/drm/xe/xe_gt_throttle_sysfs.c
+++ b/drivers/gpu/drm/xe/xe_gt_throttle.c
@@ -9,14 +9,14 @@
#include "xe_device.h"
#include "xe_gt.h"
#include "xe_gt_sysfs.h"
-#include "xe_gt_throttle_sysfs.h"
+#include "xe_gt_throttle.h"
#include "xe_mmio.h"
#include "xe_pm.h"
/**
* DOC: Xe GT Throttle
*
- * Provides sysfs entries for frequency throttle reasons in GT
+ * Provides sysfs entries and other helpers for frequency throttle reasons in GT
*
* device/gt#/freq0/throttle/status - Overall status
* device/gt#/freq0/throttle/reason_pl1 - Frequency throttle due to PL1
@@ -35,7 +35,7 @@ dev_to_gt(struct device *dev)
return kobj_to_gt(dev->kobj.parent);
}
-static u32 read_perf_limit_reasons(struct xe_gt *gt)
+u32 xe_gt_throttle_get_limit_reasons(struct xe_gt *gt)
{
u32 reg;
@@ -51,63 +51,63 @@ static u32 read_perf_limit_reasons(struct xe_gt *gt)
static u32 read_status(struct xe_gt *gt)
{
- u32 status = read_perf_limit_reasons(gt) & GT0_PERF_LIMIT_REASONS_MASK;
+ u32 status = xe_gt_throttle_get_limit_reasons(gt) & GT0_PERF_LIMIT_REASONS_MASK;
return status;
}
static u32 read_reason_pl1(struct xe_gt *gt)
{
- u32 pl1 = read_perf_limit_reasons(gt) & POWER_LIMIT_1_MASK;
+ u32 pl1 = xe_gt_throttle_get_limit_reasons(gt) & POWER_LIMIT_1_MASK;
return pl1;
}
static u32 read_reason_pl2(struct xe_gt *gt)
{
- u32 pl2 = read_perf_limit_reasons(gt) & POWER_LIMIT_2_MASK;
+ u32 pl2 = xe_gt_throttle_get_limit_reasons(gt) & POWER_LIMIT_2_MASK;
return pl2;
}
static u32 read_reason_pl4(struct xe_gt *gt)
{
- u32 pl4 = read_perf_limit_reasons(gt) & POWER_LIMIT_4_MASK;
+ u32 pl4 = xe_gt_throttle_get_limit_reasons(gt) & POWER_LIMIT_4_MASK;
return pl4;
}
static u32 read_reason_thermal(struct xe_gt *gt)
{
- u32 thermal = read_perf_limit_reasons(gt) & THERMAL_LIMIT_MASK;
+ u32 thermal = xe_gt_throttle_get_limit_reasons(gt) & THERMAL_LIMIT_MASK;
return thermal;
}
static u32 read_reason_prochot(struct xe_gt *gt)
{
- u32 prochot = read_perf_limit_reasons(gt) & PROCHOT_MASK;
+ u32 prochot = xe_gt_throttle_get_limit_reasons(gt) & PROCHOT_MASK;
return prochot;
}
static u32 read_reason_ratl(struct xe_gt *gt)
{
- u32 ratl = read_perf_limit_reasons(gt) & RATL_MASK;
+ u32 ratl = xe_gt_throttle_get_limit_reasons(gt) & RATL_MASK;
return ratl;
}
static u32 read_reason_vr_thermalert(struct xe_gt *gt)
{
- u32 thermalert = read_perf_limit_reasons(gt) & VR_THERMALERT_MASK;
+ u32 thermalert = xe_gt_throttle_get_limit_reasons(gt) & VR_THERMALERT_MASK;
return thermalert;
}
static u32 read_reason_vr_tdc(struct xe_gt *gt)
{
- u32 tdc = read_perf_limit_reasons(gt) & VR_TDC_MASK;
+ u32 tdc = xe_gt_throttle_get_limit_reasons(gt) & VR_TDC_MASK;
return tdc;
}
@@ -229,14 +229,14 @@ static const struct attribute_group throttle_group_attrs = {
.attrs = throttle_attrs,
};
-static void gt_throttle_sysfs_fini(struct drm_device *drm, void *arg)
+static void gt_throttle_sysfs_fini(void *arg)
{
struct xe_gt *gt = arg;
sysfs_remove_group(gt->freq, &throttle_group_attrs);
}
-int xe_gt_throttle_sysfs_init(struct xe_gt *gt)
+int xe_gt_throttle_init(struct xe_gt *gt)
{
struct xe_device *xe = gt_to_xe(gt);
int err;
@@ -245,5 +245,5 @@ int xe_gt_throttle_sysfs_init(struct xe_gt *gt)
if (err)
return err;
- return drmm_add_action_or_reset(&xe->drm, gt_throttle_sysfs_fini, gt);
+ return devm_add_action_or_reset(xe->drm.dev, gt_throttle_sysfs_fini, gt);
}
diff --git a/drivers/gpu/drm/xe/xe_gt_throttle.h b/drivers/gpu/drm/xe/xe_gt_throttle.h
new file mode 100644
index 000000000000..02277494715d
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_gt_throttle.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2023 Intel Corporation
+ */
+
+#ifndef _XE_GT_THROTTLE_H_
+#define _XE_GT_THROTTLE_H_
+
+#include <linux/types.h>
+
+struct xe_gt;
+
+int xe_gt_throttle_init(struct xe_gt *gt);
+
+u32 xe_gt_throttle_get_limit_reasons(struct xe_gt *gt);
+
+#endif /* _XE_GT_THROTTLE_H_ */
diff --git a/drivers/gpu/drm/xe/xe_gt_throttle_sysfs.h b/drivers/gpu/drm/xe/xe_gt_throttle_sysfs.h
deleted file mode 100644
index 6c61e6f228a8..000000000000
--- a/drivers/gpu/drm/xe/xe_gt_throttle_sysfs.h
+++ /dev/null
@@ -1,16 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/*
- * Copyright © 2023 Intel Corporation
- */
-
-#ifndef _XE_GT_THROTTLE_SYSFS_H_
-#define _XE_GT_THROTTLE_SYSFS_H_
-
-#include <drm/drm_managed.h>
-
-struct xe_gt;
-
-int xe_gt_throttle_sysfs_init(struct xe_gt *gt);
-
-#endif /* _XE_GT_THROTTLE_SYSFS_H_ */
-
diff --git a/drivers/gpu/drm/xe/xe_gt_tlb_invalidation.c b/drivers/gpu/drm/xe/xe_gt_tlb_invalidation.c
index 93df2d7969b3..d9359976ab8b 100644
--- a/drivers/gpu/drm/xe/xe_gt_tlb_invalidation.c
+++ b/drivers/gpu/drm/xe/xe_gt_tlb_invalidation.c
@@ -7,20 +7,38 @@
#include "abi/guc_actions_abi.h"
#include "xe_device.h"
+#include "xe_force_wake.h"
#include "xe_gt.h"
#include "xe_gt_printk.h"
#include "xe_guc.h"
#include "xe_guc_ct.h"
#include "xe_mmio.h"
+#include "xe_sriov.h"
#include "xe_trace.h"
#include "regs/xe_guc_regs.h"
-#define TLB_TIMEOUT (HZ / 4)
+/*
+ * TLB inval depends on pending commands in the CT queue and then the real
+ * invalidation time. Double up the time to process full CT queue
+ * just to be on the safe side.
+ */
+static long tlb_timeout_jiffies(struct xe_gt *gt)
+{
+ /* this reflects what HW/GuC needs to process TLB inv request */
+ const long hw_tlb_timeout = HZ / 4;
+
+ /* this estimates actual delay caused by the CTB transport */
+ long delay = xe_guc_ct_queue_proc_time_jiffies(&gt->uc.guc.ct);
+
+ return hw_tlb_timeout + 2 * delay;
+}
+
static void xe_gt_tlb_fence_timeout(struct work_struct *work)
{
struct xe_gt *gt = container_of(work, struct xe_gt,
tlb_invalidation.fence_tdr.work);
+ struct xe_device *xe = gt_to_xe(gt);
struct xe_gt_tlb_invalidation_fence *fence, *next;
spin_lock_irq(&gt->tlb_invalidation.pending_lock);
@@ -29,10 +47,10 @@ static void xe_gt_tlb_fence_timeout(struct work_struct *work)
s64 since_inval_ms = ktime_ms_delta(ktime_get(),
fence->invalidation_time);
- if (msecs_to_jiffies(since_inval_ms) < TLB_TIMEOUT)
+ if (msecs_to_jiffies(since_inval_ms) < tlb_timeout_jiffies(gt))
break;
- trace_xe_gt_tlb_invalidation_fence_timeout(fence);
+ trace_xe_gt_tlb_invalidation_fence_timeout(xe, fence);
xe_gt_err(gt, "TLB invalidation fence timeout, seqno=%d recv=%d",
fence->seqno, gt->tlb_invalidation.seqno_recv);
@@ -44,7 +62,7 @@ static void xe_gt_tlb_fence_timeout(struct work_struct *work)
if (!list_empty(&gt->tlb_invalidation.pending_fences))
queue_delayed_work(system_wq,
&gt->tlb_invalidation.fence_tdr,
- TLB_TIMEOUT);
+ tlb_timeout_jiffies(gt));
spin_unlock_irq(&gt->tlb_invalidation.pending_lock);
}
@@ -70,18 +88,18 @@ int xe_gt_tlb_invalidation_init(struct xe_gt *gt)
}
static void
-__invalidation_fence_signal(struct xe_gt_tlb_invalidation_fence *fence)
+__invalidation_fence_signal(struct xe_device *xe, struct xe_gt_tlb_invalidation_fence *fence)
{
- trace_xe_gt_tlb_invalidation_fence_signal(fence);
+ trace_xe_gt_tlb_invalidation_fence_signal(xe, fence);
dma_fence_signal(&fence->base);
dma_fence_put(&fence->base);
}
static void
-invalidation_fence_signal(struct xe_gt_tlb_invalidation_fence *fence)
+invalidation_fence_signal(struct xe_device *xe, struct xe_gt_tlb_invalidation_fence *fence)
{
list_del(&fence->link);
- __invalidation_fence_signal(fence);
+ __invalidation_fence_signal(xe, fence);
}
/**
@@ -120,7 +138,7 @@ void xe_gt_tlb_invalidation_reset(struct xe_gt *gt)
list_for_each_entry_safe(fence, next,
&gt->tlb_invalidation.pending_fences, link)
- invalidation_fence_signal(fence);
+ invalidation_fence_signal(gt_to_xe(gt), fence);
spin_unlock_irq(&gt->tlb_invalidation.pending_lock);
mutex_unlock(&gt->uc.guc.ct.lock);
}
@@ -143,6 +161,7 @@ static int send_tlb_invalidation(struct xe_guc *guc,
u32 *action, int len)
{
struct xe_gt *gt = guc_to_gt(guc);
+ struct xe_device *xe = gt_to_xe(gt);
int seqno;
int ret;
@@ -156,7 +175,7 @@ static int send_tlb_invalidation(struct xe_guc *guc,
seqno = gt->tlb_invalidation.seqno;
if (fence) {
fence->seqno = seqno;
- trace_xe_gt_tlb_invalidation_fence_send(fence);
+ trace_xe_gt_tlb_invalidation_fence_send(xe, fence);
}
action[1] = seqno;
ret = xe_guc_ct_send_locked(&guc->ct, action, len,
@@ -170,7 +189,7 @@ static int send_tlb_invalidation(struct xe_guc *guc,
* we can just go ahead and signal the fence here.
*/
if (tlb_invalidation_seqno_past(gt, seqno)) {
- __invalidation_fence_signal(fence);
+ __invalidation_fence_signal(xe, fence);
} else {
fence->invalidation_time = ktime_get();
list_add_tail(&fence->link,
@@ -179,11 +198,11 @@ static int send_tlb_invalidation(struct xe_guc *guc,
if (list_is_singular(&gt->tlb_invalidation.pending_fences))
queue_delayed_work(system_wq,
&gt->tlb_invalidation.fence_tdr,
- TLB_TIMEOUT);
+ tlb_timeout_jiffies(gt));
}
spin_unlock_irq(&gt->tlb_invalidation.pending_lock);
} else if (ret < 0 && fence) {
- __invalidation_fence_signal(fence);
+ __invalidation_fence_signal(xe, fence);
}
if (!ret) {
gt->tlb_invalidation.seqno = (gt->tlb_invalidation.seqno + 1) %
@@ -245,7 +264,10 @@ int xe_gt_tlb_invalidation_ggtt(struct xe_gt *gt)
return seqno;
xe_gt_tlb_invalidation_wait(gt, seqno);
- } else if (xe_device_uc_enabled(xe)) {
+ } else if (xe_device_uc_enabled(xe) && !xe_device_wedged(xe)) {
+ if (IS_SRIOV_VF(xe))
+ return 0;
+
xe_gt_WARN_ON(gt, xe_force_wake_get(gt_to_fw(gt), XE_FW_GT));
if (xe->info.platform == XE_PVC || GRAPHICS_VER(xe) >= 20) {
xe_mmio_write32(gt, PVC_GUC_TLB_INV_DESC1,
@@ -263,11 +285,15 @@ int xe_gt_tlb_invalidation_ggtt(struct xe_gt *gt)
}
/**
- * xe_gt_tlb_invalidation_vma - Issue a TLB invalidation on this GT for a VMA
+ * xe_gt_tlb_invalidation_range - Issue a TLB invalidation on this GT for an
+ * address range
+ *
* @gt: graphics tile
* @fence: invalidation fence which will be signal on TLB invalidation
* completion, can be NULL
- * @vma: VMA to invalidate
+ * @start: start address
+ * @end: end address
+ * @asid: address space id
*
* Issue a range based TLB invalidation if supported, if not fallback to a full
* TLB invalidation. Completion of TLB is asynchronous and caller can either use
@@ -277,21 +303,19 @@ int xe_gt_tlb_invalidation_ggtt(struct xe_gt *gt)
* Return: Seqno which can be passed to xe_gt_tlb_invalidation_wait on success,
* negative error code on error.
*/
-int xe_gt_tlb_invalidation_vma(struct xe_gt *gt,
- struct xe_gt_tlb_invalidation_fence *fence,
- struct xe_vma *vma)
+int xe_gt_tlb_invalidation_range(struct xe_gt *gt,
+ struct xe_gt_tlb_invalidation_fence *fence,
+ u64 start, u64 end, u32 asid)
{
struct xe_device *xe = gt_to_xe(gt);
#define MAX_TLB_INVALIDATION_LEN 7
u32 action[MAX_TLB_INVALIDATION_LEN];
int len = 0;
- xe_gt_assert(gt, vma);
-
/* Execlists not supported */
if (gt_to_xe(gt)->info.force_execlist) {
if (fence)
- __invalidation_fence_signal(fence);
+ __invalidation_fence_signal(xe, fence);
return 0;
}
@@ -301,9 +325,9 @@ int xe_gt_tlb_invalidation_vma(struct xe_gt *gt,
if (!xe->info.has_range_tlb_invalidation) {
action[len++] = MAKE_INVAL_OP(XE_GUC_TLB_INVAL_FULL);
} else {
- u64 start = xe_vma_start(vma);
- u64 length = xe_vma_size(vma);
- u64 align, end;
+ u64 orig_start = start;
+ u64 length = end - start;
+ u64 align;
if (length < SZ_4K)
length = SZ_4K;
@@ -315,12 +339,12 @@ int xe_gt_tlb_invalidation_vma(struct xe_gt *gt,
* address mask covering the required range.
*/
align = roundup_pow_of_two(length);
- start = ALIGN_DOWN(xe_vma_start(vma), align);
- end = ALIGN(xe_vma_end(vma), align);
+ start = ALIGN_DOWN(start, align);
+ end = ALIGN(end, align);
length = align;
while (start + length < end) {
length <<= 1;
- start = ALIGN_DOWN(xe_vma_start(vma), length);
+ start = ALIGN_DOWN(orig_start, length);
}
/*
@@ -329,16 +353,17 @@ int xe_gt_tlb_invalidation_vma(struct xe_gt *gt,
*/
if (length >= SZ_2M) {
length = max_t(u64, SZ_16M, length);
- start = ALIGN_DOWN(xe_vma_start(vma), length);
+ start = ALIGN_DOWN(orig_start, length);
}
xe_gt_assert(gt, length >= SZ_4K);
xe_gt_assert(gt, is_power_of_2(length));
- xe_gt_assert(gt, !(length & GENMASK(ilog2(SZ_16M) - 1, ilog2(SZ_2M) + 1)));
+ xe_gt_assert(gt, !(length & GENMASK(ilog2(SZ_16M) - 1,
+ ilog2(SZ_2M) + 1)));
xe_gt_assert(gt, IS_ALIGNED(start, length));
action[len++] = MAKE_INVAL_OP(XE_GUC_TLB_INVAL_PAGE_SELECTIVE);
- action[len++] = xe_vma_vm(vma)->usm.asid;
+ action[len++] = asid;
action[len++] = lower_32_bits(start);
action[len++] = upper_32_bits(start);
action[len++] = ilog2(length) - ilog2(SZ_4K);
@@ -350,12 +375,37 @@ int xe_gt_tlb_invalidation_vma(struct xe_gt *gt,
}
/**
+ * xe_gt_tlb_invalidation_vma - Issue a TLB invalidation on this GT for a VMA
+ * @gt: graphics tile
+ * @fence: invalidation fence which will be signal on TLB invalidation
+ * completion, can be NULL
+ * @vma: VMA to invalidate
+ *
+ * Issue a range based TLB invalidation if supported, if not fallback to a full
+ * TLB invalidation. Completion of TLB is asynchronous and caller can either use
+ * the invalidation fence or seqno + xe_gt_tlb_invalidation_wait to wait for
+ * completion.
+ *
+ * Return: Seqno which can be passed to xe_gt_tlb_invalidation_wait on success,
+ * negative error code on error.
+ */
+int xe_gt_tlb_invalidation_vma(struct xe_gt *gt,
+ struct xe_gt_tlb_invalidation_fence *fence,
+ struct xe_vma *vma)
+{
+ xe_gt_assert(gt, vma);
+
+ return xe_gt_tlb_invalidation_range(gt, fence, xe_vma_start(vma),
+ xe_vma_end(vma),
+ xe_vma_vm(vma)->usm.asid);
+}
+
+/**
* xe_gt_tlb_invalidation_wait - Wait for TLB to complete
* @gt: graphics tile
* @seqno: seqno to wait which was returned from xe_gt_tlb_invalidation
*
- * Wait for 200ms for a TLB invalidation to complete, in practice we always
- * should receive the TLB invalidation within 200ms.
+ * Wait for tlb_timeout_jiffies() for a TLB invalidation to complete.
*
* Return: 0 on success, -ETIME on TLB invalidation timeout
*/
@@ -374,7 +424,7 @@ int xe_gt_tlb_invalidation_wait(struct xe_gt *gt, int seqno)
*/
ret = wait_event_timeout(guc->ct.wq,
tlb_invalidation_seqno_past(gt, seqno),
- TLB_TIMEOUT);
+ tlb_timeout_jiffies(gt));
if (!ret) {
struct drm_printer p = xe_gt_err_printer(gt);
@@ -402,6 +452,7 @@ int xe_gt_tlb_invalidation_wait(struct xe_gt *gt, int seqno)
int xe_guc_tlb_invalidation_done_handler(struct xe_guc *guc, u32 *msg, u32 len)
{
struct xe_gt *gt = guc_to_gt(guc);
+ struct xe_device *xe = gt_to_xe(gt);
struct xe_gt_tlb_invalidation_fence *fence, *next;
unsigned long flags;
@@ -438,18 +489,18 @@ int xe_guc_tlb_invalidation_done_handler(struct xe_guc *guc, u32 *msg, u32 len)
list_for_each_entry_safe(fence, next,
&gt->tlb_invalidation.pending_fences, link) {
- trace_xe_gt_tlb_invalidation_fence_recv(fence);
+ trace_xe_gt_tlb_invalidation_fence_recv(xe, fence);
if (!tlb_invalidation_seqno_past(gt, fence->seqno))
break;
- invalidation_fence_signal(fence);
+ invalidation_fence_signal(xe, fence);
}
if (!list_empty(&gt->tlb_invalidation.pending_fences))
mod_delayed_work(system_wq,
&gt->tlb_invalidation.fence_tdr,
- TLB_TIMEOUT);
+ tlb_timeout_jiffies(gt));
else
cancel_delayed_work(&gt->tlb_invalidation.fence_tdr);
diff --git a/drivers/gpu/drm/xe/xe_gt_tlb_invalidation.h b/drivers/gpu/drm/xe/xe_gt_tlb_invalidation.h
index fbb743d80d2c..bf3bebd9f985 100644
--- a/drivers/gpu/drm/xe/xe_gt_tlb_invalidation.h
+++ b/drivers/gpu/drm/xe/xe_gt_tlb_invalidation.h
@@ -20,6 +20,9 @@ int xe_gt_tlb_invalidation_ggtt(struct xe_gt *gt);
int xe_gt_tlb_invalidation_vma(struct xe_gt *gt,
struct xe_gt_tlb_invalidation_fence *fence,
struct xe_vma *vma);
+int xe_gt_tlb_invalidation_range(struct xe_gt *gt,
+ struct xe_gt_tlb_invalidation_fence *fence,
+ u64 start, u64 end, u32 asid);
int xe_gt_tlb_invalidation_wait(struct xe_gt *gt, int seqno);
int xe_guc_tlb_invalidation_done_handler(struct xe_guc *guc, u32 *msg, u32 len);
diff --git a/drivers/gpu/drm/xe/xe_gt_topology.c b/drivers/gpu/drm/xe/xe_gt_topology.c
index 3733e7a6860d..25ff03ab8448 100644
--- a/drivers/gpu/drm/xe/xe_gt_topology.c
+++ b/drivers/gpu/drm/xe/xe_gt_topology.c
@@ -108,7 +108,9 @@ gen_l3_mask_from_pattern(struct xe_device *xe, xe_l3_bank_mask_t dst,
{
unsigned long bit;
- xe_assert(xe, fls(mask) <= patternbits);
+ xe_assert(xe, find_last_bit(pattern, XE_MAX_L3_BANK_MASK_BITS) < patternbits ||
+ bitmap_empty(pattern, XE_MAX_L3_BANK_MASK_BITS));
+ xe_assert(xe, !mask || patternbits * (__fls(mask) + 1) <= XE_MAX_L3_BANK_MASK_BITS);
for_each_set_bit(bit, &mask, 32) {
xe_l3_bank_mask_t shifted_pattern = {};
@@ -278,3 +280,13 @@ bool xe_gt_topology_has_dss_in_quadrant(struct xe_gt *gt, int quad)
return quad_first < (quad + 1) * dss_per_quad;
}
+
+bool xe_gt_has_geometry_dss(struct xe_gt *gt, unsigned int dss)
+{
+ return test_bit(dss, gt->fuse_topo.g_dss_mask);
+}
+
+bool xe_gt_has_compute_dss(struct xe_gt *gt, unsigned int dss)
+{
+ return test_bit(dss, gt->fuse_topo.c_dss_mask);
+}
diff --git a/drivers/gpu/drm/xe/xe_gt_topology.h b/drivers/gpu/drm/xe/xe_gt_topology.h
index b3e357777a6e..746b325bbf6e 100644
--- a/drivers/gpu/drm/xe/xe_gt_topology.h
+++ b/drivers/gpu/drm/xe/xe_gt_topology.h
@@ -33,4 +33,7 @@ bool xe_dss_mask_empty(const xe_dss_mask_t mask);
bool
xe_gt_topology_has_dss_in_quadrant(struct xe_gt *gt, int quad);
+bool xe_gt_has_geometry_dss(struct xe_gt *gt, unsigned int dss);
+bool xe_gt_has_compute_dss(struct xe_gt *gt, unsigned int dss);
+
#endif /* _XE_GT_TOPOLOGY_H_ */
diff --git a/drivers/gpu/drm/xe/xe_gt_types.h b/drivers/gpu/drm/xe/xe_gt_types.h
index cfdc761ff7f4..6b5e0b45efb0 100644
--- a/drivers/gpu/drm/xe/xe_gt_types.h
+++ b/drivers/gpu/drm/xe/xe_gt_types.h
@@ -9,8 +9,10 @@
#include "xe_force_wake_types.h"
#include "xe_gt_idle_types.h"
#include "xe_gt_sriov_pf_types.h"
+#include "xe_gt_sriov_vf_types.h"
#include "xe_hw_engine_types.h"
#include "xe_hw_fence_types.h"
+#include "xe_oa.h"
#include "xe_reg_sr_types.h"
#include "xe_sa_types.h"
#include "xe_uc_types.h"
@@ -110,20 +112,20 @@ struct xe_gt {
struct {
/** @info.type: type of GT */
enum xe_gt_type type;
- /** @info.id: Unique ID of this GT within the PCI Device */
- u8 id;
/** @info.reference_clock: clock frequency */
u32 reference_clock;
- /** @info.engine_mask: mask of engines present on GT */
- u64 engine_mask;
/**
- * @info.__engine_mask: mask of engines present on GT read from
- * xe_pci.c, used to fake reading the engine_mask from the
- * hwconfig blob.
+ * @info.engine_mask: mask of engines present on GT. Some of
+ * them may be reserved in runtime and not available for user.
+ * See @user_engines.mask
*/
- u64 __engine_mask;
+ u64 engine_mask;
/** @info.gmdid: raw GMD_ID value from hardware */
u32 gmdid;
+ /** @info.id: Unique ID of this GT within the PCI Device */
+ u8 id;
+ /** @info.has_indirect_ring_state: GT has indirect ring state support */
+ u8 has_indirect_ring_state:1;
} info;
/**
@@ -147,6 +149,8 @@ struct xe_gt {
union {
/** @sriov.pf: PF data. Valid only if driver is running as PF */
struct xe_gt_sriov_pf pf;
+ /** @sriov.vf: VF data. Valid only if driver is running as VF */
+ struct xe_gt_sriov_vf vf;
} sriov;
/**
@@ -369,6 +373,24 @@ struct xe_gt {
/** @wa_active.oob: bitmap with active OOB workaroudns */
unsigned long *oob;
} wa_active;
+
+ /** @user_engines: engines present in GT and available to userspace */
+ struct {
+ /**
+ * @user_engines.mask: like @info->engine_mask, but take in
+ * consideration only engines available to userspace
+ */
+ u64 mask;
+
+ /**
+ * @user_engines.instances_per_class: aggregate per class the
+ * number of engines available to userspace
+ */
+ u8 instances_per_class[XE_ENGINE_CLASS_MAX];
+ } user_engines;
+
+ /** @oa: oa observation subsystem per gt info */
+ struct xe_oa_gt oa;
};
#endif
diff --git a/drivers/gpu/drm/xe/xe_guc.c b/drivers/gpu/drm/xe/xe_guc.c
index 240e7a4bbff1..de0fe9e65746 100644
--- a/drivers/gpu/drm/xe/xe_guc.c
+++ b/drivers/gpu/drm/xe/xe_guc.c
@@ -19,8 +19,11 @@
#include "xe_force_wake.h"
#include "xe_gt.h"
#include "xe_gt_printk.h"
+#include "xe_gt_sriov_vf.h"
+#include "xe_gt_throttle.h"
#include "xe_guc_ads.h"
#include "xe_guc_ct.h"
+#include "xe_guc_db_mgr.h"
#include "xe_guc_hwconfig.h"
#include "xe_guc_log.h"
#include "xe_guc_pc.h"
@@ -239,7 +242,7 @@ static void guc_write_params(struct xe_guc *guc)
xe_mmio_write32(gt, SOFT_SCRATCH(1 + i), guc->params[i]);
}
-static void guc_fini(struct drm_device *drm, void *arg)
+static void guc_fini_hw(void *arg)
{
struct xe_guc *guc = arg;
struct xe_gt *gt = guc_to_gt(guc);
@@ -293,6 +296,23 @@ static int xe_guc_realloc_post_hwconfig(struct xe_guc *guc)
return 0;
}
+static int vf_guc_init(struct xe_guc *guc)
+{
+ int err;
+
+ xe_guc_comm_init_early(guc);
+
+ err = xe_guc_ct_init(&guc->ct);
+ if (err)
+ return err;
+
+ err = xe_guc_relay_init(&guc->relay);
+ if (err)
+ return err;
+
+ return 0;
+}
+
int xe_guc_init(struct xe_guc *guc)
{
struct xe_device *xe = guc_to_xe(guc);
@@ -307,6 +327,13 @@ int xe_guc_init(struct xe_guc *guc)
if (!xe_uc_fw_is_enabled(&guc->fw))
return 0;
+ if (IS_SRIOV_VF(xe)) {
+ ret = vf_guc_init(guc);
+ if (ret)
+ goto out;
+ return 0;
+ }
+
ret = xe_guc_log_init(&guc->log);
if (ret)
goto out;
@@ -323,7 +350,7 @@ int xe_guc_init(struct xe_guc *guc)
if (ret)
goto out;
- ret = drmm_add_action_or_reset(&xe->drm, guc_fini, guc);
+ ret = devm_add_action_or_reset(xe->drm.dev, guc_fini_hw, guc);
if (ret)
goto out;
@@ -340,6 +367,19 @@ out:
return ret;
}
+static int vf_guc_init_post_hwconfig(struct xe_guc *guc)
+{
+ int err;
+
+ err = xe_guc_submit_init(guc, xe_gt_sriov_vf_guc_ids(guc_to_gt(guc)));
+ if (err)
+ return err;
+
+ /* XXX xe_guc_db_mgr_init not needed for now */
+
+ return 0;
+}
+
/**
* xe_guc_init_post_hwconfig - initialize GuC post hwconfig load
* @guc: The GuC object
@@ -350,12 +390,23 @@ int xe_guc_init_post_hwconfig(struct xe_guc *guc)
{
int ret;
+ if (IS_SRIOV_VF(guc_to_xe(guc)))
+ return vf_guc_init_post_hwconfig(guc);
+
ret = xe_guc_realloc_post_hwconfig(guc);
if (ret)
return ret;
guc_init_params_post_hwconfig(guc);
+ ret = xe_guc_submit_init(guc, ~0);
+ if (ret)
+ return ret;
+
+ ret = xe_guc_db_mgr_init(&guc->dbm, ~0);
+ if (ret)
+ return ret;
+
ret = xe_guc_pc_init(&guc->pc);
if (ret)
return ret;
@@ -379,6 +430,9 @@ int xe_guc_reset(struct xe_guc *guc)
xe_force_wake_assert_held(gt_to_fw(gt), XE_FW_GT);
+ if (IS_SRIOV_VF(gt_to_xe(gt)))
+ return xe_gt_sriov_vf_bootstrap(gt);
+
xe_mmio_write32(gt, GDRST, GRDOM_GUC);
ret = xe_mmio_wait32(gt, GDRST, GRDOM_GUC, 0, 5000, &gdrst, false);
@@ -422,6 +476,9 @@ static void guc_prepare_xfer(struct xe_guc *guc)
xe_mmio_write32(gt, GUC_SHIM_CONTROL, shim_flags);
xe_mmio_write32(gt, GT_PM_CONFIG, GT_DOORBELL_ENABLE);
+
+ /* Make sure GuC receives ARAT interrupts */
+ xe_mmio_rmw32(gt, PMINTRMSK, ARAT_EXPIRED_INTRMSK, 0);
}
/*
@@ -451,64 +508,203 @@ static int guc_xfer_rsa(struct xe_guc *guc)
return 0;
}
-static int guc_wait_ucode(struct xe_guc *guc)
+/*
+ * Check a previously read GuC status register (GUC_STATUS) looking for
+ * known terminal states (either completion or failure) of either the
+ * microkernel status field or the boot ROM status field. Returns +1 for
+ * successful completion, -1 for failure and 0 for any intermediate state.
+ */
+static int guc_load_done(u32 status)
{
- struct xe_gt *gt = guc_to_gt(guc);
- u32 status;
- int ret;
+ u32 uk_val = REG_FIELD_GET(GS_UKERNEL_MASK, status);
+ u32 br_val = REG_FIELD_GET(GS_BOOTROM_MASK, status);
+
+ switch (uk_val) {
+ case XE_GUC_LOAD_STATUS_READY:
+ return 1;
+
+ case XE_GUC_LOAD_STATUS_ERROR_DEVID_BUILD_MISMATCH:
+ case XE_GUC_LOAD_STATUS_GUC_PREPROD_BUILD_MISMATCH:
+ case XE_GUC_LOAD_STATUS_ERROR_DEVID_INVALID_GUCTYPE:
+ case XE_GUC_LOAD_STATUS_HWCONFIG_ERROR:
+ case XE_GUC_LOAD_STATUS_DPC_ERROR:
+ case XE_GUC_LOAD_STATUS_EXCEPTION:
+ case XE_GUC_LOAD_STATUS_INIT_DATA_INVALID:
+ case XE_GUC_LOAD_STATUS_MPU_DATA_INVALID:
+ case XE_GUC_LOAD_STATUS_INIT_MMIO_SAVE_RESTORE_INVALID:
+ return -1;
+ }
+
+ switch (br_val) {
+ case XE_BOOTROM_STATUS_NO_KEY_FOUND:
+ case XE_BOOTROM_STATUS_RSA_FAILED:
+ case XE_BOOTROM_STATUS_PAVPC_FAILED:
+ case XE_BOOTROM_STATUS_WOPCM_FAILED:
+ case XE_BOOTROM_STATUS_LOADLOC_FAILED:
+ case XE_BOOTROM_STATUS_JUMP_FAILED:
+ case XE_BOOTROM_STATUS_RC6CTXCONFIG_FAILED:
+ case XE_BOOTROM_STATUS_MPUMAP_INCORRECT:
+ case XE_BOOTROM_STATUS_EXCEPTION:
+ case XE_BOOTROM_STATUS_PROD_KEY_CHECK_FAILURE:
+ return -1;
+ }
+
+ return 0;
+}
+static s32 guc_pc_get_cur_freq(struct xe_guc_pc *guc_pc)
+{
+ u32 freq;
+ int ret = xe_guc_pc_get_cur_freq(guc_pc, &freq);
+
+ return ret ? ret : freq;
+}
+
+/*
+ * Wait for the GuC to start up.
+ *
+ * Measurements indicate this should take no more than 20ms (assuming the GT
+ * clock is at maximum frequency). However, thermal throttling and other issues
+ * can prevent the clock hitting max and thus making the load take significantly
+ * longer. Allow up to 200ms as a safety margin for real world worst case situations.
+ *
+ * However, bugs anywhere from KMD to GuC to PCODE to fan failure in a CI farm can
+ * lead to even longer times. E.g. if the GT is clamped to minimum frequency then
+ * the load times can be in the seconds range. So the timeout is increased for debug
+ * builds to ensure that problems can be correctly analysed. For release builds, the
+ * timeout is kept short so that users don't wait forever to find out that there is a
+ * problem. In either case, if the load took longer than is reasonable even with some
+ * 'sensible' throttling, then flag a warning because something is not right.
+ *
+ * Note that there is a limit on how long an individual usleep_range() can wait for,
+ * hence longer waits require wrapping a shorter wait in a loop.
+ *
+ * Note that the only reason an end user should hit the shorter timeout is in case of
+ * extreme thermal throttling. And a system that is that hot during boot is probably
+ * dead anyway!
+ */
+#if defined(CONFIG_DRM_XE_DEBUG)
+#define GUC_LOAD_RETRY_LIMIT 20
+#else
+#define GUC_LOAD_RETRY_LIMIT 3
+#endif
+#define GUC_LOAD_TIME_WARN_MS 200
+
+static void guc_wait_ucode(struct xe_guc *guc)
+{
+ struct xe_gt *gt = guc_to_gt(guc);
+ struct xe_guc_pc *guc_pc = &gt->uc.guc.pc;
+ ktime_t before, after, delta;
+ int load_done;
+ u32 status = 0;
+ int count = 0;
+ u64 delta_ms;
+ u32 before_freq;
+
+ before_freq = xe_guc_pc_get_act_freq(guc_pc);
+ before = ktime_get();
/*
- * Wait for the GuC to start up.
- * NB: Docs recommend not using the interrupt for completion.
- * Measurements indicate this should take no more than 20ms
- * (assuming the GT clock is at maximum frequency). So, a
- * timeout here indicates that the GuC has failed and is unusable.
- * (Higher levels of the driver may decide to reset the GuC and
- * attempt the ucode load again if this happens.)
- *
- * FIXME: There is a known (but exceedingly unlikely) race condition
- * where the asynchronous frequency management code could reduce
- * the GT clock while a GuC reload is in progress (during a full
- * GT reset). A fix is in progress but there are complex locking
- * issues to be resolved. In the meantime bump the timeout to
- * 200ms. Even at slowest clock, this should be sufficient. And
- * in the working case, a larger timeout makes no difference.
+ * Note, can't use any kind of timing information from the call to xe_mmio_wait.
+ * It could return a thousand intermediate stages at random times. Instead, must
+ * manually track the total time taken and locally implement the timeout.
*/
- ret = xe_mmio_wait32(gt, GUC_STATUS, GS_UKERNEL_MASK,
- FIELD_PREP(GS_UKERNEL_MASK, XE_GUC_LOAD_STATUS_READY),
- 200000, &status, false);
+ do {
+ u32 last_status = status & (GS_UKERNEL_MASK | GS_BOOTROM_MASK);
+ int ret;
- if (ret) {
- xe_gt_info(gt, "GuC load failed: status = 0x%08X\n", status);
- xe_gt_info(gt, "GuC status: Reset = %u, BootROM = %#X, UKernel = %#X, MIA = %#X, Auth = %#X\n",
- REG_FIELD_GET(GS_MIA_IN_RESET, status),
- REG_FIELD_GET(GS_BOOTROM_MASK, status),
- REG_FIELD_GET(GS_UKERNEL_MASK, status),
- REG_FIELD_GET(GS_MIA_MASK, status),
- REG_FIELD_GET(GS_AUTH_STATUS_MASK, status));
-
- if ((status & GS_BOOTROM_MASK) == GS_BOOTROM_RSA_FAILED) {
- xe_gt_info(gt, "GuC firmware signature verification failed\n");
- ret = -ENOEXEC;
+ /*
+ * Wait for any change (intermediate or terminal) in the status register.
+ * Note, the return value is a don't care. The only failure code is timeout
+ * but the timeouts need to be accumulated over all the intermediate partial
+ * timeouts rather than allowing a huge timeout each time. So basically, need
+ * to treat a timeout no different to a value change.
+ */
+ ret = xe_mmio_wait32_not(gt, GUC_STATUS, GS_UKERNEL_MASK | GS_BOOTROM_MASK,
+ last_status, 1000 * 1000, &status, false);
+ if (ret < 0)
+ count++;
+ after = ktime_get();
+ delta = ktime_sub(after, before);
+ delta_ms = ktime_to_ms(delta);
+
+ load_done = guc_load_done(status);
+ if (load_done != 0)
+ break;
+
+ if (delta_ms >= (GUC_LOAD_RETRY_LIMIT * 1000))
+ break;
+
+ xe_gt_dbg(gt, "load still in progress, timeouts = %d, freq = %dMHz (req %dMHz), status = 0x%08X [0x%02X/%02X]\n",
+ count, xe_guc_pc_get_act_freq(guc_pc),
+ guc_pc_get_cur_freq(guc_pc), status,
+ REG_FIELD_GET(GS_BOOTROM_MASK, status),
+ REG_FIELD_GET(GS_UKERNEL_MASK, status));
+ } while (1);
+
+ if (load_done != 1) {
+ u32 ukernel = REG_FIELD_GET(GS_UKERNEL_MASK, status);
+ u32 bootrom = REG_FIELD_GET(GS_BOOTROM_MASK, status);
+
+ xe_gt_err(gt, "load failed: status = 0x%08X, time = %lldms, freq = %dMHz (req %dMHz), done = %d\n",
+ status, delta_ms, xe_guc_pc_get_act_freq(guc_pc),
+ guc_pc_get_cur_freq(guc_pc), load_done);
+ xe_gt_err(gt, "load failed: status: Reset = %d, BootROM = 0x%02X, UKernel = 0x%02X, MIA = 0x%02X, Auth = 0x%02X\n",
+ REG_FIELD_GET(GS_MIA_IN_RESET, status),
+ bootrom, ukernel,
+ REG_FIELD_GET(GS_MIA_MASK, status),
+ REG_FIELD_GET(GS_AUTH_STATUS_MASK, status));
+
+ switch (bootrom) {
+ case XE_BOOTROM_STATUS_NO_KEY_FOUND:
+ xe_gt_err(gt, "invalid key requested, header = 0x%08X\n",
+ xe_mmio_read32(gt, GUC_HEADER_INFO));
+ break;
+
+ case XE_BOOTROM_STATUS_RSA_FAILED:
+ xe_gt_err(gt, "firmware signature verification failed\n");
+ break;
+
+ case XE_BOOTROM_STATUS_PROD_KEY_CHECK_FAILURE:
+ xe_gt_err(gt, "firmware production part check failure\n");
+ break;
}
- if (REG_FIELD_GET(GS_UKERNEL_MASK, status) ==
- XE_GUC_LOAD_STATUS_EXCEPTION) {
- xe_gt_info(gt, "GuC firmware exception. EIP: %#x\n",
- xe_mmio_read32(gt, SOFT_SCRATCH(13)));
- ret = -ENXIO;
+ switch (ukernel) {
+ case XE_GUC_LOAD_STATUS_EXCEPTION:
+ xe_gt_err(gt, "firmware exception. EIP: %#x\n",
+ xe_mmio_read32(gt, SOFT_SCRATCH(13)));
+ break;
+
+ case XE_GUC_LOAD_STATUS_INIT_MMIO_SAVE_RESTORE_INVALID:
+ xe_gt_err(gt, "illegal register in save/restore workaround list\n");
+ break;
+
+ case XE_GUC_LOAD_STATUS_HWCONFIG_START:
+ xe_gt_err(gt, "still extracting hwconfig table.\n");
+ break;
}
+
+ xe_device_declare_wedged(gt_to_xe(gt));
+ } else if (delta_ms > GUC_LOAD_TIME_WARN_MS) {
+ xe_gt_warn(gt, "excessive init time: %lldms! [status = 0x%08X, timeouts = %d]\n",
+ delta_ms, status, count);
+ xe_gt_warn(gt, "excessive init time: [freq = %dMHz (req = %dMHz), before = %dMHz, perf_limit_reasons = 0x%08X]\n",
+ xe_guc_pc_get_act_freq(guc_pc), guc_pc_get_cur_freq(guc_pc),
+ before_freq, xe_gt_throttle_get_limit_reasons(gt));
} else {
- xe_gt_dbg(gt, "GuC successfully loaded\n");
+ xe_gt_dbg(gt, "init took %lldms, freq = %dMHz (req = %dMHz), before = %dMHz, status = 0x%08X, timeouts = %d\n",
+ delta_ms, xe_guc_pc_get_act_freq(guc_pc), guc_pc_get_cur_freq(guc_pc),
+ before_freq, status, count);
}
-
- return ret;
}
static int __xe_guc_upload(struct xe_guc *guc)
{
int ret;
+ /* Raise GT freq to speed up HuC/GuC load */
+ xe_guc_pc_raise_unslice(&guc->pc);
+
guc_write_params(guc);
guc_prepare_xfer(guc);
@@ -532,9 +728,7 @@ static int __xe_guc_upload(struct xe_guc *guc)
goto out;
/* Wait for authentication */
- ret = guc_wait_ucode(guc);
- if (ret)
- goto out;
+ guc_wait_ucode(guc);
xe_uc_fw_change_status(&guc->fw, XE_UC_FIRMWARE_RUNNING);
return 0;
@@ -544,6 +738,38 @@ out:
return 0 /* FIXME: ret, don't want to stop load currently */;
}
+static int vf_guc_min_load_for_hwconfig(struct xe_guc *guc)
+{
+ struct xe_gt *gt = guc_to_gt(guc);
+ int ret;
+
+ ret = xe_gt_sriov_vf_bootstrap(gt);
+ if (ret)
+ return ret;
+
+ ret = xe_gt_sriov_vf_query_config(gt);
+ if (ret)
+ return ret;
+
+ ret = xe_guc_hwconfig_init(guc);
+ if (ret)
+ return ret;
+
+ ret = xe_guc_enable_communication(guc);
+ if (ret)
+ return ret;
+
+ ret = xe_gt_sriov_vf_connect(gt);
+ if (ret)
+ return ret;
+
+ ret = xe_gt_sriov_vf_query_runtime(gt);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
/**
* xe_guc_min_load_for_hwconfig - load minimal GuC and read hwconfig table
* @guc: The GuC object
@@ -559,9 +785,11 @@ int xe_guc_min_load_for_hwconfig(struct xe_guc *guc)
{
int ret;
+ if (IS_SRIOV_VF(guc_to_xe(guc)))
+ return vf_guc_min_load_for_hwconfig(guc);
+
xe_guc_ads_populate_minimal(&guc->ads);
- /* Raise GT freq to speed up HuC/GuC load */
xe_guc_pc_init_early(&guc->pc);
ret = __xe_guc_upload(guc);
@@ -631,8 +859,6 @@ int xe_guc_enable_communication(struct xe_guc *guc)
struct xe_device *xe = guc_to_xe(guc);
int err;
- guc_enable_irq(guc);
-
if (IS_SRIOV_VF(xe) && xe_device_has_memirq(xe)) {
struct xe_gt *gt = guc_to_gt(guc);
struct xe_tile *tile = gt_to_tile(gt);
@@ -640,11 +866,10 @@ int xe_guc_enable_communication(struct xe_guc *guc)
err = xe_memirq_init_guc(&tile->sriov.vf.memirq, guc);
if (err)
return err;
+ } else {
+ guc_enable_irq(guc);
}
- xe_mmio_rmw32(guc_to_gt(guc), PMINTRMSK,
- ARAT_EXPIRED_INTRMSK, 0);
-
err = xe_guc_ct_enable(&guc->ct);
if (err)
return err;
@@ -871,7 +1096,7 @@ void xe_guc_irq_handler(struct xe_guc *guc, const u16 iir)
void xe_guc_sanitize(struct xe_guc *guc)
{
- xe_uc_fw_change_status(&guc->fw, XE_UC_FIRMWARE_LOADABLE);
+ xe_uc_fw_sanitize(&guc->fw);
xe_guc_ct_disable(&guc->ct);
guc->submission_state.enabled = false;
}
@@ -888,28 +1113,31 @@ void xe_guc_reset_wait(struct xe_guc *guc)
void xe_guc_stop_prepare(struct xe_guc *guc)
{
- XE_WARN_ON(xe_guc_pc_stop(&guc->pc));
+ if (!IS_SRIOV_VF(guc_to_xe(guc))) {
+ int err;
+
+ err = xe_guc_pc_stop(&guc->pc);
+ xe_gt_WARN(guc_to_gt(guc), err, "Failed to stop GuC PC: %pe\n",
+ ERR_PTR(err));
+ }
}
-int xe_guc_stop(struct xe_guc *guc)
+void xe_guc_stop(struct xe_guc *guc)
{
- int ret;
-
xe_guc_ct_stop(&guc->ct);
- ret = xe_guc_submit_stop(guc);
- if (ret)
- return ret;
-
- return 0;
+ xe_guc_submit_stop(guc);
}
int xe_guc_start(struct xe_guc *guc)
{
- int ret;
+ if (!IS_SRIOV_VF(guc_to_xe(guc))) {
+ int err;
- ret = xe_guc_pc_start(&guc->pc);
- XE_WARN_ON(ret);
+ err = xe_guc_pc_start(&guc->pc);
+ xe_gt_WARN(guc_to_gt(guc), err, "Failed to start GuC PC: %pe\n",
+ ERR_PTR(err));
+ }
return xe_guc_submit_start(guc);
}
@@ -952,28 +1180,17 @@ void xe_guc_print_info(struct xe_guc *guc, struct drm_printer *p)
}
/**
- * xe_guc_in_reset() - Detect if GuC MIA is in reset.
- * @guc: The GuC object
- *
- * This function detects runtime resume from d3cold by leveraging
- * GUC_STATUS, GUC doesn't get reset during d3hot,
- * it strictly to be called from RPM resume handler.
+ * xe_guc_declare_wedged() - Declare GuC wedged
+ * @guc: the GuC object
*
- * Return: true if failed to get forcewake or GuC MIA is in Reset,
- * otherwise false.
+ * Wedge the GuC which stops all submission, saves desired debug state, and
+ * cleans up anything which could timeout.
*/
-bool xe_guc_in_reset(struct xe_guc *guc)
+void xe_guc_declare_wedged(struct xe_guc *guc)
{
- struct xe_gt *gt = guc_to_gt(guc);
- u32 status;
- int err;
-
- err = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
- if (err)
- return true;
+ xe_gt_assert(guc_to_gt(guc), guc_to_xe(guc)->wedged.mode);
- status = xe_mmio_read32(gt, GUC_STATUS);
- xe_force_wake_put(gt_to_fw(gt), XE_FW_GT);
-
- return status & GS_MIA_IN_RESET;
+ xe_guc_reset_prepare(guc);
+ xe_guc_ct_stop(&guc->ct);
+ xe_guc_submit_wedge(guc);
}
diff --git a/drivers/gpu/drm/xe/xe_guc.h b/drivers/gpu/drm/xe/xe_guc.h
index 94f2dc5f6f90..e0bbf98f849d 100644
--- a/drivers/gpu/drm/xe/xe_guc.h
+++ b/drivers/gpu/drm/xe/xe_guc.h
@@ -35,9 +35,9 @@ void xe_guc_print_info(struct xe_guc *guc, struct drm_printer *p);
int xe_guc_reset_prepare(struct xe_guc *guc);
void xe_guc_reset_wait(struct xe_guc *guc);
void xe_guc_stop_prepare(struct xe_guc *guc);
-int xe_guc_stop(struct xe_guc *guc);
+void xe_guc_stop(struct xe_guc *guc);
int xe_guc_start(struct xe_guc *guc);
-bool xe_guc_in_reset(struct xe_guc *guc);
+void xe_guc_declare_wedged(struct xe_guc *guc);
static inline u16 xe_engine_class_to_guc_class(enum xe_engine_class class)
{
diff --git a/drivers/gpu/drm/xe/xe_guc_ads.c b/drivers/gpu/drm/xe/xe_guc_ads.c
index 7f5a523795c8..1c60b685dbc6 100644
--- a/drivers/gpu/drm/xe/xe_guc_ads.c
+++ b/drivers/gpu/drm/xe/xe_guc_ads.c
@@ -9,13 +9,16 @@
#include <generated/xe_wa_oob.h>
+#include "abi/guc_actions_abi.h"
#include "regs/xe_engine_regs.h"
#include "regs/xe_gt_regs.h"
#include "regs/xe_guc_regs.h"
#include "xe_bo.h"
#include "xe_gt.h"
#include "xe_gt_ccs_mode.h"
+#include "xe_gt_printk.h"
#include "xe_guc.h"
+#include "xe_guc_ct.h"
#include "xe_hw_engine.h"
#include "xe_lrc.h"
#include "xe_map.h"
@@ -265,7 +268,6 @@ static u32 engine_enable_mask(struct xe_gt *gt, enum xe_engine_class class)
static size_t calculate_golden_lrc_size(struct xe_guc_ads *ads)
{
- struct xe_device *xe = ads_to_xe(ads);
struct xe_gt *gt = ads_to_gt(ads);
size_t total_size = 0, alloc_size, real_size;
int class;
@@ -274,7 +276,7 @@ static size_t calculate_golden_lrc_size(struct xe_guc_ads *ads)
if (!engine_enable_mask(gt, class))
continue;
- real_size = xe_lrc_size(xe, class);
+ real_size = xe_gt_lrc_size(gt, class);
alloc_size = PAGE_ALIGN(real_size);
total_size += alloc_size;
}
@@ -440,11 +442,18 @@ int xe_guc_ads_init_post_hwconfig(struct xe_guc_ads *ads)
static void guc_policies_init(struct xe_guc_ads *ads)
{
+ struct xe_device *xe = ads_to_xe(ads);
+ u32 global_flags = 0;
+
ads_blob_write(ads, policies.dpc_promote_time,
GLOBAL_POLICY_DEFAULT_DPC_PROMOTE_TIME_US);
ads_blob_write(ads, policies.max_num_work_items,
GLOBAL_POLICY_MAX_NUM_WI);
- ads_blob_write(ads, policies.global_flags, 0);
+
+ if (xe->wedged.mode == 2)
+ global_flags |= GLOBAL_POLICY_DISABLE_ENGINE_RESET;
+
+ ads_blob_write(ads, policies.global_flags, global_flags);
ads_blob_write(ads, policies.is_valid, 1);
}
@@ -765,7 +774,7 @@ static void guc_populate_golden_lrc(struct xe_guc_ads *ads)
xe_gt_assert(gt, gt->default_lrc[class]);
- real_size = xe_lrc_size(xe, class);
+ real_size = xe_gt_lrc_size(gt, class);
alloc_size = PAGE_ALIGN(real_size);
total_size += alloc_size;
@@ -799,3 +808,57 @@ void xe_guc_ads_populate_post_load(struct xe_guc_ads *ads)
{
guc_populate_golden_lrc(ads);
}
+
+static int guc_ads_action_update_policies(struct xe_guc_ads *ads, u32 policy_offset)
+{
+ struct xe_guc_ct *ct = &ads_to_guc(ads)->ct;
+ u32 action[] = {
+ XE_GUC_ACTION_GLOBAL_SCHED_POLICY_CHANGE,
+ policy_offset
+ };
+
+ return xe_guc_ct_send(ct, action, ARRAY_SIZE(action), 0, 0);
+}
+
+/**
+ * xe_guc_ads_scheduler_policy_toggle_reset - Toggle reset policy
+ * @ads: Additional data structures object
+ *
+ * This function update the GuC's engine reset policy based on wedged.mode.
+ *
+ * Return: 0 on success, and negative error code otherwise.
+ */
+int xe_guc_ads_scheduler_policy_toggle_reset(struct xe_guc_ads *ads)
+{
+ struct xe_device *xe = ads_to_xe(ads);
+ struct xe_gt *gt = ads_to_gt(ads);
+ struct xe_tile *tile = gt_to_tile(gt);
+ struct guc_policies *policies;
+ struct xe_bo *bo;
+ int ret = 0;
+
+ policies = kmalloc(sizeof(*policies), GFP_KERNEL);
+ if (!policies)
+ return -ENOMEM;
+
+ 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
+ policies->global_flags &= ~GLOBAL_POLICY_DISABLE_ENGINE_RESET;
+
+ bo = xe_managed_bo_create_from_data(xe, tile, policies, sizeof(struct guc_policies),
+ XE_BO_FLAG_VRAM_IF_DGFX(tile) |
+ XE_BO_FLAG_GGTT);
+ if (IS_ERR(bo)) {
+ ret = PTR_ERR(bo);
+ goto out;
+ }
+
+ ret = guc_ads_action_update_policies(ads, xe_bo_ggtt_addr(bo));
+out:
+ kfree(policies);
+ return ret;
+}
diff --git a/drivers/gpu/drm/xe/xe_guc_ads.h b/drivers/gpu/drm/xe/xe_guc_ads.h
index 138ef6267671..2e6674c760ff 100644
--- a/drivers/gpu/drm/xe/xe_guc_ads.h
+++ b/drivers/gpu/drm/xe/xe_guc_ads.h
@@ -6,12 +6,13 @@
#ifndef _XE_GUC_ADS_H_
#define _XE_GUC_ADS_H_
-#include "xe_guc_ads_types.h"
+struct xe_guc_ads;
int xe_guc_ads_init(struct xe_guc_ads *ads);
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);
#endif
diff --git a/drivers/gpu/drm/xe/xe_guc_ct.c b/drivers/gpu/drm/xe/xe_guc_ct.c
index 0151d29b3c58..7d2e937da1d8 100644
--- a/drivers/gpu/drm/xe/xe_guc_ct.c
+++ b/drivers/gpu/drm/xe/xe_guc_ct.c
@@ -22,13 +22,14 @@
#include "xe_gt_pagefault.h"
#include "xe_gt_printk.h"
#include "xe_gt_sriov_pf_control.h"
+#include "xe_gt_sriov_pf_monitor.h"
#include "xe_gt_tlb_invalidation.h"
#include "xe_guc.h"
#include "xe_guc_relay.h"
#include "xe_guc_submit.h"
#include "xe_map.h"
#include "xe_pm.h"
-#include "xe_trace.h"
+#include "xe_trace_guc.h"
/* Used when a CT send wants to block and / or receive data */
struct g2h_fence {
@@ -111,6 +112,23 @@ ct_to_xe(struct xe_guc_ct *ct)
#define CTB_G2H_BUFFER_SIZE (4 * CTB_H2G_BUFFER_SIZE)
#define G2H_ROOM_BUFFER_SIZE (CTB_G2H_BUFFER_SIZE / 4)
+/**
+ * xe_guc_ct_queue_proc_time_jiffies - Return maximum time to process a full
+ * CT command queue
+ * @ct: the &xe_guc_ct. Unused at this moment but will be used in the future.
+ *
+ * Observation is that a 4KiB buffer full of commands takes a little over a
+ * second to process. Use that to calculate maximum time to process a full CT
+ * command queue.
+ *
+ * Return: Maximum time to process a full CT queue in jiffies.
+ */
+long xe_guc_ct_queue_proc_time_jiffies(struct xe_guc_ct *ct)
+{
+ BUILD_BUG_ON(!IS_ALIGNED(CTB_H2G_BUFFER_SIZE, SZ_4));
+ return (CTB_H2G_BUFFER_SIZE / SZ_4K) * HZ;
+}
+
static size_t guc_ct_size(void)
{
return 2 * CTB_DESC_SIZE + CTB_H2G_BUFFER_SIZE +
@@ -125,7 +143,9 @@ static void guc_ct_fini(struct drm_device *drm, void *arg)
xa_destroy(&ct->fence_lookup);
}
+static void receive_g2h(struct xe_guc_ct *ct);
static void g2h_worker_func(struct work_struct *w);
+static void safe_mode_worker_func(struct work_struct *w);
static void primelockdep(struct xe_guc_ct *ct)
{
@@ -154,6 +174,7 @@ int xe_guc_ct_init(struct xe_guc_ct *ct)
spin_lock_init(&ct->fast_lock);
xa_init(&ct->fence_lookup);
INIT_WORK(&ct->g2h_worker, g2h_worker_func);
+ INIT_DELAYED_WORK(&ct->safe_mode_worker, safe_mode_worker_func);
init_waitqueue_head(&ct->wq);
init_waitqueue_head(&ct->g2h_fence_wq);
@@ -320,6 +341,42 @@ static void xe_guc_ct_set_state(struct xe_guc_ct *ct,
mutex_unlock(&ct->lock);
}
+static bool ct_needs_safe_mode(struct xe_guc_ct *ct)
+{
+ return !pci_dev_msi_enabled(to_pci_dev(ct_to_xe(ct)->drm.dev));
+}
+
+static bool ct_restart_safe_mode_worker(struct xe_guc_ct *ct)
+{
+ if (!ct_needs_safe_mode(ct))
+ return false;
+
+ queue_delayed_work(ct->g2h_wq, &ct->safe_mode_worker, HZ / 10);
+ return true;
+}
+
+static void safe_mode_worker_func(struct work_struct *w)
+{
+ struct xe_guc_ct *ct = container_of(w, struct xe_guc_ct, safe_mode_worker.work);
+
+ receive_g2h(ct);
+
+ if (!ct_restart_safe_mode_worker(ct))
+ xe_gt_dbg(ct_to_gt(ct), "GuC CT safe-mode canceled\n");
+}
+
+static void ct_enter_safe_mode(struct xe_guc_ct *ct)
+{
+ if (ct_restart_safe_mode_worker(ct))
+ xe_gt_dbg(ct_to_gt(ct), "GuC CT safe-mode enabled\n");
+}
+
+static void ct_exit_safe_mode(struct xe_guc_ct *ct)
+{
+ if (cancel_delayed_work_sync(&ct->safe_mode_worker))
+ xe_gt_dbg(ct_to_gt(ct), "GuC CT safe-mode disabled\n");
+}
+
int xe_guc_ct_enable(struct xe_guc_ct *ct)
{
struct xe_device *xe = ct_to_xe(ct);
@@ -349,6 +406,9 @@ int xe_guc_ct_enable(struct xe_guc_ct *ct)
wake_up_all(&ct->wq);
xe_gt_dbg(gt, "GuC CT communication channel enabled\n");
+ if (ct_needs_safe_mode(ct))
+ ct_enter_safe_mode(ct);
+
return 0;
err_out:
@@ -372,6 +432,7 @@ static void stop_g2h_handler(struct xe_guc_ct *ct)
void xe_guc_ct_disable(struct xe_guc_ct *ct)
{
xe_guc_ct_set_state(ct, XE_GUC_CT_STATE_DISABLED);
+ ct_exit_safe_mode(ct);
stop_g2h_handler(ct);
}
@@ -527,7 +588,7 @@ static int h2g_write(struct xe_guc_ct *ct, const u32 *action, u32 len,
/* Update descriptor */
desc_write(xe, h2g, tail, h2g->info.tail);
- trace_xe_guc_ctb_h2g(gt->info.id, *(action - 1), full_len,
+ trace_xe_guc_ctb_h2g(xe, gt->info.id, *(action - 1), full_len,
desc_read(xe, h2g, head), h2g->info.tail);
return 0;
@@ -640,6 +701,7 @@ static int guc_ct_send_locked(struct xe_guc_ct *ct, const u32 *action, u32 len,
u32 g2h_len, u32 num_g2h,
struct g2h_fence *g2h_fence)
{
+ struct xe_device *xe = ct_to_xe(ct);
struct xe_gt *gt = ct_to_gt(ct);
struct drm_printer p = xe_gt_info_printer(gt);
unsigned int sleep_period_ms = 1;
@@ -667,7 +729,7 @@ try_again:
if (sleep_period_ms == 1024)
goto broken;
- trace_xe_guc_ct_h2g_flow_control(h2g->info.head, h2g->info.tail,
+ trace_xe_guc_ct_h2g_flow_control(xe, h2g->info.head, h2g->info.tail,
h2g->info.size,
h2g->info.space,
len + GUC_CTB_HDR_LEN);
@@ -679,7 +741,7 @@ try_again:
struct xe_device *xe = ct_to_xe(ct);
struct guc_ctb *g2h = &ct->ctbs.g2h;
- trace_xe_guc_ct_g2h_flow_control(g2h->info.head,
+ trace_xe_guc_ct_g2h_flow_control(xe, g2h->info.head,
desc_read(xe, g2h, tail),
g2h->info.size,
g2h->info.space,
@@ -832,12 +894,12 @@ retry_same_fence:
}
if (g2h_fence.retry) {
- xe_gt_warn(gt, "H2G retry, action 0x%04x, reason %u",
- action[0], g2h_fence.reason);
+ xe_gt_dbg(gt, "H2G action %#x retrying: reason %#x\n",
+ action[0], g2h_fence.reason);
goto retry;
}
if (g2h_fence.fail) {
- xe_gt_err(gt, "H2G send failed, action 0x%04x, error %d, hint %u",
+ xe_gt_err(gt, "H2G request %#x failed: error %#x hint %#x\n",
action[0], g2h_fence.error, g2h_fence.hint);
ret = -EIO;
}
@@ -1071,6 +1133,9 @@ static int process_g2h_msg(struct xe_guc_ct *ct, u32 *msg, u32 len)
case GUC_ACTION_GUC2PF_VF_STATE_NOTIFY:
ret = xe_gt_sriov_pf_control_process_guc2pf(gt, hxg, hxg_len);
break;
+ case GUC_ACTION_GUC2PF_ADVERSE_EVENT:
+ ret = xe_gt_sriov_pf_monitor_process_guc2pf(gt, hxg, hxg_len);
+ break;
default:
xe_gt_err(gt, "unexpected G2H action 0x%04x\n", action);
}
@@ -1166,8 +1231,8 @@ static int g2h_read(struct xe_guc_ct *ct, u32 *msg, bool fast_path)
g2h->info.head = (head + avail) % g2h->info.size;
desc_write(xe, g2h, head, g2h->info.head);
- trace_xe_guc_ctb_g2h(ct_to_gt(ct)->info.id, action, len,
- g2h->info.head, tail);
+ trace_xe_guc_ctb_g2h(xe, ct_to_gt(ct)->info.id,
+ action, len, g2h->info.head, tail);
return len;
}
@@ -1256,9 +1321,8 @@ static int dequeue_one_g2h(struct xe_guc_ct *ct)
return 1;
}
-static void g2h_worker_func(struct work_struct *w)
+static void receive_g2h(struct xe_guc_ct *ct)
{
- struct xe_guc_ct *ct = container_of(w, struct xe_guc_ct, g2h_worker);
struct xe_gt *gt = ct_to_gt(ct);
bool ongoing;
int ret;
@@ -1307,6 +1371,13 @@ static void g2h_worker_func(struct work_struct *w)
xe_pm_runtime_put(ct_to_xe(ct));
}
+static void g2h_worker_func(struct work_struct *w)
+{
+ struct xe_guc_ct *ct = container_of(w, struct xe_guc_ct, g2h_worker);
+
+ receive_g2h(ct);
+}
+
static void guc_ctb_snapshot_capture(struct xe_device *xe, struct guc_ctb *ctb,
struct guc_ctb_snapshot *snapshot,
bool atomic)
diff --git a/drivers/gpu/drm/xe/xe_guc_ct.h b/drivers/gpu/drm/xe/xe_guc_ct.h
index 105bb8e99a8d..190202fce2d0 100644
--- a/drivers/gpu/drm/xe/xe_guc_ct.h
+++ b/drivers/gpu/drm/xe/xe_guc_ct.h
@@ -64,4 +64,6 @@ xe_guc_ct_send_block_no_fail(struct xe_guc_ct *ct, const u32 *action, u32 len)
return xe_guc_ct_send_recv_no_fail(ct, action, len, NULL);
}
+long xe_guc_ct_queue_proc_time_jiffies(struct xe_guc_ct *ct);
+
#endif
diff --git a/drivers/gpu/drm/xe/xe_guc_ct_types.h b/drivers/gpu/drm/xe/xe_guc_ct_types.h
index fede4c6e93cb..761cb9031298 100644
--- a/drivers/gpu/drm/xe/xe_guc_ct_types.h
+++ b/drivers/gpu/drm/xe/xe_guc_ct_types.h
@@ -110,6 +110,8 @@ struct xe_guc_ct {
u32 g2h_outstanding;
/** @g2h_worker: worker to process G2H messages */
struct work_struct g2h_worker;
+ /** @safe_mode_worker: worker to check G2H messages with IRQ disabled */
+ struct delayed_work safe_mode_worker;
/** @state: CT state */
enum xe_guc_ct_state state;
/** @fence_seqno: G2H fence seqno - 16 bits used by CT */
diff --git a/drivers/gpu/drm/xe/xe_guc_db_mgr.c b/drivers/gpu/drm/xe/xe_guc_db_mgr.c
index 8d9a0287df6b..6767e8076e6b 100644
--- a/drivers/gpu/drm/xe/xe_guc_db_mgr.c
+++ b/drivers/gpu/drm/xe/xe_guc_db_mgr.c
@@ -106,7 +106,8 @@ int xe_guc_db_mgr_init(struct xe_guc_db_mgr *dbm, unsigned int count)
if (ret)
return ret;
done:
- xe_gt_dbg(dbm_to_gt(dbm), "using %u doorbell(s)\n", dbm->count);
+ xe_gt_dbg(dbm_to_gt(dbm), "using %u doorbell%s\n",
+ dbm->count, str_plural(dbm->count));
return 0;
}
diff --git a/drivers/gpu/drm/xe/xe_guc_id_mgr.c b/drivers/gpu/drm/xe/xe_guc_id_mgr.c
index 0fb7c6b78c31..cd0549d0ef89 100644
--- a/drivers/gpu/drm/xe/xe_guc_id_mgr.c
+++ b/drivers/gpu/drm/xe/xe_guc_id_mgr.c
@@ -97,7 +97,8 @@ int xe_guc_id_mgr_init(struct xe_guc_id_mgr *idm, unsigned int limit)
if (ret)
return ret;
- xe_gt_info(idm_to_gt(idm), "using %u GUC ID(s)\n", idm->total);
+ xe_gt_info(idm_to_gt(idm), "using %u GUC ID%s\n",
+ idm->total, str_plural(idm->total));
return 0;
}
diff --git a/drivers/gpu/drm/xe/xe_guc_klv_helpers.c b/drivers/gpu/drm/xe/xe_guc_klv_helpers.c
index ceca949932a0..9d99fe266d97 100644
--- a/drivers/gpu/drm/xe/xe_guc_klv_helpers.c
+++ b/drivers/gpu/drm/xe/xe_guc_klv_helpers.c
@@ -8,6 +8,7 @@
#include "abi/guc_klvs_abi.h"
#include "xe_guc_klv_helpers.h"
+#include "xe_guc_klv_thresholds_set.h"
#define make_u64(hi, lo) ((u64)((u64)(u32)(hi) << 32 | (u32)(lo)))
@@ -48,6 +49,17 @@ const char *xe_guc_klv_key_to_string(u16 key)
return "begin_db_id";
case GUC_KLV_VF_CFG_BEGIN_CONTEXT_ID_KEY:
return "begin_ctx_id";
+
+ /* VF CFG threshold keys */
+#define define_threshold_key_to_string_case(TAG, NAME, ...) \
+ \
+ case MAKE_GUC_KLV_VF_CFG_THRESHOLD_KEY(TAG): \
+ return #NAME;
+
+ /* private: auto-generated case statements */
+ MAKE_XE_GUC_KLV_THRESHOLDS_SET(define_threshold_key_to_string_case)
+#undef define_threshold_key_to_string_case
+
default:
return "(unknown)";
}
diff --git a/drivers/gpu/drm/xe/xe_guc_klv_helpers.h b/drivers/gpu/drm/xe/xe_guc_klv_helpers.h
index b835e0ebe6db..c676d21c173b 100644
--- a/drivers/gpu/drm/xe/xe_guc_klv_helpers.h
+++ b/drivers/gpu/drm/xe/xe_guc_klv_helpers.h
@@ -6,6 +6,7 @@
#ifndef _XE_GUC_KLV_HELPERS_H_
#define _XE_GUC_KLV_HELPERS_H_
+#include <linux/args.h>
#include <linux/types.h>
struct drm_printer;
@@ -38,6 +39,18 @@ int xe_guc_klv_count(const u32 *klvs, u32 num_dwords);
FIELD_PREP_CONST(GUC_KLV_0_LEN, (len)))
/**
+ * MAKE_GUC_KLV_KEY - Prepare KLV KEY name based on unique KLV definition tag.
+ * @TAG: unique tag of the KLV definition
+ */
+#define MAKE_GUC_KLV_KEY(TAG) CONCATENATE(CONCATENATE(GUC_KLV_, TAG), _KEY)
+
+/**
+ * MAKE_GUC_KLV_LEN - Prepare KLV LEN name based on unique KLV definition tag.
+ * @TAG: unique tag of the KLV definition
+ */
+#define MAKE_GUC_KLV_LEN(TAG) CONCATENATE(CONCATENATE(GUC_KLV_, TAG), _LEN)
+
+/**
* PREP_GUC_KLV_TAG - Prepare KLV header value based on unique KLV definition tag.
* @TAG: unique tag of the KLV definition
*
@@ -46,6 +59,6 @@ int xe_guc_klv_count(const u32 *klvs, u32 num_dwords);
* Return: value of the KLV header (u32).
*/
#define PREP_GUC_KLV_TAG(TAG) \
- PREP_GUC_KLV_CONST(GUC_KLV_##TAG##_KEY, GUC_KLV_##TAG##_LEN)
+ PREP_GUC_KLV_CONST(MAKE_GUC_KLV_KEY(TAG), MAKE_GUC_KLV_LEN(TAG))
#endif
diff --git a/drivers/gpu/drm/xe/xe_guc_klv_thresholds_set.h b/drivers/gpu/drm/xe/xe_guc_klv_thresholds_set.h
new file mode 100644
index 000000000000..da0fedbbdbaf
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_guc_klv_thresholds_set.h
@@ -0,0 +1,64 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2024 Intel Corporation
+ */
+
+#ifndef _XE_GUC_KLV_THRESHOLDS_SET_H_
+#define _XE_GUC_KLV_THRESHOLDS_SET_H_
+
+#include "abi/guc_klvs_abi.h"
+#include "xe_guc_klv_helpers.h"
+#include "xe_guc_klv_thresholds_set_types.h"
+
+/**
+ * MAKE_GUC_KLV_VF_CFG_THRESHOLD_KEY - Prepare the name of the KLV key constant.
+ * @TAG: unique tag of the GuC threshold KLV key.
+ */
+#define MAKE_GUC_KLV_VF_CFG_THRESHOLD_KEY(TAG) \
+ MAKE_GUC_KLV_KEY(CONCATENATE(VF_CFG_THRESHOLD_, TAG))
+
+/**
+ * xe_guc_klv_threshold_key_to_index - Find index of the tracked GuC threshold.
+ * @key: GuC threshold KLV key.
+ *
+ * This translation is automatically generated using &MAKE_XE_GUC_KLV_THRESHOLDS_SET.
+ * Return: index of the GuC threshold KLV or -1 if not found.
+ */
+static inline int xe_guc_klv_threshold_key_to_index(u32 key)
+{
+ switch (key) {
+#define define_xe_guc_klv_threshold_key_to_index_case(TAG, ...) \
+ \
+ case MAKE_GUC_KLV_VF_CFG_THRESHOLD_KEY(TAG): \
+ return MAKE_XE_GUC_KLV_THRESHOLD_INDEX(TAG);
+
+ /* private: auto-generated case statements */
+ MAKE_XE_GUC_KLV_THRESHOLDS_SET(define_xe_guc_klv_threshold_key_to_index_case)
+ }
+ return -1;
+#undef define_xe_guc_klv_threshold_key_to_index_case
+}
+
+/**
+ * xe_guc_klv_threshold_index_to_key - Get tracked GuC threshold KLV key.
+ * @index: GuC threshold KLV index.
+ *
+ * This translation is automatically generated using &MAKE_XE_GUC_KLV_THRESHOLDS_SET.
+ * Return: key of the GuC threshold KLV or 0 on malformed index.
+ */
+static inline u32 xe_guc_klv_threshold_index_to_key(enum xe_guc_klv_threshold_index index)
+{
+ switch (index) {
+#define define_xe_guc_klv_threshold_index_to_key_case(TAG, ...) \
+ \
+ case MAKE_XE_GUC_KLV_THRESHOLD_INDEX(TAG): \
+ return MAKE_GUC_KLV_VF_CFG_THRESHOLD_KEY(TAG);
+
+ /* private: auto-generated case statements */
+ MAKE_XE_GUC_KLV_THRESHOLDS_SET(define_xe_guc_klv_threshold_index_to_key_case)
+ }
+ return 0; /* unreachable */
+#undef define_xe_guc_klv_threshold_index_to_key_case
+}
+
+#endif
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
new file mode 100644
index 000000000000..0a028c94756d
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_guc_klv_thresholds_set_types.h
@@ -0,0 +1,68 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2024 Intel Corporation
+ */
+
+#ifndef _XE_GUC_KLV_THRESHOLDS_SET_TYPES_H_
+#define _XE_GUC_KLV_THRESHOLDS_SET_TYPES_H_
+
+#include "xe_args.h"
+
+/**
+ * MAKE_XE_GUC_KLV_THRESHOLDS_SET - Generate various GuC thresholds definitions.
+ * @define: name of the inner macro to expand.
+ *
+ * The GuC firmware is able to monitor VF's adverse activity and will notify the
+ * PF driver once any threshold is exceeded.
+ *
+ * This super macro allows various conversions between the GuC adverse event
+ * threshold KLV definitions and the driver code without repeating similar code
+ * or risking missing some cases.
+ *
+ * For each GuC threshold definition, the inner macro &define will be provided
+ * with the &TAG, that corresponds to the GuC threshold KLV key name defined by
+ * ABI and the associated &NAME, that may be used in code or debugfs/sysfs::
+ *
+ * define(TAG, NAME)
+ */
+#define MAKE_XE_GUC_KLV_THRESHOLDS_SET(define) \
+ define(CAT_ERR, cat_error_count) \
+ define(ENGINE_RESET, engine_reset_count) \
+ define(PAGE_FAULT, page_fault_count) \
+ define(H2G_STORM, guc_time_us) \
+ define(IRQ_STORM, irq_time_us) \
+ define(DOORBELL_STORM, doorbell_time_us) \
+ /* end */
+
+/**
+ * XE_GUC_KLV_NUM_THRESHOLDS - Number of GuC thresholds KLVs.
+ *
+ * Calculated automatically using &MAKE_XE_GUC_KLV_THRESHOLDS_SET.
+ */
+#define XE_GUC_KLV_NUM_THRESHOLDS \
+ (CALL_ARGS(COUNT_ARGS, MAKE_XE_GUC_KLV_THRESHOLDS_SET(ARGS_SEP_COMMA)) - 1)
+
+/**
+ * MAKE_XE_GUC_KLV_THRESHOLD_INDEX - Create enumerator name.
+ * @TAG: unique TAG of the enum xe_guc_klv_threshold_index.
+ */
+#define MAKE_XE_GUC_KLV_THRESHOLD_INDEX(TAG) \
+ CONCATENATE(XE_GUC_KLV_THRESHOLD_INDEX_, TAG)
+
+/**
+ * enum xe_guc_klv_threshold_index - Index of the tracked GuC threshold.
+ *
+ * This enum is automatically generated using &MAKE_XE_GUC_KLV_THRESHOLDS_SET.
+ * All these generated enumerators will only be used by the also generated code.
+ */
+enum xe_guc_klv_threshold_index {
+#define define_xe_guc_klv_threshold_index_enum(TAG, ...) \
+ \
+ MAKE_XE_GUC_KLV_THRESHOLD_INDEX(TAG),
+
+ /* private: auto-generated enum definitions */
+ MAKE_XE_GUC_KLV_THRESHOLDS_SET(define_xe_guc_klv_threshold_index_enum)
+#undef define_xe_guc_klv_threshold_index_enum
+};
+
+#endif
diff --git a/drivers/gpu/drm/xe/xe_guc_pc.c b/drivers/gpu/drm/xe/xe_guc_pc.c
index 509649d0e65e..32e93a8127d4 100644
--- a/drivers/gpu/drm/xe/xe_guc_pc.c
+++ b/drivers/gpu/drm/xe/xe_guc_pc.c
@@ -8,21 +8,26 @@
#include <linux/delay.h>
#include <drm/drm_managed.h>
+#include <generated/xe_wa_oob.h>
-#include "abi/guc_actions_abi.h"
#include "abi/guc_actions_slpc_abi.h"
#include "regs/xe_gt_regs.h"
#include "regs/xe_regs.h"
#include "xe_bo.h"
#include "xe_device.h"
+#include "xe_force_wake.h"
#include "xe_gt.h"
#include "xe_gt_idle.h"
-#include "xe_gt_sysfs.h"
+#include "xe_gt_printk.h"
#include "xe_gt_types.h"
+#include "xe_guc.h"
#include "xe_guc_ct.h"
#include "xe_map.h"
#include "xe_mmio.h"
#include "xe_pcode.h"
+#include "xe_pm.h"
+#include "xe_sriov.h"
+#include "xe_wa.h"
#define MCHBAR_MIRROR_BASE_SNB 0x140000
@@ -40,6 +45,9 @@
#define GT_FREQUENCY_MULTIPLIER 50
#define GT_FREQUENCY_SCALER 3
+#define LNL_MERT_FREQ_CAP 800
+#define BMG_MERT_FREQ_CAP 2133
+
/**
* DOC: GuC Power Conservation (PC)
*
@@ -66,29 +74,27 @@
*
*/
-static struct xe_guc *
-pc_to_guc(struct xe_guc_pc *pc)
+static struct xe_guc *pc_to_guc(struct xe_guc_pc *pc)
{
return container_of(pc, struct xe_guc, pc);
}
-static struct xe_device *
-pc_to_xe(struct xe_guc_pc *pc)
+static struct xe_guc_ct *pc_to_ct(struct xe_guc_pc *pc)
{
- struct xe_guc *guc = pc_to_guc(pc);
- struct xe_gt *gt = container_of(guc, struct xe_gt, uc.guc);
+ return &pc_to_guc(pc)->ct;
+}
- return gt_to_xe(gt);
+static struct xe_gt *pc_to_gt(struct xe_guc_pc *pc)
+{
+ return guc_to_gt(pc_to_guc(pc));
}
-static struct xe_gt *
-pc_to_gt(struct xe_guc_pc *pc)
+static struct xe_device *pc_to_xe(struct xe_guc_pc *pc)
{
- return container_of(pc, struct xe_gt, uc.guc.pc);
+ return guc_to_xe(pc_to_guc(pc));
}
-static struct iosys_map *
-pc_to_maps(struct xe_guc_pc *pc)
+static struct iosys_map *pc_to_maps(struct xe_guc_pc *pc)
{
return &pc->bo->vmap;
}
@@ -129,32 +135,33 @@ static int wait_for_pc_state(struct xe_guc_pc *pc,
static int pc_action_reset(struct xe_guc_pc *pc)
{
- struct xe_guc_ct *ct = &pc_to_guc(pc)->ct;
- int ret;
+ struct xe_guc_ct *ct = pc_to_ct(pc);
u32 action[] = {
GUC_ACTION_HOST2GUC_PC_SLPC_REQUEST,
SLPC_EVENT(SLPC_EVENT_RESET, 2),
xe_bo_ggtt_addr(pc->bo),
0,
};
+ int ret;
ret = xe_guc_ct_send(ct, action, ARRAY_SIZE(action), 0, 0);
if (ret)
- drm_err(&pc_to_xe(pc)->drm, "GuC PC reset: %pe", ERR_PTR(ret));
+ xe_gt_err(pc_to_gt(pc), "GuC PC reset failed: %pe\n",
+ ERR_PTR(ret));
return ret;
}
static int pc_action_query_task_state(struct xe_guc_pc *pc)
{
- struct xe_guc_ct *ct = &pc_to_guc(pc)->ct;
- int ret;
+ struct xe_guc_ct *ct = pc_to_ct(pc);
u32 action[] = {
GUC_ACTION_HOST2GUC_PC_SLPC_REQUEST,
SLPC_EVENT(SLPC_EVENT_QUERY_TASK_STATE, 2),
xe_bo_ggtt_addr(pc->bo),
0,
};
+ int ret;
if (wait_for_pc_state(pc, SLPC_GLOBAL_STATE_RUNNING))
return -EAGAIN;
@@ -162,47 +169,68 @@ static int pc_action_query_task_state(struct xe_guc_pc *pc)
/* Blocking here to ensure the results are ready before reading them */
ret = xe_guc_ct_send_block(ct, action, ARRAY_SIZE(action));
if (ret)
- drm_err(&pc_to_xe(pc)->drm,
- "GuC PC query task state failed: %pe", ERR_PTR(ret));
+ xe_gt_err(pc_to_gt(pc), "GuC PC query task state failed: %pe\n",
+ ERR_PTR(ret));
return ret;
}
static int pc_action_set_param(struct xe_guc_pc *pc, u8 id, u32 value)
{
- struct xe_guc_ct *ct = &pc_to_guc(pc)->ct;
- int ret;
+ struct xe_guc_ct *ct = pc_to_ct(pc);
u32 action[] = {
GUC_ACTION_HOST2GUC_PC_SLPC_REQUEST,
SLPC_EVENT(SLPC_EVENT_PARAMETER_SET, 2),
id,
value,
};
+ int ret;
if (wait_for_pc_state(pc, SLPC_GLOBAL_STATE_RUNNING))
return -EAGAIN;
ret = xe_guc_ct_send(ct, action, ARRAY_SIZE(action), 0, 0);
if (ret)
- drm_err(&pc_to_xe(pc)->drm, "GuC PC set param failed: %pe",
- ERR_PTR(ret));
+ xe_gt_err(pc_to_gt(pc), "GuC PC set param[%u]=%u failed: %pe\n",
+ id, value, ERR_PTR(ret));
return ret;
}
-static int pc_action_setup_gucrc(struct xe_guc_pc *pc, u32 mode)
+static int pc_action_unset_param(struct xe_guc_pc *pc, u8 id)
{
+ u32 action[] = {
+ GUC_ACTION_HOST2GUC_PC_SLPC_REQUEST,
+ SLPC_EVENT(SLPC_EVENT_PARAMETER_UNSET, 1),
+ id,
+ };
struct xe_guc_ct *ct = &pc_to_guc(pc)->ct;
+ int ret;
+
+ if (wait_for_pc_state(pc, SLPC_GLOBAL_STATE_RUNNING))
+ return -EAGAIN;
+
+ ret = xe_guc_ct_send(ct, action, ARRAY_SIZE(action), 0, 0);
+ if (ret)
+ xe_gt_err(pc_to_gt(pc), "GuC PC unset param failed: %pe",
+ ERR_PTR(ret));
+
+ return ret;
+}
+
+static int pc_action_setup_gucrc(struct xe_guc_pc *pc, u32 mode)
+{
+ struct xe_guc_ct *ct = pc_to_ct(pc);
u32 action[] = {
- XE_GUC_ACTION_SETUP_PC_GUCRC,
+ GUC_ACTION_HOST2GUC_SETUP_PC_GUCRC,
mode,
};
int ret;
ret = xe_guc_ct_send(ct, action, ARRAY_SIZE(action), 0, 0);
if (ret)
- drm_err(&pc_to_xe(pc)->drm, "GuC RC enable failed: %pe",
- ERR_PTR(ret));
+ xe_gt_err(pc_to_gt(pc), "GuC RC enable mode=%u failed: %pe\n",
+ mode, ERR_PTR(ret));
return ret;
}
@@ -673,18 +701,43 @@ static void pc_init_fused_rp_values(struct xe_guc_pc *pc)
tgl_init_fused_rp_values(pc);
}
+static u32 pc_max_freq_cap(struct xe_guc_pc *pc)
+{
+ struct xe_gt *gt = pc_to_gt(pc);
+
+ if (XE_WA(gt, 22019338487)) {
+ if (xe_gt_is_media_type(gt))
+ return min(LNL_MERT_FREQ_CAP, pc->rp0_freq);
+ else
+ return min(BMG_MERT_FREQ_CAP, pc->rp0_freq);
+ } else {
+ return pc->rp0_freq;
+ }
+}
+
/**
- * xe_guc_pc_init_early - Initialize RPx values and request a higher GT
+ * xe_guc_pc_raise_unslice - Initialize RPx values and request a higher GT
* frequency to allow faster GuC load times
* @pc: Xe_GuC_PC instance
*/
+void xe_guc_pc_raise_unslice(struct xe_guc_pc *pc)
+{
+ struct xe_gt *gt = pc_to_gt(pc);
+
+ xe_force_wake_assert_held(gt_to_fw(gt), XE_FW_GT);
+ pc_set_cur_freq(pc, pc_max_freq_cap(pc));
+}
+
+/**
+ * xe_guc_pc_init_early - Initialize RPx values
+ * @pc: Xe_GuC_PC instance
+ */
void xe_guc_pc_init_early(struct xe_guc_pc *pc)
{
struct xe_gt *gt = pc_to_gt(pc);
xe_force_wake_assert_held(gt_to_fw(gt), XE_FW_GT);
pc_init_fused_rp_values(pc);
- pc_set_cur_freq(pc, pc->rp0_freq);
}
static int pc_adjust_freq_bounds(struct xe_guc_pc *pc)
@@ -740,6 +793,56 @@ static int pc_adjust_requested_freq(struct xe_guc_pc *pc)
return ret;
}
+static int pc_set_mert_freq_cap(struct xe_guc_pc *pc)
+{
+ int ret = 0;
+
+ if (XE_WA(pc_to_gt(pc), 22019338487)) {
+ /*
+ * Get updated min/max and stash them.
+ */
+ ret = xe_guc_pc_get_min_freq(pc, &pc->stashed_min_freq);
+ if (!ret)
+ ret = xe_guc_pc_get_max_freq(pc, &pc->stashed_max_freq);
+ if (ret)
+ return ret;
+
+ /*
+ * Ensure min and max are bound by MERT_FREQ_CAP until driver loads.
+ */
+ mutex_lock(&pc->freq_lock);
+ ret = pc_set_min_freq(pc, min(pc->rpe_freq, pc_max_freq_cap(pc)));
+ if (!ret)
+ ret = pc_set_max_freq(pc, min(pc->rp0_freq, pc_max_freq_cap(pc)));
+ mutex_unlock(&pc->freq_lock);
+ }
+
+ return ret;
+}
+
+/**
+ * xe_guc_pc_restore_stashed_freq - Set min/max back to stashed values
+ * @pc: The GuC PC
+ *
+ * Returns: 0 on success,
+ * error code on failure
+ */
+int xe_guc_pc_restore_stashed_freq(struct xe_guc_pc *pc)
+{
+ int ret = 0;
+
+ if (IS_SRIOV_VF(pc_to_xe(pc)) || pc_to_xe(pc)->info.skip_guc_pc)
+ return 0;
+
+ mutex_lock(&pc->freq_lock);
+ ret = pc_set_max_freq(pc, pc->stashed_max_freq);
+ if (!ret)
+ ret = pc_set_min_freq(pc, pc->stashed_min_freq);
+ mutex_unlock(&pc->freq_lock);
+
+ return ret;
+}
+
/**
* xe_guc_pc_gucrc_disable - Disable GuC RC
* @pc: Xe_GuC_PC instance
@@ -757,7 +860,7 @@ int xe_guc_pc_gucrc_disable(struct xe_guc_pc *pc)
if (xe->info.skip_guc_pc)
return 0;
- ret = pc_action_setup_gucrc(pc, XE_GUCRC_HOST_CONTROL);
+ ret = pc_action_setup_gucrc(pc, GUCRC_HOST_CONTROL);
if (ret)
return ret;
@@ -772,6 +875,41 @@ int xe_guc_pc_gucrc_disable(struct xe_guc_pc *pc)
return 0;
}
+/**
+ * xe_guc_pc_override_gucrc_mode - override GUCRC mode
+ * @pc: Xe_GuC_PC instance
+ * @mode: new value of the mode.
+ *
+ * Return: 0 on success, negative error code on error
+ */
+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;
+}
+
+/**
+ * xe_guc_pc_unset_gucrc_mode - unset GUCRC mode override
+ * @pc: Xe_GuC_PC instance
+ *
+ * Return: 0 on success, negative error code on error
+ */
+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;
+}
+
static void pc_init_pcode_freq(struct xe_guc_pc *pc)
{
u32 min = DIV_ROUND_CLOSEST(pc->rpn_freq, GT_FREQUENCY_MULTIPLIER);
@@ -845,7 +983,7 @@ int xe_guc_pc_start(struct xe_guc_pc *pc)
goto out;
if (wait_for_pc_state(pc, SLPC_GLOBAL_STATE_RUNNING)) {
- drm_err(&pc_to_xe(pc)->drm, "GuC PC Start failed\n");
+ xe_gt_err(gt, "GuC PC Start failed\n");
ret = -EIO;
goto out;
}
@@ -854,13 +992,17 @@ int xe_guc_pc_start(struct xe_guc_pc *pc)
if (ret)
goto out;
+ ret = pc_set_mert_freq_cap(pc);
+ if (ret)
+ goto out;
+
if (xe->info.platform == XE_PVC) {
xe_guc_pc_gucrc_disable(pc);
ret = 0;
goto out;
}
- ret = pc_action_setup_gucrc(pc, XE_GUCRC_FIRMWARE_CONTROL);
+ ret = pc_action_setup_gucrc(pc, GUCRC_FIRMWARE_CONTROL);
out:
XE_WARN_ON(xe_force_wake_put(gt_to_fw(gt), XE_FORCEWAKE_ALL));
@@ -888,23 +1030,24 @@ int xe_guc_pc_stop(struct xe_guc_pc *pc)
}
/**
- * xe_guc_pc_fini - Finalize GuC's Power Conservation component
- * @drm: DRM device
+ * xe_guc_pc_fini_hw - Finalize GuC's Power Conservation component
* @arg: opaque pointer that should point to Xe_GuC_PC instance
*/
-static void xe_guc_pc_fini(struct drm_device *drm, void *arg)
+static void xe_guc_pc_fini_hw(void *arg)
{
struct xe_guc_pc *pc = arg;
struct xe_device *xe = pc_to_xe(pc);
- if (xe->info.skip_guc_pc) {
- xe_gt_idle_disable_c6(pc_to_gt(pc));
+ if (xe_device_wedged(xe))
return;
- }
XE_WARN_ON(xe_force_wake_get(gt_to_fw(pc_to_gt(pc)), XE_FORCEWAKE_ALL));
XE_WARN_ON(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), pc->rpe_freq));
+
xe_force_wake_put(gt_to_fw(pc_to_gt(pc)), XE_FORCEWAKE_ALL);
}
@@ -937,5 +1080,5 @@ int xe_guc_pc_init(struct xe_guc_pc *pc)
pc->bo = bo;
- return drmm_add_action_or_reset(&xe->drm, xe_guc_pc_fini, pc);
+ return devm_add_action_or_reset(xe->drm.dev, xe_guc_pc_fini_hw, pc);
}
diff --git a/drivers/gpu/drm/xe/xe_guc_pc.h b/drivers/gpu/drm/xe/xe_guc_pc.h
index d3680d89490e..efda432fadfc 100644
--- a/drivers/gpu/drm/xe/xe_guc_pc.h
+++ b/drivers/gpu/drm/xe/xe_guc_pc.h
@@ -6,12 +6,17 @@
#ifndef _XE_GUC_PC_H_
#define _XE_GUC_PC_H_
-#include "xe_guc_pc_types.h"
+#include <linux/types.h>
+
+struct xe_guc_pc;
+enum slpc_gucrc_mode;
int xe_guc_pc_init(struct xe_guc_pc *pc);
int xe_guc_pc_start(struct xe_guc_pc *pc);
int xe_guc_pc_stop(struct xe_guc_pc *pc);
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 xe_guc_pc_unset_gucrc_mode(struct xe_guc_pc *pc);
u32 xe_guc_pc_get_act_freq(struct xe_guc_pc *pc);
int xe_guc_pc_get_cur_freq(struct xe_guc_pc *pc, u32 *freq);
@@ -27,4 +32,7 @@ enum xe_gt_idle_state xe_guc_pc_c_status(struct xe_guc_pc *pc);
u64 xe_guc_pc_rc6_residency(struct xe_guc_pc *pc);
u64 xe_guc_pc_mc6_residency(struct xe_guc_pc *pc);
void xe_guc_pc_init_early(struct xe_guc_pc *pc);
+int xe_guc_pc_restore_stashed_freq(struct xe_guc_pc *pc);
+void xe_guc_pc_raise_unslice(struct xe_guc_pc *pc);
+
#endif /* _XE_GUC_PC_H_ */
diff --git a/drivers/gpu/drm/xe/xe_guc_pc_types.h b/drivers/gpu/drm/xe/xe_guc_pc_types.h
index 2afd0dbc3542..13810be015db 100644
--- a/drivers/gpu/drm/xe/xe_guc_pc_types.h
+++ b/drivers/gpu/drm/xe/xe_guc_pc_types.h
@@ -25,6 +25,10 @@ struct xe_guc_pc {
u32 user_requested_min;
/** @user_requested_max: Stash the maximum requested freq by user */
u32 user_requested_max;
+ /** @stashed_min_freq: Stash the current minimum freq */
+ u32 stashed_min_freq;
+ /** @stashed_max_freq: Stash the current maximum freq */
+ u32 stashed_max_freq;
/** @freq_lock: Let's protect the frequencies */
struct mutex freq_lock;
/** @freq_ready: Only handle freq changes, if they are really ready */
diff --git a/drivers/gpu/drm/xe/xe_guc_relay.c b/drivers/gpu/drm/xe/xe_guc_relay.c
index c0a2d8d5d3b3..ade6162dc259 100644
--- a/drivers/gpu/drm/xe/xe_guc_relay.c
+++ b/drivers/gpu/drm/xe/xe_guc_relay.c
@@ -19,6 +19,7 @@
#include "xe_device.h"
#include "xe_gt.h"
#include "xe_gt_sriov_printk.h"
+#include "xe_gt_sriov_pf_service.h"
#include "xe_guc.h"
#include "xe_guc_ct.h"
#include "xe_guc_hxg_helpers.h"
@@ -664,6 +665,7 @@ static int relay_testloop_action_handler(struct xe_guc_relay *relay, u32 origin,
static int relay_action_handler(struct xe_guc_relay *relay, u32 origin,
const u32 *msg, u32 len, u32 *response, u32 size)
{
+ struct xe_gt *gt = relay_to_gt(relay);
u32 type;
int ret;
@@ -674,8 +676,10 @@ static int relay_action_handler(struct xe_guc_relay *relay, u32 origin,
type = FIELD_GET(GUC_HXG_MSG_0_TYPE, msg[0]);
- /* XXX: PF services will be added later */
- ret = -EOPNOTSUPP;
+ if (IS_SRIOV_PF(relay_to_xe(relay)))
+ ret = xe_gt_sriov_pf_service_process_request(gt, origin, msg, len, response, size);
+ else
+ ret = -EOPNOTSUPP;
if (type == GUC_HXG_TYPE_EVENT)
relay_assert(relay, ret <= 0);
@@ -757,7 +761,14 @@ static void relay_process_incoming_action(struct xe_guc_relay *relay)
static bool relay_needs_worker(struct xe_guc_relay *relay)
{
- return !list_empty(&relay->incoming_actions);
+ bool is_empty;
+
+ spin_lock(&relay->lock);
+ is_empty = list_empty(&relay->incoming_actions);
+ spin_unlock(&relay->lock);
+
+ return !is_empty;
+
}
static void relay_kick_worker(struct xe_guc_relay *relay)
diff --git a/drivers/gpu/drm/xe/xe_guc_submit.c b/drivers/gpu/drm/xe/xe_guc_submit.c
index c7d38469fb46..8d7e7f4bbff7 100644
--- a/drivers/gpu/drm/xe/xe_guc_submit.c
+++ b/drivers/gpu/drm/xe/xe_guc_submit.c
@@ -10,6 +10,7 @@
#include <linux/circ_buf.h>
#include <linux/delay.h>
#include <linux/dma-fence-array.h>
+#include <linux/math64.h>
#include <drm/drm_managed.h>
@@ -23,6 +24,7 @@
#include "xe_force_wake.h"
#include "xe_gpu_scheduler.h"
#include "xe_gt.h"
+#include "xe_gt_clock.h"
#include "xe_gt_printk.h"
#include "xe_guc.h"
#include "xe_guc_ct.h"
@@ -35,6 +37,7 @@
#include "xe_macros.h"
#include "xe_map.h"
#include "xe_mocs.h"
+#include "xe_pm.h"
#include "xe_ring_ops_types.h"
#include "xe_sched_job.h"
#include "xe_trace.h"
@@ -52,13 +55,17 @@ exec_queue_to_guc(struct xe_exec_queue *q)
* engine done being processed).
*/
#define EXEC_QUEUE_STATE_REGISTERED (1 << 0)
-#define ENGINE_STATE_ENABLED (1 << 1)
-#define EXEC_QUEUE_STATE_PENDING_ENABLE (1 << 2)
+#define EXEC_QUEUE_STATE_ENABLED (1 << 1)
+#define EXEC_QUEUE_STATE_PENDING_ENABLE (1 << 2)
#define EXEC_QUEUE_STATE_PENDING_DISABLE (1 << 3)
#define EXEC_QUEUE_STATE_DESTROYED (1 << 4)
-#define ENGINE_STATE_SUSPENDED (1 << 5)
-#define EXEC_QUEUE_STATE_RESET (1 << 6)
-#define ENGINE_STATE_KILLED (1 << 7)
+#define EXEC_QUEUE_STATE_SUSPENDED (1 << 5)
+#define EXEC_QUEUE_STATE_RESET (1 << 6)
+#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)
static bool exec_queue_registered(struct xe_exec_queue *q)
{
@@ -77,17 +84,17 @@ static void clear_exec_queue_registered(struct xe_exec_queue *q)
static bool exec_queue_enabled(struct xe_exec_queue *q)
{
- return atomic_read(&q->guc->state) & ENGINE_STATE_ENABLED;
+ return atomic_read(&q->guc->state) & EXEC_QUEUE_STATE_ENABLED;
}
static void set_exec_queue_enabled(struct xe_exec_queue *q)
{
- atomic_or(ENGINE_STATE_ENABLED, &q->guc->state);
+ atomic_or(EXEC_QUEUE_STATE_ENABLED, &q->guc->state);
}
static void clear_exec_queue_enabled(struct xe_exec_queue *q)
{
- atomic_and(~ENGINE_STATE_ENABLED, &q->guc->state);
+ atomic_and(~EXEC_QUEUE_STATE_ENABLED, &q->guc->state);
}
static bool exec_queue_pending_enable(struct xe_exec_queue *q)
@@ -132,27 +139,27 @@ static void set_exec_queue_destroyed(struct xe_exec_queue *q)
static bool exec_queue_banned(struct xe_exec_queue *q)
{
- return (q->flags & EXEC_QUEUE_FLAG_BANNED);
+ return atomic_read(&q->guc->state) & EXEC_QUEUE_STATE_BANNED;
}
static void set_exec_queue_banned(struct xe_exec_queue *q)
{
- q->flags |= EXEC_QUEUE_FLAG_BANNED;
+ atomic_or(EXEC_QUEUE_STATE_BANNED, &q->guc->state);
}
static bool exec_queue_suspended(struct xe_exec_queue *q)
{
- return atomic_read(&q->guc->state) & ENGINE_STATE_SUSPENDED;
+ return atomic_read(&q->guc->state) & EXEC_QUEUE_STATE_SUSPENDED;
}
static void set_exec_queue_suspended(struct xe_exec_queue *q)
{
- atomic_or(ENGINE_STATE_SUSPENDED, &q->guc->state);
+ atomic_or(EXEC_QUEUE_STATE_SUSPENDED, &q->guc->state);
}
static void clear_exec_queue_suspended(struct xe_exec_queue *q)
{
- atomic_and(~ENGINE_STATE_SUSPENDED, &q->guc->state);
+ atomic_and(~EXEC_QUEUE_STATE_SUSPENDED, &q->guc->state);
}
static bool exec_queue_reset(struct xe_exec_queue *q)
@@ -167,17 +174,54 @@ static void set_exec_queue_reset(struct xe_exec_queue *q)
static bool exec_queue_killed(struct xe_exec_queue *q)
{
- return atomic_read(&q->guc->state) & ENGINE_STATE_KILLED;
+ return atomic_read(&q->guc->state) & EXEC_QUEUE_STATE_KILLED;
}
static void set_exec_queue_killed(struct xe_exec_queue *q)
{
- atomic_or(ENGINE_STATE_KILLED, &q->guc->state);
+ atomic_or(EXEC_QUEUE_STATE_KILLED, &q->guc->state);
}
-static bool exec_queue_killed_or_banned(struct xe_exec_queue *q)
+static bool exec_queue_wedged(struct xe_exec_queue *q)
{
- return exec_queue_killed(q) || exec_queue_banned(q);
+ return atomic_read(&q->guc->state) & EXEC_QUEUE_STATE_WEDGED;
+}
+
+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 bool exec_queue_killed_or_banned_or_wedged(struct xe_exec_queue *q)
+{
+ return (atomic_read(&q->guc->state) &
+ (EXEC_QUEUE_STATE_WEDGED | EXEC_QUEUE_STATE_KILLED |
+ EXEC_QUEUE_STATE_BANNED));
}
#ifdef CONFIG_PROVE_LOCKING
@@ -240,6 +284,17 @@ static void guc_submit_fini(struct drm_device *drm, void *arg)
free_submit_wq(guc);
}
+static void guc_submit_wedged_fini(struct drm_device *drm, void *arg)
+{
+ struct xe_guc *guc = arg;
+ struct xe_exec_queue *q;
+ unsigned long index;
+
+ xa_for_each(&guc->submission_state.exec_queue_lookup, index, q)
+ if (exec_queue_wedged(q))
+ xe_exec_queue_put(q);
+}
+
static const struct xe_exec_queue_ops guc_exec_queue_ops;
static void primelockdep(struct xe_guc *guc)
@@ -250,13 +305,25 @@ static void primelockdep(struct xe_guc *guc)
fs_reclaim_acquire(GFP_KERNEL);
mutex_lock(&guc->submission_state.lock);
- might_lock(&guc->submission_state.suspend.lock);
mutex_unlock(&guc->submission_state.lock);
fs_reclaim_release(GFP_KERNEL);
}
-int xe_guc_submit_init(struct xe_guc *guc)
+/**
+ * xe_guc_submit_init() - Initialize GuC submission.
+ * @guc: the &xe_guc to initialize
+ * @num_ids: number of GuC context IDs to use
+ *
+ * The bare-metal or PF driver can pass ~0 as &num_ids to indicate that all
+ * GuC context IDs supported by the GuC firmware should be used for submission.
+ *
+ * Only VF drivers will have to provide explicit number of GuC context IDs
+ * that they can use for submission.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_guc_submit_init(struct xe_guc *guc, unsigned int num_ids)
{
struct xe_device *xe = guc_to_xe(guc);
struct xe_gt *gt = guc_to_gt(guc);
@@ -266,7 +333,7 @@ int xe_guc_submit_init(struct xe_guc *guc)
if (err)
return err;
- err = xe_guc_id_mgr_init(&guc->submission_state.idm, ~0);
+ err = xe_guc_id_mgr_init(&guc->submission_state.idm, num_ids);
if (err)
return err;
@@ -278,9 +345,6 @@ int xe_guc_submit_init(struct xe_guc *guc)
xa_init(&guc->submission_state.exec_queue_lookup);
- spin_lock_init(&guc->submission_state.suspend.lock);
- guc->submission_state.suspend.context = dma_fence_context_alloc(1);
-
primelockdep(guc);
return drmm_add_action_or_reset(&xe->drm, guc_submit_fini, guc);
@@ -430,9 +494,9 @@ 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_)
-static void __register_mlrc_engine(struct xe_guc *guc,
- struct xe_exec_queue *q,
- struct guc_ctxt_registration_info *info)
+static void __register_mlrc_exec_queue(struct xe_guc *guc,
+ struct xe_exec_queue *q,
+ struct guc_ctxt_registration_info *info)
{
#define MAX_MLRC_REG_SIZE (13 + XE_HW_ENGINE_MAX_INSTANCE * 2)
struct xe_device *xe = guc_to_xe(guc);
@@ -457,7 +521,7 @@ static void __register_mlrc_engine(struct xe_guc *guc,
action[len++] = info->hwlrca_hi;
for (i = 1; i < q->width; ++i) {
- struct xe_lrc *lrc = q->lrc + i;
+ struct xe_lrc *lrc = q->lrc[i];
action[len++] = lower_32_bits(xe_lrc_descriptor(lrc));
action[len++] = upper_32_bits(xe_lrc_descriptor(lrc));
@@ -469,8 +533,8 @@ static void __register_mlrc_engine(struct xe_guc *guc,
xe_guc_ct_send(&guc->ct, action, len, 0, 0);
}
-static void __register_engine(struct xe_guc *guc,
- struct guc_ctxt_registration_info *info)
+static void __register_exec_queue(struct xe_guc *guc,
+ struct guc_ctxt_registration_info *info)
{
u32 action[] = {
XE_GUC_ACTION_REGISTER_CONTEXT,
@@ -490,11 +554,11 @@ static void __register_engine(struct xe_guc *guc,
xe_guc_ct_send(&guc->ct, action, ARRAY_SIZE(action), 0, 0);
}
-static void register_engine(struct xe_exec_queue *q)
+static void register_exec_queue(struct xe_exec_queue *q)
{
struct xe_guc *guc = exec_queue_to_guc(q);
struct xe_device *xe = guc_to_xe(guc);
- struct xe_lrc *lrc = q->lrc;
+ struct xe_lrc *lrc = q->lrc[0];
struct guc_ctxt_registration_info info;
xe_assert(xe, !exec_queue_registered(q));
@@ -538,9 +602,9 @@ static void register_engine(struct xe_exec_queue *q)
set_exec_queue_registered(q);
trace_xe_exec_queue_register(q);
if (xe_exec_queue_is_parallel(q))
- __register_mlrc_engine(guc, q, &info);
+ __register_mlrc_exec_queue(guc, q, &info);
else
- __register_engine(guc, &info);
+ __register_exec_queue(guc, &info);
init_policies(guc, q);
}
@@ -553,7 +617,7 @@ 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);
+ struct iosys_map map = xe_lrc_parallel_map(q->lrc[0]);
unsigned int sleep_period_ms = 1;
#define AVAILABLE_SPACE \
@@ -581,7 +645,7 @@ static int wq_noop_append(struct xe_exec_queue *q)
{
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);
+ struct iosys_map map = xe_lrc_parallel_map(q->lrc[0]);
u32 len_dw = wq_space_until_wrap(q) / sizeof(u32) - 1;
if (wq_wait_for_space(q, wq_space_until_wrap(q)))
@@ -601,7 +665,7 @@ static void wq_item_append(struct xe_exec_queue *q)
{
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);
+ struct iosys_map map = xe_lrc_parallel_map(q->lrc[0]);
#define WQ_HEADER_SIZE 4 /* Includes 1 LRC address too */
u32 wqi[XE_HW_ENGINE_MAX_INSTANCE + (WQ_HEADER_SIZE - 1)];
u32 wqi_size = (q->width + (WQ_HEADER_SIZE - 1)) * sizeof(u32);
@@ -617,12 +681,12 @@ static void wq_item_append(struct xe_exec_queue *q)
wqi[i++] = FIELD_PREP(WQ_TYPE_MASK, WQ_TYPE_MULTI_LRC) |
FIELD_PREP(WQ_LEN_MASK, len_dw);
- wqi[i++] = xe_lrc_descriptor(q->lrc);
+ wqi[i++] = xe_lrc_descriptor(q->lrc[0]);
wqi[i++] = FIELD_PREP(WQ_GUC_ID_MASK, q->guc->id) |
- FIELD_PREP(WQ_RING_TAIL_MASK, q->lrc->ring.tail / sizeof(u64));
+ FIELD_PREP(WQ_RING_TAIL_MASK, q->lrc[0]->ring.tail / sizeof(u64));
wqi[i++] = 0;
for (j = 1; j < q->width; ++j) {
- struct xe_lrc *lrc = q->lrc + j;
+ struct xe_lrc *lrc = q->lrc[j];
wqi[i++] = lrc->ring.tail / sizeof(u64);
}
@@ -637,7 +701,7 @@ static void wq_item_append(struct xe_exec_queue *q)
xe_device_wmb(xe);
- map = xe_lrc_parallel_map(q->lrc);
+ map = xe_lrc_parallel_map(q->lrc[0]);
parallel_write(xe, map, wq_desc.tail, q->guc->wqi_tail);
}
@@ -646,7 +710,7 @@ static void submit_exec_queue(struct xe_exec_queue *q)
{
struct xe_guc *guc = exec_queue_to_guc(q);
struct xe_device *xe = guc_to_xe(guc);
- struct xe_lrc *lrc = q->lrc;
+ struct xe_lrc *lrc = q->lrc[0];
u32 action[3];
u32 g2h_len = 0;
u32 num_g2h = 0;
@@ -658,7 +722,7 @@ static void submit_exec_queue(struct xe_exec_queue *q)
if (xe_exec_queue_is_parallel(q))
wq_item_append(q);
else
- xe_lrc_write_ctx_reg(lrc, CTX_RING_TAIL, lrc->ring.tail);
+ xe_lrc_set_ring_tail(lrc, lrc->ring.tail);
if (exec_queue_suspended(q) && !xe_exec_queue_is_parallel(q))
return;
@@ -708,9 +772,9 @@ guc_exec_queue_run_job(struct drm_sched_job *drm_job)
trace_xe_sched_job_run(job);
- if (!exec_queue_killed_or_banned(q) && !xe_sched_job_is_error(job)) {
+ if (!exec_queue_killed_or_banned_or_wedged(q) && !xe_sched_job_is_error(job)) {
if (!exec_queue_registered(q))
- register_engine(q);
+ register_exec_queue(q);
if (!lr) /* LR jobs are emitted in the exec IOCTL */
q->ring_ops->emit_job(job);
submit_exec_queue(q);
@@ -730,6 +794,8 @@ static void guc_exec_queue_free_job(struct drm_sched_job *drm_job)
{
struct xe_sched_job *job = to_xe_sched_job(drm_job);
+ xe_exec_queue_update_run_ticks(job->q);
+
trace_xe_sched_job_free(job);
xe_sched_job_put(job);
}
@@ -781,55 +847,6 @@ static void disable_scheduling_deregister(struct xe_guc *guc,
G2H_LEN_DW_DEREGISTER_CONTEXT, 2);
}
-static void guc_exec_queue_print(struct xe_exec_queue *q, struct drm_printer *p);
-
-#if IS_ENABLED(CONFIG_DRM_XE_SIMPLE_ERROR_CAPTURE)
-static void simple_error_capture(struct xe_exec_queue *q)
-{
- struct xe_guc *guc = exec_queue_to_guc(q);
- struct xe_device *xe = guc_to_xe(guc);
- struct drm_printer p = drm_err_printer(&xe->drm, NULL);
- struct xe_hw_engine *hwe;
- enum xe_hw_engine_id id;
- u32 adj_logical_mask = q->logical_mask;
- u32 width_mask = (0x1 << q->width) - 1;
- int i;
- bool cookie;
-
- if (q->vm && !q->vm->error_capture.capture_once) {
- q->vm->error_capture.capture_once = true;
- cookie = dma_fence_begin_signalling();
- for (i = 0; q->width > 1 && i < XE_HW_ENGINE_MAX_INSTANCE;) {
- if (adj_logical_mask & BIT(i)) {
- adj_logical_mask |= width_mask << i;
- i += q->width;
- } else {
- ++i;
- }
- }
-
- if (xe_force_wake_get(gt_to_fw(guc_to_gt(guc)), XE_FORCEWAKE_ALL))
- xe_gt_info(guc_to_gt(guc),
- "failed to get forcewake for error capture");
- xe_guc_ct_print(&guc->ct, &p, true);
- guc_exec_queue_print(q, &p);
- for_each_hw_engine(hwe, guc_to_gt(guc), id) {
- if (hwe->class != q->hwe->class ||
- !(BIT(hwe->logical_instance) & adj_logical_mask))
- continue;
- xe_hw_engine_print(hwe, &p);
- }
- xe_analyze_vm(&p, q->vm, q->gt->info.id);
- xe_force_wake_put(gt_to_fw(guc_to_gt(guc)), XE_FORCEWAKE_ALL);
- dma_fence_end_signalling(cookie);
- }
-}
-#else
-static void simple_error_capture(struct xe_exec_queue *q)
-{
-}
-#endif
-
static void xe_guc_exec_queue_trigger_cleanup(struct xe_exec_queue *q)
{
struct xe_guc *guc = exec_queue_to_guc(q);
@@ -844,6 +861,51 @@ static void xe_guc_exec_queue_trigger_cleanup(struct xe_exec_queue *q)
xe_sched_tdr_queue_imm(&q->guc->sched);
}
+/**
+ * xe_guc_submit_wedge() - Wedge GuC submission
+ * @guc: the GuC object
+ *
+ * Save exec queue's registered with GuC state by taking a ref to each queue.
+ * Register a DRMM handler to drop refs upon driver unload.
+ */
+void xe_guc_submit_wedge(struct xe_guc *guc)
+{
+ struct xe_device *xe = guc_to_xe(guc);
+ struct xe_exec_queue *q;
+ unsigned long index;
+ int err;
+
+ xe_gt_assert(guc_to_gt(guc), guc_to_xe(guc)->wedged.mode);
+
+ err = drmm_add_action_or_reset(&guc_to_xe(guc)->drm,
+ guc_submit_wedged_fini, guc);
+ if (err) {
+ drm_err(&xe->drm, "Failed to register xe_guc_submit clean-up on wedged.mode=2. Although device is wedged.\n");
+ return;
+ }
+
+ mutex_lock(&guc->submission_state.lock);
+ xa_for_each(&guc->submission_state.exec_queue_lookup, index, q)
+ if (xe_exec_queue_get_unless_zero(q))
+ set_exec_queue_wedged(q);
+ mutex_unlock(&guc->submission_state.lock);
+}
+
+static bool guc_submit_hint_wedged(struct xe_guc *guc)
+{
+ struct xe_device *xe = guc_to_xe(guc);
+
+ if (xe->wedged.mode != 2)
+ return false;
+
+ if (xe_device_wedged(xe))
+ return true;
+
+ xe_device_declare_wedged(xe);
+
+ return true;
+}
+
static void xe_guc_exec_queue_lr_cleanup(struct work_struct *w)
{
struct xe_guc_exec_queue *ge =
@@ -852,10 +914,13 @@ static void xe_guc_exec_queue_lr_cleanup(struct work_struct *w)
struct xe_guc *guc = exec_queue_to_guc(q);
struct xe_device *xe = guc_to_xe(guc);
struct xe_gpu_scheduler *sched = &ge->sched;
+ bool wedged;
xe_assert(xe, xe_exec_queue_is_lr(q));
trace_xe_exec_queue_lr_cleanup(q);
+ wedged = guc_submit_hint_wedged(exec_queue_to_guc(q));
+
/* Kill the run_job / process_msg entry points */
xe_sched_submission_stop(sched);
@@ -870,7 +935,7 @@ static void xe_guc_exec_queue_lr_cleanup(struct work_struct *w)
* xe_guc_deregister_done_handler() which treats it as an unexpected
* state.
*/
- if (exec_queue_registered(q) && !exec_queue_destroyed(q)) {
+ if (!wedged && exec_queue_registered(q) && !exec_queue_destroyed(q)) {
struct xe_guc *guc = exec_queue_to_guc(q);
int ret;
@@ -895,6 +960,109 @@ static void xe_guc_exec_queue_lr_cleanup(struct work_struct *w)
xe_sched_submission_start(sched);
}
+#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)
+{
+ struct xe_gt *gt = guc_to_gt(exec_queue_to_guc(q));
+ u32 ctx_timestamp = xe_lrc_ctx_timestamp(q->lrc[0]);
+ u32 ctx_job_timestamp = xe_lrc_ctx_job_timestamp(q->lrc[0]);
+ u32 timeout_ms = q->sched_props.job_timeout_ms;
+ u32 diff;
+ u64 running_time_ms;
+
+ /*
+ * Counter wraps at ~223s at the usual 19.2MHz, be paranoid catch
+ * possible overflows with a high timeout.
+ */
+ xe_gt_assert(gt, timeout_ms < 100 * MSEC_PER_SEC);
+
+ if (ctx_timestamp < ctx_job_timestamp)
+ diff = ctx_timestamp + U32_MAX - ctx_job_timestamp;
+ else
+ diff = ctx_timestamp - ctx_job_timestamp;
+
+ /*
+ * Ensure timeout is within 5% to account for an GuC scheduling latency
+ */
+ running_time_ms =
+ ADJUST_FIVE_PERCENT(xe_gt_clock_interval_to_ms(gt, diff));
+
+ xe_gt_dbg(gt,
+ "Check job timeout: seqno=%u, lrc_seqno=%u, guc_id=%d, running_time_ms=%llu, timeout_ms=%u, diff=0x%08x",
+ xe_sched_job_seqno(job), xe_sched_job_lrc_seqno(job),
+ q->guc->id, running_time_ms, timeout_ms, diff);
+
+ return running_time_ms >= timeout_ms;
+}
+
+static void enable_scheduling(struct xe_exec_queue *q)
+{
+ MAKE_SCHED_CONTEXT_ACTION(q, ENABLE);
+ struct xe_guc *guc = exec_queue_to_guc(q);
+ int ret;
+
+ 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));
+ xe_gt_assert(guc_to_gt(guc), !exec_queue_pending_enable(q));
+
+ set_exec_queue_pending_enable(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);
+
+ ret = wait_event_timeout(guc->ct.wq,
+ !exec_queue_pending_enable(q) ||
+ guc_read_stopped(guc), HZ * 5);
+ if (!ret || guc_read_stopped(guc)) {
+ xe_gt_warn(guc_to_gt(guc), "Schedule enable failed to respond");
+ set_exec_queue_banned(q);
+ xe_gt_reset_async(q->gt);
+ xe_sched_tdr_queue_imm(&q->guc->sched);
+ }
+}
+
+static void disable_scheduling(struct xe_exec_queue *q, bool immediate)
+{
+ MAKE_SCHED_CONTEXT_ACTION(q, DISABLE);
+ struct xe_guc *guc = exec_queue_to_guc(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));
+
+ if (immediate)
+ 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);
+}
+
static enum drm_gpu_sched_stat
guc_exec_queue_timedout_job(struct drm_sched_job *drm_job)
{
@@ -902,9 +1070,10 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job)
struct xe_sched_job *tmp_job;
struct xe_exec_queue *q = job->q;
struct xe_gpu_scheduler *sched = &q->guc->sched;
- struct xe_device *xe = guc_to_xe(exec_queue_to_guc(q));
+ struct xe_guc *guc = exec_queue_to_guc(q);
int err = -ETIME;
int i = 0;
+ bool wedged, skip_timeout_check;
/*
* TDR has fired before free job worker. Common if exec queue
@@ -916,46 +1085,53 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job)
return DRM_GPU_SCHED_STAT_NOMINAL;
}
- drm_notice(&xe->drm, "Timedout job: seqno=%u, guc_id=%d, flags=0x%lx",
- xe_sched_job_seqno(job), q->guc->id, q->flags);
- xe_gt_WARN(q->gt, q->flags & EXEC_QUEUE_FLAG_KERNEL,
- "Kernel-submitted job timed out\n");
- xe_gt_WARN(q->gt, q->flags & EXEC_QUEUE_FLAG_VM && !exec_queue_killed(q),
- "VM job timed out on non-killed execqueue\n");
-
- simple_error_capture(q);
- xe_devcoredump(job);
-
- trace_xe_sched_job_timedout(job);
-
/* Kill the run_job entry point */
xe_sched_submission_stop(sched);
+ /* 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);
+
+ /* Job hasn't started, can't be timed out */
+ if (!skip_timeout_check && !xe_sched_job_started(job))
+ goto rearm;
+
/*
- * Kernel jobs should never fail, nor should VM jobs if they do
- * somethings has gone wrong and the GT needs a reset
+ * 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.
*/
- if (q->flags & EXEC_QUEUE_FLAG_KERNEL ||
- (q->flags & EXEC_QUEUE_FLAG_VM && !exec_queue_killed(q))) {
- if (!xe_sched_invalidate_job(job, 2)) {
- xe_sched_add_pending_job(sched, job);
- xe_sched_submission_start(sched);
- xe_gt_reset_async(q->gt);
- goto out;
- }
- }
+ wedged = guc_submit_hint_wedged(exec_queue_to_guc(q));
- /* Engine state now stable, disable scheduling if needed */
- if (exec_queue_registered(q)) {
- struct xe_guc *guc = exec_queue_to_guc(q);
+ /* Engine state now stable, disable scheduling to check timestamp */
+ if (!wedged && exec_queue_registered(q)) {
int ret;
if (exec_queue_reset(q))
err = -EIO;
- set_exec_queue_banned(q);
+
if (!exec_queue_destroyed(q)) {
- xe_exec_queue_get(q);
- disable_scheduling_deregister(guc, q);
+ /*
+ * Wait for any pending G2H to flush out before
+ * modifying state
+ */
+ ret = wait_event_timeout(guc->ct.wq,
+ !exec_queue_pending_enable(q) ||
+ guc_read_stopped(guc), HZ * 5);
+ if (!ret || 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);
}
/*
@@ -971,15 +1147,60 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job)
!exec_queue_pending_disable(q) ||
guc_read_stopped(guc), HZ * 5);
if (!ret || guc_read_stopped(guc)) {
- drm_warn(&xe->drm, "Schedule disable failed to respond");
- xe_sched_add_pending_job(sched, job);
- xe_sched_submission_start(sched);
+trigger_reset:
+ if (!ret)
+ xe_gt_warn(guc_to_gt(guc), "Schedule disable failed to respond");
+ 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 out;
+ 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;
+ }
+
+ xe_gt_notice(guc_to_gt(guc), "Timedout job: seqno=%u, lrc_seqno=%u, guc_id=%d, flags=0x%lx",
+ xe_sched_job_seqno(job), xe_sched_job_lrc_seqno(job),
+ q->guc->id, q->flags);
+ trace_xe_sched_job_timedout(job);
+
+ if (!exec_queue_killed(q))
+ xe_devcoredump(job);
+
+ /*
+ * Kernel jobs should never fail, nor should VM jobs if they do
+ * somethings has gone wrong and the GT needs a reset
+ */
+ xe_gt_WARN(q->gt, q->flags & EXEC_QUEUE_FLAG_KERNEL,
+ "Kernel-submitted job timed out\n");
+ xe_gt_WARN(q->gt, q->flags & EXEC_QUEUE_FLAG_VM && !exec_queue_killed(q),
+ "VM job timed out on non-killed execqueue\n");
+ 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);
@@ -989,6 +1210,7 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job)
*/
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 */
@@ -1000,7 +1222,19 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job)
/* Start fence signaling */
xe_hw_fence_irq_start(q->fence_irq);
-out:
+ return DRM_GPU_SCHED_STAT_NOMINAL;
+
+sched_enable:
+ enable_scheduling(q);
+rearm:
+ /*
+ * XXX: Ideally want to adjust timeout based on current exection time
+ * but there is not currently an easy way to do in DRM scheduler. With
+ * some thought, do this in a follow up.
+ */
+ xe_sched_add_pending_job(sched, job);
+ xe_sched_submission_start(sched);
+
return DRM_GPU_SCHED_STAT_NOMINAL;
}
@@ -1011,6 +1245,7 @@ static void __guc_exec_queue_fini_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));
trace_xe_exec_queue_destroy(q);
if (xe_exec_queue_is_lr(q))
@@ -1021,6 +1256,7 @@ static void __guc_exec_queue_fini_async(struct work_struct *w)
kfree(ge);
xe_exec_queue_fini(q);
+ xe_pm_runtime_put(guc_to_xe(guc));
}
static void guc_exec_queue_fini_async(struct xe_exec_queue *q)
@@ -1028,7 +1264,7 @@ static void guc_exec_queue_fini_async(struct xe_exec_queue *q)
INIT_WORK(&q->guc->fini_async, __guc_exec_queue_fini_async);
/* We must block on kernel engines so slabs are empty on driver unload */
- if (q->flags & EXEC_QUEUE_FLAG_PERMANENT)
+ if (q->flags & EXEC_QUEUE_FLAG_PERMANENT || exec_queue_wedged(q))
__guc_exec_queue_fini_async(&q->guc->fini_async);
else
queue_work(system_wq, &q->guc->fini_async);
@@ -1063,7 +1299,7 @@ static void __guc_exec_queue_process_msg_cleanup(struct xe_sched_msg *msg)
static bool guc_exec_queue_allowed_to_change_state(struct xe_exec_queue *q)
{
- return !exec_queue_killed_or_banned(q) && exec_queue_registered(q);
+ return !exec_queue_killed_or_banned_or_wedged(q) && exec_queue_registered(q);
}
static void __guc_exec_queue_process_msg_set_sched_props(struct xe_sched_msg *msg)
@@ -1101,7 +1337,6 @@ static void __guc_exec_queue_process_msg_suspend(struct xe_sched_msg *msg)
guc_read_stopped(guc));
if (!guc_read_stopped(guc)) {
- MAKE_SCHED_CONTEXT_ACTION(q, DISABLE);
s64 since_resume_ms =
ktime_ms_delta(ktime_get(),
q->guc->resume_time);
@@ -1112,12 +1347,7 @@ static void __guc_exec_queue_process_msg_suspend(struct xe_sched_msg *msg)
msleep(wait_ms);
set_exec_queue_suspended(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);
+ disable_scheduling(q, false);
}
} else if (q->guc->suspend_pending) {
set_exec_queue_suspended(q);
@@ -1128,19 +1358,11 @@ static void __guc_exec_queue_process_msg_suspend(struct xe_sched_msg *msg)
static void __guc_exec_queue_process_msg_resume(struct xe_sched_msg *msg)
{
struct xe_exec_queue *q = msg->private_data;
- struct xe_guc *guc = exec_queue_to_guc(q);
if (guc_exec_queue_allowed_to_change_state(q)) {
- MAKE_SCHED_CONTEXT_ACTION(q, ENABLE);
-
q->guc->resume_time = RESUME_PENDING;
clear_exec_queue_suspended(q);
- set_exec_queue_pending_enable(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);
+ enable_scheduling(q);
} else {
clear_exec_queue_suspended(q);
}
@@ -1206,7 +1428,7 @@ static int guc_exec_queue_init(struct xe_exec_queue *q)
msecs_to_jiffies(q->sched_props.job_timeout_ms);
err = xe_sched_init(&ge->sched, &drm_sched_ops, &xe_sched_ops,
get_submit_wq(guc),
- q->lrc[0].ring.size / MAX_JOB_SIZE_BYTES, 64,
+ q->lrc[0]->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)
@@ -1240,6 +1462,7 @@ static int guc_exec_queue_init(struct xe_exec_queue *q)
return 0;
err_entity:
+ mutex_unlock(&guc->submission_state.lock);
xe_sched_entity_fini(&ge->entity);
err_sched:
xe_sched_fini(&ge->sched);
@@ -1274,7 +1497,7 @@ static void guc_exec_queue_fini(struct xe_exec_queue *q)
{
struct xe_sched_msg *msg = q->guc->static_msgs + STATIC_MSG_CLEANUP;
- if (!(q->flags & EXEC_QUEUE_FLAG_PERMANENT))
+ if (!(q->flags & EXEC_QUEUE_FLAG_PERMANENT) && !exec_queue_wedged(q))
guc_exec_queue_add_msg(q, msg, CLEANUP);
else
__guc_exec_queue_fini(exec_queue_to_guc(q), q);
@@ -1285,7 +1508,8 @@ static int guc_exec_queue_set_priority(struct xe_exec_queue *q,
{
struct xe_sched_msg *msg;
- if (q->sched_props.priority == priority || exec_queue_killed_or_banned(q))
+ if (q->sched_props.priority == priority ||
+ exec_queue_killed_or_banned_or_wedged(q))
return 0;
msg = kmalloc(sizeof(*msg), GFP_KERNEL);
@@ -1303,7 +1527,7 @@ static int guc_exec_queue_set_timeslice(struct xe_exec_queue *q, u32 timeslice_u
struct xe_sched_msg *msg;
if (q->sched_props.timeslice_us == timeslice_us ||
- exec_queue_killed_or_banned(q))
+ exec_queue_killed_or_banned_or_wedged(q))
return 0;
msg = kmalloc(sizeof(*msg), GFP_KERNEL);
@@ -1322,7 +1546,7 @@ static int guc_exec_queue_set_preempt_timeout(struct xe_exec_queue *q,
struct xe_sched_msg *msg;
if (q->sched_props.preempt_timeout_us == preempt_timeout_us ||
- exec_queue_killed_or_banned(q))
+ exec_queue_killed_or_banned_or_wedged(q))
return 0;
msg = kmalloc(sizeof(*msg), GFP_KERNEL);
@@ -1339,7 +1563,7 @@ static int guc_exec_queue_suspend(struct xe_exec_queue *q)
{
struct xe_sched_msg *msg = q->guc->static_msgs + STATIC_MSG_SUSPEND;
- if (exec_queue_killed_or_banned(q) || q->guc->suspend_pending)
+ if (exec_queue_killed_or_banned_or_wedged(q) || q->guc->suspend_pending)
return -EINVAL;
q->guc->suspend_pending = true;
@@ -1369,7 +1593,7 @@ static void guc_exec_queue_resume(struct xe_exec_queue *q)
static bool guc_exec_queue_reset_status(struct xe_exec_queue *q)
{
- return exec_queue_reset(q);
+ return exec_queue_reset(q) || exec_queue_killed_or_banned_or_wedged(q);
}
/*
@@ -1400,8 +1624,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_banned(q) && exec_queue_destroyed(q)) ||
- xe_exec_queue_is_lr(q))
+ if (exec_queue_extra_ref(q) || xe_exec_queue_is_lr(q))
xe_exec_queue_put(q);
else if (exec_queue_destroyed(q))
__guc_exec_queue_fini(guc, q);
@@ -1410,7 +1633,9 @@ static void guc_exec_queue_stop(struct xe_guc *guc, struct xe_exec_queue *q)
set_exec_queue_suspended(q);
suspend_fence_signal(q);
}
- atomic_and(EXEC_QUEUE_STATE_DESTROYED | ENGINE_STATE_SUSPENDED,
+ atomic_and(EXEC_QUEUE_STATE_WEDGED | EXEC_QUEUE_STATE_BANNED |
+ EXEC_QUEUE_STATE_KILLED | EXEC_QUEUE_STATE_DESTROYED |
+ EXEC_QUEUE_STATE_SUSPENDED,
&q->guc->state);
q->guc->resume_time = 0;
trace_xe_exec_queue_stop(q);
@@ -1422,15 +1647,23 @@ static void guc_exec_queue_stop(struct xe_guc *guc, struct xe_exec_queue *q)
*/
if (!(q->flags & (EXEC_QUEUE_FLAG_KERNEL | EXEC_QUEUE_FLAG_VM))) {
struct xe_sched_job *job = xe_sched_first_pending_job(sched);
+ bool ban = false;
if (job) {
if ((xe_sched_job_started(job) &&
!xe_sched_job_completed(job)) ||
xe_sched_invalidate_job(job, 2)) {
trace_xe_sched_job_ban(job);
- xe_sched_tdr_queue_imm(&q->guc->sched);
- set_exec_queue_banned(q);
+ ban = true;
}
+ } else if (xe_exec_queue_is_lr(q) &&
+ (xe_lrc_ring_head(q->lrc[0]) != xe_lrc_ring_tail(q->lrc[0]))) {
+ ban = true;
+ }
+
+ if (ban) {
+ set_exec_queue_banned(q);
+ xe_guc_exec_queue_trigger_cleanup(q);
}
}
}
@@ -1455,10 +1688,11 @@ int xe_guc_submit_reset_prepare(struct xe_guc *guc)
void xe_guc_submit_reset_wait(struct xe_guc *guc)
{
- wait_event(guc->ct.wq, !guc_read_stopped(guc));
+ wait_event(guc->ct.wq, xe_device_wedged(guc_to_xe(guc)) ||
+ !guc_read_stopped(guc));
}
-int xe_guc_submit_stop(struct xe_guc *guc)
+void xe_guc_submit_stop(struct xe_guc *guc)
{
struct xe_exec_queue *q;
unsigned long index;
@@ -1478,19 +1712,18 @@ int xe_guc_submit_stop(struct xe_guc *guc)
* creation which is protected by guc->submission_state.lock.
*/
- return 0;
}
static void guc_exec_queue_start(struct xe_exec_queue *q)
{
struct xe_gpu_scheduler *sched = &q->guc->sched;
- if (!exec_queue_killed_or_banned(q)) {
+ if (!exec_queue_killed_or_banned_or_wedged(q)) {
int i;
trace_xe_exec_queue_resubmit(q);
for (i = 0; i < q->width; ++i)
- xe_lrc_set_ring_head(q->lrc + i, q->lrc[i].ring.tail);
+ xe_lrc_set_ring_head(q->lrc[i], q->lrc[i]->ring.tail);
xe_sched_resubmit_jobs(sched);
}
@@ -1546,30 +1779,44 @@ static void deregister_exec_queue(struct xe_guc *guc, struct xe_exec_queue *q)
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_disable(q));
+ xe_gt_assert(guc_to_gt(guc), !exec_queue_pending_enable(q));
+
trace_xe_exec_queue_deregister(q);
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)
+static void handle_sched_done(struct xe_guc *guc, struct xe_exec_queue *q,
+ u32 runnable_state)
{
trace_xe_exec_queue_scheduling_done(q);
- if (exec_queue_pending_enable(q)) {
+ if (runnable_state == 1) {
+ xe_gt_assert(guc_to_gt(guc), exec_queue_pending_enable(q));
+
q->guc->resume_time = ktime_get();
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));
+
clear_exec_queue_pending_disable(q);
if (q->guc->suspend_pending) {
suspend_fence_signal(q);
} else {
- if (exec_queue_banned(q)) {
+ if (exec_queue_banned(q) || check_timeout) {
smp_wmb();
wake_up_all(&guc->ct.wq);
}
- deregister_exec_queue(guc, q);
+ if (!check_timeout)
+ deregister_exec_queue(guc, q);
}
}
}
@@ -1579,6 +1826,7 @@ int xe_guc_sched_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];
+ u32 runnable_state = msg[1];
if (unlikely(len < 2)) {
drm_err(&xe->drm, "Invalid length %u", len);
@@ -1591,12 +1839,14 @@ int xe_guc_sched_done_handler(struct xe_guc *guc, u32 *msg, u32 len)
if (unlikely(!exec_queue_pending_enable(q) &&
!exec_queue_pending_disable(q))) {
- drm_err(&xe->drm, "Unexpected engine state 0x%04x",
- atomic_read(&q->guc->state));
+ xe_gt_err(guc_to_gt(guc),
+ "SCHED_DONE: Unexpected engine state 0x%04x, guc_id=%d, runnable_state=%u",
+ atomic_read(&q->guc->state), q->guc->id,
+ runnable_state);
return -EPROTO;
}
- handle_sched_done(guc, q);
+ handle_sched_done(guc, q, runnable_state);
return 0;
}
@@ -1607,7 +1857,7 @@ static void handle_deregister_done(struct xe_guc *guc, struct xe_exec_queue *q)
clear_exec_queue_registered(q);
- if (exec_queue_banned(q) || xe_exec_queue_is_lr(q))
+ if (exec_queue_extra_ref(q) || xe_exec_queue_is_lr(q))
xe_exec_queue_put(q);
else
__guc_exec_queue_fini(guc, q);
@@ -1630,8 +1880,9 @@ int xe_guc_deregister_done_handler(struct xe_guc *guc, u32 *msg, u32 len)
if (!exec_queue_destroyed(q) || exec_queue_pending_disable(q) ||
exec_queue_pending_enable(q) || exec_queue_enabled(q)) {
- drm_err(&xe->drm, "Unexpected engine state 0x%04x",
- atomic_read(&q->guc->state));
+ xe_gt_err(guc_to_gt(guc),
+ "DEREGISTER_DONE: Unexpected engine state 0x%04x, guc_id=%d",
+ atomic_read(&q->guc->state), q->guc->id);
return -EPROTO;
}
@@ -1642,6 +1893,7 @@ int xe_guc_deregister_done_handler(struct xe_guc *guc, u32 *msg, u32 len)
int xe_guc_exec_queue_reset_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[0];
@@ -1655,7 +1907,8 @@ int xe_guc_exec_queue_reset_handler(struct xe_guc *guc, u32 *msg, u32 len)
if (unlikely(!q))
return -EPROTO;
- drm_info(&xe->drm, "Engine reset: guc_id=%d", guc_id);
+ 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);
/* FIXME: Do error capture, most likely async */
@@ -1668,7 +1921,7 @@ int xe_guc_exec_queue_reset_handler(struct xe_guc *guc, u32 *msg, u32 len)
* guc_exec_queue_timedout_job.
*/
set_exec_queue_reset(q);
- if (!exec_queue_banned(q))
+ if (!exec_queue_banned(q) && !exec_queue_check_timeout(q))
xe_guc_exec_queue_trigger_cleanup(q);
return 0;
@@ -1677,6 +1930,7 @@ int xe_guc_exec_queue_reset_handler(struct xe_guc *guc, u32 *msg, u32 len)
int xe_guc_exec_queue_memory_cat_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[0];
@@ -1690,12 +1944,14 @@ int xe_guc_exec_queue_memory_cat_error_handler(struct xe_guc *guc, u32 *msg,
if (unlikely(!q))
return -EPROTO;
- drm_dbg(&xe->drm, "Engine memory cat error: guc_id=%d", guc_id);
+ xe_gt_dbg(gt, "Engine memory cat error: engine_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))
+ if (!exec_queue_banned(q) && !exec_queue_check_timeout(q))
xe_guc_exec_queue_trigger_cleanup(q);
return 0;
@@ -1731,7 +1987,7 @@ guc_exec_queue_wq_snapshot_capture(struct xe_exec_queue *q,
{
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);
+ struct iosys_map map = xe_lrc_parallel_map(q->lrc[0]);
int i;
snapshot->guc.wqi_head = q->guc->wqi_head;
@@ -1811,7 +2067,7 @@ xe_guc_exec_queue_snapshot_capture(struct xe_exec_queue *q)
if (snapshot->lrc) {
for (i = 0; i < q->width; ++i) {
- struct xe_lrc *lrc = q->lrc + i;
+ struct xe_lrc *lrc = q->lrc[i];
snapshot->lrc[i] = xe_lrc_snapshot_capture(lrc);
}
diff --git a/drivers/gpu/drm/xe/xe_guc_submit.h b/drivers/gpu/drm/xe/xe_guc_submit.h
index fad0421ead36..bdf8c9f3d24a 100644
--- a/drivers/gpu/drm/xe/xe_guc_submit.h
+++ b/drivers/gpu/drm/xe/xe_guc_submit.h
@@ -12,12 +12,13 @@ struct drm_printer;
struct xe_exec_queue;
struct xe_guc;
-int xe_guc_submit_init(struct xe_guc *guc);
+int xe_guc_submit_init(struct xe_guc *guc, unsigned int num_ids);
int xe_guc_submit_reset_prepare(struct xe_guc *guc);
void xe_guc_submit_reset_wait(struct xe_guc *guc);
-int xe_guc_submit_stop(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_wedge(struct xe_guc *guc);
int xe_guc_sched_done_handler(struct xe_guc *guc, u32 *msg, u32 len);
int xe_guc_deregister_done_handler(struct xe_guc *guc, u32 *msg, u32 len);
diff --git a/drivers/gpu/drm/xe/xe_guc_types.h b/drivers/gpu/drm/xe/xe_guc_types.h
index 82bd93f7867d..546ac6350a31 100644
--- a/drivers/gpu/drm/xe/xe_guc_types.h
+++ b/drivers/gpu/drm/xe/xe_guc_types.h
@@ -72,15 +72,6 @@ struct xe_guc {
atomic_t stopped;
/** @submission_state.lock: protects submission state */
struct mutex lock;
- /** @submission_state.suspend: suspend fence state */
- struct {
- /** @submission_state.suspend.lock: suspend fences lock */
- spinlock_t lock;
- /** @submission_state.suspend.context: suspend fences context */
- u64 context;
- /** @submission_state.suspend.seqno: suspend fences seqno */
- u32 seqno;
- } suspend;
#ifdef CONFIG_PROVE_LOCKING
#define NUM_SUBMIT_WQ 256
/** @submission_state.submit_wq_pool: submission ordered workqueues pool */
diff --git a/drivers/gpu/drm/xe/xe_huc.c b/drivers/gpu/drm/xe/xe_huc.c
index 39a484a57585..bec4366e5513 100644
--- a/drivers/gpu/drm/xe/xe_huc.c
+++ b/drivers/gpu/drm/xe/xe_huc.c
@@ -5,6 +5,8 @@
#include "xe_huc.h"
+#include <linux/delay.h>
+
#include <drm/drm_managed.h>
#include "abi/gsc_pxp_commands_abi.h"
@@ -16,9 +18,11 @@
#include "xe_force_wake.h"
#include "xe_gsc_submit.h"
#include "xe_gt.h"
+#include "xe_gt_printk.h"
#include "xe_guc.h"
#include "xe_map.h"
#include "xe_mmio.h"
+#include "xe_sriov.h"
#include "xe_uc_fw.h"
static struct xe_gt *
@@ -90,6 +94,9 @@ int xe_huc_init(struct xe_huc *huc)
if (!xe_uc_fw_is_enabled(&huc->fw))
return 0;
+ if (IS_SRIOV_VF(xe))
+ return 0;
+
if (huc->fw.has_gsc_headers) {
ret = huc_alloc_gsc_pkt(huc);
if (ret)
@@ -101,7 +108,7 @@ int xe_huc_init(struct xe_huc *huc)
return 0;
out:
- drm_err(&xe->drm, "HuC init failed with %d", ret);
+ xe_gt_err(gt, "HuC: initialization failed: %pe\n", ERR_PTR(ret));
return ret;
}
@@ -189,14 +196,14 @@ static int huc_auth_via_gsccs(struct xe_huc *huc)
} while (--retry && err == -EBUSY);
if (err) {
- drm_err(&xe->drm, "failed to submit GSC request to auth: %d\n", err);
+ xe_gt_err(gt, "HuC: failed to submit GSC request to auth: %pe\n", ERR_PTR(err));
return err;
}
err = xe_gsc_read_out_header(xe, &pkt->vmap, PXP43_HUC_AUTH_INOUT_SIZE,
sizeof(struct pxp43_huc_auth_out), &rd_offset);
if (err) {
- drm_err(&xe->drm, "HuC: invalid GSC reply for auth (err=%d)\n", err);
+ xe_gt_err(gt, "HuC: invalid GSC reply for auth: %pe\n", ERR_PTR(err));
return err;
}
@@ -207,7 +214,7 @@ static int huc_auth_via_gsccs(struct xe_huc *huc)
*/
out_status = huc_auth_msg_rd(xe, &pkt->vmap, rd_offset, header.status);
if (out_status != PXP_STATUS_SUCCESS && out_status != PXP_STATUS_OP_NOT_PERMITTED) {
- drm_err(&xe->drm, "auth failed with GSC error = 0x%x\n", out_status);
+ xe_gt_err(gt, "HuC: authentication failed with GSC error = %#x\n", out_status);
return -EIO;
}
@@ -236,7 +243,6 @@ bool xe_huc_is_authenticated(struct xe_huc *huc, enum xe_huc_auth_types type)
int xe_huc_auth(struct xe_huc *huc, enum xe_huc_auth_types type)
{
- struct xe_device *xe = huc_to_xe(huc);
struct xe_gt *gt = huc_to_gt(huc);
struct xe_guc *guc = huc_to_guc(huc);
int ret;
@@ -266,26 +272,26 @@ int xe_huc_auth(struct xe_huc *huc, enum xe_huc_auth_types type)
return -EINVAL;
}
if (ret) {
- drm_err(&xe->drm, "Failed to trigger HuC auth via %s: %d\n",
- huc_auth_modes[type].name, ret);
+ xe_gt_err(gt, "HuC: failed to trigger auth via %s: %pe\n",
+ huc_auth_modes[type].name, ERR_PTR(ret));
goto fail;
}
ret = xe_mmio_wait32(gt, huc_auth_modes[type].reg, huc_auth_modes[type].val,
huc_auth_modes[type].val, 100000, NULL, false);
if (ret) {
- drm_err(&xe->drm, "HuC: Firmware not verified %d\n", ret);
+ xe_gt_err(gt, "HuC: firmware not verified: %pe\n", ERR_PTR(ret));
goto fail;
}
xe_uc_fw_change_status(&huc->fw, XE_UC_FIRMWARE_RUNNING);
- drm_dbg(&xe->drm, "HuC authenticated via %s\n", huc_auth_modes[type].name);
+ xe_gt_dbg(gt, "HuC: authenticated via %s\n", huc_auth_modes[type].name);
return 0;
fail:
- drm_err(&xe->drm, "HuC: Auth via %s failed: %d\n",
- huc_auth_modes[type].name, ret);
+ xe_gt_err(gt, "HuC: authentication via %s failed: %pe\n",
+ huc_auth_modes[type].name, ERR_PTR(ret));
xe_uc_fw_change_status(&huc->fw, XE_UC_FIRMWARE_LOAD_FAIL);
return ret;
@@ -293,9 +299,7 @@ fail:
void xe_huc_sanitize(struct xe_huc *huc)
{
- if (!xe_uc_fw_is_loadable(&huc->fw))
- return;
- xe_uc_fw_change_status(&huc->fw, XE_UC_FIRMWARE_LOADABLE);
+ xe_uc_fw_sanitize(&huc->fw);
}
void xe_huc_print_info(struct xe_huc *huc, struct drm_printer *p)
diff --git a/drivers/gpu/drm/xe/xe_huc.h b/drivers/gpu/drm/xe/xe_huc.h
index 3ab56cc14b00..fa1c45e70443 100644
--- a/drivers/gpu/drm/xe/xe_huc.h
+++ b/drivers/gpu/drm/xe/xe_huc.h
@@ -6,9 +6,10 @@
#ifndef _XE_HUC_H_
#define _XE_HUC_H_
-#include "xe_huc_types.h"
+#include <linux/types.h>
struct drm_printer;
+struct xe_huc;
enum xe_huc_auth_types {
XE_HUC_AUTH_VIA_GUC = 0,
diff --git a/drivers/gpu/drm/xe/xe_hw_engine.c b/drivers/gpu/drm/xe/xe_hw_engine.c
index 455f375c1cbd..07ed9fd28f19 100644
--- a/drivers/gpu/drm/xe/xe_hw_engine.c
+++ b/drivers/gpu/drm/xe/xe_hw_engine.c
@@ -18,6 +18,7 @@
#include "xe_gt.h"
#include "xe_gt_ccs_mode.h"
#include "xe_gt_printk.h"
+#include "xe_gt_mcr.h"
#include "xe_gt_topology.h"
#include "xe_hw_fence.h"
#include "xe_irq.h"
@@ -25,6 +26,7 @@
#include "xe_macros.h"
#include "xe_mmio.h"
#include "xe_reg_sr.h"
+#include "xe_reg_whitelist.h"
#include "xe_rtp.h"
#include "xe_sched_job.h"
#include "xe_sriov.h"
@@ -267,7 +269,7 @@ static void hw_engine_fini(struct drm_device *drm, void *arg)
if (hwe->exl_port)
xe_execlist_port_destroy(hwe->exl_port);
- xe_lrc_finish(&hwe->kernel_lrc);
+ xe_lrc_put(hwe->kernel_lrc);
hwe->gt = NULL;
}
@@ -341,7 +343,7 @@ xe_hw_engine_setup_default_lrc_state(struct xe_hw_engine *hwe)
u32 blit_cctl_val = REG_FIELD_PREP(BLIT_CCTL_DST_MOCS_MASK, mocs_write_idx) |
REG_FIELD_PREP(BLIT_CCTL_SRC_MOCS_MASK, mocs_read_idx);
struct xe_rtp_process_ctx ctx = XE_RTP_PROCESS_CTX_INITIALIZER(hwe);
- const struct xe_rtp_entry_sr lrc_was[] = {
+ const struct xe_rtp_entry_sr lrc_setup[] = {
/*
* Some blitter commands do not have a field for MOCS, those
* commands will use MOCS index pointed by BLIT_CCTL.
@@ -373,7 +375,7 @@ xe_hw_engine_setup_default_lrc_state(struct xe_hw_engine *hwe)
{}
};
- xe_rtp_process_to_sr(&ctx, lrc_was, &hwe->reg_lrc);
+ xe_rtp_process_to_sr(&ctx, lrc_setup, &hwe->reg_lrc);
}
static void
@@ -526,9 +528,11 @@ static int hw_engine_init(struct xe_gt *gt, struct xe_hw_engine *hwe,
goto err_name;
}
- err = xe_lrc_init(&hwe->kernel_lrc, hwe, NULL, NULL, SZ_16K);
- if (err)
+ hwe->kernel_lrc = xe_lrc_create(hwe, NULL, SZ_16K);
+ if (IS_ERR(hwe->kernel_lrc)) {
+ err = PTR_ERR(hwe->kernel_lrc);
goto err_hwsp;
+ }
if (!xe_device_uc_enabled(xe)) {
hwe->exl_port = xe_execlist_port_create(xe, hwe);
@@ -543,7 +547,8 @@ static int hw_engine_init(struct xe_gt *gt, struct xe_hw_engine *hwe,
if (hwe->class == XE_ENGINE_CLASS_OTHER)
hwe->irq_handler = xe_gsc_hwe_irq_handler;
- xe_hw_engine_enable_ring(hwe);
+ if (!IS_SRIOV_VF(xe))
+ xe_hw_engine_enable_ring(hwe);
}
/* We reserve the highest BCS instance for USM */
@@ -553,7 +558,7 @@ static int hw_engine_init(struct xe_gt *gt, struct xe_hw_engine *hwe,
return drmm_add_action_or_reset(&xe->drm, hw_engine_fini, hwe);
err_kernel_lrc:
- xe_lrc_finish(&hwe->kernel_lrc);
+ xe_lrc_put(hwe->kernel_lrc);
err_hwsp:
xe_bo_unpin_map_no_vm(hwe->hwsp);
err_name:
@@ -716,6 +721,11 @@ static void check_gsc_availability(struct xe_gt *gt)
*/
if (!xe_uc_fw_is_available(&gt->uc.gsc.fw)) {
gt->info.engine_mask &= ~BIT(XE_HW_ENGINE_GSCCS0);
+
+ /* interrupts where previously enabled, so turn them off */
+ xe_mmio_write32(gt, GUNIT_GSC_INTR_ENABLE, 0);
+ xe_mmio_write32(gt, GUNIT_GSC_INTR_MASK, ~0);
+
drm_info(&xe->drm, "gsccs disabled due to lack of FW\n");
}
}
@@ -766,6 +776,57 @@ void xe_hw_engine_handle_irq(struct xe_hw_engine *hwe, u16 intr_vec)
xe_hw_fence_irq_run(hwe->fence_irq);
}
+static bool
+is_slice_common_per_gslice(struct xe_device *xe)
+{
+ return GRAPHICS_VERx100(xe) >= 1255;
+}
+
+static void
+xe_hw_engine_snapshot_instdone_capture(struct xe_hw_engine *hwe,
+ struct xe_hw_engine_snapshot *snapshot)
+{
+ struct xe_gt *gt = hwe->gt;
+ struct xe_device *xe = gt_to_xe(gt);
+ unsigned int dss;
+ u16 group, instance;
+
+ snapshot->reg.instdone.ring = hw_engine_mmio_read32(hwe, RING_INSTDONE(0));
+
+ if (snapshot->hwe->class != XE_ENGINE_CLASS_RENDER)
+ return;
+
+ if (is_slice_common_per_gslice(xe) == false) {
+ snapshot->reg.instdone.slice_common[0] =
+ xe_mmio_read32(gt, SC_INSTDONE);
+ snapshot->reg.instdone.slice_common_extra[0] =
+ xe_mmio_read32(gt, SC_INSTDONE_EXTRA);
+ snapshot->reg.instdone.slice_common_extra2[0] =
+ xe_mmio_read32(gt, SC_INSTDONE_EXTRA2);
+ } else {
+ for_each_geometry_dss(dss, gt, group, instance) {
+ snapshot->reg.instdone.slice_common[dss] =
+ xe_gt_mcr_unicast_read(gt, XEHPG_SC_INSTDONE, group, instance);
+ snapshot->reg.instdone.slice_common_extra[dss] =
+ xe_gt_mcr_unicast_read(gt, XEHPG_SC_INSTDONE_EXTRA, group, instance);
+ snapshot->reg.instdone.slice_common_extra2[dss] =
+ xe_gt_mcr_unicast_read(gt, XEHPG_SC_INSTDONE_EXTRA2, group, instance);
+ }
+ }
+
+ for_each_geometry_dss(dss, gt, group, instance) {
+ snapshot->reg.instdone.sampler[dss] =
+ xe_gt_mcr_unicast_read(gt, SAMPLER_INSTDONE, group, instance);
+ snapshot->reg.instdone.row[dss] =
+ xe_gt_mcr_unicast_read(gt, ROW_INSTDONE, group, instance);
+
+ if (GRAPHICS_VERx100(xe) >= 1255)
+ snapshot->reg.instdone.geom_svg[dss] =
+ xe_gt_mcr_unicast_read(gt, XEHPG_INSTDONE_GEOM_SVGUNIT,
+ group, instance);
+ }
+}
+
/**
* xe_hw_engine_snapshot_capture - Take a quick snapshot of the HW Engine.
* @hwe: Xe HW Engine.
@@ -780,6 +841,7 @@ struct xe_hw_engine_snapshot *
xe_hw_engine_snapshot_capture(struct xe_hw_engine *hwe)
{
struct xe_hw_engine_snapshot *snapshot;
+ size_t len;
u64 val;
if (!xe_hw_engine_is_valid(hwe))
@@ -790,8 +852,30 @@ xe_hw_engine_snapshot_capture(struct xe_hw_engine *hwe)
if (!snapshot)
return NULL;
+ /* Because XE_MAX_DSS_FUSE_BITS is defined in xe_gt_types.h and it
+ * includes xe_hw_engine_types.h the length of this 3 registers can't be
+ * set in struct xe_hw_engine_snapshot, so here doing additional
+ * allocations.
+ */
+ len = (XE_MAX_DSS_FUSE_BITS * sizeof(u32));
+ snapshot->reg.instdone.slice_common = kzalloc(len, GFP_ATOMIC);
+ snapshot->reg.instdone.slice_common_extra = kzalloc(len, GFP_ATOMIC);
+ snapshot->reg.instdone.slice_common_extra2 = kzalloc(len, GFP_ATOMIC);
+ snapshot->reg.instdone.sampler = kzalloc(len, GFP_ATOMIC);
+ snapshot->reg.instdone.row = kzalloc(len, GFP_ATOMIC);
+ snapshot->reg.instdone.geom_svg = kzalloc(len, GFP_ATOMIC);
+ if (!snapshot->reg.instdone.slice_common ||
+ !snapshot->reg.instdone.slice_common_extra ||
+ !snapshot->reg.instdone.slice_common_extra2 ||
+ !snapshot->reg.instdone.sampler ||
+ !snapshot->reg.instdone.row ||
+ !snapshot->reg.instdone.geom_svg) {
+ xe_hw_engine_snapshot_free(snapshot);
+ return NULL;
+ }
+
snapshot->name = kstrdup(hwe->name, GFP_ATOMIC);
- snapshot->class = hwe->class;
+ snapshot->hwe = hwe;
snapshot->logical_instance = hwe->logical_instance;
snapshot->forcewake.domain = hwe->domain;
snapshot->forcewake.ref = xe_force_wake_ref(gt_to_fw(hwe->gt),
@@ -828,6 +912,15 @@ xe_hw_engine_snapshot_capture(struct xe_hw_engine *hwe)
snapshot->reg.ring_hwstam = hw_engine_mmio_read32(hwe, RING_HWSTAM(0));
snapshot->reg.ring_hws_pga = hw_engine_mmio_read32(hwe, RING_HWS_PGA(0));
snapshot->reg.ring_start = hw_engine_mmio_read32(hwe, RING_START(0));
+ if (GRAPHICS_VERx100(hwe->gt->tile->xe) >= 2000) {
+ val = hw_engine_mmio_read32(hwe, RING_START_UDW(0));
+ snapshot->reg.ring_start |= val << 32;
+ }
+ if (xe_gt_has_indirect_ring_state(hwe->gt)) {
+ snapshot->reg.indirect_ring_state =
+ hw_engine_mmio_read32(hwe, INDIRECT_RING_STATE(0));
+ }
+
snapshot->reg.ring_head =
hw_engine_mmio_read32(hwe, RING_HEAD(0)) & HEAD_ADDR;
snapshot->reg.ring_tail =
@@ -841,13 +934,57 @@ xe_hw_engine_snapshot_capture(struct xe_hw_engine *hwe)
snapshot->reg.ring_emr = hw_engine_mmio_read32(hwe, RING_EMR(0));
snapshot->reg.ring_eir = hw_engine_mmio_read32(hwe, RING_EIR(0));
snapshot->reg.ipehr = hw_engine_mmio_read32(hwe, RING_IPEHR(0));
+ xe_hw_engine_snapshot_instdone_capture(hwe, snapshot);
- if (snapshot->class == XE_ENGINE_CLASS_COMPUTE)
+ if (snapshot->hwe->class == XE_ENGINE_CLASS_COMPUTE)
snapshot->reg.rcu_mode = xe_mmio_read32(hwe->gt, RCU_MODE);
return snapshot;
}
+static void
+xe_hw_engine_snapshot_instdone_print(struct xe_hw_engine_snapshot *snapshot, struct drm_printer *p)
+{
+ struct xe_gt *gt = snapshot->hwe->gt;
+ struct xe_device *xe = gt_to_xe(gt);
+ u16 group, instance;
+ unsigned int dss;
+
+ drm_printf(p, "\tRING_INSTDONE: 0x%08x\n", snapshot->reg.instdone.ring);
+
+ if (snapshot->hwe->class != XE_ENGINE_CLASS_RENDER)
+ return;
+
+ if (is_slice_common_per_gslice(xe) == false) {
+ drm_printf(p, "\tSC_INSTDONE[0]: 0x%08x\n",
+ snapshot->reg.instdone.slice_common[0]);
+ drm_printf(p, "\tSC_INSTDONE_EXTRA[0]: 0x%08x\n",
+ snapshot->reg.instdone.slice_common_extra[0]);
+ drm_printf(p, "\tSC_INSTDONE_EXTRA2[0]: 0x%08x\n",
+ snapshot->reg.instdone.slice_common_extra2[0]);
+ } else {
+ for_each_geometry_dss(dss, gt, group, instance) {
+ drm_printf(p, "\tSC_INSTDONE[%u]: 0x%08x\n", dss,
+ snapshot->reg.instdone.slice_common[dss]);
+ drm_printf(p, "\tSC_INSTDONE_EXTRA[%u]: 0x%08x\n", dss,
+ snapshot->reg.instdone.slice_common_extra[dss]);
+ drm_printf(p, "\tSC_INSTDONE_EXTRA2[%u]: 0x%08x\n", dss,
+ snapshot->reg.instdone.slice_common_extra2[dss]);
+ }
+ }
+
+ for_each_geometry_dss(dss, gt, group, instance) {
+ drm_printf(p, "\tSAMPLER_INSTDONE[%u]: 0x%08x\n", dss,
+ snapshot->reg.instdone.sampler[dss]);
+ drm_printf(p, "\tROW_INSTDONE[%u]: 0x%08x\n", dss,
+ snapshot->reg.instdone.row[dss]);
+
+ if (GRAPHICS_VERx100(xe) >= 1255)
+ drm_printf(p, "\tINSTDONE_GEOM_SVGUNIT[%u]: 0x%08x\n",
+ dss, snapshot->reg.instdone.geom_svg[dss]);
+ }
+}
+
/**
* xe_hw_engine_snapshot_print - Print out a given Xe HW Engine snapshot.
* @snapshot: Xe HW Engine snapshot object.
@@ -872,7 +1009,7 @@ void xe_hw_engine_snapshot_print(struct xe_hw_engine_snapshot *snapshot,
snapshot->reg.ring_execlist_status);
drm_printf(p, "\tRING_EXECLIST_SQ_CONTENTS: 0x%016llx\n",
snapshot->reg.ring_execlist_sq_contents);
- drm_printf(p, "\tRING_START: 0x%08x\n", snapshot->reg.ring_start);
+ drm_printf(p, "\tRING_START: 0x%016llx\n", snapshot->reg.ring_start);
drm_printf(p, "\tRING_HEAD: 0x%08x\n", snapshot->reg.ring_head);
drm_printf(p, "\tRING_TAIL: 0x%08x\n", snapshot->reg.ring_tail);
drm_printf(p, "\tRING_CTL: 0x%08x\n", snapshot->reg.ring_ctl);
@@ -886,10 +1023,15 @@ void xe_hw_engine_snapshot_print(struct xe_hw_engine_snapshot *snapshot,
drm_printf(p, "\tACTHD: 0x%016llx\n", snapshot->reg.ring_acthd);
drm_printf(p, "\tBBADDR: 0x%016llx\n", snapshot->reg.ring_bbaddr);
drm_printf(p, "\tDMA_FADDR: 0x%016llx\n", snapshot->reg.ring_dma_fadd);
+ drm_printf(p, "\tINDIRECT_RING_STATE: 0x%08x\n",
+ snapshot->reg.indirect_ring_state);
drm_printf(p, "\tIPEHR: 0x%08x\n", snapshot->reg.ipehr);
- if (snapshot->class == XE_ENGINE_CLASS_COMPUTE)
+ xe_hw_engine_snapshot_instdone_print(snapshot, p);
+
+ if (snapshot->hwe->class == XE_ENGINE_CLASS_COMPUTE)
drm_printf(p, "\tRCU_MODE: 0x%08x\n",
snapshot->reg.rcu_mode);
+ drm_puts(p, "\n");
}
/**
@@ -904,6 +1046,12 @@ void xe_hw_engine_snapshot_free(struct xe_hw_engine_snapshot *snapshot)
if (!snapshot)
return;
+ kfree(snapshot->reg.instdone.slice_common);
+ kfree(snapshot->reg.instdone.slice_common_extra);
+ kfree(snapshot->reg.instdone.slice_common_extra2);
+ kfree(snapshot->reg.instdone.sampler);
+ kfree(snapshot->reg.instdone.row);
+ kfree(snapshot->reg.instdone.geom_svg);
kfree(snapshot->name);
kfree(snapshot);
}
@@ -955,3 +1103,35 @@ bool xe_hw_engine_is_reserved(struct xe_hw_engine *hwe)
return xe->info.has_usm && hwe->class == XE_ENGINE_CLASS_COPY &&
hwe->instance == gt->usm.reserved_bcs_instance;
}
+
+const char *xe_hw_engine_class_to_str(enum xe_engine_class class)
+{
+ switch (class) {
+ case XE_ENGINE_CLASS_RENDER:
+ return "rcs";
+ case XE_ENGINE_CLASS_VIDEO_DECODE:
+ return "vcs";
+ case XE_ENGINE_CLASS_VIDEO_ENHANCE:
+ return "vecs";
+ case XE_ENGINE_CLASS_COPY:
+ return "bcs";
+ case XE_ENGINE_CLASS_OTHER:
+ return "other";
+ case XE_ENGINE_CLASS_COMPUTE:
+ return "ccs";
+ case XE_ENGINE_CLASS_MAX:
+ break;
+ }
+
+ return NULL;
+}
+
+u64 xe_hw_engine_read_timestamp(struct xe_hw_engine *hwe)
+{
+ return xe_mmio_read64_2x32(hwe->gt, RING_TIMESTAMP(hwe->mmio_base));
+}
+
+enum xe_force_wake_domains xe_hw_engine_to_fw_domain(struct xe_hw_engine *hwe)
+{
+ return engine_infos[hwe->engine_id].domain;
+}
diff --git a/drivers/gpu/drm/xe/xe_hw_engine.h b/drivers/gpu/drm/xe/xe_hw_engine.h
index 71968ee2f600..900c8c991430 100644
--- a/drivers/gpu/drm/xe/xe_hw_engine.h
+++ b/drivers/gpu/drm/xe/xe_hw_engine.h
@@ -67,4 +67,8 @@ static inline bool xe_hw_engine_is_valid(struct xe_hw_engine *hwe)
return hwe->name;
}
+const char *xe_hw_engine_class_to_str(enum xe_engine_class class);
+u64 xe_hw_engine_read_timestamp(struct xe_hw_engine *hwe);
+enum xe_force_wake_domains xe_hw_engine_to_fw_domain(struct xe_hw_engine *hwe);
+
#endif
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 844ec68cbbb8..b53e8d2accdb 100644
--- a/drivers/gpu/drm/xe/xe_hw_engine_class_sysfs.c
+++ b/drivers/gpu/drm/xe/xe_hw_engine_class_sysfs.c
@@ -492,7 +492,7 @@ static const struct attribute * const files[] = {
NULL
};
-static void kobj_xe_hw_engine_class_fini(struct drm_device *drm, void *arg)
+static void kobj_xe_hw_engine_class_fini(void *arg)
{
struct kobject *kobj = arg;
@@ -517,7 +517,7 @@ kobj_xe_hw_engine_class(struct xe_device *xe, struct kobject *parent, const char
}
keclass->xe = xe;
- err = drmm_add_action_or_reset(&xe->drm, kobj_xe_hw_engine_class_fini,
+ err = devm_add_action_or_reset(xe->drm.dev, kobj_xe_hw_engine_class_fini,
&keclass->base);
if (err)
return NULL;
@@ -525,7 +525,7 @@ kobj_xe_hw_engine_class(struct xe_device *xe, struct kobject *parent, const char
return keclass;
}
-static void hw_engine_class_defaults_fini(struct drm_device *drm, void *arg)
+static void hw_engine_class_defaults_fini(void *arg)
{
struct kobject *kobj = arg;
@@ -552,7 +552,7 @@ static int xe_add_hw_engine_class_defaults(struct xe_device *xe,
if (err)
goto err_object;
- return drmm_add_action_or_reset(&xe->drm, hw_engine_class_defaults_fini, kobj);
+ return devm_add_action_or_reset(xe->drm.dev, hw_engine_class_defaults_fini, kobj);
err_object:
kobject_put(kobj);
@@ -611,31 +611,13 @@ static const struct kobj_type xe_hw_engine_sysfs_kobj_type = {
.sysfs_ops = &xe_hw_engine_class_sysfs_ops,
};
-static void hw_engine_class_sysfs_fini(struct drm_device *drm, void *arg)
+static void hw_engine_class_sysfs_fini(void *arg)
{
struct kobject *kobj = arg;
kobject_put(kobj);
}
-static const char *xe_hw_engine_class_to_str(enum xe_engine_class class)
-{
- switch (class) {
- case XE_ENGINE_CLASS_RENDER:
- return "rcs";
- case XE_ENGINE_CLASS_VIDEO_DECODE:
- return "vcs";
- case XE_ENGINE_CLASS_VIDEO_ENHANCE:
- return "vecs";
- case XE_ENGINE_CLASS_COPY:
- return "bcs";
- case XE_ENGINE_CLASS_COMPUTE:
- return "ccs";
- default:
- return NULL;
- }
-}
-
/**
* xe_hw_engine_class_sysfs_init - Init HW engine classes on GT.
* @gt: Xe GT.
@@ -698,7 +680,7 @@ int xe_hw_engine_class_sysfs_init(struct xe_gt *gt)
goto err_object;
}
- return drmm_add_action_or_reset(&xe->drm, hw_engine_class_sysfs_fini, kobj);
+ return devm_add_action_or_reset(xe->drm.dev, hw_engine_class_sysfs_fini, kobj);
err_object:
kobject_put(kobj);
diff --git a/drivers/gpu/drm/xe/xe_hw_engine_types.h b/drivers/gpu/drm/xe/xe_hw_engine_types.h
index d7f828c76cc5..70e6434f150d 100644
--- a/drivers/gpu/drm/xe/xe_hw_engine_types.h
+++ b/drivers/gpu/drm/xe/xe_hw_engine_types.h
@@ -137,7 +137,7 @@ struct xe_hw_engine {
/** @hwsp: hardware status page buffer object */
struct xe_bo *hwsp;
/** @kernel_lrc: Kernel LRC (should be replaced /w an xe_engine) */
- struct xe_lrc kernel_lrc;
+ struct xe_lrc *kernel_lrc;
/** @exl_port: execlists port */
struct xe_execlist_port *exl_port;
/** @fence_irq: fence IRQ to run when a hw engine IRQ is received */
@@ -148,6 +148,8 @@ struct xe_hw_engine {
enum xe_hw_engine_id engine_id;
/** @eclass: pointer to per hw engine class interface */
struct xe_hw_engine_class_intf *eclass;
+ /** @oa_unit: oa unit for this hw engine */
+ struct xe_oa_unit *oa_unit;
};
/**
@@ -158,8 +160,8 @@ struct xe_hw_engine {
struct xe_hw_engine_snapshot {
/** @name: name of the hw engine */
char *name;
- /** @class: class of this hw engine */
- enum xe_engine_class class;
+ /** @hwe: hw engine */
+ struct xe_hw_engine *hwe;
/** @logical_instance: logical instance of this hw engine */
u16 logical_instance;
/** @forcewake: Force Wake information snapshot */
@@ -188,7 +190,7 @@ struct xe_hw_engine_snapshot {
/** @reg.ring_hws_pga: RING_HWS_PGA */
u32 ring_hws_pga;
/** @reg.ring_start: RING_START */
- u32 ring_start;
+ u64 ring_start;
/** @reg.ring_head: RING_HEAD */
u32 ring_head;
/** @reg.ring_tail: RING_TAIL */
@@ -207,10 +209,28 @@ struct xe_hw_engine_snapshot {
u32 ring_emr;
/** @reg.ring_eir: RING_EIR */
u32 ring_eir;
+ /** @reg.indirect_ring_state: INDIRECT_RING_STATE */
+ u32 indirect_ring_state;
/** @reg.ipehr: IPEHR */
u32 ipehr;
/** @reg.rcu_mode: RCU_MODE */
u32 rcu_mode;
+ struct {
+ /** @reg.instdone.ring: RING_INSTDONE */
+ u32 ring;
+ /** @reg.instdone.slice_common: SC_INSTDONE */
+ u32 *slice_common;
+ /** @reg.instdone.slice_common_extra: SC_INSTDONE_EXTRA */
+ u32 *slice_common_extra;
+ /** @reg.instdone.slice_common_extra2: SC_INSTDONE_EXTRA2 */
+ u32 *slice_common_extra2;
+ /** @reg.instdone.sampler: SAMPLER_INSTDONE */
+ u32 *sampler;
+ /** @reg.instdone.row: ROW_INSTDONE */
+ u32 *row;
+ /** @reg.instdone.geom_svg: INSTDONE_GEOM_SVGUNIT */
+ u32 *geom_svg;
+ } instdone;
} reg;
};
diff --git a/drivers/gpu/drm/xe/xe_hw_fence.c b/drivers/gpu/drm/xe/xe_hw_fence.c
index f872ef103127..45a9789cf501 100644
--- a/drivers/gpu/drm/xe/xe_hw_fence.c
+++ b/drivers/gpu/drm/xe/xe_hw_fence.c
@@ -187,7 +187,6 @@ static void xe_hw_fence_release(struct dma_fence *dma_fence)
{
struct xe_hw_fence *fence = to_xe_hw_fence(dma_fence);
- trace_xe_hw_fence_free(fence);
XE_WARN_ON(!list_empty(&fence->irq_link));
call_rcu(&dma_fence->rcu, fence_free);
}
@@ -208,23 +207,58 @@ static struct xe_hw_fence *to_xe_hw_fence(struct dma_fence *fence)
return container_of(fence, struct xe_hw_fence, dma);
}
-struct xe_hw_fence *xe_hw_fence_create(struct xe_hw_fence_ctx *ctx,
- struct iosys_map seqno_map)
+/**
+ * xe_hw_fence_alloc() - Allocate an hw fence.
+ *
+ * Allocate but don't initialize an hw fence.
+ *
+ * Return: Pointer to the allocated fence or
+ * negative error pointer on error.
+ */
+struct dma_fence *xe_hw_fence_alloc(void)
{
- struct xe_hw_fence *fence;
+ struct xe_hw_fence *hw_fence = fence_alloc();
- fence = fence_alloc();
- if (!fence)
+ if (!hw_fence)
return ERR_PTR(-ENOMEM);
- fence->ctx = ctx;
- fence->seqno_map = seqno_map;
- INIT_LIST_HEAD(&fence->irq_link);
+ return &hw_fence->dma;
+}
- dma_fence_init(&fence->dma, &xe_hw_fence_ops, &ctx->irq->lock,
- ctx->dma_fence_ctx, ctx->next_seqno++);
+/**
+ * xe_hw_fence_free() - Free an hw fence.
+ * @fence: Pointer to the fence to free.
+ *
+ * Frees an hw fence that hasn't yet been
+ * initialized.
+ */
+void xe_hw_fence_free(struct dma_fence *fence)
+{
+ fence_free(&fence->rcu);
+}
- trace_xe_hw_fence_create(fence);
+/**
+ * xe_hw_fence_init() - Initialize an hw fence.
+ * @fence: Pointer to the fence to initialize.
+ * @ctx: Pointer to the struct xe_hw_fence_ctx fence context.
+ * @seqno_map: Pointer to the map into where the seqno is blitted.
+ *
+ * Initializes a pre-allocated hw fence.
+ * After initialization, the fence is subject to normal
+ * dma-fence refcounting.
+ */
+void xe_hw_fence_init(struct dma_fence *fence, struct xe_hw_fence_ctx *ctx,
+ struct iosys_map seqno_map)
+{
+ struct xe_hw_fence *hw_fence =
+ container_of(fence, typeof(*hw_fence), dma);
+
+ hw_fence->ctx = ctx;
+ hw_fence->seqno_map = seqno_map;
+ INIT_LIST_HEAD(&hw_fence->irq_link);
+
+ dma_fence_init(fence, &xe_hw_fence_ops, &ctx->irq->lock,
+ ctx->dma_fence_ctx, ctx->next_seqno++);
- return fence;
+ trace_xe_hw_fence_create(hw_fence);
}
diff --git a/drivers/gpu/drm/xe/xe_hw_fence.h b/drivers/gpu/drm/xe/xe_hw_fence.h
index cfe5fd603787..f13a1c4982c7 100644
--- a/drivers/gpu/drm/xe/xe_hw_fence.h
+++ b/drivers/gpu/drm/xe/xe_hw_fence.h
@@ -24,7 +24,10 @@ 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);
void xe_hw_fence_ctx_finish(struct xe_hw_fence_ctx *ctx);
-struct xe_hw_fence *xe_hw_fence_create(struct xe_hw_fence_ctx *ctx,
- struct iosys_map seqno_map);
+struct dma_fence *xe_hw_fence_alloc(void);
+void xe_hw_fence_free(struct dma_fence *fence);
+
+void xe_hw_fence_init(struct dma_fence *fence, struct xe_hw_fence_ctx *ctx,
+ struct iosys_map seqno_map);
#endif
diff --git a/drivers/gpu/drm/xe/xe_hwmon.c b/drivers/gpu/drm/xe/xe_hwmon.c
index 453e601ddd5e..0c8ce09e5025 100644
--- a/drivers/gpu/drm/xe/xe_hwmon.c
+++ b/drivers/gpu/drm/xe/xe_hwmon.c
@@ -86,19 +86,29 @@ static struct xe_reg xe_hwmon_get_reg(struct xe_hwmon *hwmon, enum xe_hwmon_reg
switch (hwmon_reg) {
case REG_PKG_RAPL_LIMIT:
- if (xe->info.platform == XE_PVC && channel == CHANNEL_PKG)
+ if (xe->info.platform == XE_BATTLEMAGE) {
+ if (channel == CHANNEL_PKG)
+ return BMG_PACKAGE_RAPL_LIMIT;
+ else
+ return BMG_PLATFORM_POWER_LIMIT;
+ } else if (xe->info.platform == XE_PVC && channel == CHANNEL_PKG) {
return PVC_GT0_PACKAGE_RAPL_LIMIT;
- else if ((xe->info.platform == XE_DG2) && (channel == CHANNEL_PKG))
+ } else if ((xe->info.platform == XE_DG2) && (channel == CHANNEL_PKG)) {
return PCU_CR_PACKAGE_RAPL_LIMIT;
+ }
break;
case REG_PKG_POWER_SKU:
- if (xe->info.platform == XE_PVC && channel == CHANNEL_PKG)
+ if (xe->info.platform == XE_BATTLEMAGE)
+ return BMG_PACKAGE_POWER_SKU;
+ else if (xe->info.platform == XE_PVC && channel == CHANNEL_PKG)
return PVC_GT0_PACKAGE_POWER_SKU;
else if ((xe->info.platform == XE_DG2) && (channel == CHANNEL_PKG))
return PCU_CR_PACKAGE_POWER_SKU;
break;
case REG_PKG_POWER_SKU_UNIT:
- if (xe->info.platform == XE_PVC)
+ if (xe->info.platform == XE_BATTLEMAGE)
+ return BMG_PACKAGE_POWER_SKU_UNIT;
+ else if (xe->info.platform == XE_PVC)
return PVC_GT0_PACKAGE_POWER_SKU_UNIT;
else if (xe->info.platform == XE_DG2)
return PCU_CR_PACKAGE_POWER_SKU_UNIT;
@@ -108,10 +118,16 @@ static struct xe_reg xe_hwmon_get_reg(struct xe_hwmon *hwmon, enum xe_hwmon_reg
return GT_PERF_STATUS;
break;
case REG_PKG_ENERGY_STATUS:
- if (xe->info.platform == XE_PVC && channel == CHANNEL_PKG)
+ if (xe->info.platform == XE_BATTLEMAGE) {
+ if (channel == CHANNEL_PKG)
+ return BMG_PACKAGE_ENERGY_STATUS;
+ else
+ return BMG_PLATFORM_ENERGY_STATUS;
+ } else if (xe->info.platform == XE_PVC && channel == CHANNEL_PKG) {
return PVC_GT0_PLATFORM_ENERGY_STATUS;
- else if ((xe->info.platform == XE_DG2) && (channel == CHANNEL_PKG))
+ } else if ((xe->info.platform == XE_DG2) && (channel == CHANNEL_PKG)) {
return PCU_CR_PACKAGE_ENERGY_STATUS;
+ }
break;
default:
drm_warn(&xe->drm, "Unknown xe hwmon reg id: %d\n", hwmon_reg);
@@ -121,34 +137,6 @@ static struct xe_reg xe_hwmon_get_reg(struct xe_hwmon *hwmon, enum xe_hwmon_reg
return XE_REG(0);
}
-static void xe_hwmon_process_reg(struct xe_hwmon *hwmon, enum xe_hwmon_reg hwmon_reg,
- enum xe_hwmon_reg_operation operation, u64 *value,
- u32 clr, u32 set, int channel)
-{
- struct xe_reg reg;
-
- reg = xe_hwmon_get_reg(hwmon, hwmon_reg, channel);
-
- if (!xe_reg_is_valid(reg))
- return;
-
- switch (operation) {
- case REG_READ32:
- *value = xe_mmio_read32(hwmon->gt, reg);
- break;
- case REG_RMW32:
- *value = xe_mmio_rmw32(hwmon->gt, reg, clr, set);
- break;
- case REG_READ64:
- *value = xe_mmio_read64_2x32(hwmon->gt, reg);
- break;
- default:
- drm_warn(&gt_to_xe(hwmon->gt)->drm, "Invalid xe hwmon reg operation: %d\n",
- operation);
- break;
- }
-}
-
#define PL1_DISABLE 0
/*
@@ -160,10 +148,25 @@ static void xe_hwmon_process_reg(struct xe_hwmon *hwmon, enum xe_hwmon_reg hwmon
static void xe_hwmon_power_max_read(struct xe_hwmon *hwmon, int channel, long *value)
{
u64 reg_val, min, max;
+ struct xe_device *xe = gt_to_xe(hwmon->gt);
+ struct xe_reg rapl_limit, pkg_power_sku;
+
+ rapl_limit = xe_hwmon_get_reg(hwmon, REG_PKG_RAPL_LIMIT, channel);
+ pkg_power_sku = xe_hwmon_get_reg(hwmon, REG_PKG_POWER_SKU, channel);
+
+ /*
+ * Valid check of REG_PKG_RAPL_LIMIT is already done in xe_hwmon_power_is_visible.
+ * So not checking it again here.
+ */
+ if (!xe_reg_is_valid(pkg_power_sku)) {
+ drm_warn(&xe->drm, "pkg_power_sku invalid\n");
+ *value = 0;
+ return;
+ }
mutex_lock(&hwmon->hwmon_lock);
- xe_hwmon_process_reg(hwmon, REG_PKG_RAPL_LIMIT, REG_READ32, &reg_val, 0, 0, channel);
+ reg_val = xe_mmio_read32(hwmon->gt, rapl_limit);
/* Check if PL1 limit is disabled */
if (!(reg_val & PKG_PWR_LIM_1_EN)) {
*value = PL1_DISABLE;
@@ -173,7 +176,7 @@ static void xe_hwmon_power_max_read(struct xe_hwmon *hwmon, int channel, long *v
reg_val = REG_FIELD_GET(PKG_PWR_LIM_1, reg_val);
*value = mul_u64_u32_shr(reg_val, SF_POWER, hwmon->scl_shift_power);
- xe_hwmon_process_reg(hwmon, REG_PKG_POWER_SKU, REG_READ64, &reg_val, 0, 0, channel);
+ reg_val = xe_mmio_read64_2x32(hwmon->gt, pkg_power_sku);
min = REG_FIELD_GET(PKG_MIN_PWR, reg_val);
min = mul_u64_u32_shr(min, SF_POWER, hwmon->scl_shift_power);
max = REG_FIELD_GET(PKG_MAX_PWR, reg_val);
@@ -189,16 +192,16 @@ static int xe_hwmon_power_max_write(struct xe_hwmon *hwmon, int channel, long va
{
int ret = 0;
u64 reg_val;
+ struct xe_reg rapl_limit;
+
+ rapl_limit = xe_hwmon_get_reg(hwmon, REG_PKG_RAPL_LIMIT, channel);
mutex_lock(&hwmon->hwmon_lock);
/* Disable PL1 limit and verify, as limit cannot be disabled on all platforms */
if (value == PL1_DISABLE) {
- xe_hwmon_process_reg(hwmon, REG_PKG_RAPL_LIMIT, REG_RMW32, &reg_val,
- PKG_PWR_LIM_1_EN, 0, channel);
- xe_hwmon_process_reg(hwmon, REG_PKG_RAPL_LIMIT, REG_READ32, &reg_val,
- PKG_PWR_LIM_1_EN, 0, channel);
-
+ reg_val = xe_mmio_rmw32(hwmon->gt, rapl_limit, PKG_PWR_LIM_1_EN, 0);
+ reg_val = xe_mmio_read32(hwmon->gt, rapl_limit);
if (reg_val & PKG_PWR_LIM_1_EN) {
ret = -EOPNOTSUPP;
goto unlock;
@@ -208,9 +211,8 @@ static int xe_hwmon_power_max_write(struct xe_hwmon *hwmon, int channel, long va
/* Computation in 64-bits to avoid overflow. Round to nearest. */
reg_val = DIV_ROUND_CLOSEST_ULL((u64)value << hwmon->scl_shift_power, SF_POWER);
reg_val = PKG_PWR_LIM_1_EN | REG_FIELD_PREP(PKG_PWR_LIM_1, reg_val);
+ reg_val = xe_mmio_rmw32(hwmon->gt, rapl_limit, PKG_PWR_LIM_1_EN | PKG_PWR_LIM_1, reg_val);
- xe_hwmon_process_reg(hwmon, REG_PKG_RAPL_LIMIT, REG_RMW32, &reg_val,
- PKG_PWR_LIM_1_EN | PKG_PWR_LIM_1, reg_val, channel);
unlock:
mutex_unlock(&hwmon->hwmon_lock);
return ret;
@@ -218,9 +220,15 @@ unlock:
static void xe_hwmon_power_rated_max_read(struct xe_hwmon *hwmon, int channel, long *value)
{
+ struct xe_reg reg = xe_hwmon_get_reg(hwmon, REG_PKG_POWER_SKU, channel);
u64 reg_val;
- xe_hwmon_process_reg(hwmon, REG_PKG_POWER_SKU, REG_READ32, &reg_val, 0, 0, channel);
+ /*
+ * This sysfs file won't be visible if REG_PKG_POWER_SKU is invalid, so valid check
+ * for this register can be skipped.
+ * See xe_hwmon_power_is_visible.
+ */
+ reg_val = xe_mmio_read32(hwmon->gt, reg);
reg_val = REG_FIELD_GET(PKG_TDP, reg_val);
*value = mul_u64_u32_shr(reg_val, SF_POWER, hwmon->scl_shift_power);
}
@@ -251,8 +259,8 @@ xe_hwmon_energy_get(struct xe_hwmon *hwmon, int channel, long *energy)
struct xe_hwmon_energy_info *ei = &hwmon->ei[channel];
u64 reg_val;
- xe_hwmon_process_reg(hwmon, REG_PKG_ENERGY_STATUS, REG_READ32,
- &reg_val, 0, 0, channel);
+ reg_val = xe_mmio_read32(hwmon->gt, xe_hwmon_get_reg(hwmon, REG_PKG_ENERGY_STATUS,
+ channel));
if (reg_val >= ei->reg_val_prev)
ei->accum_energy += reg_val - ei->reg_val_prev;
@@ -278,8 +286,7 @@ xe_hwmon_power_max_interval_show(struct device *dev, struct device_attribute *at
mutex_lock(&hwmon->hwmon_lock);
- xe_hwmon_process_reg(hwmon, REG_PKG_RAPL_LIMIT,
- REG_READ32, &r, 0, 0, sensor_index);
+ r = xe_mmio_read32(hwmon->gt, xe_hwmon_get_reg(hwmon, REG_PKG_RAPL_LIMIT, sensor_index));
mutex_unlock(&hwmon->hwmon_lock);
@@ -367,8 +374,8 @@ xe_hwmon_power_max_interval_store(struct device *dev, struct device_attribute *a
mutex_lock(&hwmon->hwmon_lock);
- xe_hwmon_process_reg(hwmon, REG_PKG_RAPL_LIMIT, REG_RMW32, (u64 *)&r,
- PKG_PWR_LIM_1_TIME, rxy, sensor_index);
+ r = xe_mmio_rmw32(hwmon->gt, xe_hwmon_get_reg(hwmon, REG_PKG_RAPL_LIMIT, sensor_index),
+ PKG_PWR_LIM_1_TIME, rxy);
mutex_unlock(&hwmon->hwmon_lock);
@@ -483,8 +490,7 @@ static void xe_hwmon_get_voltage(struct xe_hwmon *hwmon, int channel, long *valu
{
u64 reg_val;
- xe_hwmon_process_reg(hwmon, REG_GT_PERF_STATUS,
- REG_READ32, &reg_val, 0, 0, channel);
+ reg_val = xe_mmio_read32(hwmon->gt, xe_hwmon_get_reg(hwmon, REG_GT_PERF_STATUS, channel));
/* HW register value in units of 2.5 millivolt */
*value = DIV_ROUND_CLOSEST(REG_FIELD_GET(VOLTAGE_MASK, reg_val) * 2500, SF_VOLTAGE);
}
@@ -550,12 +556,17 @@ xe_hwmon_curr_is_visible(const struct xe_hwmon *hwmon, u32 attr, int channel)
{
u32 uval;
+ /* hwmon sysfs attribute of current available only for package */
+ if (channel != CHANNEL_PKG)
+ return 0;
+
switch (attr) {
case hwmon_curr_crit:
- case hwmon_curr_label:
- if (channel == CHANNEL_PKG)
return (xe_hwmon_pcode_read_i1(hwmon->gt, &uval) ||
(uval & POWER_SETUP_I1_WATTS)) ? 0 : 0644;
+ case hwmon_curr_label:
+ return (xe_hwmon_pcode_read_i1(hwmon->gt, &uval) ||
+ (uval & POWER_SETUP_I1_WATTS)) ? 0 : 0444;
break;
default:
return 0;
@@ -763,14 +774,15 @@ xe_hwmon_get_preregistration_info(struct xe_device *xe)
long energy;
u64 val_sku_unit = 0;
int channel;
+ struct xe_reg pkg_power_sku_unit;
/*
* The contents of register PKG_POWER_SKU_UNIT do not change,
* so read it once and store the shift values.
*/
- if (xe_reg_is_valid(xe_hwmon_get_reg(hwmon, REG_PKG_POWER_SKU_UNIT, 0))) {
- xe_hwmon_process_reg(hwmon, REG_PKG_POWER_SKU_UNIT,
- REG_READ32, &val_sku_unit, 0, 0, 0);
+ pkg_power_sku_unit = xe_hwmon_get_reg(hwmon, REG_PKG_POWER_SKU_UNIT, 0);
+ if (xe_reg_is_valid(pkg_power_sku_unit)) {
+ val_sku_unit = xe_mmio_read32(hwmon->gt, pkg_power_sku_unit);
hwmon->scl_shift_power = REG_FIELD_GET(PKG_PWR_UNIT, val_sku_unit);
hwmon->scl_shift_energy = REG_FIELD_GET(PKG_ENERGY_UNIT, val_sku_unit);
hwmon->scl_shift_time = REG_FIELD_GET(PKG_TIME_UNIT, val_sku_unit);
diff --git a/drivers/gpu/drm/xe/xe_irq.c b/drivers/gpu/drm/xe/xe_irq.c
index 996806353171..85733f993d09 100644
--- a/drivers/gpu/drm/xe/xe_irq.c
+++ b/drivers/gpu/drm/xe/xe_irq.c
@@ -134,6 +134,9 @@ void xe_irq_enable_hwe(struct xe_gt *gt)
u32 gsc_mask = 0;
u32 heci_mask = 0;
+ if (IS_SRIOV_VF(xe) && xe_device_has_memirq(xe))
+ return;
+
if (xe_device_uc_enabled(xe)) {
irqs = GT_RENDER_USER_INTERRUPT |
GT_RENDER_PIPECTL_NOTIFY_INTERRUPT;
@@ -663,7 +666,7 @@ static irq_handler_t xe_irq_handler(struct xe_device *xe)
return xelp_irq_handler;
}
-static void irq_uninstall(struct drm_device *drm, void *arg)
+static void irq_uninstall(void *arg)
{
struct xe_device *xe = arg;
struct pci_dev *pdev = to_pci_dev(xe->drm.dev);
@@ -723,7 +726,7 @@ int xe_irq_install(struct xe_device *xe)
xe_irq_postinstall(xe);
- err = drmm_add_action_or_reset(&xe->drm, irq_uninstall, xe);
+ err = devm_add_action_or_reset(xe->drm.dev, irq_uninstall, xe);
if (err)
goto free_irq_handler;
@@ -735,11 +738,6 @@ free_irq_handler:
return err;
}
-void xe_irq_shutdown(struct xe_device *xe)
-{
- irq_uninstall(&xe->drm, xe);
-}
-
void xe_irq_suspend(struct xe_device *xe)
{
int irq = to_pci_dev(xe->drm.dev)->irq;
diff --git a/drivers/gpu/drm/xe/xe_irq.h b/drivers/gpu/drm/xe/xe_irq.h
index bc42bc90d967..067514e13675 100644
--- a/drivers/gpu/drm/xe/xe_irq.h
+++ b/drivers/gpu/drm/xe/xe_irq.h
@@ -11,7 +11,6 @@ struct xe_tile;
struct xe_gt;
int xe_irq_install(struct xe_device *xe);
-void xe_irq_shutdown(struct xe_device *xe);
void xe_irq_suspend(struct xe_device *xe);
void xe_irq_resume(struct xe_device *xe);
void xe_irq_enable_hwe(struct xe_gt *gt);
diff --git a/drivers/gpu/drm/xe/xe_lrc.c b/drivers/gpu/drm/xe/xe_lrc.c
index 615bbc372ac6..94ff62e1d95e 100644
--- a/drivers/gpu/drm/xe/xe_lrc.c
+++ b/drivers/gpu/drm/xe/xe_lrc.c
@@ -11,7 +11,6 @@
#include "instructions/xe_gfxpipe_commands.h"
#include "instructions/xe_gfx_state_commands.h"
#include "regs/xe_engine_regs.h"
-#include "regs/xe_gpu_commands.h"
#include "regs/xe_lrc_layout.h"
#include "xe_bb.h"
#include "xe_bo.h"
@@ -34,12 +33,15 @@
#define LRC_ENGINE_CLASS GENMASK_ULL(63, 61)
#define LRC_ENGINE_INSTANCE GENMASK_ULL(53, 48)
+#define LRC_INDIRECT_RING_STATE_SIZE SZ_4K
+
struct xe_lrc_snapshot {
struct xe_bo *lrc_bo;
void *lrc_snapshot;
unsigned long lrc_size, lrc_offset;
u32 context_desc;
+ u32 indirect_context_desc;
u32 head;
struct {
u32 internal;
@@ -47,6 +49,8 @@ struct xe_lrc_snapshot {
} tail;
u32 start_seqno;
u32 seqno;
+ u32 ctx_timestamp;
+ u32 ctx_job_timestamp;
};
static struct xe_device *
@@ -55,20 +59,25 @@ lrc_to_xe(struct xe_lrc *lrc)
return gt_to_xe(lrc->fence_ctx.gt);
}
-size_t xe_lrc_size(struct xe_device *xe, enum xe_engine_class class)
+size_t xe_gt_lrc_size(struct xe_gt *gt, enum xe_engine_class class)
{
+ struct xe_device *xe = gt_to_xe(gt);
+ size_t size;
+
switch (class) {
case XE_ENGINE_CLASS_RENDER:
if (GRAPHICS_VER(xe) >= 20)
- return 4 * SZ_4K;
+ size = 4 * SZ_4K;
else
- return 14 * SZ_4K;
+ size = 14 * SZ_4K;
+ break;
case XE_ENGINE_CLASS_COMPUTE:
/* 14 pages since graphics_ver == 11 */
if (GRAPHICS_VER(xe) >= 20)
- return 3 * SZ_4K;
+ size = 3 * SZ_4K;
else
- return 14 * SZ_4K;
+ size = 14 * SZ_4K;
+ break;
default:
WARN(1, "Unknown engine class: %d", class);
fallthrough;
@@ -76,8 +85,14 @@ size_t xe_lrc_size(struct xe_device *xe, enum xe_engine_class class)
case XE_ENGINE_CLASS_VIDEO_DECODE:
case XE_ENGINE_CLASS_VIDEO_ENHANCE:
case XE_ENGINE_CLASS_OTHER:
- return 2 * SZ_4K;
+ size = 2 * SZ_4K;
}
+
+ /* Add indirect ring state page */
+ if (xe_gt_has_indirect_ring_state(gt))
+ size += LRC_INDIRECT_RING_STATE_SIZE;
+
+ return size;
}
/*
@@ -508,6 +523,32 @@ static const u8 xe2_xcs_offsets[] = {
0
};
+static const u8 xe2_indirect_ring_state_offsets[] = {
+ NOP(1), /* [0x00] */
+ LRI(5, POSTED), /* [0x01] */
+ REG(0x034), /* [0x02] RING_BUFFER_HEAD */
+ REG(0x030), /* [0x04] RING_BUFFER_TAIL */
+ REG(0x038), /* [0x06] RING_BUFFER_START */
+ REG(0x048), /* [0x08] RING_BUFFER_START_UDW */
+ REG(0x03c), /* [0x0a] RING_BUFFER_CONTROL */
+
+ NOP(5), /* [0x0c] */
+ LRI(9, POSTED), /* [0x11] */
+ REG(0x168), /* [0x12] BB_ADDR_UDW */
+ REG(0x140), /* [0x14] BB_ADDR */
+ REG(0x110), /* [0x16] BB_STATE */
+ REG16(0x588), /* [0x18] BB_STACK_WRITE_PORT */
+ REG16(0x588), /* [0x20] BB_STACK_WRITE_PORT */
+ REG16(0x588), /* [0x22] BB_STACK_WRITE_PORT */
+ REG16(0x588), /* [0x24] BB_STACK_WRITE_PORT */
+ REG16(0x588), /* [0x26] BB_STACK_WRITE_PORT */
+ REG16(0x588), /* [0x28] BB_STACK_WRITE_PORT */
+
+ NOP(12), /* [0x00] */
+
+ 0
+};
+
#undef REG16
#undef REG
#undef LRI
@@ -546,6 +587,10 @@ static void set_context_control(u32 *regs, struct xe_hw_engine *hwe)
regs[CTX_CONTEXT_CONTROL] = _MASKED_BIT_ENABLE(CTX_CTRL_INHIBIT_SYN_CTX_SWITCH |
CTX_CTRL_ENGINE_CTX_RESTORE_INHIBIT);
+ if (xe_gt_has_indirect_ring_state(hwe->gt))
+ regs[CTX_CONTEXT_CONTROL] |=
+ _MASKED_BIT_ENABLE(CTX_CTRL_INDIRECT_RING_STATE_ENABLE);
+
/* TODO: Timestamp */
}
@@ -589,6 +634,11 @@ static void reset_stop_ring(u32 *regs, struct xe_hw_engine *hwe)
regs[x + 1] |= STOP_RING << 16;
}
+static inline bool xe_lrc_has_indirect_ring_state(struct xe_lrc *lrc)
+{
+ return lrc->flags & XE_LRC_FLAG_INDIRECT_RING_STATE;
+}
+
static inline u32 __xe_lrc_ring_offset(struct xe_lrc *lrc)
{
return 0;
@@ -601,12 +651,19 @@ u32 xe_lrc_pphwsp_offset(struct xe_lrc *lrc)
/* Make the magic macros work */
#define __xe_lrc_pphwsp_offset xe_lrc_pphwsp_offset
+#define __xe_lrc_regs_offset xe_lrc_regs_offset
#define LRC_SEQNO_PPHWSP_OFFSET 512
#define LRC_START_SEQNO_PPHWSP_OFFSET (LRC_SEQNO_PPHWSP_OFFSET + 8)
+#define LRC_CTX_JOB_TIMESTAMP_OFFSET (LRC_START_SEQNO_PPHWSP_OFFSET + 8)
#define LRC_PARALLEL_PPHWSP_OFFSET 2048
#define LRC_PPHWSP_SIZE SZ_4K
+u32 xe_lrc_regs_offset(struct xe_lrc *lrc)
+{
+ return xe_lrc_pphwsp_offset(lrc) + LRC_PPHWSP_SIZE;
+}
+
static size_t lrc_reg_size(struct xe_device *xe)
{
if (GRAPHICS_VERx100(xe) >= 1250)
@@ -632,15 +689,27 @@ static inline u32 __xe_lrc_start_seqno_offset(struct xe_lrc *lrc)
return xe_lrc_pphwsp_offset(lrc) + LRC_START_SEQNO_PPHWSP_OFFSET;
}
+static u32 __xe_lrc_ctx_job_timestamp_offset(struct xe_lrc *lrc)
+{
+ /* The start seqno is stored in the driver-defined portion of PPHWSP */
+ return xe_lrc_pphwsp_offset(lrc) + LRC_CTX_JOB_TIMESTAMP_OFFSET;
+}
+
static inline u32 __xe_lrc_parallel_offset(struct xe_lrc *lrc)
{
/* The parallel is stored in the driver-defined portion of PPHWSP */
return xe_lrc_pphwsp_offset(lrc) + LRC_PARALLEL_PPHWSP_OFFSET;
}
-static inline u32 __xe_lrc_regs_offset(struct xe_lrc *lrc)
+static u32 __xe_lrc_ctx_timestamp_offset(struct xe_lrc *lrc)
{
- return xe_lrc_pphwsp_offset(lrc) + LRC_PPHWSP_SIZE;
+ return __xe_lrc_regs_offset(lrc) + CTX_TIMESTAMP * sizeof(u32);
+}
+
+static inline u32 __xe_lrc_indirect_ring_offset(struct xe_lrc *lrc)
+{
+ /* Indirect ring state page is at the very end of LRC */
+ return lrc->size - LRC_INDIRECT_RING_STATE_SIZE;
}
#define DECL_MAP_ADDR_HELPERS(elem) \
@@ -662,15 +731,99 @@ DECL_MAP_ADDR_HELPERS(pphwsp)
DECL_MAP_ADDR_HELPERS(seqno)
DECL_MAP_ADDR_HELPERS(regs)
DECL_MAP_ADDR_HELPERS(start_seqno)
+DECL_MAP_ADDR_HELPERS(ctx_job_timestamp)
+DECL_MAP_ADDR_HELPERS(ctx_timestamp)
DECL_MAP_ADDR_HELPERS(parallel)
+DECL_MAP_ADDR_HELPERS(indirect_ring)
#undef DECL_MAP_ADDR_HELPERS
+/**
+ * xe_lrc_ctx_timestamp_ggtt_addr() - Get ctx timestamp GGTT address
+ * @lrc: Pointer to the lrc.
+ *
+ * Returns: ctx timestamp GGTT address
+ */
+u32 xe_lrc_ctx_timestamp_ggtt_addr(struct xe_lrc *lrc)
+{
+ return __xe_lrc_ctx_timestamp_ggtt_addr(lrc);
+}
+
+/**
+ * xe_lrc_ctx_timestamp() - Read ctx timestamp value
+ * @lrc: Pointer to the lrc.
+ *
+ * Returns: ctx timestamp value
+ */
+u32 xe_lrc_ctx_timestamp(struct xe_lrc *lrc)
+{
+ struct xe_device *xe = lrc_to_xe(lrc);
+ struct iosys_map map;
+
+ map = __xe_lrc_ctx_timestamp_map(lrc);
+ return xe_map_read32(xe, &map);
+}
+
+/**
+ * xe_lrc_ctx_job_timestamp_ggtt_addr() - Get ctx job timestamp GGTT address
+ * @lrc: Pointer to the lrc.
+ *
+ * Returns: ctx timestamp job GGTT address
+ */
+u32 xe_lrc_ctx_job_timestamp_ggtt_addr(struct xe_lrc *lrc)
+{
+ return __xe_lrc_ctx_job_timestamp_ggtt_addr(lrc);
+}
+
+/**
+ * xe_lrc_ctx_job_timestamp() - Read ctx job timestamp value
+ * @lrc: Pointer to the lrc.
+ *
+ * Returns: ctx timestamp job value
+ */
+u32 xe_lrc_ctx_job_timestamp(struct xe_lrc *lrc)
+{
+ struct xe_device *xe = lrc_to_xe(lrc);
+ struct iosys_map map;
+
+ map = __xe_lrc_ctx_job_timestamp_map(lrc);
+ return xe_map_read32(xe, &map);
+}
+
u32 xe_lrc_ggtt_addr(struct xe_lrc *lrc)
{
return __xe_lrc_pphwsp_ggtt_addr(lrc);
}
+u32 xe_lrc_indirect_ring_ggtt_addr(struct xe_lrc *lrc)
+{
+ if (!xe_lrc_has_indirect_ring_state(lrc))
+ return 0;
+
+ return __xe_lrc_indirect_ring_ggtt_addr(lrc);
+}
+
+static u32 xe_lrc_read_indirect_ctx_reg(struct xe_lrc *lrc, int reg_nr)
+{
+ struct xe_device *xe = lrc_to_xe(lrc);
+ struct iosys_map map;
+
+ map = __xe_lrc_indirect_ring_map(lrc);
+ iosys_map_incr(&map, reg_nr * sizeof(u32));
+ return xe_map_read32(xe, &map);
+}
+
+static void xe_lrc_write_indirect_ctx_reg(struct xe_lrc *lrc,
+ int reg_nr, u32 val)
+{
+ struct xe_device *xe = lrc_to_xe(lrc);
+ struct iosys_map map;
+
+ map = __xe_lrc_indirect_ring_map(lrc);
+ iosys_map_incr(&map, reg_nr * sizeof(u32));
+ xe_map_write32(xe, &map, val);
+}
+
u32 xe_lrc_read_ctx_reg(struct xe_lrc *lrc, int reg_nr)
{
struct xe_device *xe = lrc_to_xe(lrc);
@@ -693,20 +846,25 @@ void xe_lrc_write_ctx_reg(struct xe_lrc *lrc, int reg_nr, u32 val)
static void *empty_lrc_data(struct xe_hw_engine *hwe)
{
- struct xe_device *xe = gt_to_xe(hwe->gt);
+ struct xe_gt *gt = hwe->gt;
void *data;
u32 *regs;
- data = kzalloc(xe_lrc_size(xe, hwe->class), GFP_KERNEL);
+ data = kzalloc(xe_gt_lrc_size(gt, hwe->class), GFP_KERNEL);
if (!data)
return NULL;
/* 1st page: Per-Process of HW status Page */
regs = data + LRC_PPHWSP_SIZE;
- set_offsets(regs, reg_offsets(xe, hwe->class), hwe);
+ set_offsets(regs, reg_offsets(gt_to_xe(gt), hwe->class), hwe);
set_context_control(regs, hwe);
set_memory_based_intr(regs, hwe);
reset_stop_ring(regs, hwe);
+ if (xe_gt_has_indirect_ring_state(gt)) {
+ regs = data + xe_gt_lrc_size(gt, hwe->class) -
+ LRC_INDIRECT_RING_STATE_SIZE;
+ set_offsets(regs, xe2_indirect_ring_state_offsets, hwe);
+ }
return data;
}
@@ -719,11 +877,20 @@ static void xe_lrc_set_ppgtt(struct xe_lrc *lrc, struct xe_vm *vm)
xe_lrc_write_ctx_reg(lrc, CTX_PDP0_LDW, lower_32_bits(desc));
}
+static void xe_lrc_finish(struct xe_lrc *lrc)
+{
+ xe_hw_fence_ctx_finish(&lrc->fence_ctx);
+ xe_bo_lock(lrc->bo, false);
+ xe_bo_unpin(lrc->bo);
+ xe_bo_unlock(lrc->bo);
+ xe_bo_put(lrc->bo);
+}
+
#define PVC_CTX_ASID (0x2e + 1)
#define PVC_CTX_ACC_CTR_THOLD (0x2a + 1)
-int xe_lrc_init(struct xe_lrc *lrc, struct xe_hw_engine *hwe,
- struct xe_exec_queue *q, struct xe_vm *vm, u32 ring_size)
+static int xe_lrc_init(struct xe_lrc *lrc, struct xe_hw_engine *hwe,
+ struct xe_vm *vm, u32 ring_size)
{
struct xe_gt *gt = hwe->gt;
struct xe_tile *tile = gt_to_tile(gt);
@@ -731,26 +898,32 @@ int xe_lrc_init(struct xe_lrc *lrc, struct xe_hw_engine *hwe,
struct iosys_map map;
void *init_data = NULL;
u32 arb_enable;
+ u32 lrc_size;
int err;
+ kref_init(&lrc->refcount);
lrc->flags = 0;
+ lrc_size = ring_size + xe_gt_lrc_size(gt, hwe->class);
+ if (xe_gt_has_indirect_ring_state(gt))
+ lrc->flags |= XE_LRC_FLAG_INDIRECT_RING_STATE;
/*
* FIXME: Perma-pinning LRC as we don't yet support moving GGTT address
* via VM bind calls.
*/
- lrc->bo = xe_bo_create_pin_map(xe, tile, vm,
- ring_size + xe_lrc_size(xe, hwe->class),
- ttm_bo_type_kernel,
- XE_BO_FLAG_VRAM_IF_DGFX(tile) |
- XE_BO_FLAG_GGTT |
- XE_BO_FLAG_GGTT_INVALIDATE);
+ lrc->bo = xe_bo_create_pin_map(xe, tile, vm, lrc_size,
+ ttm_bo_type_kernel,
+ XE_BO_FLAG_VRAM_IF_DGFX(tile) |
+ XE_BO_FLAG_GGTT |
+ XE_BO_FLAG_GGTT_INVALIDATE);
if (IS_ERR(lrc->bo))
return PTR_ERR(lrc->bo);
+ lrc->size = lrc_size;
lrc->tile = gt_to_tile(hwe->gt);
lrc->ring.size = ring_size;
lrc->ring.tail = 0;
+ lrc->ctx_timestamp = 0;
xe_hw_fence_ctx_init(&lrc->fence_ctx, hwe->gt,
hwe->fence_irq, hwe->name);
@@ -772,10 +945,10 @@ int xe_lrc_init(struct xe_lrc *lrc, struct xe_hw_engine *hwe,
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,
- xe_lrc_size(xe, hwe->class) - LRC_PPHWSP_SIZE);
+ xe_gt_lrc_size(gt, hwe->class) - LRC_PPHWSP_SIZE);
} else {
xe_map_memcpy_to(xe, &map, 0, init_data,
- xe_lrc_size(xe, hwe->class));
+ xe_gt_lrc_size(gt, hwe->class));
kfree(init_data);
}
@@ -786,11 +959,27 @@ int xe_lrc_init(struct xe_lrc *lrc, struct xe_hw_engine *hwe,
xe_drm_client_add_bo(vm->xef->client, lrc->bo);
}
- xe_lrc_write_ctx_reg(lrc, CTX_RING_START, __xe_lrc_ring_ggtt_addr(lrc));
- xe_lrc_write_ctx_reg(lrc, CTX_RING_HEAD, 0);
- xe_lrc_write_ctx_reg(lrc, CTX_RING_TAIL, lrc->ring.tail);
- xe_lrc_write_ctx_reg(lrc, CTX_RING_CTL,
- RING_CTL_SIZE(lrc->ring.size) | RING_VALID);
+ if (xe_gt_has_indirect_ring_state(gt)) {
+ xe_lrc_write_ctx_reg(lrc, CTX_INDIRECT_RING_STATE,
+ __xe_lrc_indirect_ring_ggtt_addr(lrc));
+
+ xe_lrc_write_indirect_ctx_reg(lrc, INDIRECT_CTX_RING_START,
+ __xe_lrc_ring_ggtt_addr(lrc));
+ xe_lrc_write_indirect_ctx_reg(lrc, INDIRECT_CTX_RING_START_UDW, 0);
+ xe_lrc_write_indirect_ctx_reg(lrc, INDIRECT_CTX_RING_HEAD, 0);
+ xe_lrc_write_indirect_ctx_reg(lrc, INDIRECT_CTX_RING_TAIL, lrc->ring.tail);
+ xe_lrc_write_indirect_ctx_reg(lrc, INDIRECT_CTX_RING_CTL,
+ RING_CTL_SIZE(lrc->ring.size) | RING_VALID);
+ } else {
+ xe_lrc_write_ctx_reg(lrc, CTX_RING_START, __xe_lrc_ring_ggtt_addr(lrc));
+ xe_lrc_write_ctx_reg(lrc, CTX_RING_HEAD, 0);
+ xe_lrc_write_ctx_reg(lrc, CTX_RING_TAIL, lrc->ring.tail);
+ xe_lrc_write_ctx_reg(lrc, CTX_RING_CTL,
+ RING_CTL_SIZE(lrc->ring.size) | RING_VALID);
+ }
+
+ xe_lrc_write_ctx_reg(lrc, CTX_TIMESTAMP, 0);
+
if (xe->info.has_asid && vm)
xe_lrc_write_ctx_reg(lrc, PVC_CTX_ASID, vm->usm.asid);
@@ -825,23 +1014,81 @@ err_lrc_finish:
return err;
}
-void xe_lrc_finish(struct xe_lrc *lrc)
+/**
+ * xe_lrc_create - Create a LRC
+ * @hwe: Hardware Engine
+ * @vm: The VM (address space)
+ * @ring_size: LRC ring size
+ *
+ * Allocate and initialize the Logical Ring Context (LRC).
+ *
+ * Return pointer to created LRC upon success and an error pointer
+ * upon failure.
+ */
+struct xe_lrc *xe_lrc_create(struct xe_hw_engine *hwe, struct xe_vm *vm,
+ u32 ring_size)
{
- xe_hw_fence_ctx_finish(&lrc->fence_ctx);
- xe_bo_lock(lrc->bo, false);
- xe_bo_unpin(lrc->bo);
- xe_bo_unlock(lrc->bo);
- xe_bo_put(lrc->bo);
+ struct xe_lrc *lrc;
+ int err;
+
+ lrc = kzalloc(sizeof(*lrc), GFP_KERNEL);
+ if (!lrc)
+ return ERR_PTR(-ENOMEM);
+
+ err = xe_lrc_init(lrc, hwe, vm, ring_size);
+ if (err) {
+ kfree(lrc);
+ return ERR_PTR(err);
+ }
+
+ return lrc;
+}
+
+/**
+ * xe_lrc_destroy - Destroy the LRC
+ * @ref: reference to LRC
+ *
+ * Called when ref == 0, release resources held by the Logical Ring Context
+ * (LRC) and free the LRC memory.
+ */
+void xe_lrc_destroy(struct kref *ref)
+{
+ struct xe_lrc *lrc = container_of(ref, struct xe_lrc, refcount);
+
+ xe_lrc_finish(lrc);
+ kfree(lrc);
+}
+
+void xe_lrc_set_ring_tail(struct xe_lrc *lrc, u32 tail)
+{
+ if (xe_lrc_has_indirect_ring_state(lrc))
+ xe_lrc_write_indirect_ctx_reg(lrc, INDIRECT_CTX_RING_TAIL, tail);
+ else
+ xe_lrc_write_ctx_reg(lrc, CTX_RING_TAIL, tail);
+}
+
+u32 xe_lrc_ring_tail(struct xe_lrc *lrc)
+{
+ if (xe_lrc_has_indirect_ring_state(lrc))
+ return xe_lrc_read_indirect_ctx_reg(lrc, INDIRECT_CTX_RING_TAIL) & TAIL_ADDR;
+ else
+ return xe_lrc_read_ctx_reg(lrc, CTX_RING_TAIL) & TAIL_ADDR;
}
void xe_lrc_set_ring_head(struct xe_lrc *lrc, u32 head)
{
- xe_lrc_write_ctx_reg(lrc, CTX_RING_HEAD, head);
+ if (xe_lrc_has_indirect_ring_state(lrc))
+ xe_lrc_write_indirect_ctx_reg(lrc, INDIRECT_CTX_RING_HEAD, head);
+ else
+ xe_lrc_write_ctx_reg(lrc, CTX_RING_HEAD, head);
}
u32 xe_lrc_ring_head(struct xe_lrc *lrc)
{
- return xe_lrc_read_ctx_reg(lrc, CTX_RING_HEAD) & HEAD_ADDR;
+ if (xe_lrc_has_indirect_ring_state(lrc))
+ return xe_lrc_read_indirect_ctx_reg(lrc, INDIRECT_CTX_RING_HEAD) & HEAD_ADDR;
+ else
+ return xe_lrc_read_ctx_reg(lrc, CTX_RING_HEAD) & HEAD_ADDR;
}
u32 xe_lrc_ring_space(struct xe_lrc *lrc)
@@ -901,10 +1148,43 @@ u32 xe_lrc_seqno_ggtt_addr(struct xe_lrc *lrc)
return __xe_lrc_seqno_ggtt_addr(lrc);
}
-struct dma_fence *xe_lrc_create_seqno_fence(struct xe_lrc *lrc)
+/**
+ * xe_lrc_alloc_seqno_fence() - Allocate an lrc seqno fence.
+ *
+ * Allocate but don't initialize an lrc seqno fence.
+ *
+ * Return: Pointer to the allocated fence or
+ * negative error pointer on error.
+ */
+struct dma_fence *xe_lrc_alloc_seqno_fence(void)
{
- return &xe_hw_fence_create(&lrc->fence_ctx,
- __xe_lrc_seqno_map(lrc))->dma;
+ return xe_hw_fence_alloc();
+}
+
+/**
+ * xe_lrc_free_seqno_fence() - Free an lrc seqno fence.
+ * @fence: Pointer to the fence to free.
+ *
+ * Frees an lrc seqno fence that hasn't yet been
+ * initialized.
+ */
+void xe_lrc_free_seqno_fence(struct dma_fence *fence)
+{
+ xe_hw_fence_free(fence);
+}
+
+/**
+ * xe_lrc_init_seqno_fence() - Initialize an lrc seqno fence.
+ * @lrc: Pointer to the lrc.
+ * @fence: Pointer to the fence to initialize.
+ *
+ * Initializes a pre-allocated lrc seqno fence.
+ * After initialization, the fence is subject to normal
+ * dma-fence refcounting.
+ */
+void xe_lrc_init_seqno_fence(struct xe_lrc *lrc, struct dma_fence *fence)
+{
+ xe_hw_fence_init(fence, &lrc->fence_ctx, __xe_lrc_seqno_map(lrc));
}
s32 xe_lrc_seqno(struct xe_lrc *lrc)
@@ -1214,7 +1494,7 @@ void xe_lrc_dump_default(struct drm_printer *p,
* hardware status page.
*/
dw = gt->default_lrc[hwe_class] + LRC_PPHWSP_SIZE;
- remaining_dw = (xe_lrc_size(gt_to_xe(gt), hwe_class) - LRC_PPHWSP_SIZE) / 4;
+ remaining_dw = (xe_gt_lrc_size(gt, hwe_class) - LRC_PPHWSP_SIZE) / 4;
while (remaining_dw > 0) {
if ((*dw & XE_INSTR_CMD_TYPE) == XE_INSTR_MI) {
@@ -1354,16 +1634,19 @@ struct xe_lrc_snapshot *xe_lrc_snapshot_capture(struct xe_lrc *lrc)
if (!snapshot)
return NULL;
- snapshot->context_desc = lower_32_bits(xe_lrc_ggtt_addr(lrc));
+ snapshot->context_desc = xe_lrc_ggtt_addr(lrc);
+ snapshot->indirect_context_desc = xe_lrc_indirect_ring_ggtt_addr(lrc);
snapshot->head = xe_lrc_ring_head(lrc);
snapshot->tail.internal = lrc->ring.tail;
- snapshot->tail.memory = xe_lrc_read_ctx_reg(lrc, CTX_RING_TAIL);
+ snapshot->tail.memory = xe_lrc_ring_tail(lrc);
snapshot->start_seqno = xe_lrc_start_seqno(lrc);
snapshot->seqno = xe_lrc_seqno(lrc);
snapshot->lrc_bo = xe_bo_get(lrc->bo);
snapshot->lrc_offset = xe_lrc_pphwsp_offset(lrc);
snapshot->lrc_size = lrc->bo->size - snapshot->lrc_offset;
snapshot->lrc_snapshot = NULL;
+ snapshot->ctx_timestamp = xe_lrc_ctx_timestamp(lrc);
+ snapshot->ctx_job_timestamp = xe_lrc_ctx_job_timestamp(lrc);
return snapshot;
}
@@ -1382,7 +1665,7 @@ void xe_lrc_snapshot_capture_delayed(struct xe_lrc_snapshot *snapshot)
if (!snapshot->lrc_snapshot)
goto put_bo;
- dma_resv_lock(bo->ttm.base.resv, NULL);
+ xe_bo_lock(bo, false);
if (!ttm_bo_vmap(&bo->ttm, &src)) {
xe_map_memcpy_from(xe_bo_device(bo),
snapshot->lrc_snapshot, &src, snapshot->lrc_offset,
@@ -1392,7 +1675,7 @@ void xe_lrc_snapshot_capture_delayed(struct xe_lrc_snapshot *snapshot)
kvfree(snapshot->lrc_snapshot);
snapshot->lrc_snapshot = NULL;
}
- dma_resv_unlock(bo->ttm.base.resv);
+ xe_bo_unlock(bo);
put_bo:
xe_bo_put(bo);
}
@@ -1405,11 +1688,15 @@ void xe_lrc_snapshot_print(struct xe_lrc_snapshot *snapshot, struct drm_printer
return;
drm_printf(p, "\tHW Context Desc: 0x%08x\n", snapshot->context_desc);
+ drm_printf(p, "\tHW Indirect Ring State: 0x%08x\n",
+ snapshot->indirect_context_desc);
drm_printf(p, "\tLRC Head: (memory) %u\n", snapshot->head);
drm_printf(p, "\tLRC Tail: (internal) %u, (memory) %u\n",
snapshot->tail.internal, snapshot->tail.memory);
drm_printf(p, "\tStart seqno: (memory) %d\n", snapshot->start_seqno);
drm_printf(p, "\tSeqno: (memory) %d\n", snapshot->seqno);
+ drm_printf(p, "\tTimestamp: 0x%08x\n", snapshot->ctx_timestamp);
+ drm_printf(p, "\tJob Timestamp: 0x%08x\n", snapshot->ctx_job_timestamp);
if (!snapshot->lrc_snapshot)
return;
@@ -1444,3 +1731,22 @@ void xe_lrc_snapshot_free(struct xe_lrc_snapshot *snapshot)
xe_bo_put(snapshot->lrc_bo);
kfree(snapshot);
}
+
+/**
+ * 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
+ */
+u32 xe_lrc_update_timestamp(struct xe_lrc *lrc, u32 *old_ts)
+{
+ *old_ts = lrc->ctx_timestamp;
+
+ lrc->ctx_timestamp = xe_lrc_ctx_timestamp(lrc);
+
+ return lrc->ctx_timestamp;
+}
diff --git a/drivers/gpu/drm/xe/xe_lrc.h b/drivers/gpu/drm/xe/xe_lrc.h
index d32fa31faa2c..c24542e89318 100644
--- a/drivers/gpu/drm/xe/xe_lrc.h
+++ b/drivers/gpu/drm/xe/xe_lrc.h
@@ -5,6 +5,8 @@
#ifndef _XE_LRC_H_
#define _XE_LRC_H_
+#include <linux/types.h>
+
#include "xe_lrc_types.h"
struct drm_printer;
@@ -12,23 +14,54 @@ struct xe_bb;
struct xe_device;
struct xe_exec_queue;
enum xe_engine_class;
+struct xe_gt;
struct xe_hw_engine;
+struct xe_lrc;
+struct xe_lrc_snapshot;
struct xe_vm;
#define LRC_PPHWSP_SCRATCH_ADDR (0x34 * 4)
-int xe_lrc_init(struct xe_lrc *lrc, struct xe_hw_engine *hwe,
- struct xe_exec_queue *q, struct xe_vm *vm, u32 ring_size);
-void xe_lrc_finish(struct xe_lrc *lrc);
+struct xe_lrc *xe_lrc_create(struct xe_hw_engine *hwe, struct xe_vm *vm,
+ u32 ring_size);
+void xe_lrc_destroy(struct kref *ref);
+
+/**
+ * xe_lrc_get - Get reference to the LRC
+ * @lrc: Logical Ring Context
+ *
+ * Increment reference count of @lrc
+ */
+static inline struct xe_lrc *xe_lrc_get(struct xe_lrc *lrc)
+{
+ kref_get(&lrc->refcount);
+ return lrc;
+}
+
+/**
+ * xe_lrc_put - Put reference of the LRC
+ * @lrc: Logical Ring Context
+ *
+ * Decrement reference count of @lrc, call xe_lrc_destroy when
+ * reference count reaches 0.
+ */
+static inline void xe_lrc_put(struct xe_lrc *lrc)
+{
+ kref_put(&lrc->refcount, xe_lrc_destroy);
+}
-size_t xe_lrc_size(struct xe_device *xe, 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);
+void xe_lrc_set_ring_tail(struct xe_lrc *lrc, u32 tail);
+u32 xe_lrc_ring_tail(struct xe_lrc *lrc);
void xe_lrc_set_ring_head(struct xe_lrc *lrc, u32 head);
u32 xe_lrc_ring_head(struct xe_lrc *lrc);
u32 xe_lrc_ring_space(struct xe_lrc *lrc);
void xe_lrc_write_ring(struct xe_lrc *lrc, const void *data, size_t size);
+u32 xe_lrc_indirect_ring_ggtt_addr(struct xe_lrc *lrc);
u32 xe_lrc_ggtt_addr(struct xe_lrc *lrc);
u32 *xe_lrc_regs(struct xe_lrc *lrc);
@@ -38,7 +71,9 @@ void xe_lrc_write_ctx_reg(struct xe_lrc *lrc, int reg_nr, u32 val);
u64 xe_lrc_descriptor(struct xe_lrc *lrc);
u32 xe_lrc_seqno_ggtt_addr(struct xe_lrc *lrc);
-struct dma_fence *xe_lrc_create_seqno_fence(struct xe_lrc *lrc);
+struct dma_fence *xe_lrc_alloc_seqno_fence(void);
+void xe_lrc_free_seqno_fence(struct dma_fence *fence);
+void xe_lrc_init_seqno_fence(struct xe_lrc *lrc, struct dma_fence *fence);
s32 xe_lrc_seqno(struct xe_lrc *lrc);
u32 xe_lrc_start_seqno_ggtt_addr(struct xe_lrc *lrc);
@@ -60,4 +95,23 @@ 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);
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(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);
+
+/**
+ * xe_lrc_update_timestamp - readout LRC timestamp and update cached value
+ * @lrc: logical ring context for this exec queue
+ * @old_ts: pointer where to save the previous timestamp
+ *
+ * Read the current timestamp for this LRC and update the cached value. The
+ * previous cached value is also returned in @old_ts so the caller can calculate
+ * the delta between 2 updates. Note that this is not intended to be called from
+ * any place, but just by the paths updating the drm client utilization.
+ *
+ * Returns the current LRC timestamp
+ */
+u32 xe_lrc_update_timestamp(struct xe_lrc *lrc, u32 *old_ts);
+
#endif
diff --git a/drivers/gpu/drm/xe/xe_lrc_types.h b/drivers/gpu/drm/xe/xe_lrc_types.h
index b716df0dfb4e..71ecb453f811 100644
--- a/drivers/gpu/drm/xe/xe_lrc_types.h
+++ b/drivers/gpu/drm/xe/xe_lrc_types.h
@@ -6,6 +6,8 @@
#ifndef _XE_LRC_TYPES_H_
#define _XE_LRC_TYPES_H_
+#include <linux/kref.h>
+
#include "xe_hw_fence_types.h"
struct xe_bo;
@@ -20,12 +22,19 @@ struct xe_lrc {
*/
struct xe_bo *bo;
+ /** @size: size of lrc including any indirect ring state page */
+ u32 size;
+
/** @tile: tile which this LRC belongs to */
struct xe_tile *tile;
/** @flags: LRC flags */
+#define XE_LRC_FLAG_INDIRECT_RING_STATE 0x1
u32 flags;
+ /** @refcount: ref count of this lrc */
+ struct kref refcount;
+
/** @ring: submission ring state */
struct {
/** @ring.size: size of submission ring */
@@ -41,6 +50,9 @@ struct xe_lrc {
/** @fence_ctx: context for hw fence */
struct xe_hw_fence_ctx fence_ctx;
+
+ /** @ctx_timestamp: readout value of CTX_TIMESTAMP on last update */
+ u32 ctx_timestamp;
};
struct xe_lrc_snapshot;
diff --git a/drivers/gpu/drm/xe/xe_migrate.c b/drivers/gpu/drm/xe/xe_migrate.c
index 9f6e9b7f11c8..c9f5673353ee 100644
--- a/drivers/gpu/drm/xe/xe_migrate.c
+++ b/drivers/gpu/drm/xe/xe_migrate.c
@@ -14,8 +14,8 @@
#include <generated/xe_wa_oob.h>
+#include "instructions/xe_gpu_commands.h"
#include "instructions/xe_mi_commands.h"
-#include "regs/xe_gpu_commands.h"
#include "regs/xe_gtt_defs.h"
#include "tests/xe_test.h"
#include "xe_assert.h"
@@ -32,9 +32,8 @@
#include "xe_res_cursor.h"
#include "xe_sched_job.h"
#include "xe_sync.h"
-#include "xe_trace.h"
+#include "xe_trace_bo.h"
#include "xe_vm.h"
-#include "xe_wa.h"
/**
* struct xe_migrate - migrate context.
@@ -70,7 +69,7 @@ struct xe_migrate {
#define MAX_PREEMPTDISABLE_TRANSFER SZ_8M /* Around 1ms. */
#define MAX_CCS_LIMITED_TRANSFER SZ_4M /* XE_PAGE_SIZE * (FIELD_MAX(XE2_CCS_SIZE_MASK) + 1) */
-#define NUM_KERNEL_PDE 17
+#define NUM_KERNEL_PDE 15
#define NUM_PT_SLOTS 32
#define LEVEL0_PAGE_TABLE_ENCODE_SIZE SZ_2M
#define MAX_NUM_PTE 512
@@ -138,10 +137,11 @@ static int xe_migrate_prepare_vm(struct xe_tile *tile, struct xe_migrate *m,
struct xe_device *xe = tile_to_xe(tile);
u16 pat_index = xe->pat.idx[XE_CACHE_WB];
u8 id = tile->id;
- u32 num_entries = NUM_PT_SLOTS, num_level = vm->pt_root[id]->level;
+ u32 num_entries = NUM_PT_SLOTS, num_level = vm->pt_root[id]->level,
+ num_setup = num_level + 1;
u32 map_ofs, level, i;
struct xe_bo *bo, *batch = tile->mem.kernel_bb_pool->bo;
- u64 entry;
+ u64 entry, pt30_ofs;
/* Can't bump NUM_PT_SLOTS too high */
BUILD_BUG_ON(NUM_PT_SLOTS > SZ_2M/XE_PAGE_SIZE);
@@ -161,10 +161,12 @@ static int xe_migrate_prepare_vm(struct xe_tile *tile, struct xe_migrate *m,
if (IS_ERR(bo))
return PTR_ERR(bo);
- entry = vm->pt_ops->pde_encode_bo(bo, bo->size - XE_PAGE_SIZE, pat_index);
+ /* PT31 reserved for 2M identity map */
+ pt30_ofs = bo->size - 2 * XE_PAGE_SIZE;
+ entry = vm->pt_ops->pde_encode_bo(bo, pt30_ofs, pat_index);
xe_pt_write(xe, &vm->pt_root[id]->bo->vmap, 0, entry);
- map_ofs = (num_entries - num_level) * XE_PAGE_SIZE;
+ map_ofs = (num_entries - num_setup) * XE_PAGE_SIZE;
/* Map the entire BO in our level 0 pt */
for (i = 0, level = 0; i < num_entries; level++) {
@@ -235,7 +237,7 @@ static int xe_migrate_prepare_vm(struct xe_tile *tile, struct xe_migrate *m,
}
/* Write PDE's that point to our BO. */
- for (i = 0; i < num_entries - num_level; i++) {
+ for (i = 0; i < map_ofs / PAGE_SIZE; i++) {
entry = vm->pt_ops->pde_encode_bo(bo, (u64)i * XE_PAGE_SIZE,
pat_index);
@@ -253,28 +255,54 @@ static int xe_migrate_prepare_vm(struct xe_tile *tile, struct xe_migrate *m,
/* Identity map the entire vram at 256GiB offset */
if (IS_DGFX(xe)) {
u64 pos, ofs, flags;
+ /* XXX: Unclear if this should be usable_size? */
+ u64 vram_limit = xe->mem.vram.actual_physical_size +
+ xe->mem.vram.dpa_base;
level = 2;
ofs = map_ofs + XE_PAGE_SIZE * level + 256 * 8;
flags = vm->pt_ops->pte_encode_addr(xe, 0, pat_index, level,
true, 0);
+ xe_assert(xe, IS_ALIGNED(xe->mem.vram.usable_size, SZ_2M));
+
/*
- * Use 1GB pages, it shouldn't matter the physical amount of
- * vram is less, when we don't access it.
+ * Use 1GB pages when possible, last chunk always use 2M
+ * pages as mixing reserved memory (stolen, WOCPM) with a single
+ * mapping is not allowed on certain platforms.
*/
- for (pos = xe->mem.vram.dpa_base;
- pos < xe->mem.vram.actual_physical_size + xe->mem.vram.dpa_base;
- pos += SZ_1G, ofs += 8)
+ for (pos = xe->mem.vram.dpa_base; pos < vram_limit;
+ pos += SZ_1G, ofs += 8) {
+ if (pos + SZ_1G >= vram_limit) {
+ u64 pt31_ofs = bo->size - XE_PAGE_SIZE;
+
+ entry = vm->pt_ops->pde_encode_bo(bo, pt31_ofs,
+ pat_index);
+ xe_map_wr(xe, &bo->vmap, ofs, u64, entry);
+
+ flags = vm->pt_ops->pte_encode_addr(xe, 0,
+ pat_index,
+ level - 1,
+ true, 0);
+
+ for (ofs = pt31_ofs; pos < vram_limit;
+ pos += SZ_2M, ofs += 8)
+ xe_map_wr(xe, &bo->vmap, ofs, u64, pos | flags);
+ break; /* Ensure pos == vram_limit assert correct */
+ }
+
xe_map_wr(xe, &bo->vmap, ofs, u64, pos | flags);
+ }
+
+ xe_assert(xe, pos == vram_limit);
}
/*
* Example layout created above, with root level = 3:
* [PT0...PT7]: kernel PT's for copy/clear; 64 or 4KiB PTE's
* [PT8]: Kernel PT for VM_BIND, 4 KiB PTE's
- * [PT9...PT28]: Userspace PT's for VM_BIND, 4 KiB PTE's
- * [PT29 = PDE 0] [PT30 = PDE 1] [PT31 = PDE 2]
+ * [PT9...PT27]: Userspace PT's for VM_BIND, 4 KiB PTE's
+ * [PT28 = PDE 0] [PT29 = PDE 1] [PT30 = PDE 2] [PT31 = 2M vram identity map]
*
* This makes the lowest part of the VM point to the pagetables.
* Hence the lowest 2M in the vm should point to itself, with a few writes
@@ -300,10 +328,6 @@ static int xe_migrate_prepare_vm(struct xe_tile *tile, struct xe_migrate *m,
}
/*
- * Due to workaround 16017236439, odd instance hardware copy engines are
- * faster than even instance ones.
- * This function returns the mask involving all fast copy engines and the
- * reserved copy engine to be used as logical mask for migrate engine.
* Including the reserved copy engine is required to avoid deadlocks due to
* migrate jobs servicing the faults gets stuck behind the job that faulted.
*/
@@ -317,8 +341,7 @@ static u32 xe_migrate_usm_logical_mask(struct xe_gt *gt)
if (hwe->class != XE_ENGINE_CLASS_COPY)
continue;
- if (!XE_WA(gt, 16017236439) ||
- xe_gt_is_usm_hwe(gt, hwe) || hwe->instance & 1)
+ if (xe_gt_is_usm_hwe(gt, hwe))
logical_mask |= BIT(hwe->logical_instance);
}
@@ -369,6 +392,10 @@ struct xe_migrate *xe_migrate_init(struct xe_tile *tile)
if (!hwe || !logical_mask)
return ERR_PTR(-EINVAL);
+ /*
+ * XXX: Currently only reserving 1 (likely slow) BCS instance on
+ * PVC, may want to revisit if performance is needed.
+ */
m->q = xe_exec_queue_create(xe, vm, logical_mask, 1, hwe,
EXEC_QUEUE_FLAG_KERNEL |
EXEC_QUEUE_FLAG_PERMANENT |
@@ -385,6 +412,9 @@ struct xe_migrate *xe_migrate_init(struct xe_tile *tile)
}
mutex_init(&m->job_mutex);
+ fs_reclaim_acquire(GFP_KERNEL);
+ might_lock(&m->job_mutex);
+ fs_reclaim_release(GFP_KERNEL);
err = drmm_add_action_or_reset(&xe->drm, xe_migrate_fini, m);
if (err)
@@ -617,12 +647,6 @@ static void emit_copy(struct xe_gt *gt, struct xe_bb *bb,
bb->cs[bb->len++] = upper_32_bits(src_ofs);
}
-static int job_add_deps(struct xe_sched_job *job, struct dma_resv *resv,
- enum dma_resv_usage usage)
-{
- return drm_sched_job_add_resv_dependencies(&job->drm, resv, usage);
-}
-
static u64 xe_migrate_batch_base(struct xe_migrate *m, bool usm)
{
return usm ? m->usm_batch_base_ofs : m->batch_base_ofs;
@@ -809,7 +833,6 @@ struct dma_fence *xe_migrate_copy(struct xe_migrate *m,
IS_DGFX(xe) ? dst_is_vram : dst_is_pltt,
src_L0, ccs_ofs, copy_ccs);
- mutex_lock(&m->job_mutex);
job = xe_bb_create_migration_job(m->q, bb,
xe_migrate_batch_base(m, usm),
update_idx);
@@ -820,15 +843,16 @@ struct dma_fence *xe_migrate_copy(struct xe_migrate *m,
xe_sched_job_add_migrate_flush(job, flush_flags);
if (!fence) {
- err = job_add_deps(job, src_bo->ttm.base.resv,
- DMA_RESV_USAGE_BOOKKEEP);
+ err = xe_sched_job_add_deps(job, src_bo->ttm.base.resv,
+ DMA_RESV_USAGE_BOOKKEEP);
if (!err && src_bo != dst_bo)
- err = job_add_deps(job, dst_bo->ttm.base.resv,
- DMA_RESV_USAGE_BOOKKEEP);
+ err = xe_sched_job_add_deps(job, dst_bo->ttm.base.resv,
+ DMA_RESV_USAGE_BOOKKEEP);
if (err)
goto err_job;
}
+ mutex_lock(&m->job_mutex);
xe_sched_job_arm(job);
dma_fence_put(fence);
fence = dma_fence_get(&job->drm.s_fence->finished);
@@ -846,7 +870,6 @@ struct dma_fence *xe_migrate_copy(struct xe_migrate *m,
err_job:
xe_sched_job_put(job);
err:
- mutex_unlock(&m->job_mutex);
xe_bb_free(bb, NULL);
err_sync:
@@ -936,8 +959,8 @@ static bool has_service_copy_support(struct xe_gt *gt)
* all of the actual service copy engines (BCS1-BCS8) have been fused
* off.
*/
- return gt->info.__engine_mask & GENMASK(XE_HW_ENGINE_BCS8,
- XE_HW_ENGINE_BCS1);
+ return gt->info.engine_mask & GENMASK(XE_HW_ENGINE_BCS8,
+ XE_HW_ENGINE_BCS1);
}
static u32 emit_clear_cmd_len(struct xe_gt *gt)
@@ -1046,7 +1069,6 @@ struct dma_fence *xe_migrate_clear(struct xe_migrate *m,
flush_flags = MI_FLUSH_DW_CCS;
}
- mutex_lock(&m->job_mutex);
job = xe_bb_create_migration_job(m->q, bb,
xe_migrate_batch_base(m, usm),
update_idx);
@@ -1063,12 +1085,13 @@ struct dma_fence *xe_migrate_clear(struct xe_migrate *m,
* fences, which are always tracked as
* DMA_RESV_USAGE_KERNEL.
*/
- err = job_add_deps(job, bo->ttm.base.resv,
- DMA_RESV_USAGE_KERNEL);
+ err = xe_sched_job_add_deps(job, bo->ttm.base.resv,
+ DMA_RESV_USAGE_KERNEL);
if (err)
goto err_job;
}
+ mutex_lock(&m->job_mutex);
xe_sched_job_arm(job);
dma_fence_put(fence);
fence = dma_fence_get(&job->drm.s_fence->finished);
@@ -1085,7 +1108,6 @@ struct dma_fence *xe_migrate_clear(struct xe_migrate *m,
err_job:
xe_sched_job_put(job);
err:
- mutex_unlock(&m->job_mutex);
xe_bb_free(bb, NULL);
err_sync:
/* Sync partial copies if any. FIXME: job_mutex? */
@@ -1336,7 +1358,7 @@ xe_migrate_update_pgtables(struct xe_migrate *m,
GFP_KERNEL, true, 0);
if (IS_ERR(sa_bo)) {
err = PTR_ERR(sa_bo);
- goto err;
+ goto err_bb;
}
ppgtt_ofs = NUM_KERNEL_PDE +
@@ -1379,21 +1401,18 @@ xe_migrate_update_pgtables(struct xe_migrate *m,
write_pgtable(tile, bb, 0, &updates[i], pt_update);
}
- if (!q)
- mutex_lock(&m->job_mutex);
-
job = xe_bb_create_migration_job(q ?: m->q, bb,
xe_migrate_batch_base(m, usm),
update_idx);
if (IS_ERR(job)) {
err = PTR_ERR(job);
- goto err_bb;
+ goto err_sa;
}
/* Wait on BO move */
if (bo) {
- err = job_add_deps(job, bo->ttm.base.resv,
- DMA_RESV_USAGE_KERNEL);
+ err = xe_sched_job_add_deps(job, bo->ttm.base.resv,
+ DMA_RESV_USAGE_KERNEL);
if (err)
goto err_job;
}
@@ -1403,8 +1422,8 @@ xe_migrate_update_pgtables(struct xe_migrate *m,
* trigger preempts before moving forward
*/
if (first_munmap_rebind) {
- err = job_add_deps(job, xe_vm_resv(vm),
- DMA_RESV_USAGE_BOOKKEEP);
+ err = xe_sched_job_add_deps(job, xe_vm_resv(vm),
+ DMA_RESV_USAGE_BOOKKEEP);
if (err)
goto err_job;
}
@@ -1422,6 +1441,9 @@ xe_migrate_update_pgtables(struct xe_migrate *m,
if (err)
goto err_job;
}
+ if (!q)
+ mutex_lock(&m->job_mutex);
+
xe_sched_job_arm(job);
fence = dma_fence_get(&job->drm.s_fence->finished);
xe_sched_job_push(job);
@@ -1436,12 +1458,10 @@ xe_migrate_update_pgtables(struct xe_migrate *m,
err_job:
xe_sched_job_put(job);
+err_sa:
+ drm_suballoc_free(sa_bo, NULL);
err_bb:
- if (!q)
- mutex_unlock(&m->job_mutex);
xe_bb_free(bb, NULL);
-err:
- drm_suballoc_free(sa_bo, NULL);
return ERR_PTR(err);
}
diff --git a/drivers/gpu/drm/xe/xe_mmio.c b/drivers/gpu/drm/xe/xe_mmio.c
index 334637511e75..f92faad4b96d 100644
--- a/drivers/gpu/drm/xe/xe_mmio.c
+++ b/drivers/gpu/drm/xe/xe_mmio.c
@@ -3,337 +3,37 @@
* Copyright © 2021-2023 Intel Corporation
*/
-#include <linux/minmax.h>
-
#include "xe_mmio.h"
+#include <linux/delay.h>
+#include <linux/io-64-nonatomic-lo-hi.h>
+#include <linux/minmax.h>
+#include <linux/pci.h>
+
#include <drm/drm_managed.h>
-#include <drm/xe_drm.h>
+#include <drm/drm_print.h>
-#include "regs/xe_engine_regs.h"
-#include "regs/xe_gt_regs.h"
+#include "regs/xe_bars.h"
#include "regs/xe_regs.h"
-#include "xe_bo.h"
#include "xe_device.h"
-#include "xe_ggtt.h"
#include "xe_gt.h"
-#include "xe_gt_mcr.h"
+#include "xe_gt_printk.h"
+#include "xe_gt_sriov_vf.h"
#include "xe_macros.h"
-#include "xe_module.h"
#include "xe_sriov.h"
-#include "xe_tile.h"
-
-#define XEHP_MTCFG_ADDR XE_REG(0x101800)
-#define TILE_COUNT REG_GENMASK(15, 8)
-
-#define BAR_SIZE_SHIFT 20
-
-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;
-
- if (pci_resource_len(pdev, resno))
- pci_release_resource(pdev, resno);
-
- ret = pci_resize_resource(pdev, resno, bar_size);
- 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
- */
-static void xe_resize_vram_bar(struct xe_device *xe)
-{
- u64 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;
- u32 bar_size_mask;
- u32 pci_cmd;
- int i;
-
- /* gather some relevant info */
- current_size = pci_resource_len(pdev, LMEM_BAR);
- bar_size_mask = pci_rebar_get_possible_sizes(pdev, LMEM_BAR);
-
- if (!bar_size_mask)
- return;
-
- /* set to a specific size? */
- if (force_vram_bar_size) {
- u32 bar_size_bit;
-
- rebar_size = force_vram_bar_size * (resource_size_t)SZ_1M;
-
- bar_size_bit = bar_size_mask & BIT(pci_rebar_bytes_to_size(rebar_size));
-
- if (!bar_size_bit) {
- drm_info(&xe->drm,
- "Requested size: %lluMiB is not supported by rebar sizes: 0x%x. Leaving default: %lluMiB\n",
- (u64)rebar_size >> 20, bar_size_mask, (u64)current_size >> 20);
- return;
- }
-
- rebar_size = 1ULL << (__fls(bar_size_bit) + BAR_SIZE_SHIFT);
-
- if (rebar_size == current_size)
- return;
- } else {
- rebar_size = 1ULL << (__fls(bar_size_mask) + BAR_SIZE_SHIFT);
-
- /* 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);
+#include "xe_trace.h"
- _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 xe_pci_resource_valid(struct pci_dev *pdev, int bar)
-{
- if (!pci_resource_flags(pdev, bar))
- return false;
-
- if (pci_resource_flags(pdev, bar) & IORESOURCE_UNSET)
- return false;
-
- if (!pci_resource_len(pdev, bar))
- return false;
-
- return true;
-}
-
-static int xe_determine_lmem_bar_size(struct xe_device *xe)
-{
- struct pci_dev *pdev = to_pci_dev(xe->drm.dev);
-
- if (!xe_pci_resource_valid(pdev, LMEM_BAR)) {
- drm_err(&xe->drm, "pci resource is not valid\n");
- return -ENXIO;
- }
-
- xe_resize_vram_bar(xe);
-
- xe->mem.vram.io_start = pci_resource_start(pdev, LMEM_BAR);
- xe->mem.vram.io_size = pci_resource_len(pdev, LMEM_BAR);
- if (!xe->mem.vram.io_size)
- return -EIO;
-
- /* XXX: Need to change when xe link code is ready */
- xe->mem.vram.dpa_base = 0;
-
- /* set up a map to the total memory area. */
- xe->mem.vram.mapping = ioremap_wc(xe->mem.vram.io_start, xe->mem.vram.io_size);
-
- return 0;
-}
-
-static inline u64 get_flat_ccs_offset(struct xe_gt *gt, u64 tile_size)
-{
- struct xe_device *xe = gt_to_xe(gt);
- u64 offset;
- u32 reg;
-
- if (GRAPHICS_VER(xe) >= 20) {
- u64 ccs_size = tile_size / 512;
- u64 offset_hi, offset_lo;
- u32 nodes, num_enabled;
-
- reg = xe_mmio_read32(gt, MIRROR_FUSE3);
- nodes = REG_FIELD_GET(XE2_NODE_ENABLE_MASK, reg);
- num_enabled = hweight32(nodes); /* Number of enabled l3 nodes */
-
- reg = xe_gt_mcr_unicast_read_any(gt, XE2_FLAT_CCS_BASE_RANGE_LOWER);
- offset_lo = REG_FIELD_GET(XE2_FLAT_CCS_BASE_LOWER_ADDR_MASK, reg);
-
- reg = xe_gt_mcr_unicast_read_any(gt, XE2_FLAT_CCS_BASE_RANGE_UPPER);
- offset_hi = REG_FIELD_GET(XE2_FLAT_CCS_BASE_UPPER_ADDR_MASK, reg);
-
- offset = offset_hi << 32; /* HW view bits 39:32 */
- offset |= offset_lo << 6; /* HW view bits 31:6 */
- offset *= num_enabled; /* convert to SW view */
-
- /* We don't expect any holes */
- xe_assert_msg(xe, offset == (xe_mmio_read64_2x32(gt, GSMBASE) - ccs_size),
- "Hole between CCS and GSM.\n");
- } else {
- reg = xe_gt_mcr_unicast_read_any(gt, XEHP_FLAT_CCS_BASE_ADDR);
- offset = (u64)REG_FIELD_GET(XEHP_FLAT_CCS_PTR, reg) * SZ_64K;
- }
-
- return offset;
-}
-
-/**
- * xe_mmio_tile_vram_size() - Collect vram size and offset information
- * @tile: tile to get info for
- * @vram_size: available vram (size - device reserved portions)
- * @tile_size: actual vram size
- * @tile_offset: physical start point in the vram address space
- *
- * There are 4 places for size information:
- * - io size (from pci_resource_len of LMEM bar) (only used for small bar and DG1)
- * - TILEx size (actual vram size)
- * - GSMBASE offset (TILEx - "stolen")
- * - CSSBASE offset (TILEx - CSS space necessary)
- *
- * CSSBASE is always a lower/smaller offset then GSMBASE.
- *
- * The actual available size of memory is to the CCS or GSM base.
- * NOTE: multi-tile bases will include the tile offset.
- *
- */
-static int xe_mmio_tile_vram_size(struct xe_tile *tile, u64 *vram_size,
- u64 *tile_size, u64 *tile_offset)
-{
- struct xe_device *xe = tile_to_xe(tile);
- struct xe_gt *gt = tile->primary_gt;
- u64 offset;
- int err;
- u32 reg;
-
- err = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
- if (err)
- return err;
-
- /* actual size */
- if (unlikely(xe->info.platform == XE_DG1)) {
- *tile_size = pci_resource_len(to_pci_dev(xe->drm.dev), LMEM_BAR);
- *tile_offset = 0;
- } else {
- reg = xe_gt_mcr_unicast_read_any(gt, XEHP_TILE_ADDR_RANGE(gt->info.id));
- *tile_size = (u64)REG_FIELD_GET(GENMASK(14, 8), reg) * SZ_1G;
- *tile_offset = (u64)REG_FIELD_GET(GENMASK(7, 1), reg) * SZ_1G;
- }
-
- /* minus device usage */
- if (xe->info.has_flat_ccs) {
- offset = get_flat_ccs_offset(gt, *tile_size);
- } else {
- offset = xe_mmio_read64_2x32(gt, GSMBASE);
- }
-
- /* remove the tile offset so we have just the available size */
- *vram_size = offset - *tile_offset;
-
- return xe_force_wake_put(gt_to_fw(gt), XE_FW_GT);
-}
-
-int xe_mmio_probe_vram(struct xe_device *xe)
+static void tiles_fini(void *arg)
{
+ struct xe_device *xe = arg;
struct xe_tile *tile;
- resource_size_t io_size;
- u64 available_size = 0;
- u64 total_size = 0;
- u64 tile_offset;
- u64 tile_size;
- u64 vram_size;
- int err;
- u8 id;
-
- if (!IS_DGFX(xe))
- return 0;
-
- /* Get the size of the root tile's vram for later accessibility comparison */
- tile = xe_device_get_root_tile(xe);
- err = xe_mmio_tile_vram_size(tile, &vram_size, &tile_size, &tile_offset);
- if (err)
- return err;
-
- err = xe_determine_lmem_bar_size(xe);
- if (err)
- return err;
-
- drm_info(&xe->drm, "VISIBLE VRAM: %pa, %pa\n", &xe->mem.vram.io_start,
- &xe->mem.vram.io_size);
-
- io_size = xe->mem.vram.io_size;
-
- /* tile specific ranges */
- for_each_tile(tile, xe, id) {
- err = xe_mmio_tile_vram_size(tile, &vram_size, &tile_size, &tile_offset);
- if (err)
- return err;
-
- tile->mem.vram.actual_physical_size = tile_size;
- tile->mem.vram.io_start = xe->mem.vram.io_start + tile_offset;
- tile->mem.vram.io_size = min_t(u64, vram_size, io_size);
-
- if (!tile->mem.vram.io_size) {
- drm_err(&xe->drm, "Tile without any CPU visible VRAM. Aborting.\n");
- return -ENODEV;
- }
-
- tile->mem.vram.dpa_base = xe->mem.vram.dpa_base + tile_offset;
- tile->mem.vram.usable_size = vram_size;
- tile->mem.vram.mapping = xe->mem.vram.mapping + tile_offset;
-
- if (tile->mem.vram.io_size < tile->mem.vram.usable_size)
- drm_info(&xe->drm, "Small BAR device\n");
- drm_info(&xe->drm, "VRAM[%u, %u]: Actual physical size %pa, usable size exclude stolen %pa, CPU accessible size %pa\n", id,
- tile->id, &tile->mem.vram.actual_physical_size, &tile->mem.vram.usable_size, &tile->mem.vram.io_size);
- drm_info(&xe->drm, "VRAM[%u, %u]: DPA range: [%pa-%llx], io range: [%pa-%llx]\n", id, tile->id,
- &tile->mem.vram.dpa_base, tile->mem.vram.dpa_base + (u64)tile->mem.vram.actual_physical_size,
- &tile->mem.vram.io_start, tile->mem.vram.io_start + (u64)tile->mem.vram.io_size);
-
- /* calculate total size using tile size to get the correct HW sizing */
- total_size += tile_size;
- available_size += vram_size;
-
- if (total_size > xe->mem.vram.io_size) {
- drm_info(&xe->drm, "VRAM: %pa is larger than resource %pa\n",
- &total_size, &xe->mem.vram.io_size);
- }
-
- io_size -= min_t(u64, tile_size, io_size);
- }
-
- xe->mem.vram.actual_physical_size = total_size;
-
- drm_info(&xe->drm, "Total VRAM: %pa, %pa\n", &xe->mem.vram.io_start,
- &xe->mem.vram.actual_physical_size);
- drm_info(&xe->drm, "Available VRAM: %pa, %pa\n", &xe->mem.vram.io_start,
- &available_size);
+ int id;
- return 0;
+ for_each_tile(tile, xe, id)
+ tile->mmio.regs = NULL;
}
-void xe_mmio_probe_tiles(struct xe_device *xe)
+int xe_mmio_probe_tiles(struct xe_device *xe)
{
size_t tile_mmio_size = SZ_16M, tile_mmio_ext_size = xe->info.tile_mmio_ext_size;
u8 id, tile_count = xe->info.tile_count;
@@ -384,15 +84,16 @@ add_mmio_ext:
regs += tile_mmio_ext_size;
}
}
+
+ return devm_add_action_or_reset(xe->drm.dev, tiles_fini, xe);
}
-static void mmio_fini(struct drm_device *drm, void *arg)
+static void mmio_fini(void *arg)
{
struct xe_device *xe = arg;
pci_iounmap(to_pci_dev(xe->drm.dev), xe->mmio.regs);
- if (xe->mem.vram.mapping)
- iounmap(xe->mem.vram.mapping);
+ xe->mmio.regs = NULL;
}
int xe_mmio_init(struct xe_device *xe)
@@ -407,7 +108,7 @@ int xe_mmio_init(struct xe_device *xe)
* registers (0-4MB), reserved space (4MB-8MB) and GGTT (8MB-16MB).
*/
xe->mmio.size = pci_resource_len(pdev, mmio_bar);
- xe->mmio.regs = pci_iomap(pdev, mmio_bar, 0);
+ xe->mmio.regs = pci_iomap(pdev, mmio_bar, GTTMMADR_BAR);
if (xe->mmio.regs == NULL) {
drm_err(&xe->drm, "failed to map registers\n");
return -EIO;
@@ -417,47 +118,56 @@ int xe_mmio_init(struct xe_device *xe)
root_tile->mmio.size = SZ_16M;
root_tile->mmio.regs = xe->mmio.regs;
- return drmm_add_action_or_reset(&xe->drm, mmio_fini, xe);
+ return devm_add_action_or_reset(xe->drm.dev, mmio_fini, xe);
}
u8 xe_mmio_read8(struct xe_gt *gt, struct xe_reg reg)
{
struct xe_tile *tile = gt_to_tile(gt);
+ u32 addr = xe_mmio_adjusted_addr(gt, reg.addr);
+ u8 val;
- if (reg.addr < gt->mmio.adj_limit)
- reg.addr += gt->mmio.adj_offset;
+ val = readb((reg.ext ? tile->mmio_ext.regs : tile->mmio.regs) + addr);
+ trace_xe_reg_rw(gt, false, addr, val, sizeof(val));
- return readb((reg.ext ? tile->mmio_ext.regs : tile->mmio.regs) + reg.addr);
+ return val;
}
u16 xe_mmio_read16(struct xe_gt *gt, struct xe_reg reg)
{
struct xe_tile *tile = gt_to_tile(gt);
+ u32 addr = xe_mmio_adjusted_addr(gt, reg.addr);
+ u16 val;
- if (reg.addr < gt->mmio.adj_limit)
- reg.addr += gt->mmio.adj_offset;
+ val = readw((reg.ext ? tile->mmio_ext.regs : tile->mmio.regs) + addr);
+ trace_xe_reg_rw(gt, false, addr, val, sizeof(val));
- return readw((reg.ext ? tile->mmio_ext.regs : tile->mmio.regs) + reg.addr);
+ return val;
}
void xe_mmio_write32(struct xe_gt *gt, struct xe_reg reg, u32 val)
{
struct xe_tile *tile = gt_to_tile(gt);
+ u32 addr = xe_mmio_adjusted_addr(gt, reg.addr);
- if (reg.addr < gt->mmio.adj_limit)
- reg.addr += gt->mmio.adj_offset;
-
- writel(val, (reg.ext ? tile->mmio_ext.regs : tile->mmio.regs) + reg.addr);
+ trace_xe_reg_rw(gt, true, addr, val, sizeof(val));
+ writel(val, (reg.ext ? tile->mmio_ext.regs : tile->mmio.regs) + addr);
}
u32 xe_mmio_read32(struct xe_gt *gt, struct xe_reg reg)
{
struct xe_tile *tile = gt_to_tile(gt);
+ u32 addr = xe_mmio_adjusted_addr(gt, reg.addr);
+ u32 val;
- if (reg.addr < gt->mmio.adj_limit)
- reg.addr += gt->mmio.adj_offset;
+ if (!reg.vf && IS_SRIOV_VF(gt_to_xe(gt)))
+ val = xe_gt_sriov_vf_read32(gt, reg);
+ else
+ val = readl((reg.ext ? tile->mmio_ext.regs : tile->mmio.regs) + addr);
- return readl((reg.ext ? tile->mmio_ext.regs : tile->mmio.regs) + reg.addr);
+ trace_xe_reg_rw(gt, false, addr, val, sizeof(val));
+
+ return val;
}
u32 xe_mmio_rmw32(struct xe_gt *gt, struct xe_reg reg, u32 clr, u32 set)
@@ -486,10 +196,9 @@ bool xe_mmio_in_range(const struct xe_gt *gt,
const struct xe_mmio_range *range,
struct xe_reg reg)
{
- if (reg.addr < gt->mmio.adj_limit)
- reg.addr += gt->mmio.adj_offset;
+ u32 addr = xe_mmio_adjusted_addr(gt, reg.addr);
- return range && reg.addr >= range->start && reg.addr <= range->end;
+ return range && addr >= range->start && addr <= range->end;
}
/**
@@ -519,10 +228,11 @@ u64 xe_mmio_read64_2x32(struct xe_gt *gt, struct xe_reg reg)
struct xe_reg reg_udw = { .addr = reg.addr + 0x4 };
u32 ldw, udw, oldudw, retries;
- if (reg.addr < gt->mmio.adj_limit) {
- reg.addr += gt->mmio.adj_offset;
- reg_udw.addr += gt->mmio.adj_offset;
- }
+ reg.addr = xe_mmio_adjusted_addr(gt, reg.addr);
+ reg_udw.addr = xe_mmio_adjusted_addr(gt, reg_udw.addr);
+
+ /* we shouldn't adjust just one register address */
+ xe_gt_assert(gt, reg_udw.addr == reg.addr + 0x4);
oldudw = xe_mmio_read32(gt, reg_udw);
for (retries = 5; retries; --retries) {
@@ -601,3 +311,64 @@ int xe_mmio_wait32(struct xe_gt *gt, struct xe_reg reg, u32 mask, u32 val, u32 t
return ret;
}
+
+/**
+ * xe_mmio_wait32_not() - Wait for a register to return anything other than the given masked value
+ * @gt: MMIO target GT
+ * @reg: register to read value from
+ * @mask: mask to be applied to the value read from the register
+ * @val: value to match after applying the mask
+ * @timeout_us: time out after this period of time. Wait logic tries to be
+ * smart, applying an exponential backoff until @timeout_us is reached.
+ * @out_val: if not NULL, points where to store the last unmasked value
+ * @atomic: needs to be true if calling from an atomic context
+ *
+ * This function polls for a masked value to change from a given value and
+ * returns zero on success or -ETIMEDOUT if timed out.
+ *
+ * Note that @timeout_us represents the minimum amount of time to wait before
+ * giving up. The actual time taken by this function can be a little more than
+ * @timeout_us for different reasons, specially in non-atomic contexts. Thus,
+ * it is possible that this function succeeds even after @timeout_us has passed.
+ */
+int xe_mmio_wait32_not(struct xe_gt *gt, struct xe_reg reg, u32 mask, u32 val, u32 timeout_us,
+ u32 *out_val, bool atomic)
+{
+ ktime_t cur = ktime_get_raw();
+ const ktime_t end = ktime_add_us(cur, timeout_us);
+ int ret = -ETIMEDOUT;
+ s64 wait = 10;
+ u32 read;
+
+ for (;;) {
+ read = xe_mmio_read32(gt, reg);
+ if ((read & mask) != val) {
+ ret = 0;
+ break;
+ }
+
+ cur = ktime_get_raw();
+ if (!ktime_before(cur, end))
+ break;
+
+ if (ktime_after(ktime_add_us(cur, wait), end))
+ wait = ktime_us_delta(end, cur);
+
+ if (atomic)
+ udelay(wait);
+ else
+ usleep_range(wait, wait << 1);
+ wait <<= 1;
+ }
+
+ if (ret != 0) {
+ read = xe_mmio_read32(gt, reg);
+ if ((read & mask) != val)
+ ret = 0;
+ }
+
+ if (out_val)
+ *out_val = read;
+
+ return ret;
+}
diff --git a/drivers/gpu/drm/xe/xe_mmio.h b/drivers/gpu/drm/xe/xe_mmio.h
index a3cd7b3036c7..6ae0cc32c651 100644
--- a/drivers/gpu/drm/xe/xe_mmio.h
+++ b/drivers/gpu/drm/xe/xe_mmio.h
@@ -6,22 +6,13 @@
#ifndef _XE_MMIO_H_
#define _XE_MMIO_H_
-#include <linux/delay.h>
-#include <linux/io-64-nonatomic-lo-hi.h>
-
-#include "regs/xe_reg_defs.h"
-#include "xe_device_types.h"
-#include "xe_gt_printk.h"
#include "xe_gt_types.h"
-struct drm_device;
-struct drm_file;
struct xe_device;
-
-#define LMEM_BAR 2
+struct xe_reg;
int xe_mmio_init(struct xe_device *xe);
-void xe_mmio_probe_tiles(struct xe_device *xe);
+int xe_mmio_probe_tiles(struct xe_device *xe);
u8 xe_mmio_read8(struct xe_gt *gt, struct xe_reg reg);
u16 xe_mmio_read16(struct xe_gt *gt, struct xe_reg reg);
@@ -35,5 +26,14 @@ int xe_mmio_probe_vram(struct xe_device *xe);
u64 xe_mmio_read64_2x32(struct xe_gt *gt, struct xe_reg reg);
int xe_mmio_wait32(struct xe_gt *gt, struct xe_reg reg, u32 mask, u32 val, u32 timeout_us,
u32 *out_val, bool atomic);
+int xe_mmio_wait32_not(struct xe_gt *gt, struct xe_reg reg, u32 mask, u32 val, u32 timeout_us,
+ u32 *out_val, bool atomic);
+
+static inline u32 xe_mmio_adjusted_addr(const struct xe_gt *gt, u32 addr)
+{
+ if (addr < gt->mmio.adj_limit)
+ addr += gt->mmio.adj_offset;
+ return addr;
+}
#endif
diff --git a/drivers/gpu/drm/xe/xe_mocs.c b/drivers/gpu/drm/xe/xe_mocs.c
index 1e92f8ee07ba..7ff0ac5b799a 100644
--- a/drivers/gpu/drm/xe/xe_mocs.c
+++ b/drivers/gpu/drm/xe/xe_mocs.c
@@ -9,10 +9,13 @@
#include "xe_bo.h"
#include "xe_device.h"
#include "xe_exec_queue.h"
+#include "xe_force_wake.h"
#include "xe_gt.h"
#include "xe_gt_mcr.h"
+#include "xe_gt_printk.h"
#include "xe_mmio.h"
#include "xe_platform_types.h"
+#include "xe_pm.h"
#include "xe_sriov.h"
#include "xe_step_types.h"
@@ -36,34 +39,31 @@ struct xe_mocs_entry {
u16 used;
};
+struct xe_mocs_info;
+
+struct xe_mocs_ops {
+ void (*dump)(struct xe_mocs_info *mocs, unsigned int flags,
+ struct xe_gt *gt, struct drm_printer *p);
+};
+
struct xe_mocs_info {
- unsigned int size;
- unsigned int n_entries;
+ /*
+ * Size of the spec's suggested MOCS programming table. The list of
+ * table entries from the spec can potentially be smaller than the
+ * number of hardware registers used to program the MOCS table; in such
+ * cases the registers for the remaining indices will be programmed to
+ * match unused_entries_index.
+ */
+ unsigned int table_size;
+ /* Number of MOCS entries supported by the hardware */
+ unsigned int num_mocs_regs;
const struct xe_mocs_entry *table;
+ const struct xe_mocs_ops *ops;
u8 uc_index;
u8 wb_index;
u8 unused_entries_index;
};
-/* Defines for the tables (XXX_MOCS_0 - XXX_MOCS_63) */
-#define _LE_CACHEABILITY(value) ((value) << 0)
-#define _LE_TGT_CACHE(value) ((value) << 2)
-#define LE_LRUM(value) ((value) << 4)
-#define LE_AOM(value) ((value) << 6)
-#define LE_RSC(value) ((value) << 7)
-#define LE_SCC(value) ((value) << 8)
-#define LE_PFM(value) ((value) << 11)
-#define LE_SCF(value) ((value) << 14)
-#define LE_COS(value) ((value) << 15)
-#define LE_SSE(value) ((value) << 17)
-
-/* Defines for the tables (LNCFMOCS0 - LNCFMOCS31) - two entries per word */
-#define L3_ESC(value) ((value) << 0)
-#define L3_SCC(value) ((value) << 1)
-#define _L3_CACHEABILITY(value) ((value) << 4)
-#define L3_GLBGO(value) ((value) << 6)
-#define L3_LKUP(value) ((value) << 7)
-
/* Defines for the tables (GLOB_MOCS_0 - GLOB_MOCS_16) */
#define IG_PAT REG_BIT(8)
#define L3_CACHE_POLICY_MASK REG_GENMASK(5, 4)
@@ -80,22 +80,22 @@ struct xe_mocs_info {
* Note: LE_0_PAGETABLE works only up to Gen11; for newer gens it means
* the same as LE_UC
*/
-#define LE_0_PAGETABLE _LE_CACHEABILITY(0)
-#define LE_1_UC _LE_CACHEABILITY(1)
-#define LE_2_WT _LE_CACHEABILITY(2)
-#define LE_3_WB _LE_CACHEABILITY(3)
+#define LE_0_PAGETABLE LE_CACHEABILITY(0)
+#define LE_1_UC LE_CACHEABILITY(1)
+#define LE_2_WT LE_CACHEABILITY(2)
+#define LE_3_WB LE_CACHEABILITY(3)
/* Target cache */
-#define LE_TC_0_PAGETABLE _LE_TGT_CACHE(0)
-#define LE_TC_1_LLC _LE_TGT_CACHE(1)
-#define LE_TC_2_LLC_ELLC _LE_TGT_CACHE(2)
-#define LE_TC_3_LLC_ELLC_ALT _LE_TGT_CACHE(3)
+#define LE_TC_0_PAGETABLE LE_TGT_CACHE(0)
+#define LE_TC_1_LLC LE_TGT_CACHE(1)
+#define LE_TC_2_LLC_ELLC LE_TGT_CACHE(2)
+#define LE_TC_3_LLC_ELLC_ALT LE_TGT_CACHE(3)
/* L3 caching options */
-#define L3_0_DIRECT _L3_CACHEABILITY(0)
-#define L3_1_UC _L3_CACHEABILITY(1)
-#define L3_2_RESERVED _L3_CACHEABILITY(2)
-#define L3_3_WB _L3_CACHEABILITY(3)
+#define L3_0_DIRECT L3_CACHEABILITY(0)
+#define L3_1_UC L3_CACHEABILITY(1)
+#define L3_2_RESERVED L3_CACHEABILITY(2)
+#define L3_3_WB L3_CACHEABILITY(3)
/* L4 caching options */
#define L4_0_WB REG_FIELD_PREP(L4_CACHE_POLICY_MASK, 0)
@@ -107,6 +107,8 @@ struct xe_mocs_info {
#define XE2_L3_1_XD REG_FIELD_PREP(L3_CACHE_POLICY_MASK, 1)
#define XE2_L3_3_UC REG_FIELD_PREP(L3_CACHE_POLICY_MASK, 3)
+#define XE2_L3_CLOS_MASK REG_GENMASK(7, 6)
+
#define MOCS_ENTRY(__idx, __control_value, __l3cc_value) \
[__idx] = { \
.control_value = __control_value, \
@@ -255,6 +257,84 @@ static const struct xe_mocs_entry gen12_mocs_desc[] = {
L3_1_UC)
};
+static bool regs_are_mcr(struct xe_gt *gt)
+{
+ struct xe_device *xe = gt_to_xe(gt);
+
+ if (xe_gt_is_media_type(gt))
+ return MEDIA_VER(xe) >= 20;
+ else
+ return GRAPHICS_VERx100(xe) >= 1250;
+}
+
+static void xelp_lncf_dump(struct xe_mocs_info *info, struct xe_gt *gt, struct drm_printer *p)
+{
+ unsigned int i, j;
+ u32 reg_val;
+
+ drm_printf(p, "LNCFCMOCS[idx] = [ESC, SCC, L3CC] (value)\n\n");
+
+ for (i = 0, j = 0; i < (info->num_mocs_regs + 1) / 2; i++, j++) {
+ if (regs_are_mcr(gt))
+ reg_val = xe_gt_mcr_unicast_read_any(gt, XEHP_LNCFCMOCS(i));
+ else
+ reg_val = xe_mmio_read32(gt, XELP_LNCFCMOCS(i));
+
+ drm_printf(p, "LNCFCMOCS[%2d] = [%u, %u, %u] (%#8x)\n",
+ j++,
+ !!(reg_val & L3_ESC_MASK),
+ REG_FIELD_GET(L3_SCC_MASK, reg_val),
+ REG_FIELD_GET(L3_CACHEABILITY_MASK, reg_val),
+ reg_val);
+
+ drm_printf(p, "LNCFCMOCS[%2d] = [%u, %u, %u] (%#8x)\n",
+ j,
+ !!(reg_val & L3_UPPER_IDX_ESC_MASK),
+ REG_FIELD_GET(L3_UPPER_IDX_SCC_MASK, reg_val),
+ REG_FIELD_GET(L3_UPPER_IDX_CACHEABILITY_MASK, reg_val),
+ reg_val);
+ }
+}
+
+static void xelp_mocs_dump(struct xe_mocs_info *info, unsigned int flags,
+ struct xe_gt *gt, struct drm_printer *p)
+{
+ unsigned int i;
+ u32 reg_val;
+
+ if (flags & HAS_GLOBAL_MOCS) {
+ drm_printf(p, "Global mocs table configuration:\n");
+ drm_printf(p, "GLOB_MOCS[idx] = [LeCC, TC, LRUM, AOM, RSC, SCC, PFM, SCF, CoS, SSE] (value)\n\n");
+
+ for (i = 0; i < info->num_mocs_regs; i++) {
+ if (regs_are_mcr(gt))
+ reg_val = xe_gt_mcr_unicast_read_any(gt, XEHP_GLOBAL_MOCS(i));
+ else
+ reg_val = xe_mmio_read32(gt, XELP_GLOBAL_MOCS(i));
+
+ drm_printf(p, "GLOB_MOCS[%2d] = [%u, %u, %u, %u, %u, %u, %u, %u, %u, %u ] (%#8x)\n",
+ i,
+ REG_FIELD_GET(LE_CACHEABILITY_MASK, reg_val),
+ REG_FIELD_GET(LE_TGT_CACHE_MASK, reg_val),
+ REG_FIELD_GET(LE_LRUM_MASK, reg_val),
+ !!(reg_val & LE_AOM_MASK),
+ !!(reg_val & LE_RSC_MASK),
+ REG_FIELD_GET(LE_SCC_MASK, reg_val),
+ REG_FIELD_GET(LE_PFM_MASK, reg_val),
+ !!(reg_val & LE_SCF_MASK),
+ REG_FIELD_GET(LE_COS_MASK, reg_val),
+ REG_FIELD_GET(LE_SSE_MASK, reg_val),
+ reg_val);
+ }
+ }
+
+ xelp_lncf_dump(info, gt, p);
+}
+
+static const struct xe_mocs_ops xelp_mocs_ops = {
+ .dump = xelp_mocs_dump,
+};
+
static const struct xe_mocs_entry dg1_mocs_desc[] = {
/* UC */
MOCS_ENTRY(1, 0, L3_1_UC),
@@ -291,6 +371,40 @@ static const struct xe_mocs_entry dg2_mocs_desc[] = {
MOCS_ENTRY(3, 0, L3_3_WB | L3_LKUP(1)),
};
+static void xehp_lncf_dump(struct xe_mocs_info *info, unsigned int flags,
+ struct xe_gt *gt, struct drm_printer *p)
+{
+ unsigned int i, j;
+ u32 reg_val;
+
+ drm_printf(p, "LNCFCMOCS[idx] = [UCL3LOOKUP, GLBGO, L3CC] (value)\n\n");
+
+ for (i = 0, j = 0; i < (info->num_mocs_regs + 1) / 2; i++, j++) {
+ if (regs_are_mcr(gt))
+ reg_val = xe_gt_mcr_unicast_read_any(gt, XEHP_LNCFCMOCS(i));
+ else
+ reg_val = xe_mmio_read32(gt, XELP_LNCFCMOCS(i));
+
+ drm_printf(p, "LNCFCMOCS[%2d] = [%u, %u, %u] (%#8x)\n",
+ j++,
+ !!(reg_val & L3_LKUP_MASK),
+ !!(reg_val & L3_GLBGO_MASK),
+ REG_FIELD_GET(L3_CACHEABILITY_MASK, reg_val),
+ reg_val);
+
+ drm_printf(p, "LNCFCMOCS[%2d] = [%u, %u, %u] (%#8x)\n",
+ j,
+ !!(reg_val & L3_UPPER_LKUP_MASK),
+ !!(reg_val & L3_UPPER_GLBGO_MASK),
+ REG_FIELD_GET(L3_UPPER_IDX_CACHEABILITY_MASK, reg_val),
+ reg_val);
+ }
+}
+
+static const struct xe_mocs_ops xehp_mocs_ops = {
+ .dump = xehp_lncf_dump,
+};
+
static const struct xe_mocs_entry pvc_mocs_desc[] = {
/* Error */
MOCS_ENTRY(0, 0, L3_3_WB),
@@ -302,6 +416,36 @@ static const struct xe_mocs_entry pvc_mocs_desc[] = {
MOCS_ENTRY(2, 0, L3_3_WB),
};
+static void pvc_mocs_dump(struct xe_mocs_info *info, unsigned int flags, struct xe_gt *gt,
+ struct drm_printer *p)
+{
+ unsigned int i, j;
+ u32 reg_val;
+
+ drm_printf(p, "LNCFCMOCS[idx] = [ L3CC ] (value)\n\n");
+
+ for (i = 0, j = 0; i < (info->num_mocs_regs + 1) / 2; i++, j++) {
+ if (regs_are_mcr(gt))
+ reg_val = xe_gt_mcr_unicast_read_any(gt, XEHP_LNCFCMOCS(i));
+ else
+ reg_val = xe_mmio_read32(gt, XELP_LNCFCMOCS(i));
+
+ drm_printf(p, "LNCFCMOCS[%2d] = [ %u ] (%#8x)\n",
+ j++,
+ REG_FIELD_GET(L3_CACHEABILITY_MASK, reg_val),
+ reg_val);
+
+ drm_printf(p, "LNCFCMOCS[%2d] = [ %u ] (%#8x)\n",
+ j,
+ REG_FIELD_GET(L3_UPPER_IDX_CACHEABILITY_MASK, reg_val),
+ reg_val);
+ }
+}
+
+static const struct xe_mocs_ops pvc_mocs_ops = {
+ .dump = pvc_mocs_dump,
+};
+
static const struct xe_mocs_entry mtl_mocs_desc[] = {
/* Error - Reserved for Non-Use */
MOCS_ENTRY(0,
@@ -353,6 +497,36 @@ static const struct xe_mocs_entry mtl_mocs_desc[] = {
L3_GLBGO(1) | L3_1_UC),
};
+static void mtl_mocs_dump(struct xe_mocs_info *info, unsigned int flags,
+ struct xe_gt *gt, struct drm_printer *p)
+{
+ unsigned int i;
+ u32 reg_val;
+
+ drm_printf(p, "Global mocs table configuration:\n");
+ drm_printf(p, "GLOB_MOCS[idx] = [IG_PAT, L4_CACHE_POLICY] (value)\n\n");
+
+ for (i = 0; i < info->num_mocs_regs; i++) {
+ if (regs_are_mcr(gt))
+ reg_val = xe_gt_mcr_unicast_read_any(gt, XEHP_GLOBAL_MOCS(i));
+ else
+ reg_val = xe_mmio_read32(gt, XELP_GLOBAL_MOCS(i));
+
+ drm_printf(p, "GLOB_MOCS[%2d] = [%u, %u] (%#8x)\n",
+ i,
+ !!(reg_val & IG_PAT),
+ REG_FIELD_GET(L4_CACHE_POLICY_MASK, reg_val),
+ reg_val);
+ }
+
+ /* MTL lncf mocs table pattern is similar to that of xehp */
+ xehp_lncf_dump(info, flags, gt, p);
+}
+
+static const struct xe_mocs_ops mtl_mocs_ops = {
+ .dump = mtl_mocs_dump,
+};
+
static const struct xe_mocs_entry xe2_mocs_table[] = {
/* Defer to PAT */
MOCS_ENTRY(0, XE2_L3_0_WB | L4_3_UC, 0),
@@ -366,6 +540,34 @@ static const struct xe_mocs_entry xe2_mocs_table[] = {
MOCS_ENTRY(4, IG_PAT | XE2_L3_0_WB | L4_0_WB, 0),
};
+static void xe2_mocs_dump(struct xe_mocs_info *info, unsigned int flags,
+ struct xe_gt *gt, struct drm_printer *p)
+{
+ unsigned int i;
+ u32 reg_val;
+
+ drm_printf(p, "Global mocs table configuration:\n");
+ drm_printf(p, "GLOB_MOCS[idx] = [IG_PAT, L3_CLOS, L3_CACHE_POLICY, L4_CACHE_POLICY] (value)\n\n");
+
+ for (i = 0; i < info->num_mocs_regs; i++) {
+ if (regs_are_mcr(gt))
+ reg_val = xe_gt_mcr_unicast_read_any(gt, XEHP_GLOBAL_MOCS(i));
+ else
+ reg_val = xe_mmio_read32(gt, XELP_GLOBAL_MOCS(i));
+
+ drm_printf(p, "GLOB_MOCS[%2d] = [%u, %u, %u] (%#8x)\n",
+ i,
+ !!(reg_val & IG_PAT),
+ REG_FIELD_GET(XE2_L3_CLOS_MASK, reg_val),
+ REG_FIELD_GET(L4_CACHE_POLICY_MASK, reg_val),
+ reg_val);
+ }
+}
+
+static const struct xe_mocs_ops xe2_mocs_ops = {
+ .dump = xe2_mocs_dump,
+};
+
static unsigned int get_mocs_settings(struct xe_device *xe,
struct xe_mocs_info *info)
{
@@ -376,44 +578,49 @@ static unsigned int get_mocs_settings(struct xe_device *xe,
switch (xe->info.platform) {
case XE_LUNARLAKE:
case XE_BATTLEMAGE:
- info->size = ARRAY_SIZE(xe2_mocs_table);
+ info->ops = &xe2_mocs_ops;
+ info->table_size = ARRAY_SIZE(xe2_mocs_table);
info->table = xe2_mocs_table;
- info->n_entries = XE2_NUM_MOCS_ENTRIES;
+ info->num_mocs_regs = XE2_NUM_MOCS_ENTRIES;
info->uc_index = 3;
info->wb_index = 4;
info->unused_entries_index = 4;
break;
case XE_PVC:
- info->size = ARRAY_SIZE(pvc_mocs_desc);
+ info->ops = &pvc_mocs_ops;
+ info->table_size = ARRAY_SIZE(pvc_mocs_desc);
info->table = pvc_mocs_desc;
- info->n_entries = PVC_NUM_MOCS_ENTRIES;
+ info->num_mocs_regs = PVC_NUM_MOCS_ENTRIES;
info->uc_index = 1;
info->wb_index = 2;
info->unused_entries_index = 2;
break;
case XE_METEORLAKE:
- info->size = ARRAY_SIZE(mtl_mocs_desc);
+ info->ops = &mtl_mocs_ops;
+ info->table_size = ARRAY_SIZE(mtl_mocs_desc);
info->table = mtl_mocs_desc;
- info->n_entries = MTL_NUM_MOCS_ENTRIES;
+ info->num_mocs_regs = MTL_NUM_MOCS_ENTRIES;
info->uc_index = 9;
info->unused_entries_index = 1;
break;
case XE_DG2:
- info->size = ARRAY_SIZE(dg2_mocs_desc);
+ info->ops = &xehp_mocs_ops;
+ info->table_size = ARRAY_SIZE(dg2_mocs_desc);
info->table = dg2_mocs_desc;
info->uc_index = 1;
/*
* Last entry is RO on hardware, don't bother with what was
* written when checking later
*/
- info->n_entries = XELP_NUM_MOCS_ENTRIES - 1;
+ info->num_mocs_regs = XELP_NUM_MOCS_ENTRIES - 1;
info->unused_entries_index = 3;
break;
case XE_DG1:
- info->size = ARRAY_SIZE(dg1_mocs_desc);
+ info->ops = &xelp_mocs_ops;
+ info->table_size = ARRAY_SIZE(dg1_mocs_desc);
info->table = dg1_mocs_desc;
info->uc_index = 1;
- info->n_entries = XELP_NUM_MOCS_ENTRIES;
+ info->num_mocs_regs = XELP_NUM_MOCS_ENTRIES;
info->unused_entries_index = 5;
break;
case XE_TIGERLAKE:
@@ -421,9 +628,10 @@ static unsigned int get_mocs_settings(struct xe_device *xe,
case XE_ALDERLAKE_S:
case XE_ALDERLAKE_P:
case XE_ALDERLAKE_N:
- info->size = ARRAY_SIZE(gen12_mocs_desc);
+ info->ops = &xelp_mocs_ops;
+ info->table_size = ARRAY_SIZE(gen12_mocs_desc);
info->table = gen12_mocs_desc;
- info->n_entries = XELP_NUM_MOCS_ENTRIES;
+ info->num_mocs_regs = XELP_NUM_MOCS_ENTRIES;
info->uc_index = 3;
info->unused_entries_index = 2;
break;
@@ -442,10 +650,8 @@ static unsigned int get_mocs_settings(struct xe_device *xe,
*/
xe_assert(xe, info->unused_entries_index != 0);
- if (XE_WARN_ON(info->size > info->n_entries)) {
- info->table = NULL;
- return 0;
- }
+ xe_assert(xe, info->ops && info->ops->dump);
+ xe_assert(xe, info->table_size <= info->num_mocs_regs);
if (!IS_DGFX(xe) || GRAPHICS_VER(xe) >= 20)
flags |= HAS_GLOBAL_MOCS;
@@ -462,33 +668,20 @@ static unsigned int get_mocs_settings(struct xe_device *xe,
static u32 get_entry_control(const struct xe_mocs_info *info,
unsigned int index)
{
- if (index < info->size && info->table[index].used)
+ if (index < info->table_size && info->table[index].used)
return info->table[index].control_value;
return info->table[info->unused_entries_index].control_value;
}
-static bool regs_are_mcr(struct xe_gt *gt)
-{
- struct xe_device *xe = gt_to_xe(gt);
-
- if (xe_gt_is_media_type(gt))
- return MEDIA_VER(xe) >= 20;
- else
- return GRAPHICS_VERx100(xe) >= 1250;
-}
-
static void __init_mocs_table(struct xe_gt *gt,
const struct xe_mocs_info *info)
{
unsigned int i;
u32 mocs;
- xe_gt_WARN_ONCE(gt, !info->unused_entries_index,
- "Unused entries index should have been defined\n");
+ mocs_dbg(gt, "mocs entries: %d\n", info->num_mocs_regs);
- mocs_dbg(gt, "mocs entries: %d\n", info->n_entries);
-
- for (i = 0; i < info->n_entries; i++) {
+ for (i = 0; i < info->num_mocs_regs; i++) {
mocs = get_entry_control(info, i);
mocs_dbg(gt, "GLOB_MOCS[%d] 0x%x 0x%x\n", i,
@@ -509,7 +702,7 @@ static void __init_mocs_table(struct xe_gt *gt,
static u16 get_entry_l3cc(const struct xe_mocs_info *info,
unsigned int index)
{
- if (index < info->size && info->table[index].used)
+ if (index < info->table_size && info->table[index].used)
return info->table[index].l3cc_value;
return info->table[info->unused_entries_index].l3cc_value;
}
@@ -525,9 +718,9 @@ static void init_l3cc_table(struct xe_gt *gt,
unsigned int i;
u32 l3cc;
- mocs_dbg(gt, "l3cc entries: %d\n", info->n_entries);
+ mocs_dbg(gt, "l3cc entries: %d\n", info->num_mocs_regs);
- for (i = 0; i < (info->n_entries + 1) / 2; i++) {
+ for (i = 0; i < (info->num_mocs_regs + 1) / 2; i++) {
l3cc = l3cc_combine(get_entry_l3cc(info, 2 * i),
get_entry_l3cc(info, 2 * i + 1));
@@ -578,6 +771,30 @@ void xe_mocs_init(struct xe_gt *gt)
init_l3cc_table(gt, &table);
}
+void xe_mocs_dump(struct xe_gt *gt, struct drm_printer *p)
+{
+ struct xe_mocs_info table;
+ unsigned int flags;
+ u32 ret;
+ struct xe_device *xe = gt_to_xe(gt);
+
+ flags = get_mocs_settings(xe, &table);
+
+ xe_pm_runtime_get_noresume(xe);
+ ret = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
+
+ if (ret)
+ goto err_fw;
+
+ table.ops->dump(&table, flags, gt, p);
+
+ xe_force_wake_put(gt_to_fw(gt), XE_FW_GT);
+
+err_fw:
+ xe_assert(xe, !ret);
+ xe_pm_runtime_put(xe);
+}
+
#if IS_ENABLED(CONFIG_DRM_XE_KUNIT_TEST)
#include "tests/xe_mocs.c"
#endif
diff --git a/drivers/gpu/drm/xe/xe_mocs.h b/drivers/gpu/drm/xe/xe_mocs.h
index 053754c5a94e..dc972ffd4d07 100644
--- a/drivers/gpu/drm/xe/xe_mocs.h
+++ b/drivers/gpu/drm/xe/xe_mocs.h
@@ -6,12 +6,17 @@
#ifndef _XE_MOCS_H_
#define _XE_MOCS_H_
-#include <linux/types.h>
-
-struct xe_exec_queue;
+struct drm_printer;
struct xe_gt;
void xe_mocs_init_early(struct xe_gt *gt);
void xe_mocs_init(struct xe_gt *gt);
+/**
+ * xe_mocs_dump - Dump mocs table
+ * @gt: GT structure
+ * @p: Printer to dump info to
+ */
+void xe_mocs_dump(struct xe_gt *gt, struct drm_printer *p);
+
#endif
diff --git a/drivers/gpu/drm/xe/xe_module.c b/drivers/gpu/drm/xe/xe_module.c
index ceb8345cbca6..499540add465 100644
--- a/drivers/gpu/drm/xe/xe_module.c
+++ b/drivers/gpu/drm/xe/xe_module.c
@@ -11,12 +11,14 @@
#include "xe_drv.h"
#include "xe_hw_fence.h"
#include "xe_pci.h"
+#include "xe_observation.h"
#include "xe_sched_job.h"
struct xe_modparam xe_modparam = {
.enable_display = true,
.guc_log_level = 5,
.force_probe = CONFIG_DRM_XE_FORCE_PROBE,
+ .wedged_mode = 1,
/* the rest are 0 by default */
};
@@ -55,6 +57,10 @@ MODULE_PARM_DESC(max_vfs,
"(0 = no VFs [default]; N = allow up to N VFs)");
#endif
+module_param_named_unsafe(wedged_mode, xe_modparam.wedged_mode, int, 0600);
+MODULE_PARM_DESC(wedged_mode,
+ "Module's default policy for the wedged mode - 0=never, 1=upon-critical-errors[default], 2=upon-any-hang");
+
struct init_funcs {
int (*init)(void);
void (*exit)(void);
@@ -73,6 +79,10 @@ static const struct init_funcs init_funcs[] = {
.init = xe_register_pci_driver,
.exit = xe_unregister_pci_driver,
},
+ {
+ .init = xe_observation_sysctl_register,
+ .exit = xe_observation_sysctl_unregister,
+ },
};
static int __init xe_init(void)
diff --git a/drivers/gpu/drm/xe/xe_module.h b/drivers/gpu/drm/xe/xe_module.h
index b369984f08ec..61a0d28a28c8 100644
--- a/drivers/gpu/drm/xe/xe_module.h
+++ b/drivers/gpu/drm/xe/xe_module.h
@@ -21,6 +21,7 @@ struct xe_modparam {
#ifdef CONFIG_PCI_IOV
unsigned int max_vfs;
#endif
+ int wedged_mode;
};
extern struct xe_modparam xe_modparam;
diff --git a/drivers/gpu/drm/xe/xe_oa.c b/drivers/gpu/drm/xe/xe_oa.c
new file mode 100644
index 000000000000..6d69f751bf78
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_oa.c
@@ -0,0 +1,2512 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2023-2024 Intel Corporation
+ */
+
+#include <linux/anon_inodes.h>
+#include <linux/delay.h>
+#include <linux/nospec.h>
+#include <linux/poll.h>
+
+#include <drm/drm_drv.h>
+#include <drm/drm_managed.h>
+#include <drm/xe_drm.h>
+
+#include "abi/guc_actions_slpc_abi.h"
+#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 "regs/xe_oa_regs.h"
+#include "xe_assert.h"
+#include "xe_bb.h"
+#include "xe_bo.h"
+#include "xe_device.h"
+#include "xe_exec_queue.h"
+#include "xe_force_wake.h"
+#include "xe_gt.h"
+#include "xe_gt_mcr.h"
+#include "xe_gt_printk.h"
+#include "xe_guc_pc.h"
+#include "xe_lrc.h"
+#include "xe_macros.h"
+#include "xe_mmio.h"
+#include "xe_oa.h"
+#include "xe_observation.h"
+#include "xe_pm.h"
+#include "xe_sched_job.h"
+#include "xe_sriov.h"
+
+#define DEFAULT_POLL_FREQUENCY_HZ 200
+#define DEFAULT_POLL_PERIOD_NS (NSEC_PER_SEC / DEFAULT_POLL_FREQUENCY_HZ)
+#define XE_OA_UNIT_INVALID U32_MAX
+
+struct xe_oa_reg {
+ struct xe_reg addr;
+ u32 value;
+};
+
+struct xe_oa_config {
+ struct xe_oa *oa;
+
+ char uuid[UUID_STRING_LEN + 1];
+ int id;
+
+ const struct xe_oa_reg *regs;
+ u32 regs_len;
+
+ struct attribute_group sysfs_metric;
+ struct attribute *attrs[2];
+ struct kobj_attribute sysfs_metric_id;
+
+ struct kref ref;
+ struct rcu_head rcu;
+};
+
+struct flex {
+ struct xe_reg reg;
+ u32 offset;
+ u32 value;
+};
+
+struct xe_oa_open_param {
+ u32 oa_unit_id;
+ bool sample;
+ u32 metric_set;
+ enum xe_oa_format_name oa_format;
+ int period_exponent;
+ bool disabled;
+ int exec_queue_id;
+ int engine_instance;
+ struct xe_exec_queue *exec_q;
+ struct xe_hw_engine *hwe;
+ bool no_preempt;
+};
+
+struct xe_oa_config_bo {
+ struct llist_node node;
+
+ struct xe_oa_config *oa_config;
+ struct xe_bb *bb;
+};
+
+#define DRM_FMT(x) DRM_XE_OA_FMT_TYPE_##x
+
+static const struct xe_oa_format oa_formats[] = {
+ [XE_OA_FORMAT_C4_B8] = { 7, 64, DRM_FMT(OAG) },
+ [XE_OA_FORMAT_A12] = { 0, 64, DRM_FMT(OAG) },
+ [XE_OA_FORMAT_A12_B8_C8] = { 2, 128, DRM_FMT(OAG) },
+ [XE_OA_FORMAT_A32u40_A4u32_B8_C8] = { 5, 256, DRM_FMT(OAG) },
+ [XE_OAR_FORMAT_A32u40_A4u32_B8_C8] = { 5, 256, DRM_FMT(OAR) },
+ [XE_OA_FORMAT_A24u40_A14u32_B8_C8] = { 5, 256, DRM_FMT(OAG) },
+ [XE_OAC_FORMAT_A24u64_B8_C8] = { 1, 320, DRM_FMT(OAC), HDR_64_BIT },
+ [XE_OAC_FORMAT_A22u32_R2u32_B8_C8] = { 2, 192, DRM_FMT(OAC), HDR_64_BIT },
+ [XE_OAM_FORMAT_MPEC8u64_B8_C8] = { 1, 192, DRM_FMT(OAM_MPEC), HDR_64_BIT },
+ [XE_OAM_FORMAT_MPEC8u32_B8_C8] = { 2, 128, DRM_FMT(OAM_MPEC), HDR_64_BIT },
+ [XE_OA_FORMAT_PEC64u64] = { 1, 576, DRM_FMT(PEC), HDR_64_BIT, 1, 0 },
+ [XE_OA_FORMAT_PEC64u64_B8_C8] = { 1, 640, DRM_FMT(PEC), HDR_64_BIT, 1, 1 },
+ [XE_OA_FORMAT_PEC64u32] = { 1, 320, DRM_FMT(PEC), HDR_64_BIT },
+ [XE_OA_FORMAT_PEC32u64_G1] = { 5, 320, DRM_FMT(PEC), HDR_64_BIT, 1, 0 },
+ [XE_OA_FORMAT_PEC32u32_G1] = { 5, 192, DRM_FMT(PEC), HDR_64_BIT },
+ [XE_OA_FORMAT_PEC32u64_G2] = { 6, 320, DRM_FMT(PEC), HDR_64_BIT, 1, 0 },
+ [XE_OA_FORMAT_PEC32u32_G2] = { 6, 192, DRM_FMT(PEC), HDR_64_BIT },
+ [XE_OA_FORMAT_PEC36u64_G1_32_G2_4] = { 3, 320, DRM_FMT(PEC), HDR_64_BIT, 1, 0 },
+ [XE_OA_FORMAT_PEC36u64_G1_4_G2_32] = { 4, 320, DRM_FMT(PEC), HDR_64_BIT, 1, 0 },
+};
+
+static u32 xe_oa_circ_diff(struct xe_oa_stream *stream, u32 tail, u32 head)
+{
+ return tail >= head ? tail - head :
+ tail + stream->oa_buffer.circ_size - head;
+}
+
+static u32 xe_oa_circ_incr(struct xe_oa_stream *stream, u32 ptr, u32 n)
+{
+ return ptr + n >= stream->oa_buffer.circ_size ?
+ ptr + n - stream->oa_buffer.circ_size : ptr + n;
+}
+
+static void xe_oa_config_release(struct kref *ref)
+{
+ struct xe_oa_config *oa_config =
+ container_of(ref, typeof(*oa_config), ref);
+
+ kfree(oa_config->regs);
+
+ kfree_rcu(oa_config, rcu);
+}
+
+static void xe_oa_config_put(struct xe_oa_config *oa_config)
+{
+ if (!oa_config)
+ return;
+
+ kref_put(&oa_config->ref, xe_oa_config_release);
+}
+
+static struct xe_oa_config *xe_oa_config_get(struct xe_oa_config *oa_config)
+{
+ return kref_get_unless_zero(&oa_config->ref) ? oa_config : NULL;
+}
+
+static struct xe_oa_config *xe_oa_get_oa_config(struct xe_oa *oa, int metrics_set)
+{
+ struct xe_oa_config *oa_config;
+
+ rcu_read_lock();
+ oa_config = idr_find(&oa->metrics_idr, metrics_set);
+ if (oa_config)
+ oa_config = xe_oa_config_get(oa_config);
+ rcu_read_unlock();
+
+ return oa_config;
+}
+
+static void free_oa_config_bo(struct xe_oa_config_bo *oa_bo)
+{
+ xe_oa_config_put(oa_bo->oa_config);
+ xe_bb_free(oa_bo->bb, NULL);
+ kfree(oa_bo);
+}
+
+static const struct xe_oa_regs *__oa_regs(struct xe_oa_stream *stream)
+{
+ return &stream->hwe->oa_unit->regs;
+}
+
+static u32 xe_oa_hw_tail_read(struct xe_oa_stream *stream)
+{
+ return xe_mmio_read32(stream->gt, __oa_regs(stream)->oa_tail_ptr) &
+ OAG_OATAILPTR_MASK;
+}
+
+#define oa_report_header_64bit(__s) \
+ ((__s)->oa_buffer.format->header == HDR_64_BIT)
+
+static u64 oa_report_id(struct xe_oa_stream *stream, void *report)
+{
+ return oa_report_header_64bit(stream) ? *(u64 *)report : *(u32 *)report;
+}
+
+static void oa_report_id_clear(struct xe_oa_stream *stream, u32 *report)
+{
+ if (oa_report_header_64bit(stream))
+ *(u64 *)report = 0;
+ else
+ *report = 0;
+}
+
+static u64 oa_timestamp(struct xe_oa_stream *stream, void *report)
+{
+ return oa_report_header_64bit(stream) ?
+ *((u64 *)report + 1) :
+ *((u32 *)report + 1);
+}
+
+static void oa_timestamp_clear(struct xe_oa_stream *stream, u32 *report)
+{
+ if (oa_report_header_64bit(stream))
+ *(u64 *)&report[2] = 0;
+ else
+ report[1] = 0;
+}
+
+static bool xe_oa_buffer_check_unlocked(struct xe_oa_stream *stream)
+{
+ u32 gtt_offset = xe_bo_ggtt_addr(stream->oa_buffer.bo);
+ int report_size = stream->oa_buffer.format->size;
+ u32 tail, hw_tail;
+ unsigned long flags;
+ bool pollin;
+ u32 partial_report_size;
+
+ spin_lock_irqsave(&stream->oa_buffer.ptr_lock, flags);
+
+ hw_tail = xe_oa_hw_tail_read(stream);
+ hw_tail -= gtt_offset;
+
+ /*
+ * The tail pointer increases in 64 byte (cacheline size), not in report_size
+ * increments. Also report size may not be a power of 2. Compute potential
+ * partially landed report in OA buffer.
+ */
+ partial_report_size = xe_oa_circ_diff(stream, hw_tail, stream->oa_buffer.tail);
+ partial_report_size %= report_size;
+
+ /* Subtract partial amount off the tail */
+ hw_tail = xe_oa_circ_diff(stream, hw_tail, partial_report_size);
+
+ tail = hw_tail;
+
+ /*
+ * Walk the stream backward until we find a report with report id and timestamp
+ * not 0. We can't tell whether a report has fully landed in memory before the
+ * report id and timestamp of the following report have landed.
+ *
+ * This is assuming that the writes of the OA unit land in memory in the order
+ * they were written. If not : (╯°□°)╯︵ ┻━┻
+ */
+ while (xe_oa_circ_diff(stream, tail, stream->oa_buffer.tail) >= report_size) {
+ void *report = stream->oa_buffer.vaddr + tail;
+
+ if (oa_report_id(stream, report) || oa_timestamp(stream, report))
+ break;
+
+ tail = xe_oa_circ_diff(stream, tail, report_size);
+ }
+
+ if (xe_oa_circ_diff(stream, hw_tail, tail) > report_size)
+ drm_dbg(&stream->oa->xe->drm,
+ "unlanded report(s) head=0x%x tail=0x%x hw_tail=0x%x\n",
+ stream->oa_buffer.head, tail, hw_tail);
+
+ stream->oa_buffer.tail = tail;
+
+ pollin = xe_oa_circ_diff(stream, stream->oa_buffer.tail,
+ stream->oa_buffer.head) >= report_size;
+
+ spin_unlock_irqrestore(&stream->oa_buffer.ptr_lock, flags);
+
+ return pollin;
+}
+
+static enum hrtimer_restart xe_oa_poll_check_timer_cb(struct hrtimer *hrtimer)
+{
+ struct xe_oa_stream *stream =
+ container_of(hrtimer, typeof(*stream), poll_check_timer);
+
+ if (xe_oa_buffer_check_unlocked(stream)) {
+ stream->pollin = true;
+ wake_up(&stream->poll_wq);
+ }
+
+ hrtimer_forward_now(hrtimer, ns_to_ktime(stream->poll_period_ns));
+
+ return HRTIMER_RESTART;
+}
+
+static int xe_oa_append_report(struct xe_oa_stream *stream, char __user *buf,
+ size_t count, size_t *offset, const u8 *report)
+{
+ int report_size = stream->oa_buffer.format->size;
+ int report_size_partial;
+ u8 *oa_buf_end;
+
+ if ((count - *offset) < report_size)
+ return -ENOSPC;
+
+ buf += *offset;
+
+ oa_buf_end = stream->oa_buffer.vaddr + stream->oa_buffer.circ_size;
+ report_size_partial = oa_buf_end - report;
+
+ if (report_size_partial < report_size) {
+ if (copy_to_user(buf, report, report_size_partial))
+ return -EFAULT;
+ buf += report_size_partial;
+
+ if (copy_to_user(buf, stream->oa_buffer.vaddr,
+ report_size - report_size_partial))
+ return -EFAULT;
+ } else if (copy_to_user(buf, report, report_size)) {
+ return -EFAULT;
+ }
+
+ *offset += report_size;
+
+ return 0;
+}
+
+static int xe_oa_append_reports(struct xe_oa_stream *stream, char __user *buf,
+ size_t count, size_t *offset)
+{
+ int report_size = stream->oa_buffer.format->size;
+ u8 *oa_buf_base = stream->oa_buffer.vaddr;
+ u32 gtt_offset = xe_bo_ggtt_addr(stream->oa_buffer.bo);
+ size_t start_offset = *offset;
+ unsigned long flags;
+ u32 head, tail;
+ int ret = 0;
+
+ spin_lock_irqsave(&stream->oa_buffer.ptr_lock, flags);
+ head = stream->oa_buffer.head;
+ tail = stream->oa_buffer.tail;
+ spin_unlock_irqrestore(&stream->oa_buffer.ptr_lock, flags);
+
+ xe_assert(stream->oa->xe,
+ head < stream->oa_buffer.circ_size && tail < stream->oa_buffer.circ_size);
+
+ for (; xe_oa_circ_diff(stream, tail, head);
+ head = xe_oa_circ_incr(stream, head, report_size)) {
+ u8 *report = oa_buf_base + head;
+
+ ret = xe_oa_append_report(stream, buf, count, offset, report);
+ if (ret)
+ break;
+
+ if (!(stream->oa_buffer.circ_size % report_size)) {
+ /* Clear out report id and timestamp to detect unlanded reports */
+ oa_report_id_clear(stream, (void *)report);
+ oa_timestamp_clear(stream, (void *)report);
+ } else {
+ u8 *oa_buf_end = stream->oa_buffer.vaddr + stream->oa_buffer.circ_size;
+ u32 part = oa_buf_end - report;
+
+ /* Zero out the entire report */
+ if (report_size <= part) {
+ memset(report, 0, report_size);
+ } else {
+ memset(report, 0, part);
+ memset(oa_buf_base, 0, report_size - part);
+ }
+ }
+ }
+
+ if (start_offset != *offset) {
+ struct xe_reg oaheadptr = __oa_regs(stream)->oa_head_ptr;
+
+ spin_lock_irqsave(&stream->oa_buffer.ptr_lock, flags);
+ xe_mmio_write32(stream->gt, oaheadptr,
+ (head + gtt_offset) & OAG_OAHEADPTR_MASK);
+ stream->oa_buffer.head = head;
+ spin_unlock_irqrestore(&stream->oa_buffer.ptr_lock, flags);
+ }
+
+ return ret;
+}
+
+static void xe_oa_init_oa_buffer(struct xe_oa_stream *stream)
+{
+ u32 gtt_offset = xe_bo_ggtt_addr(stream->oa_buffer.bo);
+ u32 oa_buf = gtt_offset | OABUFFER_SIZE_16M | OAG_OABUFFER_MEMORY_SELECT;
+ unsigned long flags;
+
+ spin_lock_irqsave(&stream->oa_buffer.ptr_lock, flags);
+
+ xe_mmio_write32(stream->gt, __oa_regs(stream)->oa_status, 0);
+ xe_mmio_write32(stream->gt, __oa_regs(stream)->oa_head_ptr,
+ gtt_offset & OAG_OAHEADPTR_MASK);
+ stream->oa_buffer.head = 0;
+ /*
+ * PRM says: "This MMIO must be set before the OATAILPTR register and after the
+ * OAHEADPTR register. This is to enable proper functionality of the overflow bit".
+ */
+ xe_mmio_write32(stream->gt, __oa_regs(stream)->oa_buffer, oa_buf);
+ xe_mmio_write32(stream->gt, __oa_regs(stream)->oa_tail_ptr,
+ gtt_offset & OAG_OATAILPTR_MASK);
+
+ /* Mark that we need updated tail pointer to read from */
+ stream->oa_buffer.tail = 0;
+
+ spin_unlock_irqrestore(&stream->oa_buffer.ptr_lock, flags);
+
+ /* Zero out the OA buffer since we rely on zero report id and timestamp fields */
+ memset(stream->oa_buffer.vaddr, 0, stream->oa_buffer.bo->size);
+}
+
+static u32 __format_to_oactrl(const struct xe_oa_format *format, int counter_sel_mask)
+{
+ return ((format->counter_select << (ffs(counter_sel_mask) - 1)) & counter_sel_mask) |
+ REG_FIELD_PREP(OA_OACONTROL_REPORT_BC_MASK, format->bc_report) |
+ REG_FIELD_PREP(OA_OACONTROL_COUNTER_SIZE_MASK, format->counter_size);
+}
+
+static u32 __oa_ccs_select(struct xe_oa_stream *stream)
+{
+ u32 val;
+
+ if (stream->hwe->class != XE_ENGINE_CLASS_COMPUTE)
+ return 0;
+
+ val = REG_FIELD_PREP(OAG_OACONTROL_OA_CCS_SELECT_MASK, stream->hwe->instance);
+ xe_assert(stream->oa->xe,
+ REG_FIELD_GET(OAG_OACONTROL_OA_CCS_SELECT_MASK, val) == stream->hwe->instance);
+ return val;
+}
+
+static void xe_oa_enable(struct xe_oa_stream *stream)
+{
+ const struct xe_oa_format *format = stream->oa_buffer.format;
+ const struct xe_oa_regs *regs;
+ u32 val;
+
+ /*
+ * BSpec: 46822: Bit 0. Even if stream->sample is 0, for OAR to function, the OA
+ * buffer must be correctly initialized
+ */
+ xe_oa_init_oa_buffer(stream);
+
+ regs = __oa_regs(stream);
+ val = __format_to_oactrl(format, regs->oa_ctrl_counter_select_mask) |
+ __oa_ccs_select(stream) | OAG_OACONTROL_OA_COUNTER_ENABLE;
+
+ xe_mmio_write32(stream->gt, regs->oa_ctrl, val);
+}
+
+static void xe_oa_disable(struct xe_oa_stream *stream)
+{
+ xe_mmio_write32(stream->gt, __oa_regs(stream)->oa_ctrl, 0);
+ if (xe_mmio_wait32(stream->gt, __oa_regs(stream)->oa_ctrl,
+ OAG_OACONTROL_OA_COUNTER_ENABLE, 0, 50000, NULL, false))
+ drm_err(&stream->oa->xe->drm,
+ "wait for OA to be disabled timed out\n");
+
+ if (GRAPHICS_VERx100(stream->oa->xe) <= 1270 && GRAPHICS_VERx100(stream->oa->xe) != 1260) {
+ /* <= XE_METEORLAKE except XE_PVC */
+ xe_mmio_write32(stream->gt, OA_TLB_INV_CR, 1);
+ if (xe_mmio_wait32(stream->gt, OA_TLB_INV_CR, 1, 0, 50000, NULL, false))
+ drm_err(&stream->oa->xe->drm,
+ "wait for OA tlb invalidate timed out\n");
+ }
+}
+
+static int xe_oa_wait_unlocked(struct xe_oa_stream *stream)
+{
+ /* We might wait indefinitely if periodic sampling is not enabled */
+ if (!stream->periodic)
+ return -EINVAL;
+
+ return wait_event_interruptible(stream->poll_wq,
+ xe_oa_buffer_check_unlocked(stream));
+}
+
+#define OASTATUS_RELEVANT_BITS (OASTATUS_MMIO_TRG_Q_FULL | OASTATUS_COUNTER_OVERFLOW | \
+ OASTATUS_BUFFER_OVERFLOW | OASTATUS_REPORT_LOST)
+
+static int __xe_oa_read(struct xe_oa_stream *stream, char __user *buf,
+ size_t count, size_t *offset)
+{
+ /* Only clear our bits to avoid side-effects */
+ stream->oa_status = xe_mmio_rmw32(stream->gt, __oa_regs(stream)->oa_status,
+ OASTATUS_RELEVANT_BITS, 0);
+ /*
+ * Signal to userspace that there is non-zero OA status to read via
+ * @DRM_XE_OBSERVATION_IOCTL_STATUS observation stream fd ioctl
+ */
+ if (stream->oa_status & OASTATUS_RELEVANT_BITS)
+ return -EIO;
+
+ return xe_oa_append_reports(stream, buf, count, offset);
+}
+
+static ssize_t xe_oa_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct xe_oa_stream *stream = file->private_data;
+ size_t offset = 0;
+ int ret;
+
+ /* Can't read from disabled streams */
+ if (!stream->enabled || !stream->sample)
+ return -EINVAL;
+
+ if (!(file->f_flags & O_NONBLOCK)) {
+ do {
+ ret = xe_oa_wait_unlocked(stream);
+ if (ret)
+ return ret;
+
+ mutex_lock(&stream->stream_lock);
+ ret = __xe_oa_read(stream, buf, count, &offset);
+ mutex_unlock(&stream->stream_lock);
+ } while (!offset && !ret);
+ } else {
+ mutex_lock(&stream->stream_lock);
+ ret = __xe_oa_read(stream, buf, count, &offset);
+ mutex_unlock(&stream->stream_lock);
+ }
+
+ /*
+ * Typically we clear pollin here in order to wait for the new hrtimer callback
+ * before unblocking. The exception to this is if __xe_oa_read returns -ENOSPC,
+ * which means that more OA data is available than could fit in the user provided
+ * buffer. In this case we want the next poll() call to not block.
+ *
+ * Also in case of -EIO, we have already waited for data before returning
+ * -EIO, so need to wait again
+ */
+ if (ret != -ENOSPC && ret != -EIO)
+ stream->pollin = false;
+
+ /* Possible values for ret are 0, -EFAULT, -ENOSPC, -EIO, -EINVAL, ... */
+ return offset ?: (ret ?: -EAGAIN);
+}
+
+static __poll_t xe_oa_poll_locked(struct xe_oa_stream *stream,
+ struct file *file, poll_table *wait)
+{
+ __poll_t events = 0;
+
+ poll_wait(file, &stream->poll_wq, wait);
+
+ /*
+ * We don't explicitly check whether there's something to read here since this
+ * path may be hot depending on what else userspace is polling, or on the timeout
+ * in use. We rely on hrtimer xe_oa_poll_check_timer_cb to notify us when there
+ * are samples to read
+ */
+ if (stream->pollin)
+ events |= EPOLLIN;
+
+ return events;
+}
+
+static __poll_t xe_oa_poll(struct file *file, poll_table *wait)
+{
+ struct xe_oa_stream *stream = file->private_data;
+ __poll_t ret;
+
+ mutex_lock(&stream->stream_lock);
+ ret = xe_oa_poll_locked(stream, file, wait);
+ mutex_unlock(&stream->stream_lock);
+
+ return ret;
+}
+
+static int xe_oa_submit_bb(struct xe_oa_stream *stream, struct xe_bb *bb)
+{
+ struct xe_sched_job *job;
+ struct dma_fence *fence;
+ long timeout;
+ int err = 0;
+
+ /* Kernel configuration is issued on stream->k_exec_q, not stream->exec_q */
+ job = xe_bb_create_job(stream->k_exec_q, bb);
+ if (IS_ERR(job)) {
+ err = PTR_ERR(job);
+ goto exit;
+ }
+
+ xe_sched_job_arm(job);
+ fence = dma_fence_get(&job->drm.s_fence->finished);
+ xe_sched_job_push(job);
+
+ timeout = dma_fence_wait_timeout(fence, false, HZ);
+ dma_fence_put(fence);
+ if (timeout < 0)
+ err = timeout;
+ else if (!timeout)
+ err = -ETIME;
+exit:
+ return err;
+}
+
+static void write_cs_mi_lri(struct xe_bb *bb, const struct xe_oa_reg *reg_data, u32 n_regs)
+{
+ u32 i;
+
+#define MI_LOAD_REGISTER_IMM_MAX_REGS (126)
+
+ for (i = 0; i < n_regs; i++) {
+ if ((i % MI_LOAD_REGISTER_IMM_MAX_REGS) == 0) {
+ u32 n_lri = min_t(u32, n_regs - i,
+ MI_LOAD_REGISTER_IMM_MAX_REGS);
+
+ bb->cs[bb->len++] = MI_LOAD_REGISTER_IMM | MI_LRI_NUM_REGS(n_lri);
+ }
+ bb->cs[bb->len++] = reg_data[i].addr.addr;
+ bb->cs[bb->len++] = reg_data[i].value;
+ }
+}
+
+static int num_lri_dwords(int num_regs)
+{
+ int count = 0;
+
+ if (num_regs > 0) {
+ count += DIV_ROUND_UP(num_regs, MI_LOAD_REGISTER_IMM_MAX_REGS);
+ count += num_regs * 2;
+ }
+
+ return count;
+}
+
+static void xe_oa_free_oa_buffer(struct xe_oa_stream *stream)
+{
+ xe_bo_unpin_map_no_vm(stream->oa_buffer.bo);
+}
+
+static void xe_oa_free_configs(struct xe_oa_stream *stream)
+{
+ struct xe_oa_config_bo *oa_bo, *tmp;
+
+ xe_oa_config_put(stream->oa_config);
+ llist_for_each_entry_safe(oa_bo, tmp, stream->oa_config_bos.first, node)
+ free_oa_config_bo(oa_bo);
+}
+
+static void xe_oa_store_flex(struct xe_oa_stream *stream, struct xe_lrc *lrc,
+ struct xe_bb *bb, const struct flex *flex, u32 count)
+{
+ u32 offset = xe_bo_ggtt_addr(lrc->bo);
+
+ do {
+ bb->cs[bb->len++] = MI_STORE_DATA_IMM | BIT(22) /* GGTT */ | 2;
+ bb->cs[bb->len++] = offset + flex->offset * sizeof(u32);
+ bb->cs[bb->len++] = 0;
+ bb->cs[bb->len++] = flex->value;
+
+ } while (flex++, --count);
+}
+
+static int xe_oa_modify_ctx_image(struct xe_oa_stream *stream, struct xe_lrc *lrc,
+ const struct flex *flex, u32 count)
+{
+ struct xe_bb *bb;
+ int err;
+
+ bb = xe_bb_new(stream->gt, 4 * count, false);
+ if (IS_ERR(bb)) {
+ err = PTR_ERR(bb);
+ goto exit;
+ }
+
+ xe_oa_store_flex(stream, lrc, bb, flex, count);
+
+ err = xe_oa_submit_bb(stream, bb);
+ xe_bb_free(bb, NULL);
+exit:
+ return err;
+}
+
+static int xe_oa_load_with_lri(struct xe_oa_stream *stream, struct xe_oa_reg *reg_lri)
+{
+ struct xe_bb *bb;
+ int err;
+
+ bb = xe_bb_new(stream->gt, 3, false);
+ if (IS_ERR(bb)) {
+ err = PTR_ERR(bb);
+ goto exit;
+ }
+
+ write_cs_mi_lri(bb, reg_lri, 1);
+
+ err = xe_oa_submit_bb(stream, bb);
+ xe_bb_free(bb, NULL);
+exit:
+ return err;
+}
+
+static int xe_oa_configure_oar_context(struct xe_oa_stream *stream, bool enable)
+{
+ const struct xe_oa_format *format = stream->oa_buffer.format;
+ struct xe_lrc *lrc = stream->exec_q->lrc[0];
+ u32 regs_offset = xe_lrc_regs_offset(lrc) / sizeof(u32);
+ u32 oacontrol = __format_to_oactrl(format, OAR_OACONTROL_COUNTER_SEL_MASK) |
+ (enable ? OAR_OACONTROL_COUNTER_ENABLE : 0);
+
+ struct flex regs_context[] = {
+ {
+ OACTXCONTROL(stream->hwe->mmio_base),
+ stream->oa->ctx_oactxctrl_offset[stream->hwe->class] + 1,
+ enable ? OA_COUNTER_RESUME : 0,
+ },
+ {
+ RING_CONTEXT_CONTROL(stream->hwe->mmio_base),
+ regs_offset + CTX_CONTEXT_CONTROL,
+ _MASKED_FIELD(CTX_CTRL_OAC_CONTEXT_ENABLE,
+ enable ? CTX_CTRL_OAC_CONTEXT_ENABLE : 0)
+ },
+ };
+ struct xe_oa_reg reg_lri = { OAR_OACONTROL, oacontrol };
+ int err;
+
+ /* Modify stream hwe context image with regs_context */
+ err = xe_oa_modify_ctx_image(stream, stream->exec_q->lrc[0],
+ regs_context, ARRAY_SIZE(regs_context));
+ if (err)
+ return err;
+
+ /* Apply reg_lri using LRI */
+ return xe_oa_load_with_lri(stream, &reg_lri);
+}
+
+static int xe_oa_configure_oac_context(struct xe_oa_stream *stream, bool enable)
+{
+ const struct xe_oa_format *format = stream->oa_buffer.format;
+ struct xe_lrc *lrc = stream->exec_q->lrc[0];
+ u32 regs_offset = xe_lrc_regs_offset(lrc) / sizeof(u32);
+ u32 oacontrol = __format_to_oactrl(format, OAR_OACONTROL_COUNTER_SEL_MASK) |
+ (enable ? OAR_OACONTROL_COUNTER_ENABLE : 0);
+ struct flex regs_context[] = {
+ {
+ OACTXCONTROL(stream->hwe->mmio_base),
+ stream->oa->ctx_oactxctrl_offset[stream->hwe->class] + 1,
+ enable ? OA_COUNTER_RESUME : 0,
+ },
+ {
+ RING_CONTEXT_CONTROL(stream->hwe->mmio_base),
+ regs_offset + CTX_CONTEXT_CONTROL,
+ _MASKED_FIELD(CTX_CTRL_OAC_CONTEXT_ENABLE,
+ enable ? CTX_CTRL_OAC_CONTEXT_ENABLE : 0) |
+ _MASKED_FIELD(CTX_CTRL_RUN_ALONE,
+ enable ? CTX_CTRL_RUN_ALONE : 0),
+ },
+ };
+ struct xe_oa_reg reg_lri = { OAC_OACONTROL, oacontrol };
+ int err;
+
+ /* Set ccs select to enable programming of OAC_OACONTROL */
+ xe_mmio_write32(stream->gt, __oa_regs(stream)->oa_ctrl, __oa_ccs_select(stream));
+
+ /* Modify stream hwe context image with regs_context */
+ err = xe_oa_modify_ctx_image(stream, stream->exec_q->lrc[0],
+ regs_context, ARRAY_SIZE(regs_context));
+ if (err)
+ return err;
+
+ /* Apply reg_lri using LRI */
+ return xe_oa_load_with_lri(stream, &reg_lri);
+}
+
+static int xe_oa_configure_oa_context(struct xe_oa_stream *stream, bool enable)
+{
+ switch (stream->hwe->class) {
+ case XE_ENGINE_CLASS_RENDER:
+ return xe_oa_configure_oar_context(stream, enable);
+ case XE_ENGINE_CLASS_COMPUTE:
+ return xe_oa_configure_oac_context(stream, enable);
+ default:
+ /* Video engines do not support MI_REPORT_PERF_COUNT */
+ return 0;
+ }
+}
+
+#define HAS_OA_BPC_REPORTING(xe) (GRAPHICS_VERx100(xe) >= 1255)
+
+static u32 oag_configure_mmio_trigger(const struct xe_oa_stream *stream, bool enable)
+{
+ return _MASKED_FIELD(OAG_OA_DEBUG_DISABLE_MMIO_TRG,
+ enable && stream && stream->sample ?
+ 0 : OAG_OA_DEBUG_DISABLE_MMIO_TRG);
+}
+
+static void xe_oa_disable_metric_set(struct xe_oa_stream *stream)
+{
+ u32 sqcnt1;
+
+ /*
+ * Wa_1508761755:xehpsdv, dg2
+ * Enable thread stall DOP gating and EU DOP gating.
+ */
+ if (stream->oa->xe->info.platform == XE_DG2) {
+ xe_gt_mcr_multicast_write(stream->gt, ROW_CHICKEN,
+ _MASKED_BIT_DISABLE(STALL_DOP_GATING_DISABLE));
+ xe_gt_mcr_multicast_write(stream->gt, ROW_CHICKEN2,
+ _MASKED_BIT_DISABLE(DISABLE_DOP_GATING));
+ }
+
+ xe_mmio_write32(stream->gt, __oa_regs(stream)->oa_debug,
+ oag_configure_mmio_trigger(stream, false));
+
+ /* disable the context save/restore or OAR counters */
+ if (stream->exec_q)
+ xe_oa_configure_oa_context(stream, false);
+
+ /* Make sure we disable noa to save power. */
+ xe_mmio_rmw32(stream->gt, RPM_CONFIG1, GT_NOA_ENABLE, 0);
+
+ sqcnt1 = SQCNT1_PMON_ENABLE |
+ (HAS_OA_BPC_REPORTING(stream->oa->xe) ? SQCNT1_OABPC : 0);
+
+ /* Reset PMON Enable to save power. */
+ xe_mmio_rmw32(stream->gt, XELPMP_SQCNT1, sqcnt1, 0);
+}
+
+static void xe_oa_stream_destroy(struct xe_oa_stream *stream)
+{
+ struct xe_oa_unit *u = stream->hwe->oa_unit;
+ struct xe_gt *gt = stream->hwe->gt;
+
+ if (WARN_ON(stream != u->exclusive_stream))
+ return;
+
+ WRITE_ONCE(u->exclusive_stream, NULL);
+
+ mutex_destroy(&stream->stream_lock);
+
+ xe_oa_disable_metric_set(stream);
+ xe_exec_queue_put(stream->k_exec_q);
+
+ xe_oa_free_oa_buffer(stream);
+
+ XE_WARN_ON(xe_force_wake_put(gt_to_fw(gt), XE_FORCEWAKE_ALL));
+ xe_pm_runtime_put(stream->oa->xe);
+
+ /* Wa_1509372804:pvc: Unset the override of GUCRC mode to enable rc6 */
+ if (stream->override_gucrc)
+ xe_gt_WARN_ON(gt, xe_guc_pc_unset_gucrc_mode(&gt->uc.guc.pc));
+
+ xe_oa_free_configs(stream);
+}
+
+static int xe_oa_alloc_oa_buffer(struct xe_oa_stream *stream)
+{
+ struct xe_bo *bo;
+
+ BUILD_BUG_ON_NOT_POWER_OF_2(XE_OA_BUFFER_SIZE);
+ BUILD_BUG_ON(XE_OA_BUFFER_SIZE < SZ_128K || XE_OA_BUFFER_SIZE > SZ_16M);
+
+ bo = xe_bo_create_pin_map(stream->oa->xe, stream->gt->tile, NULL,
+ XE_OA_BUFFER_SIZE, ttm_bo_type_kernel,
+ XE_BO_FLAG_SYSTEM | XE_BO_FLAG_GGTT);
+ if (IS_ERR(bo))
+ return PTR_ERR(bo);
+
+ stream->oa_buffer.bo = bo;
+ /* mmap implementation requires OA buffer to be in system memory */
+ xe_assert(stream->oa->xe, bo->vmap.is_iomem == 0);
+ stream->oa_buffer.vaddr = bo->vmap.vaddr;
+ return 0;
+}
+
+static struct xe_oa_config_bo *
+__xe_oa_alloc_config_buffer(struct xe_oa_stream *stream, struct xe_oa_config *oa_config)
+{
+ struct xe_oa_config_bo *oa_bo;
+ size_t config_length;
+ struct xe_bb *bb;
+
+ oa_bo = kzalloc(sizeof(*oa_bo), GFP_KERNEL);
+ if (!oa_bo)
+ return ERR_PTR(-ENOMEM);
+
+ config_length = num_lri_dwords(oa_config->regs_len);
+ config_length = ALIGN(sizeof(u32) * config_length, XE_PAGE_SIZE) / sizeof(u32);
+
+ bb = xe_bb_new(stream->gt, config_length, false);
+ if (IS_ERR(bb))
+ goto err_free;
+
+ write_cs_mi_lri(bb, oa_config->regs, oa_config->regs_len);
+
+ oa_bo->bb = bb;
+ oa_bo->oa_config = xe_oa_config_get(oa_config);
+ llist_add(&oa_bo->node, &stream->oa_config_bos);
+
+ return oa_bo;
+err_free:
+ kfree(oa_bo);
+ return ERR_CAST(bb);
+}
+
+static struct xe_oa_config_bo *
+xe_oa_alloc_config_buffer(struct xe_oa_stream *stream, struct xe_oa_config *oa_config)
+{
+ struct xe_oa_config_bo *oa_bo;
+
+ /* Look for the buffer in the already allocated BOs attached to the stream */
+ llist_for_each_entry(oa_bo, stream->oa_config_bos.first, node) {
+ if (oa_bo->oa_config == oa_config &&
+ memcmp(oa_bo->oa_config->uuid, oa_config->uuid,
+ sizeof(oa_config->uuid)) == 0)
+ goto out;
+ }
+
+ oa_bo = __xe_oa_alloc_config_buffer(stream, oa_config);
+out:
+ return oa_bo;
+}
+
+static int xe_oa_emit_oa_config(struct xe_oa_stream *stream, struct xe_oa_config *config)
+{
+#define NOA_PROGRAM_ADDITIONAL_DELAY_US 500
+ struct xe_oa_config_bo *oa_bo;
+ int err, us = NOA_PROGRAM_ADDITIONAL_DELAY_US;
+
+ oa_bo = xe_oa_alloc_config_buffer(stream, config);
+ if (IS_ERR(oa_bo)) {
+ err = PTR_ERR(oa_bo);
+ goto exit;
+ }
+
+ err = xe_oa_submit_bb(stream, oa_bo->bb);
+
+ /* Additional empirical delay needed for NOA programming after registers are written */
+ usleep_range(us, 2 * us);
+exit:
+ return err;
+}
+
+static u32 oag_report_ctx_switches(const struct xe_oa_stream *stream)
+{
+ /* If user didn't require OA reports, ask HW not to emit ctx switch reports */
+ return _MASKED_FIELD(OAG_OA_DEBUG_DISABLE_CTX_SWITCH_REPORTS,
+ stream->sample ?
+ 0 : OAG_OA_DEBUG_DISABLE_CTX_SWITCH_REPORTS);
+}
+
+static int xe_oa_enable_metric_set(struct xe_oa_stream *stream)
+{
+ u32 oa_debug, sqcnt1;
+ int ret;
+
+ /*
+ * Wa_1508761755:xehpsdv, dg2
+ * EU NOA signals behave incorrectly if EU clock gating is enabled.
+ * Disable thread stall DOP gating and EU DOP gating.
+ */
+ if (stream->oa->xe->info.platform == XE_DG2) {
+ xe_gt_mcr_multicast_write(stream->gt, ROW_CHICKEN,
+ _MASKED_BIT_ENABLE(STALL_DOP_GATING_DISABLE));
+ xe_gt_mcr_multicast_write(stream->gt, ROW_CHICKEN2,
+ _MASKED_BIT_ENABLE(DISABLE_DOP_GATING));
+ }
+
+ /* Disable clk ratio reports */
+ oa_debug = OAG_OA_DEBUG_DISABLE_CLK_RATIO_REPORTS |
+ OAG_OA_DEBUG_INCLUDE_CLK_RATIO;
+
+ if (GRAPHICS_VER(stream->oa->xe) >= 20)
+ oa_debug |=
+ /* The three bits below are needed to get PEC counters running */
+ OAG_OA_DEBUG_START_TRIGGER_SCOPE_CONTROL |
+ OAG_OA_DEBUG_DISABLE_START_TRG_2_COUNT_QUAL |
+ OAG_OA_DEBUG_DISABLE_START_TRG_1_COUNT_QUAL;
+
+ xe_mmio_write32(stream->gt, __oa_regs(stream)->oa_debug,
+ _MASKED_BIT_ENABLE(oa_debug) |
+ oag_report_ctx_switches(stream) |
+ oag_configure_mmio_trigger(stream, true));
+
+ xe_mmio_write32(stream->gt, __oa_regs(stream)->oa_ctx_ctrl, stream->periodic ?
+ (OAG_OAGLBCTXCTRL_COUNTER_RESUME |
+ OAG_OAGLBCTXCTRL_TIMER_ENABLE |
+ REG_FIELD_PREP(OAG_OAGLBCTXCTRL_TIMER_PERIOD_MASK,
+ stream->period_exponent)) : 0);
+
+ /*
+ * Initialize Super Queue Internal Cnt Register
+ * Set PMON Enable in order to collect valid metrics
+ * Enable bytes per clock reporting
+ */
+ sqcnt1 = SQCNT1_PMON_ENABLE |
+ (HAS_OA_BPC_REPORTING(stream->oa->xe) ? SQCNT1_OABPC : 0);
+
+ xe_mmio_rmw32(stream->gt, XELPMP_SQCNT1, 0, sqcnt1);
+
+ /* Configure OAR/OAC */
+ if (stream->exec_q) {
+ ret = xe_oa_configure_oa_context(stream, true);
+ if (ret)
+ return ret;
+ }
+
+ return xe_oa_emit_oa_config(stream, stream->oa_config);
+}
+
+static void xe_oa_stream_enable(struct xe_oa_stream *stream)
+{
+ stream->pollin = false;
+
+ xe_oa_enable(stream);
+
+ if (stream->sample)
+ hrtimer_start(&stream->poll_check_timer,
+ ns_to_ktime(stream->poll_period_ns),
+ HRTIMER_MODE_REL_PINNED);
+}
+
+static void xe_oa_stream_disable(struct xe_oa_stream *stream)
+{
+ xe_oa_disable(stream);
+
+ if (stream->sample)
+ hrtimer_cancel(&stream->poll_check_timer);
+}
+
+static int xe_oa_enable_preempt_timeslice(struct xe_oa_stream *stream)
+{
+ struct xe_exec_queue *q = stream->exec_q;
+ int ret1, ret2;
+
+ /* Best effort recovery: try to revert both to original, irrespective of error */
+ ret1 = q->ops->set_timeslice(q, stream->hwe->eclass->sched_props.timeslice_us);
+ ret2 = q->ops->set_preempt_timeout(q, stream->hwe->eclass->sched_props.preempt_timeout_us);
+ if (ret1 || ret2)
+ goto err;
+ return 0;
+err:
+ drm_dbg(&stream->oa->xe->drm, "%s failed ret1 %d ret2 %d\n", __func__, ret1, ret2);
+ return ret1 ?: ret2;
+}
+
+static int xe_oa_disable_preempt_timeslice(struct xe_oa_stream *stream)
+{
+ struct xe_exec_queue *q = stream->exec_q;
+ int ret;
+
+ /* Setting values to 0 will disable timeslice and preempt_timeout */
+ ret = q->ops->set_timeslice(q, 0);
+ if (ret)
+ goto err;
+
+ ret = q->ops->set_preempt_timeout(q, 0);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ xe_oa_enable_preempt_timeslice(stream);
+ drm_dbg(&stream->oa->xe->drm, "%s failed %d\n", __func__, ret);
+ return ret;
+}
+
+static int xe_oa_enable_locked(struct xe_oa_stream *stream)
+{
+ if (stream->enabled)
+ return 0;
+
+ if (stream->no_preempt) {
+ int ret = xe_oa_disable_preempt_timeslice(stream);
+
+ if (ret)
+ return ret;
+ }
+
+ xe_oa_stream_enable(stream);
+
+ stream->enabled = true;
+ return 0;
+}
+
+static int xe_oa_disable_locked(struct xe_oa_stream *stream)
+{
+ int ret = 0;
+
+ if (!stream->enabled)
+ return 0;
+
+ xe_oa_stream_disable(stream);
+
+ if (stream->no_preempt)
+ ret = xe_oa_enable_preempt_timeslice(stream);
+
+ stream->enabled = false;
+ return ret;
+}
+
+static long xe_oa_config_locked(struct xe_oa_stream *stream, u64 arg)
+{
+ struct drm_xe_ext_set_property ext;
+ long ret = stream->oa_config->id;
+ struct xe_oa_config *config;
+ int err;
+
+ err = __copy_from_user(&ext, u64_to_user_ptr(arg), sizeof(ext));
+ if (XE_IOCTL_DBG(stream->oa->xe, err))
+ return -EFAULT;
+
+ if (XE_IOCTL_DBG(stream->oa->xe, ext.pad) ||
+ XE_IOCTL_DBG(stream->oa->xe, ext.base.name != DRM_XE_OA_EXTENSION_SET_PROPERTY) ||
+ XE_IOCTL_DBG(stream->oa->xe, ext.base.next_extension) ||
+ XE_IOCTL_DBG(stream->oa->xe, ext.property != DRM_XE_OA_PROPERTY_OA_METRIC_SET))
+ return -EINVAL;
+
+ config = xe_oa_get_oa_config(stream->oa, ext.value);
+ if (!config)
+ return -ENODEV;
+
+ if (config != stream->oa_config) {
+ err = xe_oa_emit_oa_config(stream, config);
+ if (!err)
+ config = xchg(&stream->oa_config, config);
+ else
+ ret = err;
+ }
+
+ xe_oa_config_put(config);
+
+ return ret;
+}
+
+static long xe_oa_status_locked(struct xe_oa_stream *stream, unsigned long arg)
+{
+ struct drm_xe_oa_stream_status status = {};
+ void __user *uaddr = (void __user *)arg;
+
+ /* Map from register to uapi bits */
+ if (stream->oa_status & OASTATUS_REPORT_LOST)
+ status.oa_status |= DRM_XE_OASTATUS_REPORT_LOST;
+ if (stream->oa_status & OASTATUS_BUFFER_OVERFLOW)
+ status.oa_status |= DRM_XE_OASTATUS_BUFFER_OVERFLOW;
+ if (stream->oa_status & OASTATUS_COUNTER_OVERFLOW)
+ status.oa_status |= DRM_XE_OASTATUS_COUNTER_OVERFLOW;
+ if (stream->oa_status & OASTATUS_MMIO_TRG_Q_FULL)
+ status.oa_status |= DRM_XE_OASTATUS_MMIO_TRG_Q_FULL;
+
+ if (copy_to_user(uaddr, &status, sizeof(status)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static long xe_oa_info_locked(struct xe_oa_stream *stream, unsigned long arg)
+{
+ struct drm_xe_oa_stream_info info = { .oa_buf_size = XE_OA_BUFFER_SIZE, };
+ void __user *uaddr = (void __user *)arg;
+
+ if (copy_to_user(uaddr, &info, sizeof(info)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static long xe_oa_ioctl_locked(struct xe_oa_stream *stream,
+ unsigned int cmd,
+ unsigned long arg)
+{
+ switch (cmd) {
+ case DRM_XE_OBSERVATION_IOCTL_ENABLE:
+ return xe_oa_enable_locked(stream);
+ case DRM_XE_OBSERVATION_IOCTL_DISABLE:
+ return xe_oa_disable_locked(stream);
+ case DRM_XE_OBSERVATION_IOCTL_CONFIG:
+ return xe_oa_config_locked(stream, arg);
+ case DRM_XE_OBSERVATION_IOCTL_STATUS:
+ return xe_oa_status_locked(stream, arg);
+ case DRM_XE_OBSERVATION_IOCTL_INFO:
+ return xe_oa_info_locked(stream, arg);
+ }
+
+ return -EINVAL;
+}
+
+static long xe_oa_ioctl(struct file *file,
+ unsigned int cmd,
+ unsigned long arg)
+{
+ struct xe_oa_stream *stream = file->private_data;
+ long ret;
+
+ mutex_lock(&stream->stream_lock);
+ ret = xe_oa_ioctl_locked(stream, cmd, arg);
+ mutex_unlock(&stream->stream_lock);
+
+ return ret;
+}
+
+static void xe_oa_destroy_locked(struct xe_oa_stream *stream)
+{
+ if (stream->enabled)
+ xe_oa_disable_locked(stream);
+
+ xe_oa_stream_destroy(stream);
+
+ if (stream->exec_q)
+ xe_exec_queue_put(stream->exec_q);
+
+ kfree(stream);
+}
+
+static int xe_oa_release(struct inode *inode, struct file *file)
+{
+ struct xe_oa_stream *stream = file->private_data;
+ struct xe_gt *gt = stream->gt;
+
+ mutex_lock(&gt->oa.gt_lock);
+ xe_oa_destroy_locked(stream);
+ mutex_unlock(&gt->oa.gt_lock);
+
+ /* Release the reference the OA stream kept on the driver */
+ drm_dev_put(&gt_to_xe(gt)->drm);
+
+ return 0;
+}
+
+static int xe_oa_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct xe_oa_stream *stream = file->private_data;
+ struct xe_bo *bo = stream->oa_buffer.bo;
+ unsigned long start = vma->vm_start;
+ int i, ret;
+
+ if (xe_observation_paranoid && !perfmon_capable()) {
+ drm_dbg(&stream->oa->xe->drm, "Insufficient privilege to map OA buffer\n");
+ return -EACCES;
+ }
+
+ /* Can mmap the entire OA buffer or nothing (no partial OA buffer mmaps) */
+ if (vma->vm_end - vma->vm_start != XE_OA_BUFFER_SIZE) {
+ drm_dbg(&stream->oa->xe->drm, "Wrong mmap size, must be OA buffer size\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Only support VM_READ, enforce MAP_PRIVATE by checking for
+ * VM_MAYSHARE, don't copy the vma on fork
+ */
+ if (vma->vm_flags & (VM_WRITE | VM_EXEC | VM_SHARED | VM_MAYSHARE)) {
+ drm_dbg(&stream->oa->xe->drm, "mmap must be read only\n");
+ return -EINVAL;
+ }
+ vm_flags_mod(vma, VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP | VM_DONTCOPY,
+ VM_MAYWRITE | VM_MAYEXEC);
+
+ xe_assert(stream->oa->xe, bo->ttm.ttm->num_pages ==
+ (vma->vm_end - vma->vm_start) >> PAGE_SHIFT);
+ for (i = 0; i < bo->ttm.ttm->num_pages; i++) {
+ ret = remap_pfn_range(vma, start, page_to_pfn(bo->ttm.ttm->pages[i]),
+ PAGE_SIZE, vma->vm_page_prot);
+ if (ret)
+ break;
+
+ start += PAGE_SIZE;
+ }
+
+ return ret;
+}
+
+static const struct file_operations xe_oa_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .release = xe_oa_release,
+ .poll = xe_oa_poll,
+ .read = xe_oa_read,
+ .unlocked_ioctl = xe_oa_ioctl,
+ .mmap = xe_oa_mmap,
+};
+
+static bool engine_supports_mi_query(struct xe_hw_engine *hwe)
+{
+ return hwe->class == XE_ENGINE_CLASS_RENDER ||
+ hwe->class == XE_ENGINE_CLASS_COMPUTE;
+}
+
+static bool xe_oa_find_reg_in_lri(u32 *state, u32 reg, u32 *offset, u32 end)
+{
+ u32 idx = *offset;
+ u32 len = min(MI_LRI_LEN(state[idx]) + idx, end);
+ bool found = false;
+
+ idx++;
+ for (; idx < len; idx += 2) {
+ if (state[idx] == reg) {
+ found = true;
+ break;
+ }
+ }
+
+ *offset = idx;
+ return found;
+}
+
+#define IS_MI_LRI_CMD(x) (REG_FIELD_GET(MI_OPCODE, (x)) == \
+ REG_FIELD_GET(MI_OPCODE, MI_LOAD_REGISTER_IMM))
+
+static u32 xe_oa_context_image_offset(struct xe_oa_stream *stream, u32 reg)
+{
+ struct xe_lrc *lrc = stream->exec_q->lrc[0];
+ u32 len = (xe_gt_lrc_size(stream->gt, stream->hwe->class) +
+ lrc->ring.size) / sizeof(u32);
+ u32 offset = xe_lrc_regs_offset(lrc) / sizeof(u32);
+ u32 *state = (u32 *)lrc->bo->vmap.vaddr;
+
+ if (drm_WARN_ON(&stream->oa->xe->drm, !state))
+ return U32_MAX;
+
+ for (; offset < len; ) {
+ if (IS_MI_LRI_CMD(state[offset])) {
+ /*
+ * We expect reg-value pairs in MI_LRI command, so
+ * MI_LRI_LEN() should be even
+ */
+ drm_WARN_ON(&stream->oa->xe->drm,
+ MI_LRI_LEN(state[offset]) & 0x1);
+
+ if (xe_oa_find_reg_in_lri(state, reg, &offset, len))
+ break;
+ } else {
+ offset++;
+ }
+ }
+
+ return offset < len ? offset : U32_MAX;
+}
+
+static int xe_oa_set_ctx_ctrl_offset(struct xe_oa_stream *stream)
+{
+ struct xe_reg reg = OACTXCONTROL(stream->hwe->mmio_base);
+ u32 offset = stream->oa->ctx_oactxctrl_offset[stream->hwe->class];
+
+ /* Do this only once. Failure is stored as offset of U32_MAX */
+ if (offset)
+ goto exit;
+
+ offset = xe_oa_context_image_offset(stream, reg.addr);
+ stream->oa->ctx_oactxctrl_offset[stream->hwe->class] = offset;
+
+ drm_dbg(&stream->oa->xe->drm, "%s oa ctx control at 0x%08x dword offset\n",
+ stream->hwe->name, offset);
+exit:
+ return offset && offset != U32_MAX ? 0 : -ENODEV;
+}
+
+static int xe_oa_stream_init(struct xe_oa_stream *stream,
+ struct xe_oa_open_param *param)
+{
+ struct xe_oa_unit *u = param->hwe->oa_unit;
+ struct xe_gt *gt = param->hwe->gt;
+ int ret;
+
+ stream->exec_q = param->exec_q;
+ stream->poll_period_ns = DEFAULT_POLL_PERIOD_NS;
+ stream->hwe = param->hwe;
+ stream->gt = stream->hwe->gt;
+ stream->oa_buffer.format = &stream->oa->oa_formats[param->oa_format];
+
+ stream->sample = param->sample;
+ stream->periodic = param->period_exponent > 0;
+ stream->period_exponent = param->period_exponent;
+ stream->no_preempt = param->no_preempt;
+
+ /*
+ * For Xe2+, when overrun mode is enabled, there are no partial reports at the end
+ * of buffer, making the OA buffer effectively a non-power-of-2 size circular
+ * buffer whose size, circ_size, is a multiple of the report size
+ */
+ if (GRAPHICS_VER(stream->oa->xe) >= 20 &&
+ stream->hwe->oa_unit->type == DRM_XE_OA_UNIT_TYPE_OAG && stream->sample)
+ stream->oa_buffer.circ_size =
+ XE_OA_BUFFER_SIZE - XE_OA_BUFFER_SIZE % stream->oa_buffer.format->size;
+ else
+ stream->oa_buffer.circ_size = XE_OA_BUFFER_SIZE;
+
+ if (stream->exec_q && engine_supports_mi_query(stream->hwe)) {
+ /* If we don't find the context offset, just return error */
+ ret = xe_oa_set_ctx_ctrl_offset(stream);
+ if (ret) {
+ drm_err(&stream->oa->xe->drm,
+ "xe_oa_set_ctx_ctrl_offset failed for %s\n",
+ stream->hwe->name);
+ goto exit;
+ }
+ }
+
+ stream->oa_config = xe_oa_get_oa_config(stream->oa, param->metric_set);
+ if (!stream->oa_config) {
+ drm_dbg(&stream->oa->xe->drm, "Invalid OA config id=%i\n", param->metric_set);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ /*
+ * Wa_1509372804:pvc
+ *
+ * GuC reset of engines causes OA to lose configuration
+ * state. Prevent this by overriding GUCRC mode.
+ */
+ if (stream->oa->xe->info.platform == XE_PVC) {
+ ret = xe_guc_pc_override_gucrc_mode(&gt->uc.guc.pc,
+ SLPC_GUCRC_MODE_GUCRC_NO_RC6);
+ if (ret)
+ goto err_free_configs;
+
+ stream->override_gucrc = true;
+ }
+
+ /* Take runtime pm ref and forcewake to disable RC6 */
+ xe_pm_runtime_get(stream->oa->xe);
+ XE_WARN_ON(xe_force_wake_get(gt_to_fw(gt), XE_FORCEWAKE_ALL));
+
+ ret = xe_oa_alloc_oa_buffer(stream);
+ if (ret)
+ goto err_fw_put;
+
+ stream->k_exec_q = xe_exec_queue_create(stream->oa->xe, NULL,
+ BIT(stream->hwe->logical_instance), 1,
+ stream->hwe, EXEC_QUEUE_FLAG_KERNEL, 0);
+ if (IS_ERR(stream->k_exec_q)) {
+ ret = PTR_ERR(stream->k_exec_q);
+ drm_err(&stream->oa->xe->drm, "gt%d, hwe %s, xe_exec_queue_create failed=%d",
+ stream->gt->info.id, stream->hwe->name, ret);
+ goto err_free_oa_buf;
+ }
+
+ ret = xe_oa_enable_metric_set(stream);
+ if (ret) {
+ drm_dbg(&stream->oa->xe->drm, "Unable to enable metric set\n");
+ goto err_put_k_exec_q;
+ }
+
+ drm_dbg(&stream->oa->xe->drm, "opening stream oa config uuid=%s\n",
+ stream->oa_config->uuid);
+
+ WRITE_ONCE(u->exclusive_stream, stream);
+
+ hrtimer_init(&stream->poll_check_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ stream->poll_check_timer.function = xe_oa_poll_check_timer_cb;
+ init_waitqueue_head(&stream->poll_wq);
+
+ spin_lock_init(&stream->oa_buffer.ptr_lock);
+ mutex_init(&stream->stream_lock);
+
+ return 0;
+
+err_put_k_exec_q:
+ xe_oa_disable_metric_set(stream);
+ xe_exec_queue_put(stream->k_exec_q);
+err_free_oa_buf:
+ xe_oa_free_oa_buffer(stream);
+err_fw_put:
+ XE_WARN_ON(xe_force_wake_put(gt_to_fw(gt), XE_FORCEWAKE_ALL));
+ xe_pm_runtime_put(stream->oa->xe);
+ if (stream->override_gucrc)
+ xe_gt_WARN_ON(gt, xe_guc_pc_unset_gucrc_mode(&gt->uc.guc.pc));
+err_free_configs:
+ xe_oa_free_configs(stream);
+exit:
+ return ret;
+}
+
+static int xe_oa_stream_open_ioctl_locked(struct xe_oa *oa,
+ struct xe_oa_open_param *param)
+{
+ struct xe_oa_stream *stream;
+ int stream_fd;
+ int ret;
+
+ /* We currently only allow exclusive access */
+ if (param->hwe->oa_unit->exclusive_stream) {
+ drm_dbg(&oa->xe->drm, "OA unit already in use\n");
+ ret = -EBUSY;
+ goto exit;
+ }
+
+ stream = kzalloc(sizeof(*stream), GFP_KERNEL);
+ if (!stream) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+ stream->oa = oa;
+ ret = xe_oa_stream_init(stream, param);
+ if (ret)
+ goto err_free;
+
+ if (!param->disabled) {
+ ret = xe_oa_enable_locked(stream);
+ if (ret)
+ goto err_destroy;
+ }
+
+ stream_fd = anon_inode_getfd("[xe_oa]", &xe_oa_fops, stream, 0);
+ if (stream_fd < 0) {
+ ret = stream_fd;
+ goto err_disable;
+ }
+
+ /* Hold a reference on the drm device till stream_fd is released */
+ drm_dev_get(&stream->oa->xe->drm);
+
+ return stream_fd;
+err_disable:
+ if (!param->disabled)
+ xe_oa_disable_locked(stream);
+err_destroy:
+ xe_oa_stream_destroy(stream);
+err_free:
+ kfree(stream);
+exit:
+ return ret;
+}
+
+/**
+ * xe_oa_timestamp_frequency - Return OA timestamp frequency
+ * @gt: @xe_gt
+ *
+ * OA timestamp frequency = CS timestamp frequency in most platforms. On some
+ * platforms OA unit ignores the CTC_SHIFT and the 2 timestamps differ. In such
+ * cases, return the adjusted CS timestamp frequency to the user.
+ */
+u32 xe_oa_timestamp_frequency(struct xe_gt *gt)
+{
+ u32 reg, shift;
+
+ /*
+ * Wa_18013179988:dg2
+ * Wa_14015568240:pvc
+ * Wa_14015846243:mtl
+ */
+ switch (gt_to_xe(gt)->info.platform) {
+ case XE_DG2:
+ case XE_PVC:
+ case XE_METEORLAKE:
+ xe_pm_runtime_get(gt_to_xe(gt));
+ reg = xe_mmio_read32(gt, RPM_CONFIG0);
+ xe_pm_runtime_put(gt_to_xe(gt));
+
+ shift = REG_FIELD_GET(RPM_CONFIG0_CTC_SHIFT_PARAMETER_MASK, reg);
+ return gt->info.reference_clock << (3 - shift);
+
+ default:
+ return gt->info.reference_clock;
+ }
+}
+
+static u64 oa_exponent_to_ns(struct xe_gt *gt, int exponent)
+{
+ u64 nom = (2ULL << exponent) * NSEC_PER_SEC;
+ u32 den = xe_oa_timestamp_frequency(gt);
+
+ return div_u64(nom + den - 1, den);
+}
+
+static bool engine_supports_oa_format(const struct xe_hw_engine *hwe, int type)
+{
+ switch (hwe->oa_unit->type) {
+ case DRM_XE_OA_UNIT_TYPE_OAG:
+ return type == DRM_XE_OA_FMT_TYPE_OAG || type == DRM_XE_OA_FMT_TYPE_OAR ||
+ type == DRM_XE_OA_FMT_TYPE_OAC || type == DRM_XE_OA_FMT_TYPE_PEC;
+ case DRM_XE_OA_UNIT_TYPE_OAM:
+ return type == DRM_XE_OA_FMT_TYPE_OAM || type == DRM_XE_OA_FMT_TYPE_OAM_MPEC;
+ default:
+ return false;
+ }
+}
+
+static int decode_oa_format(struct xe_oa *oa, u64 fmt, enum xe_oa_format_name *name)
+{
+ u32 counter_size = FIELD_GET(DRM_XE_OA_FORMAT_MASK_COUNTER_SIZE, fmt);
+ u32 counter_sel = FIELD_GET(DRM_XE_OA_FORMAT_MASK_COUNTER_SEL, fmt);
+ u32 bc_report = FIELD_GET(DRM_XE_OA_FORMAT_MASK_BC_REPORT, fmt);
+ u32 type = FIELD_GET(DRM_XE_OA_FORMAT_MASK_FMT_TYPE, fmt);
+ int idx;
+
+ for_each_set_bit(idx, oa->format_mask, __XE_OA_FORMAT_MAX) {
+ const struct xe_oa_format *f = &oa->oa_formats[idx];
+
+ if (counter_size == f->counter_size && bc_report == f->bc_report &&
+ type == f->type && counter_sel == f->counter_select) {
+ *name = idx;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+/**
+ * xe_oa_unit_id - Return OA unit ID for a hardware engine
+ * @hwe: @xe_hw_engine
+ *
+ * Return OA unit ID for a hardware engine when available
+ */
+u16 xe_oa_unit_id(struct xe_hw_engine *hwe)
+{
+ return hwe->oa_unit && hwe->oa_unit->num_engines ?
+ hwe->oa_unit->oa_unit_id : U16_MAX;
+}
+
+static int xe_oa_assign_hwe(struct xe_oa *oa, struct xe_oa_open_param *param)
+{
+ struct xe_gt *gt;
+ int i, ret = 0;
+
+ if (param->exec_q) {
+ /* When we have an exec_q, get hwe from the exec_q */
+ param->hwe = xe_gt_hw_engine(param->exec_q->gt, param->exec_q->class,
+ param->engine_instance, true);
+ } else {
+ struct xe_hw_engine *hwe;
+ enum xe_hw_engine_id id;
+
+ /* Else just get the first hwe attached to the oa unit */
+ for_each_gt(gt, oa->xe, i) {
+ for_each_hw_engine(hwe, gt, id) {
+ if (xe_oa_unit_id(hwe) == param->oa_unit_id) {
+ param->hwe = hwe;
+ goto out;
+ }
+ }
+ }
+ }
+out:
+ if (!param->hwe || xe_oa_unit_id(param->hwe) != param->oa_unit_id) {
+ drm_dbg(&oa->xe->drm, "Unable to find hwe (%d, %d) for OA unit ID %d\n",
+ param->exec_q ? param->exec_q->class : -1,
+ param->engine_instance, param->oa_unit_id);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int xe_oa_set_prop_oa_unit_id(struct xe_oa *oa, u64 value,
+ struct xe_oa_open_param *param)
+{
+ if (value >= oa->oa_unit_ids) {
+ drm_dbg(&oa->xe->drm, "OA unit ID out of range %lld\n", value);
+ return -EINVAL;
+ }
+ param->oa_unit_id = value;
+ return 0;
+}
+
+static int xe_oa_set_prop_sample_oa(struct xe_oa *oa, u64 value,
+ struct xe_oa_open_param *param)
+{
+ param->sample = value;
+ return 0;
+}
+
+static int xe_oa_set_prop_metric_set(struct xe_oa *oa, u64 value,
+ struct xe_oa_open_param *param)
+{
+ param->metric_set = value;
+ return 0;
+}
+
+static int xe_oa_set_prop_oa_format(struct xe_oa *oa, u64 value,
+ struct xe_oa_open_param *param)
+{
+ int ret = decode_oa_format(oa, value, &param->oa_format);
+
+ if (ret) {
+ drm_dbg(&oa->xe->drm, "Unsupported OA report format %#llx\n", value);
+ return ret;
+ }
+ return 0;
+}
+
+static int xe_oa_set_prop_oa_exponent(struct xe_oa *oa, u64 value,
+ struct xe_oa_open_param *param)
+{
+#define OA_EXPONENT_MAX 31
+
+ if (value > OA_EXPONENT_MAX) {
+ drm_dbg(&oa->xe->drm, "OA timer exponent too high (> %u)\n", OA_EXPONENT_MAX);
+ return -EINVAL;
+ }
+ param->period_exponent = value;
+ return 0;
+}
+
+static int xe_oa_set_prop_disabled(struct xe_oa *oa, u64 value,
+ struct xe_oa_open_param *param)
+{
+ param->disabled = value;
+ return 0;
+}
+
+static int xe_oa_set_prop_exec_queue_id(struct xe_oa *oa, u64 value,
+ struct xe_oa_open_param *param)
+{
+ param->exec_queue_id = value;
+ return 0;
+}
+
+static int xe_oa_set_prop_engine_instance(struct xe_oa *oa, u64 value,
+ struct xe_oa_open_param *param)
+{
+ param->engine_instance = value;
+ return 0;
+}
+
+static int xe_oa_set_no_preempt(struct xe_oa *oa, u64 value,
+ struct xe_oa_open_param *param)
+{
+ param->no_preempt = value;
+ return 0;
+}
+
+typedef int (*xe_oa_set_property_fn)(struct xe_oa *oa, u64 value,
+ struct xe_oa_open_param *param);
+static const xe_oa_set_property_fn xe_oa_set_property_funcs[] = {
+ [DRM_XE_OA_PROPERTY_OA_UNIT_ID] = xe_oa_set_prop_oa_unit_id,
+ [DRM_XE_OA_PROPERTY_SAMPLE_OA] = xe_oa_set_prop_sample_oa,
+ [DRM_XE_OA_PROPERTY_OA_METRIC_SET] = xe_oa_set_prop_metric_set,
+ [DRM_XE_OA_PROPERTY_OA_FORMAT] = xe_oa_set_prop_oa_format,
+ [DRM_XE_OA_PROPERTY_OA_PERIOD_EXPONENT] = xe_oa_set_prop_oa_exponent,
+ [DRM_XE_OA_PROPERTY_OA_DISABLED] = xe_oa_set_prop_disabled,
+ [DRM_XE_OA_PROPERTY_EXEC_QUEUE_ID] = xe_oa_set_prop_exec_queue_id,
+ [DRM_XE_OA_PROPERTY_OA_ENGINE_INSTANCE] = xe_oa_set_prop_engine_instance,
+ [DRM_XE_OA_PROPERTY_NO_PREEMPT] = xe_oa_set_no_preempt,
+};
+
+static int xe_oa_user_ext_set_property(struct xe_oa *oa, u64 extension,
+ struct xe_oa_open_param *param)
+{
+ u64 __user *address = u64_to_user_ptr(extension);
+ struct drm_xe_ext_set_property ext;
+ int err;
+ u32 idx;
+
+ err = __copy_from_user(&ext, address, sizeof(ext));
+ if (XE_IOCTL_DBG(oa->xe, err))
+ return -EFAULT;
+
+ if (XE_IOCTL_DBG(oa->xe, ext.property >= ARRAY_SIZE(xe_oa_set_property_funcs)) ||
+ XE_IOCTL_DBG(oa->xe, ext.pad))
+ return -EINVAL;
+
+ idx = array_index_nospec(ext.property, ARRAY_SIZE(xe_oa_set_property_funcs));
+ return xe_oa_set_property_funcs[idx](oa, ext.value, param);
+}
+
+typedef int (*xe_oa_user_extension_fn)(struct xe_oa *oa, u64 extension,
+ struct xe_oa_open_param *param);
+static const xe_oa_user_extension_fn xe_oa_user_extension_funcs[] = {
+ [DRM_XE_OA_EXTENSION_SET_PROPERTY] = xe_oa_user_ext_set_property,
+};
+
+#define MAX_USER_EXTENSIONS 16
+static int xe_oa_user_extensions(struct xe_oa *oa, u64 extension, int ext_number,
+ struct xe_oa_open_param *param)
+{
+ u64 __user *address = u64_to_user_ptr(extension);
+ struct drm_xe_user_extension ext;
+ int err;
+ u32 idx;
+
+ if (XE_IOCTL_DBG(oa->xe, ext_number >= MAX_USER_EXTENSIONS))
+ return -E2BIG;
+
+ err = __copy_from_user(&ext, address, sizeof(ext));
+ if (XE_IOCTL_DBG(oa->xe, err))
+ return -EFAULT;
+
+ if (XE_IOCTL_DBG(oa->xe, ext.pad) ||
+ XE_IOCTL_DBG(oa->xe, ext.name >= ARRAY_SIZE(xe_oa_user_extension_funcs)))
+ return -EINVAL;
+
+ idx = array_index_nospec(ext.name, ARRAY_SIZE(xe_oa_user_extension_funcs));
+ err = xe_oa_user_extension_funcs[idx](oa, extension, param);
+ if (XE_IOCTL_DBG(oa->xe, err))
+ return err;
+
+ if (ext.next_extension)
+ return xe_oa_user_extensions(oa, ext.next_extension, ++ext_number, param);
+
+ return 0;
+}
+
+/**
+ * xe_oa_stream_open_ioctl - Opens an OA stream
+ * @dev: @drm_device
+ * @data: pointer to struct @drm_xe_oa_config
+ * @file: @drm_file
+ *
+ * The functions opens an OA stream. An OA stream, opened with specified
+ * properties, enables OA counter samples to be collected, either
+ * periodically (time based sampling), or on request (using OA queries)
+ */
+int xe_oa_stream_open_ioctl(struct drm_device *dev, u64 data, struct drm_file *file)
+{
+ struct xe_device *xe = to_xe_device(dev);
+ struct xe_oa *oa = &xe->oa;
+ struct xe_file *xef = to_xe_file(file);
+ struct xe_oa_open_param param = {};
+ const struct xe_oa_format *f;
+ bool privileged_op = true;
+ int ret;
+
+ if (!oa->xe) {
+ drm_dbg(&xe->drm, "xe oa interface not available for this system\n");
+ return -ENODEV;
+ }
+
+ ret = xe_oa_user_extensions(oa, data, 0, &param);
+ if (ret)
+ return ret;
+
+ if (param.exec_queue_id > 0) {
+ param.exec_q = xe_exec_queue_lookup(xef, param.exec_queue_id);
+ if (XE_IOCTL_DBG(oa->xe, !param.exec_q))
+ return -ENOENT;
+
+ if (param.exec_q->width > 1)
+ drm_dbg(&oa->xe->drm, "exec_q->width > 1, programming only exec_q->lrc[0]\n");
+ }
+
+ /*
+ * Query based sampling (using MI_REPORT_PERF_COUNT) with OAR/OAC,
+ * without global stream access, can be an unprivileged operation
+ */
+ if (param.exec_q && !param.sample)
+ privileged_op = false;
+
+ if (param.no_preempt) {
+ if (!param.exec_q) {
+ drm_dbg(&oa->xe->drm, "Preemption disable without exec_q!\n");
+ ret = -EINVAL;
+ goto err_exec_q;
+ }
+ privileged_op = true;
+ }
+
+ if (privileged_op && xe_observation_paranoid && !perfmon_capable()) {
+ drm_dbg(&oa->xe->drm, "Insufficient privileges to open xe OA stream\n");
+ ret = -EACCES;
+ goto err_exec_q;
+ }
+
+ if (!param.exec_q && !param.sample) {
+ drm_dbg(&oa->xe->drm, "Only OA report sampling supported\n");
+ ret = -EINVAL;
+ goto err_exec_q;
+ }
+
+ ret = xe_oa_assign_hwe(oa, &param);
+ if (ret)
+ goto err_exec_q;
+
+ f = &oa->oa_formats[param.oa_format];
+ if (!param.oa_format || !f->size ||
+ !engine_supports_oa_format(param.hwe, f->type)) {
+ drm_dbg(&oa->xe->drm, "Invalid OA format %d type %d size %d for class %d\n",
+ param.oa_format, f->type, f->size, param.hwe->class);
+ ret = -EINVAL;
+ goto err_exec_q;
+ }
+
+ if (param.period_exponent > 0) {
+ u64 oa_period, oa_freq_hz;
+
+ /* Requesting samples from OAG buffer is a privileged operation */
+ if (!param.sample) {
+ drm_dbg(&oa->xe->drm, "OA_EXPONENT specified without SAMPLE_OA\n");
+ ret = -EINVAL;
+ goto err_exec_q;
+ }
+ oa_period = oa_exponent_to_ns(param.hwe->gt, param.period_exponent);
+ oa_freq_hz = div64_u64(NSEC_PER_SEC, oa_period);
+ drm_dbg(&oa->xe->drm, "Using periodic sampling freq %lld Hz\n", oa_freq_hz);
+ }
+
+ mutex_lock(&param.hwe->gt->oa.gt_lock);
+ ret = xe_oa_stream_open_ioctl_locked(oa, &param);
+ mutex_unlock(&param.hwe->gt->oa.gt_lock);
+err_exec_q:
+ if (ret < 0 && param.exec_q)
+ xe_exec_queue_put(param.exec_q);
+ return ret;
+}
+
+static bool xe_oa_is_valid_flex_addr(struct xe_oa *oa, u32 addr)
+{
+ static const struct xe_reg flex_eu_regs[] = {
+ EU_PERF_CNTL0,
+ EU_PERF_CNTL1,
+ EU_PERF_CNTL2,
+ EU_PERF_CNTL3,
+ EU_PERF_CNTL4,
+ EU_PERF_CNTL5,
+ EU_PERF_CNTL6,
+ };
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(flex_eu_regs); i++) {
+ if (flex_eu_regs[i].addr == addr)
+ return true;
+ }
+ return false;
+}
+
+static bool xe_oa_reg_in_range_table(u32 addr, const struct xe_mmio_range *table)
+{
+ while (table->start && table->end) {
+ if (addr >= table->start && addr <= table->end)
+ return true;
+
+ table++;
+ }
+
+ return false;
+}
+
+static const struct xe_mmio_range xehp_oa_b_counters[] = {
+ { .start = 0xdc48, .end = 0xdc48 }, /* OAA_ENABLE_REG */
+ { .start = 0xdd00, .end = 0xdd48 }, /* OAG_LCE0_0 - OAA_LENABLE_REG */
+ {}
+};
+
+static const struct xe_mmio_range gen12_oa_b_counters[] = {
+ { .start = 0x2b2c, .end = 0x2b2c }, /* OAG_OA_PESS */
+ { .start = 0xd900, .end = 0xd91c }, /* OAG_OASTARTTRIG[1-8] */
+ { .start = 0xd920, .end = 0xd93c }, /* OAG_OAREPORTTRIG1[1-8] */
+ { .start = 0xd940, .end = 0xd97c }, /* OAG_CEC[0-7][0-1] */
+ { .start = 0xdc00, .end = 0xdc3c }, /* OAG_SCEC[0-7][0-1] */
+ { .start = 0xdc40, .end = 0xdc40 }, /* OAG_SPCTR_CNF */
+ { .start = 0xdc44, .end = 0xdc44 }, /* OAA_DBG_REG */
+ {}
+};
+
+static const struct xe_mmio_range mtl_oam_b_counters[] = {
+ { .start = 0x393000, .end = 0x39301c }, /* OAM_STARTTRIG1[1-8] */
+ { .start = 0x393020, .end = 0x39303c }, /* OAM_REPORTTRIG1[1-8] */
+ { .start = 0x393040, .end = 0x39307c }, /* OAM_CEC[0-7][0-1] */
+ { .start = 0x393200, .end = 0x39323C }, /* MPES[0-7] */
+ {}
+};
+
+static const struct xe_mmio_range xe2_oa_b_counters[] = {
+ { .start = 0x393200, .end = 0x39323C }, /* MPES_0_MPES_SAG - MPES_7_UPPER_MPES_SAG */
+ { .start = 0x394200, .end = 0x39423C }, /* MPES_0_MPES_SCMI0 - MPES_7_UPPER_MPES_SCMI0 */
+ { .start = 0x394A00, .end = 0x394A3C }, /* MPES_0_MPES_SCMI1 - MPES_7_UPPER_MPES_SCMI1 */
+ {},
+};
+
+static bool xe_oa_is_valid_b_counter_addr(struct xe_oa *oa, u32 addr)
+{
+ return xe_oa_reg_in_range_table(addr, xehp_oa_b_counters) ||
+ xe_oa_reg_in_range_table(addr, gen12_oa_b_counters) ||
+ xe_oa_reg_in_range_table(addr, mtl_oam_b_counters) ||
+ (GRAPHICS_VER(oa->xe) >= 20 &&
+ xe_oa_reg_in_range_table(addr, xe2_oa_b_counters));
+}
+
+static const struct xe_mmio_range mtl_oa_mux_regs[] = {
+ { .start = 0x0d00, .end = 0x0d04 }, /* RPM_CONFIG[0-1] */
+ { .start = 0x0d0c, .end = 0x0d2c }, /* NOA_CONFIG[0-8] */
+ { .start = 0x9840, .end = 0x9840 }, /* GDT_CHICKEN_BITS */
+ { .start = 0x9884, .end = 0x9888 }, /* NOA_WRITE */
+ { .start = 0x38d100, .end = 0x38d114}, /* VISACTL */
+ {}
+};
+
+static const struct xe_mmio_range gen12_oa_mux_regs[] = {
+ { .start = 0x0d00, .end = 0x0d04 }, /* RPM_CONFIG[0-1] */
+ { .start = 0x0d0c, .end = 0x0d2c }, /* NOA_CONFIG[0-8] */
+ { .start = 0x9840, .end = 0x9840 }, /* GDT_CHICKEN_BITS */
+ { .start = 0x9884, .end = 0x9888 }, /* NOA_WRITE */
+ { .start = 0x20cc, .end = 0x20cc }, /* WAIT_FOR_RC6_EXIT */
+ {}
+};
+
+static const struct xe_mmio_range xe2_oa_mux_regs[] = {
+ { .start = 0x5194, .end = 0x5194 }, /* SYS_MEM_LAT_MEASURE_MERTF_GRP_3D */
+ { .start = 0x8704, .end = 0x8704 }, /* LMEM_LAT_MEASURE_MCFG_GRP */
+ { .start = 0xB1BC, .end = 0xB1BC }, /* L3_BANK_LAT_MEASURE_LBCF_GFX */
+ { .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 */
+ {},
+};
+
+static bool xe_oa_is_valid_mux_addr(struct xe_oa *oa, u32 addr)
+{
+ if (GRAPHICS_VER(oa->xe) >= 20)
+ return xe_oa_reg_in_range_table(addr, xe2_oa_mux_regs);
+ else if (GRAPHICS_VERx100(oa->xe) >= 1270)
+ return xe_oa_reg_in_range_table(addr, mtl_oa_mux_regs);
+ else
+ return xe_oa_reg_in_range_table(addr, gen12_oa_mux_regs);
+}
+
+static bool xe_oa_is_valid_config_reg_addr(struct xe_oa *oa, u32 addr)
+{
+ return xe_oa_is_valid_flex_addr(oa, addr) ||
+ xe_oa_is_valid_b_counter_addr(oa, addr) ||
+ xe_oa_is_valid_mux_addr(oa, addr);
+}
+
+static struct xe_oa_reg *
+xe_oa_alloc_regs(struct xe_oa *oa, bool (*is_valid)(struct xe_oa *oa, u32 addr),
+ u32 __user *regs, u32 n_regs)
+{
+ struct xe_oa_reg *oa_regs;
+ int err;
+ u32 i;
+
+ oa_regs = kmalloc_array(n_regs, sizeof(*oa_regs), GFP_KERNEL);
+ if (!oa_regs)
+ return ERR_PTR(-ENOMEM);
+
+ for (i = 0; i < n_regs; i++) {
+ u32 addr, value;
+
+ err = get_user(addr, regs);
+ if (err)
+ goto addr_err;
+
+ if (!is_valid(oa, addr)) {
+ drm_dbg(&oa->xe->drm, "Invalid oa_reg address: %X\n", addr);
+ err = -EINVAL;
+ goto addr_err;
+ }
+
+ err = get_user(value, regs + 1);
+ if (err)
+ goto addr_err;
+
+ oa_regs[i].addr = XE_REG(addr);
+ oa_regs[i].value = value;
+
+ regs += 2;
+ }
+
+ return oa_regs;
+
+addr_err:
+ kfree(oa_regs);
+ return ERR_PTR(err);
+}
+
+static ssize_t show_dynamic_id(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ char *buf)
+{
+ struct xe_oa_config *oa_config =
+ container_of(attr, typeof(*oa_config), sysfs_metric_id);
+
+ return sysfs_emit(buf, "%d\n", oa_config->id);
+}
+
+static int create_dynamic_oa_sysfs_entry(struct xe_oa *oa,
+ struct xe_oa_config *oa_config)
+{
+ sysfs_attr_init(&oa_config->sysfs_metric_id.attr);
+ oa_config->sysfs_metric_id.attr.name = "id";
+ oa_config->sysfs_metric_id.attr.mode = 0444;
+ oa_config->sysfs_metric_id.show = show_dynamic_id;
+ oa_config->sysfs_metric_id.store = NULL;
+
+ oa_config->attrs[0] = &oa_config->sysfs_metric_id.attr;
+ oa_config->attrs[1] = NULL;
+
+ oa_config->sysfs_metric.name = oa_config->uuid;
+ oa_config->sysfs_metric.attrs = oa_config->attrs;
+
+ return sysfs_create_group(oa->metrics_kobj, &oa_config->sysfs_metric);
+}
+
+/**
+ * xe_oa_add_config_ioctl - Adds one OA config
+ * @dev: @drm_device
+ * @data: pointer to struct @drm_xe_oa_config
+ * @file: @drm_file
+ *
+ * The functions adds an OA config to the set of OA configs maintained in
+ * the kernel. The config determines which OA metrics are collected for an
+ * OA stream.
+ */
+int xe_oa_add_config_ioctl(struct drm_device *dev, u64 data, struct drm_file *file)
+{
+ struct xe_device *xe = to_xe_device(dev);
+ struct xe_oa *oa = &xe->oa;
+ struct drm_xe_oa_config param;
+ struct drm_xe_oa_config *arg = &param;
+ struct xe_oa_config *oa_config, *tmp;
+ struct xe_oa_reg *regs;
+ int err, id;
+
+ if (!oa->xe) {
+ drm_dbg(&xe->drm, "xe oa interface not available for this system\n");
+ return -ENODEV;
+ }
+
+ if (xe_observation_paranoid && !perfmon_capable()) {
+ drm_dbg(&oa->xe->drm, "Insufficient privileges to add xe OA config\n");
+ return -EACCES;
+ }
+
+ err = __copy_from_user(&param, u64_to_user_ptr(data), sizeof(param));
+ if (XE_IOCTL_DBG(oa->xe, err))
+ return -EFAULT;
+
+ if (XE_IOCTL_DBG(oa->xe, arg->extensions) ||
+ XE_IOCTL_DBG(oa->xe, !arg->regs_ptr) ||
+ XE_IOCTL_DBG(oa->xe, !arg->n_regs))
+ return -EINVAL;
+
+ oa_config = kzalloc(sizeof(*oa_config), GFP_KERNEL);
+ if (!oa_config)
+ return -ENOMEM;
+
+ oa_config->oa = oa;
+ kref_init(&oa_config->ref);
+
+ if (!uuid_is_valid(arg->uuid)) {
+ drm_dbg(&oa->xe->drm, "Invalid uuid format for OA config\n");
+ err = -EINVAL;
+ goto reg_err;
+ }
+
+ /* Last character in oa_config->uuid will be 0 because oa_config is kzalloc */
+ memcpy(oa_config->uuid, arg->uuid, sizeof(arg->uuid));
+
+ oa_config->regs_len = arg->n_regs;
+ regs = xe_oa_alloc_regs(oa, xe_oa_is_valid_config_reg_addr,
+ u64_to_user_ptr(arg->regs_ptr),
+ arg->n_regs);
+ if (IS_ERR(regs)) {
+ drm_dbg(&oa->xe->drm, "Failed to create OA config for mux_regs\n");
+ err = PTR_ERR(regs);
+ goto reg_err;
+ }
+ oa_config->regs = regs;
+
+ err = mutex_lock_interruptible(&oa->metrics_lock);
+ if (err)
+ goto reg_err;
+
+ /* We shouldn't have too many configs, so this iteration shouldn't be too costly */
+ idr_for_each_entry(&oa->metrics_idr, tmp, id) {
+ if (!strcmp(tmp->uuid, oa_config->uuid)) {
+ drm_dbg(&oa->xe->drm, "OA config already exists with this uuid\n");
+ err = -EADDRINUSE;
+ goto sysfs_err;
+ }
+ }
+
+ err = create_dynamic_oa_sysfs_entry(oa, oa_config);
+ if (err) {
+ drm_dbg(&oa->xe->drm, "Failed to create sysfs entry for OA config\n");
+ goto sysfs_err;
+ }
+
+ oa_config->id = idr_alloc(&oa->metrics_idr, oa_config, 1, 0, GFP_KERNEL);
+ if (oa_config->id < 0) {
+ drm_dbg(&oa->xe->drm, "Failed to create sysfs entry for OA config\n");
+ err = oa_config->id;
+ goto sysfs_err;
+ }
+
+ mutex_unlock(&oa->metrics_lock);
+
+ drm_dbg(&oa->xe->drm, "Added config %s id=%i\n", oa_config->uuid, oa_config->id);
+
+ return oa_config->id;
+
+sysfs_err:
+ mutex_unlock(&oa->metrics_lock);
+reg_err:
+ xe_oa_config_put(oa_config);
+ drm_dbg(&oa->xe->drm, "Failed to add new OA config\n");
+ return err;
+}
+
+/**
+ * xe_oa_remove_config_ioctl - Removes one OA config
+ * @dev: @drm_device
+ * @data: pointer to struct @drm_xe_observation_param
+ * @file: @drm_file
+ */
+int xe_oa_remove_config_ioctl(struct drm_device *dev, u64 data, struct drm_file *file)
+{
+ struct xe_device *xe = to_xe_device(dev);
+ struct xe_oa *oa = &xe->oa;
+ struct xe_oa_config *oa_config;
+ u64 arg, *ptr = u64_to_user_ptr(data);
+ int ret;
+
+ if (!oa->xe) {
+ drm_dbg(&xe->drm, "xe oa interface not available for this system\n");
+ return -ENODEV;
+ }
+
+ if (xe_observation_paranoid && !perfmon_capable()) {
+ drm_dbg(&oa->xe->drm, "Insufficient privileges to remove xe OA config\n");
+ return -EACCES;
+ }
+
+ ret = get_user(arg, ptr);
+ if (XE_IOCTL_DBG(oa->xe, ret))
+ return ret;
+
+ ret = mutex_lock_interruptible(&oa->metrics_lock);
+ if (ret)
+ return ret;
+
+ oa_config = idr_find(&oa->metrics_idr, arg);
+ if (!oa_config) {
+ drm_dbg(&oa->xe->drm, "Failed to remove unknown OA config\n");
+ ret = -ENOENT;
+ goto err_unlock;
+ }
+
+ WARN_ON(arg != oa_config->id);
+
+ sysfs_remove_group(oa->metrics_kobj, &oa_config->sysfs_metric);
+ idr_remove(&oa->metrics_idr, arg);
+
+ mutex_unlock(&oa->metrics_lock);
+
+ drm_dbg(&oa->xe->drm, "Removed config %s id=%i\n", oa_config->uuid, oa_config->id);
+
+ xe_oa_config_put(oa_config);
+
+ return 0;
+
+err_unlock:
+ mutex_unlock(&oa->metrics_lock);
+ return ret;
+}
+
+/**
+ * xe_oa_register - Xe OA registration
+ * @xe: @xe_device
+ *
+ * Exposes the metrics sysfs directory upon completion of module initialization
+ */
+void xe_oa_register(struct xe_device *xe)
+{
+ struct xe_oa *oa = &xe->oa;
+
+ if (!oa->xe)
+ return;
+
+ oa->metrics_kobj = kobject_create_and_add("metrics",
+ &xe->drm.primary->kdev->kobj);
+}
+
+/**
+ * xe_oa_unregister - Xe OA de-registration
+ * @xe: @xe_device
+ */
+void xe_oa_unregister(struct xe_device *xe)
+{
+ struct xe_oa *oa = &xe->oa;
+
+ if (!oa->metrics_kobj)
+ return;
+
+ kobject_put(oa->metrics_kobj);
+ oa->metrics_kobj = NULL;
+}
+
+static u32 num_oa_units_per_gt(struct xe_gt *gt)
+{
+ return 1;
+}
+
+static u32 __hwe_oam_unit(struct xe_hw_engine *hwe)
+{
+ if (GRAPHICS_VERx100(gt_to_xe(hwe->gt)) >= 1270) {
+ /*
+ * There's 1 SAMEDIA gt and 1 OAM per SAMEDIA gt. All media slices
+ * within the gt use the same OAM. All MTL/LNL SKUs list 1 SA MEDIA
+ */
+ xe_gt_WARN_ON(hwe->gt, hwe->gt->info.type != XE_GT_TYPE_MEDIA);
+
+ return 0;
+ }
+
+ return XE_OA_UNIT_INVALID;
+}
+
+static u32 __hwe_oa_unit(struct xe_hw_engine *hwe)
+{
+ switch (hwe->class) {
+ case XE_ENGINE_CLASS_RENDER:
+ case XE_ENGINE_CLASS_COMPUTE:
+ return 0;
+
+ case XE_ENGINE_CLASS_VIDEO_DECODE:
+ case XE_ENGINE_CLASS_VIDEO_ENHANCE:
+ return __hwe_oam_unit(hwe);
+
+ default:
+ return XE_OA_UNIT_INVALID;
+ }
+}
+
+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,
+ };
+}
+
+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,
+ };
+}
+
+static void __xe_oa_init_oa_units(struct xe_gt *gt)
+{
+ const u32 mtl_oa_base[] = { 0x13000 };
+ int i, num_units = gt->oa.num_oa_units;
+
+ for (i = 0; i < num_units; i++) {
+ struct xe_oa_unit *u = &gt->oa.oa_unit[i];
+
+ if (gt->info.type != XE_GT_TYPE_MEDIA) {
+ u->regs = __oag_regs();
+ u->type = DRM_XE_OA_UNIT_TYPE_OAG;
+ } else if (GRAPHICS_VERx100(gt_to_xe(gt)) >= 1270) {
+ u->regs = __oam_regs(mtl_oa_base[i]);
+ u->type = DRM_XE_OA_UNIT_TYPE_OAM;
+ }
+
+ /* Ensure MMIO trigger remains disabled till there is a stream */
+ xe_mmio_write32(gt, u->regs.oa_debug,
+ oag_configure_mmio_trigger(NULL, false));
+
+ /* Set oa_unit_ids now to ensure ids remain contiguous */
+ u->oa_unit_id = gt_to_xe(gt)->oa.oa_unit_ids++;
+ }
+}
+
+static int xe_oa_init_gt(struct xe_gt *gt)
+{
+ u32 num_oa_units = num_oa_units_per_gt(gt);
+ struct xe_hw_engine *hwe;
+ enum xe_hw_engine_id id;
+ struct xe_oa_unit *u;
+
+ u = drmm_kcalloc(&gt_to_xe(gt)->drm, num_oa_units, sizeof(*u), GFP_KERNEL);
+ if (!u)
+ return -ENOMEM;
+
+ for_each_hw_engine(hwe, gt, id) {
+ u32 index = __hwe_oa_unit(hwe);
+
+ hwe->oa_unit = NULL;
+ if (index < num_oa_units) {
+ u[index].num_engines++;
+ hwe->oa_unit = &u[index];
+ }
+ }
+
+ /*
+ * Fused off engines can result in oa_unit's with num_engines == 0. These units
+ * will appear in OA unit query, but no OA streams can be opened on them.
+ */
+ gt->oa.num_oa_units = num_oa_units;
+ gt->oa.oa_unit = u;
+
+ __xe_oa_init_oa_units(gt);
+
+ drmm_mutex_init(&gt_to_xe(gt)->drm, &gt->oa.gt_lock);
+
+ return 0;
+}
+
+static int xe_oa_init_oa_units(struct xe_oa *oa)
+{
+ struct xe_gt *gt;
+ int i, ret;
+
+ for_each_gt(gt, oa->xe, i) {
+ ret = xe_oa_init_gt(gt);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static void oa_format_add(struct xe_oa *oa, enum xe_oa_format_name format)
+{
+ __set_bit(format, oa->format_mask);
+}
+
+static void xe_oa_init_supported_formats(struct xe_oa *oa)
+{
+ if (GRAPHICS_VER(oa->xe) >= 20) {
+ /* Xe2+ */
+ oa_format_add(oa, XE_OAM_FORMAT_MPEC8u64_B8_C8);
+ oa_format_add(oa, XE_OAM_FORMAT_MPEC8u32_B8_C8);
+ oa_format_add(oa, XE_OA_FORMAT_PEC64u64);
+ oa_format_add(oa, XE_OA_FORMAT_PEC64u64_B8_C8);
+ oa_format_add(oa, XE_OA_FORMAT_PEC64u32);
+ oa_format_add(oa, XE_OA_FORMAT_PEC32u64_G1);
+ oa_format_add(oa, XE_OA_FORMAT_PEC32u32_G1);
+ oa_format_add(oa, XE_OA_FORMAT_PEC32u64_G2);
+ oa_format_add(oa, XE_OA_FORMAT_PEC32u32_G2);
+ oa_format_add(oa, XE_OA_FORMAT_PEC36u64_G1_32_G2_4);
+ oa_format_add(oa, XE_OA_FORMAT_PEC36u64_G1_4_G2_32);
+ } else if (GRAPHICS_VERx100(oa->xe) >= 1270) {
+ /* XE_METEORLAKE */
+ oa_format_add(oa, XE_OAR_FORMAT_A32u40_A4u32_B8_C8);
+ oa_format_add(oa, XE_OA_FORMAT_A24u40_A14u32_B8_C8);
+ oa_format_add(oa, XE_OAC_FORMAT_A24u64_B8_C8);
+ oa_format_add(oa, XE_OAC_FORMAT_A22u32_R2u32_B8_C8);
+ oa_format_add(oa, XE_OAM_FORMAT_MPEC8u64_B8_C8);
+ oa_format_add(oa, XE_OAM_FORMAT_MPEC8u32_B8_C8);
+ } else if (GRAPHICS_VERx100(oa->xe) >= 1255) {
+ /* XE_DG2, XE_PVC */
+ oa_format_add(oa, XE_OAR_FORMAT_A32u40_A4u32_B8_C8);
+ oa_format_add(oa, XE_OA_FORMAT_A24u40_A14u32_B8_C8);
+ oa_format_add(oa, XE_OAC_FORMAT_A24u64_B8_C8);
+ oa_format_add(oa, XE_OAC_FORMAT_A22u32_R2u32_B8_C8);
+ } else {
+ /* Gen12+ */
+ xe_assert(oa->xe, GRAPHICS_VER(oa->xe) >= 12);
+ oa_format_add(oa, XE_OA_FORMAT_A12);
+ oa_format_add(oa, XE_OA_FORMAT_A12_B8_C8);
+ oa_format_add(oa, XE_OA_FORMAT_A32u40_A4u32_B8_C8);
+ oa_format_add(oa, XE_OA_FORMAT_C4_B8);
+ }
+}
+
+/**
+ * xe_oa_init - OA initialization during device probe
+ * @xe: @xe_device
+ *
+ * Return: 0 on success or a negative error code on failure
+ */
+int xe_oa_init(struct xe_device *xe)
+{
+ struct xe_oa *oa = &xe->oa;
+ int ret;
+
+ /* Support OA only with GuC submission and Gen12+ */
+ if (!xe_device_uc_enabled(xe) || GRAPHICS_VER(xe) < 12)
+ return 0;
+
+ if (IS_SRIOV_VF(xe))
+ return 0;
+
+ oa->xe = xe;
+ oa->oa_formats = oa_formats;
+
+ drmm_mutex_init(&oa->xe->drm, &oa->metrics_lock);
+ idr_init_base(&oa->metrics_idr, 1);
+
+ ret = xe_oa_init_oa_units(oa);
+ if (ret) {
+ drm_err(&xe->drm, "OA initialization failed (%pe)\n", ERR_PTR(ret));
+ goto exit;
+ }
+
+ xe_oa_init_supported_formats(oa);
+ return 0;
+exit:
+ oa->xe = NULL;
+ return ret;
+}
+
+static int destroy_config(int id, void *p, void *data)
+{
+ xe_oa_config_put(p);
+ return 0;
+}
+
+/**
+ * xe_oa_fini - OA de-initialization during device remove
+ * @xe: @xe_device
+ */
+void xe_oa_fini(struct xe_device *xe)
+{
+ struct xe_oa *oa = &xe->oa;
+
+ if (!oa->xe)
+ return;
+
+ idr_for_each(&oa->metrics_idr, destroy_config, oa);
+ idr_destroy(&oa->metrics_idr);
+
+ oa->xe = NULL;
+}
diff --git a/drivers/gpu/drm/xe/xe_oa.h b/drivers/gpu/drm/xe/xe_oa.h
new file mode 100644
index 000000000000..87a38820c317
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_oa.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2023-2024 Intel Corporation
+ */
+
+#ifndef _XE_OA_H_
+#define _XE_OA_H_
+
+#include "xe_oa_types.h"
+
+struct drm_device;
+struct drm_file;
+struct xe_device;
+struct xe_gt;
+struct xe_hw_engine;
+
+int xe_oa_init(struct xe_device *xe);
+void xe_oa_fini(struct xe_device *xe);
+void xe_oa_register(struct xe_device *xe);
+void xe_oa_unregister(struct xe_device *xe);
+int xe_oa_stream_open_ioctl(struct drm_device *dev, u64 data, struct drm_file *file);
+int xe_oa_add_config_ioctl(struct drm_device *dev, u64 data, struct drm_file *file);
+int xe_oa_remove_config_ioctl(struct drm_device *dev, u64 data, struct drm_file *file);
+u32 xe_oa_timestamp_frequency(struct xe_gt *gt);
+u16 xe_oa_unit_id(struct xe_hw_engine *hwe);
+
+#endif
diff --git a/drivers/gpu/drm/xe/xe_oa_types.h b/drivers/gpu/drm/xe/xe_oa_types.h
new file mode 100644
index 000000000000..540c3ec53a6d
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_oa_types.h
@@ -0,0 +1,242 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2023-2024 Intel Corporation
+ */
+
+#ifndef _XE_OA_TYPES_H_
+#define _XE_OA_TYPES_H_
+
+#include <linux/bitops.h>
+#include <linux/idr.h>
+#include <linux/mutex.h>
+#include <linux/types.h>
+
+#include <drm/xe_drm.h>
+#include "regs/xe_reg_defs.h"
+#include "xe_hw_engine_types.h"
+
+#define XE_OA_BUFFER_SIZE SZ_16M
+
+enum xe_oa_report_header {
+ HDR_32_BIT = 0,
+ HDR_64_BIT,
+};
+
+enum xe_oa_format_name {
+ XE_OA_FORMAT_C4_B8,
+
+ /* Gen8+ */
+ XE_OA_FORMAT_A12,
+ XE_OA_FORMAT_A12_B8_C8,
+ XE_OA_FORMAT_A32u40_A4u32_B8_C8,
+
+ /* DG2 */
+ XE_OAR_FORMAT_A32u40_A4u32_B8_C8,
+ XE_OA_FORMAT_A24u40_A14u32_B8_C8,
+
+ /* DG2/MTL OAC */
+ XE_OAC_FORMAT_A24u64_B8_C8,
+ XE_OAC_FORMAT_A22u32_R2u32_B8_C8,
+
+ /* MTL OAM */
+ XE_OAM_FORMAT_MPEC8u64_B8_C8,
+ XE_OAM_FORMAT_MPEC8u32_B8_C8,
+
+ /* Xe2+ */
+ XE_OA_FORMAT_PEC64u64,
+ XE_OA_FORMAT_PEC64u64_B8_C8,
+ XE_OA_FORMAT_PEC64u32,
+ XE_OA_FORMAT_PEC32u64_G1,
+ XE_OA_FORMAT_PEC32u32_G1,
+ XE_OA_FORMAT_PEC32u64_G2,
+ XE_OA_FORMAT_PEC32u32_G2,
+ XE_OA_FORMAT_PEC36u64_G1_32_G2_4,
+ XE_OA_FORMAT_PEC36u64_G1_4_G2_32,
+
+ __XE_OA_FORMAT_MAX,
+};
+
+/**
+ * struct xe_oa_format - Format fields for supported OA formats. OA format
+ * properties are specified in PRM/Bspec 52198 and 60942
+ */
+struct xe_oa_format {
+ /** @counter_select: counter select value (see Bspec 52198/60942) */
+ u32 counter_select;
+ /** @size: record size as written by HW (multiple of 64 byte cachelines) */
+ int size;
+ /** @type: of enum @drm_xe_oa_format_type */
+ int type;
+ /** @header: 32 or 64 bit report headers */
+ enum xe_oa_report_header header;
+ /** @counter_size: counter size value (see Bspec 60942) */
+ u16 counter_size;
+ /** @bc_report: BC report value (see Bspec 60942) */
+ u16 bc_report;
+};
+
+/** struct xe_oa_regs - Registers for each OA unit */
+struct xe_oa_regs {
+ u32 base;
+ struct xe_reg oa_head_ptr;
+ struct xe_reg oa_tail_ptr;
+ struct xe_reg oa_buffer;
+ struct xe_reg oa_ctx_ctrl;
+ struct xe_reg oa_ctrl;
+ struct xe_reg oa_debug;
+ struct xe_reg oa_status;
+ u32 oa_ctrl_counter_select_mask;
+};
+
+/**
+ * struct xe_oa_unit - Hardware OA unit
+ */
+struct xe_oa_unit {
+ /** @oa_unit_id: identifier for the OA unit */
+ u16 oa_unit_id;
+
+ /** @type: Type of OA unit - OAM, OAG etc. */
+ enum drm_xe_oa_unit_type type;
+
+ /** @regs: OA registers for programming the OA unit */
+ struct xe_oa_regs regs;
+
+ /** @num_engines: number of engines attached to this OA unit */
+ u32 num_engines;
+
+ /** @exclusive_stream: The stream currently using the OA unit */
+ struct xe_oa_stream *exclusive_stream;
+};
+
+/**
+ * struct xe_oa_gt - OA per-gt information
+ */
+struct xe_oa_gt {
+ /** @gt_lock: lock protecting create/destroy OA streams */
+ struct mutex gt_lock;
+
+ /** @num_oa_units: number of oa units for each gt */
+ u32 num_oa_units;
+
+ /** @oa_unit: array of oa_units */
+ struct xe_oa_unit *oa_unit;
+};
+
+/**
+ * struct xe_oa - OA device level information
+ */
+struct xe_oa {
+ /** @xe: back pointer to xe device */
+ struct xe_device *xe;
+
+ /** @metrics_kobj: kobj for metrics sysfs */
+ struct kobject *metrics_kobj;
+
+ /** @metrics_lock: lock protecting add/remove configs */
+ struct mutex metrics_lock;
+
+ /** @metrics_idr: List of dynamic configurations (struct xe_oa_config) */
+ struct idr metrics_idr;
+
+ /** @ctx_oactxctrl_offset: offset of OACTXCONTROL register in context image */
+ u32 ctx_oactxctrl_offset[XE_ENGINE_CLASS_MAX];
+
+ /** @oa_formats: tracks all OA formats across platforms */
+ const struct xe_oa_format *oa_formats;
+
+ /** @format_mask: tracks valid OA formats for a platform */
+ unsigned long format_mask[BITS_TO_LONGS(__XE_OA_FORMAT_MAX)];
+
+ /** @oa_unit_ids: tracks oa unit ids assigned across gt's */
+ u16 oa_unit_ids;
+};
+
+/** @xe_oa_buffer: State of the stream OA buffer */
+struct xe_oa_buffer {
+ /** @format: data format */
+ const struct xe_oa_format *format;
+
+ /** @format: xe_bo backing the OA buffer */
+ struct xe_bo *bo;
+
+ /** @vaddr: mapped vaddr of the OA buffer */
+ u8 *vaddr;
+
+ /** @ptr_lock: Lock protecting reads/writes to head/tail pointers */
+ spinlock_t ptr_lock;
+
+ /** @head: Cached head to read from */
+ u32 head;
+
+ /** @tail: The last verified cached tail where HW has completed writing */
+ u32 tail;
+
+ /** @circ_size: The effective circular buffer size, for Xe2+ */
+ u32 circ_size;
+};
+
+/**
+ * struct xe_oa_stream - state for a single open stream FD
+ */
+struct xe_oa_stream {
+ /** @oa: xe_oa backpointer */
+ struct xe_oa *oa;
+
+ /** @gt: gt associated with the oa stream */
+ struct xe_gt *gt;
+
+ /** @hwe: hardware engine associated with this oa stream */
+ struct xe_hw_engine *hwe;
+
+ /** @stream_lock: Lock serializing stream operations */
+ struct mutex stream_lock;
+
+ /** @sample: true if DRM_XE_OA_PROP_SAMPLE_OA is provided */
+ bool sample;
+
+ /** @exec_q: Exec queue corresponding to DRM_XE_OA_PROPERTY_EXEC_QUEUE_ID */
+ struct xe_exec_queue *exec_q;
+
+ /** @k_exec_q: kernel exec_q used for OA programming batch submissions */
+ struct xe_exec_queue *k_exec_q;
+
+ /** @enabled: Whether the stream is currently enabled */
+ bool enabled;
+
+ /** @oa_config: OA configuration used by the stream */
+ struct xe_oa_config *oa_config;
+
+ /** @oa_config_bos: List of struct @xe_oa_config_bo's */
+ struct llist_head oa_config_bos;
+
+ /** @poll_check_timer: Timer to periodically check for data in the OA buffer */
+ struct hrtimer poll_check_timer;
+
+ /** @poll_wq: Wait queue for waiting for OA data to be available */
+ wait_queue_head_t poll_wq;
+
+ /** @pollin: Whether there is data available to read */
+ bool pollin;
+
+ /** @periodic: Whether periodic sampling is currently enabled */
+ bool periodic;
+
+ /** @period_exponent: OA unit sampling frequency is derived from this */
+ int period_exponent;
+
+ /** @oa_buffer: OA buffer for the stream */
+ struct xe_oa_buffer oa_buffer;
+
+ /** @poll_period_ns: hrtimer period for checking OA buffer for available data */
+ u64 poll_period_ns;
+
+ /** @override_gucrc: GuC RC has been overridden for the OA stream */
+ bool override_gucrc;
+
+ /** @oa_status: temporary storage for oa_status register value */
+ u32 oa_status;
+
+ /** @no_preempt: Whether preemption and timeslicing is disabled for stream exec_q */
+ u32 no_preempt;
+};
+#endif
diff --git a/drivers/gpu/drm/xe/xe_observation.c b/drivers/gpu/drm/xe/xe_observation.c
new file mode 100644
index 000000000000..fcb584b42a7d
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_observation.c
@@ -0,0 +1,93 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2023-2024 Intel Corporation
+ */
+
+#include <linux/errno.h>
+#include <linux/sysctl.h>
+
+#include <drm/xe_drm.h>
+
+#include "xe_oa.h"
+#include "xe_observation.h"
+
+u32 xe_observation_paranoid = true;
+static struct ctl_table_header *sysctl_header;
+
+static int xe_oa_ioctl(struct drm_device *dev, struct drm_xe_observation_param *arg,
+ struct drm_file *file)
+{
+ switch (arg->observation_op) {
+ case DRM_XE_OBSERVATION_OP_STREAM_OPEN:
+ return xe_oa_stream_open_ioctl(dev, arg->param, file);
+ case DRM_XE_OBSERVATION_OP_ADD_CONFIG:
+ return xe_oa_add_config_ioctl(dev, arg->param, file);
+ case DRM_XE_OBSERVATION_OP_REMOVE_CONFIG:
+ return xe_oa_remove_config_ioctl(dev, arg->param, file);
+ default:
+ return -EINVAL;
+ }
+}
+
+/**
+ * xe_observation_ioctl - The top level observation layer ioctl
+ * @dev: @drm_device
+ * @data: pointer to struct @drm_xe_observation_param
+ * @file: @drm_file
+ *
+ * The function is called for different observation streams types and
+ * allows execution of different operations supported by those stream
+ * types.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_observation_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
+{
+ struct drm_xe_observation_param *arg = data;
+
+ if (arg->extensions)
+ return -EINVAL;
+
+ switch (arg->observation_type) {
+ case DRM_XE_OBSERVATION_TYPE_OA:
+ return xe_oa_ioctl(dev, arg, file);
+ default:
+ return -EINVAL;
+ }
+}
+
+static struct ctl_table observation_ctl_table[] = {
+ {
+ .procname = "observation_paranoid",
+ .data = &xe_observation_paranoid,
+ .maxlen = sizeof(xe_observation_paranoid),
+ .mode = 0644,
+ .proc_handler = proc_dointvec_minmax,
+ .extra1 = SYSCTL_ZERO,
+ .extra2 = SYSCTL_ONE,
+ },
+ {}
+};
+
+/**
+ * xe_observation_sysctl_register - Register xe_observation_paranoid sysctl
+ *
+ * Normally only superuser/root can access observation stream
+ * data. However, superuser can set xe_observation_paranoid sysctl to 0 to
+ * allow non-privileged users to also access observation data.
+ *
+ * Return: always returns 0
+ */
+int xe_observation_sysctl_register(void)
+{
+ sysctl_header = register_sysctl("dev/xe", observation_ctl_table);
+ return 0;
+}
+
+/**
+ * xe_observation_sysctl_unregister - Unregister xe_observation_paranoid sysctl
+ */
+void xe_observation_sysctl_unregister(void)
+{
+ unregister_sysctl_table(sysctl_header);
+}
diff --git a/drivers/gpu/drm/xe/xe_observation.h b/drivers/gpu/drm/xe/xe_observation.h
new file mode 100644
index 000000000000..17816998e966
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_observation.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2023-2024 Intel Corporation
+ */
+
+#ifndef _XE_OBSERVATION_H_
+#define _XE_OBSERVATION_H_
+
+#include <linux/types.h>
+
+struct drm_device;
+struct drm_file;
+
+extern u32 xe_observation_paranoid;
+
+int xe_observation_ioctl(struct drm_device *dev, void *data, struct drm_file *file);
+int xe_observation_sysctl_register(void);
+void xe_observation_sysctl_unregister(void);
+
+#endif
diff --git a/drivers/gpu/drm/xe/xe_pat.c b/drivers/gpu/drm/xe/xe_pat.c
index d5b516f115ad..4ee32ee1cc88 100644
--- a/drivers/gpu/drm/xe/xe_pat.c
+++ b/drivers/gpu/drm/xe/xe_pat.c
@@ -10,6 +10,7 @@
#include "regs/xe_reg_defs.h"
#include "xe_assert.h"
#include "xe_device.h"
+#include "xe_force_wake.h"
#include "xe_gt.h"
#include "xe_gt_mcr.h"
#include "xe_mmio.h"
diff --git a/drivers/gpu/drm/xe/xe_pci.c b/drivers/gpu/drm/xe/xe_pci.c
index f326dbb1cecd..732ee0d02124 100644
--- a/drivers/gpu/drm/xe/xe_pci.c
+++ b/drivers/gpu/drm/xe/xe_pci.c
@@ -13,16 +13,19 @@
#include <drm/drm_color_mgmt.h>
#include <drm/drm_drv.h>
-#include <drm/xe_pciids.h>
+#include <drm/intel/xe_pciids.h>
#include "display/xe_display.h"
#include "regs/xe_gt_regs.h"
#include "xe_device.h"
#include "xe_drv.h"
#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_sriov.h"
#include "xe_pci_types.h"
#include "xe_pm.h"
#include "xe_sriov.h"
@@ -40,12 +43,6 @@ struct xe_subplatform_desc {
const u16 *pciidlist;
};
-struct xe_gt_desc {
- enum xe_gt_type type;
- u32 mmio_adj_limit;
- u32 mmio_adj_offset;
-};
-
struct xe_device_desc {
/* Should only ever be set for platforms without GMD_ID */
const struct xe_graphics_desc *graphics;
@@ -74,7 +71,7 @@ __diag_push();
__diag_ignore_all("-Woverride-init", "Allow field overrides in table");
#define PLATFORM(x) \
- .platform = (x), \
+ .platform = XE_##x, \
.platform_name = #x
#define NOP(x) x
@@ -146,6 +143,7 @@ static const struct xe_graphics_desc graphics_xehpc = {
.vram_flags = XE_VRAM_FLAGS_NEED64K,
.has_asid = 1,
+ .has_atomic_enable_pte_bit = 1,
.has_flat_ccs = 0,
.has_usm = 1,
};
@@ -163,7 +161,9 @@ static const struct xe_graphics_desc graphics_xelpg = {
#define XE2_GFX_FEATURES \
.dma_mask_size = 46, \
.has_asid = 1, \
+ .has_atomic_enable_pte_bit = 1, \
.has_flat_ccs = 1, \
+ .has_indirect_ring_state = 1, \
.has_range_tlb_invalidation = 1, \
.has_usm = 1, \
.va_bits = 48, \
@@ -211,13 +211,14 @@ static const struct xe_media_desc media_xe2 = {
.name = "Xe2_LPM / Xe2_HPM",
.hw_engine_mask =
GENMASK(XE_HW_ENGINE_VCS7, XE_HW_ENGINE_VCS0) |
- GENMASK(XE_HW_ENGINE_VECS3, XE_HW_ENGINE_VECS0), /* TODO: GSC0 */
+ GENMASK(XE_HW_ENGINE_VECS3, XE_HW_ENGINE_VECS0) |
+ BIT(XE_HW_ENGINE_GSCCS0)
};
static const struct xe_device_desc tgl_desc = {
.graphics = &graphics_xelp,
.media = &media_xem,
- PLATFORM(XE_TIGERLAKE),
+ PLATFORM(TIGERLAKE),
.has_display = true,
.has_llc = true,
.require_force_probe = true,
@@ -226,7 +227,7 @@ static const struct xe_device_desc tgl_desc = {
static const struct xe_device_desc rkl_desc = {
.graphics = &graphics_xelp,
.media = &media_xem,
- PLATFORM(XE_ROCKETLAKE),
+ PLATFORM(ROCKETLAKE),
.has_display = true,
.has_llc = true,
.require_force_probe = true,
@@ -237,7 +238,7 @@ static const u16 adls_rpls_ids[] = { XE_RPLS_IDS(NOP), 0 };
static const struct xe_device_desc adl_s_desc = {
.graphics = &graphics_xelp,
.media = &media_xem,
- PLATFORM(XE_ALDERLAKE_S),
+ PLATFORM(ALDERLAKE_S),
.has_display = true,
.has_llc = true,
.require_force_probe = true,
@@ -252,7 +253,7 @@ static const u16 adlp_rplu_ids[] = { XE_RPLU_IDS(NOP), 0 };
static const struct xe_device_desc adl_p_desc = {
.graphics = &graphics_xelp,
.media = &media_xem,
- PLATFORM(XE_ALDERLAKE_P),
+ PLATFORM(ALDERLAKE_P),
.has_display = true,
.has_llc = true,
.require_force_probe = true,
@@ -265,7 +266,7 @@ static const struct xe_device_desc adl_p_desc = {
static const struct xe_device_desc adl_n_desc = {
.graphics = &graphics_xelp,
.media = &media_xem,
- PLATFORM(XE_ALDERLAKE_N),
+ PLATFORM(ALDERLAKE_N),
.has_display = true,
.has_llc = true,
.require_force_probe = true,
@@ -278,7 +279,7 @@ static const struct xe_device_desc dg1_desc = {
.graphics = &graphics_xelpp,
.media = &media_xem,
DGFX_FEATURES,
- PLATFORM(XE_DG1),
+ PLATFORM(DG1),
.has_display = true,
.has_heci_gscfi = 1,
.require_force_probe = true,
@@ -290,7 +291,7 @@ static const u16 dg2_g12_ids[] = { XE_DG2_G12_IDS(NOP), 0 };
#define DG2_FEATURES \
DGFX_FEATURES, \
- PLATFORM(XE_DG2), \
+ PLATFORM(DG2), \
.has_heci_gscfi = 1, \
.subplatforms = (const struct xe_subplatform_desc[]) { \
{ XE_SUBPLATFORM_DG2_G10, "G10", dg2_g10_ids }, \
@@ -320,7 +321,7 @@ static const struct xe_device_desc dg2_desc = {
static const __maybe_unused struct xe_device_desc pvc_desc = {
.graphics = &graphics_xehpc,
DGFX_FEATURES,
- PLATFORM(XE_PVC),
+ PLATFORM(PVC),
.has_display = false,
.has_heci_gscfi = 1,
.require_force_probe = true,
@@ -329,19 +330,20 @@ static const __maybe_unused struct xe_device_desc pvc_desc = {
static const struct xe_device_desc mtl_desc = {
/* .graphics and .media determined via GMD_ID */
.require_force_probe = true,
- PLATFORM(XE_METEORLAKE),
+ PLATFORM(METEORLAKE),
.has_display = true,
};
static const struct xe_device_desc lnl_desc = {
- PLATFORM(XE_LUNARLAKE),
+ PLATFORM(LUNARLAKE),
.has_display = true,
.require_force_probe = true,
};
-static const struct xe_device_desc bmg_desc __maybe_unused = {
+static const struct xe_device_desc bmg_desc = {
DGFX_FEATURES,
- PLATFORM(XE_BATTLEMAGE),
+ PLATFORM(BATTLEMAGE),
+ .has_display = true,
.require_force_probe = true,
};
@@ -388,6 +390,7 @@ static const struct pci_device_id pciidlist[] = {
XE_DG2_IDS(INTEL_VGA_DEVICE, &dg2_desc),
XE_MTL_IDS(INTEL_VGA_DEVICE, &mtl_desc),
XE_LNL_IDS(INTEL_VGA_DEVICE, &lnl_desc),
+ XE_BMG_IDS(INTEL_VGA_DEVICE, &bmg_desc),
{ }
};
MODULE_DEVICE_TABLE(pci, pciidlist);
@@ -470,10 +473,52 @@ static void read_gmdid(struct xe_device *xe, enum xe_gmdid_type type, u32 *ver,
KUNIT_STATIC_STUB_REDIRECT(read_gmdid, xe, type, ver, revid);
- if (type == GMDID_MEDIA)
- gmdid_reg.addr += MEDIA_GT_GSI_OFFSET;
+ if (IS_SRIOV_VF(xe)) {
+ /*
+ * To get the value of the GMDID register, VFs must obtain it
+ * from the GuC using MMIO communication.
+ *
+ * Note that at this point the xe_gt is not fully uninitialized
+ * and only basic access to MMIO registers is possible. To use
+ * our existing GuC communication functions we must perform at
+ * least basic xe_gt and xe_guc initialization.
+ *
+ * Since to obtain the value of GMDID_MEDIA we need to use the
+ * media GuC, temporarly tweak the gt type.
+ */
+ xe_gt_assert(gt, gt->info.type == XE_GT_TYPE_UNINITIALIZED);
+
+ if (type == GMDID_MEDIA) {
+ gt->info.id = 1;
+ gt->info.type = XE_GT_TYPE_MEDIA;
+ } else {
+ gt->info.id = 0;
+ gt->info.type = XE_GT_TYPE_MAIN;
+ }
+
+ xe_guc_comm_init_early(&gt->uc.guc);
+
+ /* Don't bother with GMDID if failed to negotiate the GuC ABI */
+ val = xe_gt_sriov_vf_bootstrap(gt) ? 0 : xe_gt_sriov_vf_gmdid(gt);
+
+ /*
+ * Only undo xe_gt.info here, the remaining changes made above
+ * will be overwritten as part of the regular initialization.
+ */
+ gt->info.id = 0;
+ gt->info.type = XE_GT_TYPE_UNINITIALIZED;
+ } else {
+ /*
+ * We need to apply the GSI offset explicitly here as at this
+ * point the xe_gt is not fully uninitialized and only basic
+ * access to MMIO registers is possible.
+ */
+ if (type == GMDID_MEDIA)
+ gmdid_reg.addr += MEDIA_GT_GSI_OFFSET;
+
+ val = xe_mmio_read32(gt, gmdid_reg);
+ }
- val = xe_mmio_read32(gt, gmdid_reg);
*ver = REG_FIELD_GET(GMD_ID_ARCH_MASK, val) * 100 + REG_FIELD_GET(GMD_ID_RELEASE_MASK, val);
*revid = REG_FIELD_GET(GMD_ID_REVID, val);
}
@@ -554,6 +599,7 @@ static int xe_info_init_early(struct xe_device *xe,
{
int err;
+ xe->info.platform_name = desc->platform_name;
xe->info.platform = desc->platform;
xe->info.subplatform = subplatform_desc ?
subplatform_desc->subplatform : XE_SUBPLATFORM_NONE;
@@ -628,6 +674,9 @@ static int xe_info_init(struct xe_device *xe,
xe->info.va_bits = graphics_desc->va_bits;
xe->info.vm_max_level = graphics_desc->vm_max_level;
xe->info.has_asid = graphics_desc->has_asid;
+ xe->info.has_atomic_enable_pte_bit = graphics_desc->has_atomic_enable_pte_bit;
+ if (xe->info.platform != XE_PVC)
+ xe->info.has_device_atomics_on_smem = 1;
xe->info.has_flat_ccs = graphics_desc->has_flat_ccs;
xe->info.has_range_tlb_invalidation = graphics_desc->has_range_tlb_invalidation;
xe->info.has_usm = graphics_desc->has_usm;
@@ -655,9 +704,10 @@ static int xe_info_init(struct xe_device *xe,
gt = tile->primary_gt;
gt->info.id = xe->info.gt_count++;
gt->info.type = XE_GT_TYPE_MAIN;
- gt->info.__engine_mask = graphics_desc->hw_engine_mask;
+ gt->info.has_indirect_ring_state = graphics_desc->has_indirect_ring_state;
+ gt->info.engine_mask = graphics_desc->hw_engine_mask;
if (MEDIA_VER(xe) < 13 && media_desc)
- gt->info.__engine_mask |= media_desc->hw_engine_mask;
+ gt->info.engine_mask |= media_desc->hw_engine_mask;
if (MEDIA_VER(xe) < 13 || !media_desc)
continue;
@@ -672,7 +722,8 @@ static int xe_info_init(struct xe_device *xe,
gt = tile->media_gt;
gt->info.type = XE_GT_TYPE_MEDIA;
- gt->info.__engine_mask = media_desc->hw_engine_mask;
+ gt->info.has_indirect_ring_state = media_desc->has_indirect_ring_state;
+ gt->info.engine_mask = media_desc->hw_engine_mask;
gt->mmio.adj_offset = MEDIA_GT_GSI_OFFSET;
gt->mmio.adj_limit = MEDIA_GT_GSI_LENGTH;
@@ -697,6 +748,9 @@ static void xe_pci_remove(struct pci_dev *pdev)
if (!xe) /* driver load aborted, nothing to cleanup */
return;
+ if (IS_SRIOV_PF(xe))
+ xe_pci_sriov_configure(pdev, 0);
+
xe_device_remove(xe);
xe_pm_runtime_fini(xe);
pci_set_drvdata(pdev, NULL);
@@ -757,7 +811,9 @@ static int xe_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if (err)
return err;
- xe_display_probe(xe);
+ err = xe_display_probe(xe);
+ if (err)
+ return err;
drm_dbg(&xe->drm, "%s %s %04x:%04x dgfx:%d gfx:%s (%d.%02d) media:%s (%d.%02d) display:%s dma_m_s:%d tc:%d gscfi:%d",
desc->platform_name,
@@ -951,6 +1007,7 @@ static struct pci_driver xe_pci_driver = {
.probe = xe_pci_probe,
.remove = xe_pci_remove,
.shutdown = xe_pci_shutdown,
+ .sriov_configure = xe_pci_sriov_configure,
#ifdef CONFIG_PM_SLEEP
.driver.pm = &xe_pm_ops,
#endif
diff --git a/drivers/gpu/drm/xe/xe_pci_sriov.c b/drivers/gpu/drm/xe/xe_pci_sriov.c
new file mode 100644
index 000000000000..aaceee748287
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_pci_sriov.c
@@ -0,0 +1,170 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2023-2024 Intel Corporation
+ */
+
+#include "xe_assert.h"
+#include "xe_device.h"
+#include "xe_gt_sriov_pf_config.h"
+#include "xe_gt_sriov_pf_control.h"
+#include "xe_pci_sriov.h"
+#include "xe_pm.h"
+#include "xe_sriov.h"
+#include "xe_sriov_pf_helpers.h"
+#include "xe_sriov_printk.h"
+
+static int pf_needs_provisioning(struct xe_gt *gt, unsigned int num_vfs)
+{
+ unsigned int n;
+
+ for (n = 1; n <= num_vfs; n++)
+ if (!xe_gt_sriov_pf_config_is_empty(gt, n))
+ return false;
+
+ return true;
+}
+
+static int pf_provision_vfs(struct xe_device *xe, unsigned int num_vfs)
+{
+ struct xe_gt *gt;
+ unsigned int id;
+ int result = 0, err;
+
+ for_each_gt(gt, xe, id) {
+ if (!pf_needs_provisioning(gt, num_vfs))
+ continue;
+ err = xe_gt_sriov_pf_config_set_fair(gt, VFID(1), num_vfs);
+ result = result ?: err;
+ }
+
+ return result;
+}
+
+static void pf_unprovision_vfs(struct xe_device *xe, unsigned int num_vfs)
+{
+ struct xe_gt *gt;
+ unsigned int id;
+ unsigned int n;
+
+ for_each_gt(gt, xe, id)
+ for (n = 1; n <= num_vfs; n++)
+ xe_gt_sriov_pf_config_release(gt, n, true);
+}
+
+static void pf_reset_vfs(struct xe_device *xe, unsigned int num_vfs)
+{
+ struct xe_gt *gt;
+ unsigned int id;
+ unsigned int n;
+
+ for_each_gt(gt, xe, id)
+ for (n = 1; n <= num_vfs; n++)
+ xe_gt_sriov_pf_control_trigger_flr(gt, n);
+}
+
+static int pf_enable_vfs(struct xe_device *xe, int num_vfs)
+{
+ struct pci_dev *pdev = to_pci_dev(xe->drm.dev);
+ int total_vfs = xe_sriov_pf_get_totalvfs(xe);
+ int err;
+
+ xe_assert(xe, IS_SRIOV_PF(xe));
+ xe_assert(xe, num_vfs > 0);
+ xe_assert(xe, num_vfs <= total_vfs);
+ xe_sriov_dbg(xe, "enabling %u VF%s\n", num_vfs, str_plural(num_vfs));
+
+ /*
+ * We must hold additional reference to the runtime PM to keep PF in D0
+ * during VFs lifetime, as our VFs do not implement the PM capability.
+ *
+ * With PF being in D0 state, all VFs will also behave as in D0 state.
+ * This will also keep GuC alive with all VFs' configurations.
+ *
+ * We will release this additional PM reference in pf_disable_vfs().
+ */
+ xe_pm_runtime_get_noresume(xe);
+
+ err = pf_provision_vfs(xe, num_vfs);
+ if (err < 0)
+ goto failed;
+
+ err = pci_enable_sriov(pdev, num_vfs);
+ if (err < 0)
+ goto failed;
+
+ xe_sriov_info(xe, "Enabled %u of %u VF%s\n",
+ num_vfs, total_vfs, str_plural(total_vfs));
+ return num_vfs;
+
+failed:
+ pf_unprovision_vfs(xe, num_vfs);
+ xe_pm_runtime_put(xe);
+
+ xe_sriov_notice(xe, "Failed to enable %u VF%s (%pe)\n",
+ num_vfs, str_plural(num_vfs), ERR_PTR(err));
+ return err;
+}
+
+static int pf_disable_vfs(struct xe_device *xe)
+{
+ struct device *dev = xe->drm.dev;
+ struct pci_dev *pdev = to_pci_dev(dev);
+ u16 num_vfs = pci_num_vf(pdev);
+
+ xe_assert(xe, IS_SRIOV_PF(xe));
+ xe_sriov_dbg(xe, "disabling %u VF%s\n", num_vfs, str_plural(num_vfs));
+
+ if (!num_vfs)
+ return 0;
+
+ pci_disable_sriov(pdev);
+
+ pf_reset_vfs(xe, num_vfs);
+
+ pf_unprovision_vfs(xe, num_vfs);
+
+ /* not needed anymore - see pf_enable_vfs() */
+ xe_pm_runtime_put(xe);
+
+ xe_sriov_info(xe, "Disabled %u VF%s\n", num_vfs, str_plural(num_vfs));
+ return 0;
+}
+
+/**
+ * xe_pci_sriov_configure - Configure SR-IOV (enable/disable VFs).
+ * @pdev: the &pci_dev
+ * @num_vfs: number of VFs to enable or zero to disable all VFs
+ *
+ * This is the Xe implementation of struct pci_driver.sriov_configure callback.
+ *
+ * This callback will be called by the PCI subsystem to enable or disable SR-IOV
+ * Virtual Functions (VFs) as requested by the used via the PCI sysfs interface.
+ *
+ * Return: number of configured VFs or a negative error code on failure.
+ */
+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;
+
+ if (num_vfs < 0)
+ return -EINVAL;
+
+ if (num_vfs > xe_sriov_pf_get_totalvfs(xe))
+ return -ERANGE;
+
+ if (num_vfs && pci_num_vf(pdev))
+ return -EBUSY;
+
+ xe_pm_runtime_get(xe);
+ if (num_vfs > 0)
+ ret = pf_enable_vfs(xe, num_vfs);
+ else
+ ret = pf_disable_vfs(xe);
+ xe_pm_runtime_put(xe);
+
+ return ret;
+}
diff --git a/drivers/gpu/drm/xe/xe_pci_sriov.h b/drivers/gpu/drm/xe/xe_pci_sriov.h
new file mode 100644
index 000000000000..c76dd0d90495
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_pci_sriov.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2023-2024 Intel Corporation
+ */
+
+#ifndef _XE_PCI_SRIOV_H_
+#define _XE_PCI_SRIOV_H_
+
+struct pci_dev;
+
+#ifdef CONFIG_PCI_IOV
+int xe_pci_sriov_configure(struct pci_dev *pdev, int num_vfs);
+#else
+static inline int xe_pci_sriov_configure(struct pci_dev *pdev, int num_vfs)
+{
+ return 0;
+}
+#endif
+
+#endif
diff --git a/drivers/gpu/drm/xe/xe_pci_types.h b/drivers/gpu/drm/xe/xe_pci_types.h
index b1ad12fa22d6..79b0f80376a4 100644
--- a/drivers/gpu/drm/xe/xe_pci_types.h
+++ b/drivers/gpu/drm/xe/xe_pci_types.h
@@ -25,7 +25,9 @@ struct xe_graphics_desc {
u8 max_remote_tiles:2;
u8 has_asid:1;
+ u8 has_atomic_enable_pte_bit:1;
u8 has_flat_ccs:1;
+ u8 has_indirect_ring_state:1;
u8 has_range_tlb_invalidation:1;
u8 has_usm:1;
};
@@ -36,6 +38,8 @@ struct xe_media_desc {
u8 rel;
u64 hw_engine_mask; /* hardware engines provided by media IP */
+
+ u8 has_indirect_ring_state:1;
};
struct gmdid_map {
diff --git a/drivers/gpu/drm/xe/xe_pcode.c b/drivers/gpu/drm/xe/xe_pcode.c
index c010ef16fbf5..9c4eefdf6642 100644
--- a/drivers/gpu/drm/xe/xe_pcode.c
+++ b/drivers/gpu/drm/xe/xe_pcode.c
@@ -10,6 +10,7 @@
#include <drm/drm_managed.h>
+#include "xe_assert.h"
#include "xe_device.h"
#include "xe_gt.h"
#include "xe_mmio.h"
@@ -124,6 +125,8 @@ static int pcode_try_request(struct xe_gt *gt, u32 mbox,
{
int slept, wait = 10;
+ xe_gt_assert(gt, timeout_us > 0);
+
for (slept = 0; slept < timeout_us; slept += wait) {
if (locked)
*status = pcode_mailbox_rw(gt, mbox, &request, NULL, 1, true,
@@ -169,6 +172,8 @@ int xe_pcode_request(struct xe_gt *gt, u32 mbox, u32 request,
u32 status;
int ret;
+ xe_gt_assert(gt, timeout_base_ms <= 3);
+
mutex_lock(&gt->pcode.lock);
ret = pcode_try_request(gt, mbox, request, reply_mask, reply, &status,
@@ -188,10 +193,9 @@ int xe_pcode_request(struct xe_gt *gt, u32 mbox, u32 request,
*/
drm_err(&gt_to_xe(gt)->drm,
"PCODE timeout, retrying with preemption disabled\n");
- drm_WARN_ON_ONCE(&gt_to_xe(gt)->drm, timeout_base_ms > 1);
preempt_disable();
ret = pcode_try_request(gt, mbox, request, reply_mask, reply, &status,
- true, timeout_base_ms * 1000, true);
+ true, 50 * 1000, true);
preempt_enable();
out:
diff --git a/drivers/gpu/drm/xe/xe_pm.c b/drivers/gpu/drm/xe/xe_pm.c
index 37fbeda12d3b..de3b5df65e48 100644
--- a/drivers/gpu/drm/xe/xe_pm.c
+++ b/drivers/gpu/drm/xe/xe_pm.c
@@ -69,7 +69,7 @@
*/
#ifdef CONFIG_LOCKDEP
-struct lockdep_map xe_pm_runtime_lockdep_map = {
+static struct lockdep_map xe_pm_runtime_lockdep_map = {
.name = "xe_pm_runtime_lockdep_map"
};
#endif
@@ -96,12 +96,12 @@ int xe_pm_suspend(struct xe_device *xe)
if (err)
goto err;
- xe_display_pm_suspend(xe);
+ xe_display_pm_suspend(xe, false);
for_each_gt(gt, xe, id) {
err = xe_gt_suspend(gt);
if (err) {
- xe_display_pm_resume(xe);
+ xe_display_pm_resume(xe, false);
goto err;
}
}
@@ -151,7 +151,7 @@ int xe_pm_resume(struct xe_device *xe)
xe_irq_resume(xe);
- xe_display_pm_resume(xe);
+ xe_display_pm_resume(xe, false);
for_each_gt(gt, xe, id)
xe_gt_resume(gt);
@@ -366,6 +366,7 @@ int xe_pm_runtime_suspend(struct xe_device *xe)
err = xe_bo_evict_all(xe);
if (err)
goto out;
+ xe_display_pm_suspend(xe, true);
}
for_each_gt(gt, xe, id) {
@@ -375,7 +376,12 @@ int xe_pm_runtime_suspend(struct xe_device *xe)
}
xe_irq_suspend(xe);
+
+ if (xe->d3cold.allowed)
+ xe_display_pm_suspend_late(xe);
out:
+ if (err)
+ xe_display_pm_resume(xe, true);
lock_map_release(&xe_pm_runtime_lockdep_map);
xe_pm_write_callback_task(xe, NULL);
return err;
@@ -398,19 +404,13 @@ int xe_pm_runtime_resume(struct xe_device *xe)
lock_map_acquire(&xe_pm_runtime_lockdep_map);
- /*
- * It can be possible that xe has allowed d3cold but other pcie devices
- * in gfx card soc would have blocked d3cold, therefore card has not
- * really lost power. Detecting primary Gt power is sufficient.
- */
- gt = xe_device_get_gt(xe, 0);
- xe->d3cold.power_lost = xe_guc_in_reset(&gt->uc.guc);
-
- if (xe->d3cold.allowed && xe->d3cold.power_lost) {
+ if (xe->d3cold.allowed) {
err = xe_pcode_ready(xe, true);
if (err)
goto out;
+ xe_display_pm_resume_early(xe);
+
/*
* This only restores pinned memory which is the memory
* required for the GT(s) to resume.
@@ -425,7 +425,8 @@ int xe_pm_runtime_resume(struct xe_device *xe)
for_each_gt(gt, xe, id)
xe_gt_resume(gt);
- if (xe->d3cold.allowed && xe->d3cold.power_lost) {
+ if (xe->d3cold.allowed) {
+ xe_display_pm_resume(xe, true);
err = xe_bo_restore_user(xe);
if (err)
goto out;
@@ -505,19 +506,20 @@ int xe_pm_runtime_get_ioctl(struct xe_device *xe)
* xe_pm_runtime_get_if_active - Get a runtime_pm reference if device active
* @xe: xe device instance
*
- * Returns: Any number greater than or equal to 0 for success, negative error
- * code otherwise.
+ * Return: True if device is awake (regardless the previous number of references)
+ * and a new reference was taken, false otherwise.
*/
-int xe_pm_runtime_get_if_active(struct xe_device *xe)
+bool xe_pm_runtime_get_if_active(struct xe_device *xe)
{
- return pm_runtime_get_if_active(xe->drm.dev);
+ return pm_runtime_get_if_active(xe->drm.dev) > 0;
}
/**
- * xe_pm_runtime_get_if_in_use - Get a runtime_pm reference and resume if needed
+ * xe_pm_runtime_get_if_in_use - Get a new reference if device is active with previous ref taken
* @xe: xe device instance
*
- * Returns: True if device is awake and the reference was taken, false otherwise.
+ * Return: True if device is awake, a previous reference had been already taken,
+ * and a new reference was now taken, false otherwise.
*/
bool xe_pm_runtime_get_if_in_use(struct xe_device *xe)
{
diff --git a/drivers/gpu/drm/xe/xe_pm.h b/drivers/gpu/drm/xe/xe_pm.h
index 18b0613fe57b..104a21ae6dfd 100644
--- a/drivers/gpu/drm/xe/xe_pm.h
+++ b/drivers/gpu/drm/xe/xe_pm.h
@@ -8,12 +8,7 @@
#include <linux/pm_runtime.h>
-/*
- * TODO: Threshold = 0 will block D3Cold.
- * Before we can move this to a higher value (like 300), we need to:
- * 1. rewrite the VRAM save / restore to avoid buffer object locks
- */
-#define DEFAULT_VRAM_THRESHOLD 0 /* in MB */
+#define DEFAULT_VRAM_THRESHOLD 300 /* in MB */
struct xe_device;
@@ -29,7 +24,7 @@ int xe_pm_runtime_resume(struct xe_device *xe);
void xe_pm_runtime_get(struct xe_device *xe);
int xe_pm_runtime_get_ioctl(struct xe_device *xe);
void xe_pm_runtime_put(struct xe_device *xe);
-int xe_pm_runtime_get_if_active(struct xe_device *xe);
+bool xe_pm_runtime_get_if_active(struct xe_device *xe);
bool xe_pm_runtime_get_if_in_use(struct xe_device *xe);
void xe_pm_runtime_get_noresume(struct xe_device *xe);
bool xe_pm_runtime_resume_and_get(struct xe_device *xe);
diff --git a/drivers/gpu/drm/xe/xe_preempt_fence.c b/drivers/gpu/drm/xe/xe_preempt_fence.c
index 7d50c6e89d8e..e8b8ae5c6485 100644
--- a/drivers/gpu/drm/xe/xe_preempt_fence.c
+++ b/drivers/gpu/drm/xe/xe_preempt_fence.c
@@ -23,11 +23,19 @@ static void preempt_fence_work_func(struct work_struct *w)
q->ops->suspend_wait(q);
dma_fence_signal(&pfence->base);
- dma_fence_end_signalling(cookie);
-
+ /*
+ * Opt for keep everything in the fence critical section. This looks really strange since we
+ * have just signalled the fence, however the preempt fences are all signalled via single
+ * global ordered-wq, therefore anything that happens in this callback can easily block
+ * progress on the entire wq, which itself may prevent other published preempt fences from
+ * ever signalling. Therefore try to keep everything here in the callback in the fence
+ * critical section. For example if something below grabs a scary lock like vm->lock,
+ * lockdep should complain since we also hold that lock whilst waiting on preempt fences to
+ * complete.
+ */
xe_vm_queue_rebind_worker(q->vm);
-
xe_exec_queue_put(q);
+ dma_fence_end_signalling(cookie);
}
static const char *
@@ -121,7 +129,7 @@ xe_preempt_fence_arm(struct xe_preempt_fence *pfence, struct xe_exec_queue *q,
list_del_init(&pfence->link);
pfence->q = xe_exec_queue_get(q);
dma_fence_init(&pfence->base, &preempt_fence_ops,
- &q->compute.lock, context, seqno);
+ &q->lr.lock, context, seqno);
return &pfence->base;
}
diff --git a/drivers/gpu/drm/xe/xe_pt.c b/drivers/gpu/drm/xe/xe_pt.c
index 5b7930f46cf3..ade9e7a3a0ad 100644
--- a/drivers/gpu/drm/xe/xe_pt.c
+++ b/drivers/gpu/drm/xe/xe_pt.c
@@ -619,9 +619,40 @@ xe_pt_stage_bind(struct xe_tile *tile, struct xe_vma *vma,
struct xe_pt *pt = xe_vma_vm(vma)->pt_root[tile->id];
int ret;
- if ((vma->gpuva.flags & XE_VMA_ATOMIC_PTE_BIT) &&
- (is_devmem || !IS_DGFX(xe)))
- xe_walk.default_pte |= XE_USM_PPGTT_PTE_AE;
+ /**
+ * Default atomic expectations for different allocation scenarios are as follows:
+ *
+ * 1. Traditional API: When the VM is not in LR mode:
+ * - Device atomics are expected to function with all allocations.
+ *
+ * 2. Compute/SVM API: When the VM is in LR mode:
+ * - Device atomics are the default behavior when the bo is placed in a single region.
+ * - In all other cases device atomics will be disabled with AE=0 until an application
+ * request differently using a ioctl like madvise.
+ */
+ if (vma->gpuva.flags & XE_VMA_ATOMIC_PTE_BIT) {
+ if (xe_vm_in_lr_mode(xe_vma_vm(vma))) {
+ if (bo && xe_bo_has_single_placement(bo))
+ xe_walk.default_pte |= XE_USM_PPGTT_PTE_AE;
+ /**
+ * If a SMEM+LMEM allocation is backed by SMEM, a device
+ * atomics will cause a gpu page fault and which then
+ * gets migrated to LMEM, bind such allocations with
+ * device atomics enabled.
+ */
+ else if (is_devmem && !xe_bo_has_single_placement(bo))
+ xe_walk.default_pte |= XE_USM_PPGTT_PTE_AE;
+ } else {
+ xe_walk.default_pte |= XE_USM_PPGTT_PTE_AE;
+ }
+
+ /**
+ * Unset AE if the platform(PVC) doesn't support it on an
+ * allocation
+ */
+ if (!xe->info.has_device_atomics_on_smem && !is_devmem)
+ xe_walk.default_pte &= ~XE_USM_PPGTT_PTE_AE;
+ }
if (is_devmem) {
xe_walk.default_pte |= XE_PPGTT_PTE_DM;
@@ -732,7 +763,7 @@ static int xe_pt_zap_ptes_entry(struct xe_ptw *parent, pgoff_t offset,
pgoff_t end_offset;
XE_WARN_ON(!*child);
- XE_WARN_ON(!level && xe_child->is_compact);
+ XE_WARN_ON(!level);
/*
* Note that we're called from an entry callback, and we're dealing
@@ -781,8 +812,9 @@ bool xe_pt_zap_ptes(struct xe_tile *tile, struct xe_vma *vma)
.tile = tile,
};
struct xe_pt *pt = xe_vma_vm(vma)->pt_root[tile->id];
+ u8 pt_mask = (vma->tile_present & ~vma->tile_invalidated);
- if (!(vma->tile_present & BIT(tile->id)))
+ if (!(pt_mask & BIT(tile->id)))
return false;
(void)xe_pt_walk_shared(&pt->base, pt->level, xe_vma_start(vma),
@@ -1075,10 +1107,12 @@ static const struct xe_migrate_pt_update_ops userptr_bind_ops = {
struct invalidation_fence {
struct xe_gt_tlb_invalidation_fence base;
struct xe_gt *gt;
- struct xe_vma *vma;
struct dma_fence *fence;
struct dma_fence_cb cb;
struct work_struct work;
+ u64 start;
+ u64 end;
+ u32 asid;
};
static const char *
@@ -1103,8 +1137,9 @@ static void invalidation_fence_cb(struct dma_fence *fence,
{
struct invalidation_fence *ifence =
container_of(cb, struct invalidation_fence, cb);
+ struct xe_device *xe = gt_to_xe(ifence->gt);
- trace_xe_gt_tlb_invalidation_fence_cb(&ifence->base);
+ trace_xe_gt_tlb_invalidation_fence_cb(xe, &ifence->base);
if (!ifence->fence->error) {
queue_work(system_wq, &ifence->work);
} else {
@@ -1119,19 +1154,21 @@ static void invalidation_fence_work_func(struct work_struct *w)
{
struct invalidation_fence *ifence =
container_of(w, struct invalidation_fence, work);
+ struct xe_device *xe = gt_to_xe(ifence->gt);
- trace_xe_gt_tlb_invalidation_fence_work_func(&ifence->base);
- xe_gt_tlb_invalidation_vma(ifence->gt, &ifence->base, ifence->vma);
+ trace_xe_gt_tlb_invalidation_fence_work_func(xe, &ifence->base);
+ xe_gt_tlb_invalidation_range(ifence->gt, &ifence->base, ifence->start,
+ ifence->end, ifence->asid);
}
static int invalidation_fence_init(struct xe_gt *gt,
struct invalidation_fence *ifence,
struct dma_fence *fence,
- struct xe_vma *vma)
+ u64 start, u64 end, u32 asid)
{
int ret;
- trace_xe_gt_tlb_invalidation_fence_create(&ifence->base);
+ trace_xe_gt_tlb_invalidation_fence_create(gt_to_xe(gt), &ifence->base);
spin_lock_irq(&gt->tlb_invalidation.lock);
dma_fence_init(&ifence->base.base, &invalidation_fence_ops,
@@ -1144,7 +1181,9 @@ static int invalidation_fence_init(struct xe_gt *gt,
dma_fence_get(&ifence->base.base); /* Ref for caller */
ifence->fence = fence;
ifence->gt = gt;
- ifence->vma = vma;
+ ifence->start = start;
+ ifence->end = end;
+ ifence->asid = asid;
INIT_WORK(&ifence->work, invalidation_fence_work_func);
ret = dma_fence_add_callback(fence, &ifence->cb, invalidation_fence_cb);
@@ -1295,8 +1334,11 @@ __xe_pt_bind_vma(struct xe_tile *tile, struct xe_vma *vma, struct xe_exec_queue
/* TLB invalidation must be done before signaling rebind */
if (ifence) {
- int err = invalidation_fence_init(tile->primary_gt, ifence, fence,
- vma);
+ int err = invalidation_fence_init(tile->primary_gt,
+ ifence, fence,
+ xe_vma_start(vma),
+ xe_vma_end(vma),
+ xe_vma_vm(vma)->usm.asid);
if (err) {
dma_fence_put(fence);
kfree(ifence);
@@ -1405,7 +1447,7 @@ static int xe_pt_stage_unbind_entry(struct xe_ptw *parent, pgoff_t offset,
struct xe_pt *xe_child = container_of(*child, typeof(*xe_child), base);
XE_WARN_ON(!*child);
- XE_WARN_ON(!level && xe_child->is_compact);
+ XE_WARN_ON(!level);
xe_pt_check_kill(addr, next, level - 1, xe_child, action, walk);
@@ -1641,7 +1683,10 @@ __xe_pt_unbind_vma(struct xe_tile *tile, struct xe_vma *vma, struct xe_exec_queu
dma_fence_wait(fence, false);
/* TLB invalidation must be done before signaling unbind */
- err = invalidation_fence_init(tile->primary_gt, ifence, fence, vma);
+ err = invalidation_fence_init(tile->primary_gt, ifence, fence,
+ xe_vma_start(vma),
+ xe_vma_end(vma),
+ xe_vma_vm(vma)->usm.asid);
if (err) {
dma_fence_put(fence);
kfree(ifence);
diff --git a/drivers/gpu/drm/xe/xe_query.c b/drivers/gpu/drm/xe/xe_query.c
index df407d73e5f5..4e01df6b1b7a 100644
--- a/drivers/gpu/drm/xe/xe_query.c
+++ b/drivers/gpu/drm/xe/xe_query.c
@@ -16,6 +16,7 @@
#include "xe_bo.h"
#include "xe_device.h"
#include "xe_exec_queue.h"
+#include "xe_force_wake.h"
#include "xe_ggtt.h"
#include "xe_gt.h"
#include "xe_guc_hwconfig.h"
@@ -454,9 +455,10 @@ static int query_hwconfig(struct xe_device *xe,
static size_t calc_topo_query_size(struct xe_device *xe)
{
return xe->info.gt_count *
- (3 * sizeof(struct drm_xe_query_topology_mask) +
+ (4 * sizeof(struct drm_xe_query_topology_mask) +
sizeof_field(struct xe_gt, fuse_topo.g_dss_mask) +
sizeof_field(struct xe_gt, fuse_topo.c_dss_mask) +
+ sizeof_field(struct xe_gt, fuse_topo.l3_bank_mask) +
sizeof_field(struct xe_gt, fuse_topo.eu_mask_per_dss));
}
@@ -510,6 +512,12 @@ static int query_gt_topology(struct xe_device *xe,
if (err)
return err;
+ topo.type = DRM_XE_TOPO_L3_BANK;
+ err = copy_mask(&query_ptr, &topo, gt->fuse_topo.l3_bank_mask,
+ sizeof(gt->fuse_topo.l3_bank_mask));
+ if (err)
+ return err;
+
topo.type = DRM_XE_TOPO_EU_PER_DSS;
err = copy_mask(&query_ptr, &topo,
gt->fuse_topo.eu_mask_per_dss,
@@ -594,6 +602,82 @@ query_uc_fw_version(struct xe_device *xe, struct drm_xe_device_query *query)
return 0;
}
+static size_t calc_oa_unit_query_size(struct xe_device *xe)
+{
+ size_t size = sizeof(struct drm_xe_query_oa_units);
+ struct xe_gt *gt;
+ int i, id;
+
+ for_each_gt(gt, xe, id) {
+ for (i = 0; i < gt->oa.num_oa_units; i++) {
+ size += sizeof(struct drm_xe_oa_unit);
+ size += gt->oa.oa_unit[i].num_engines *
+ sizeof(struct drm_xe_engine_class_instance);
+ }
+ }
+
+ return size;
+}
+
+static int query_oa_units(struct xe_device *xe,
+ struct drm_xe_device_query *query)
+{
+ void __user *query_ptr = u64_to_user_ptr(query->data);
+ size_t size = calc_oa_unit_query_size(xe);
+ struct drm_xe_query_oa_units *qoa;
+ enum xe_hw_engine_id hwe_id;
+ struct drm_xe_oa_unit *du;
+ struct xe_hw_engine *hwe;
+ struct xe_oa_unit *u;
+ int gt_id, i, j, ret;
+ struct xe_gt *gt;
+ u8 *pdu;
+
+ if (query->size == 0) {
+ query->size = size;
+ return 0;
+ } else if (XE_IOCTL_DBG(xe, query->size != size)) {
+ return -EINVAL;
+ }
+
+ qoa = kzalloc(size, GFP_KERNEL);
+ if (!qoa)
+ return -ENOMEM;
+
+ pdu = (u8 *)&qoa->oa_units[0];
+ for_each_gt(gt, xe, gt_id) {
+ for (i = 0; i < gt->oa.num_oa_units; i++) {
+ u = &gt->oa.oa_unit[i];
+ du = (struct drm_xe_oa_unit *)pdu;
+
+ du->oa_unit_id = u->oa_unit_id;
+ du->oa_unit_type = u->type;
+ du->oa_timestamp_freq = xe_oa_timestamp_frequency(gt);
+ du->capabilities = DRM_XE_OA_CAPS_BASE;
+
+ j = 0;
+ for_each_hw_engine(hwe, gt, hwe_id) {
+ if (!xe_hw_engine_is_reserved(hwe) &&
+ xe_oa_unit_id(hwe) == u->oa_unit_id) {
+ du->eci[j].engine_class =
+ xe_to_user_engine_class[hwe->class];
+ du->eci[j].engine_instance = hwe->logical_instance;
+ du->eci[j].gt_id = gt->info.id;
+ j++;
+ }
+ }
+ du->num_engines = j;
+ pdu += sizeof(*du) + j * sizeof(du->eci[0]);
+ qoa->num_oa_units++;
+ }
+ }
+
+ ret = copy_to_user(query_ptr, qoa, size);
+ kfree(qoa);
+
+ return ret ? -EFAULT : 0;
+}
+
static int (* const xe_query_funcs[])(struct xe_device *xe,
struct drm_xe_device_query *query) = {
query_engines,
@@ -604,6 +688,7 @@ static int (* const xe_query_funcs[])(struct xe_device *xe,
query_gt_topology,
query_engine_cycles,
query_uc_fw_version,
+ query_oa_units,
};
int xe_query_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
diff --git a/drivers/gpu/drm/xe/xe_reg_sr.h b/drivers/gpu/drm/xe/xe_reg_sr.h
index e3197c33afe2..51fbba423e27 100644
--- a/drivers/gpu/drm/xe/xe_reg_sr.h
+++ b/drivers/gpu/drm/xe/xe_reg_sr.h
@@ -6,8 +6,6 @@
#ifndef _XE_REG_SR_
#define _XE_REG_SR_
-#include "xe_reg_sr_types.h"
-
/*
* Reg save/restore bookkeeping
*/
@@ -15,6 +13,8 @@
struct xe_device;
struct xe_gt;
struct xe_hw_engine;
+struct xe_reg_sr;
+struct xe_reg_sr_entry;
struct drm_printer;
int xe_reg_sr_init(struct xe_reg_sr *sr, const char *name, struct xe_device *xe);
diff --git a/drivers/gpu/drm/xe/xe_reg_whitelist.c b/drivers/gpu/drm/xe/xe_reg_whitelist.c
index 3fa2ece7d228..3996934974fa 100644
--- a/drivers/gpu/drm/xe/xe_reg_whitelist.c
+++ b/drivers/gpu/drm/xe/xe_reg_whitelist.c
@@ -7,6 +7,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_gt_types.h"
#include "xe_platform_types.h"
@@ -63,7 +64,28 @@ 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))
+ },
{}
};
diff --git a/drivers/gpu/drm/xe/xe_res_cursor.h b/drivers/gpu/drm/xe/xe_res_cursor.h
index 0a306963aa8e..655af89b31a9 100644
--- a/drivers/gpu/drm/xe/xe_res_cursor.h
+++ b/drivers/gpu/drm/xe/xe_res_cursor.h
@@ -157,8 +157,6 @@ static inline void xe_res_first_sg(const struct sg_table *sg,
struct xe_res_cursor *cur)
{
XE_WARN_ON(!sg);
- XE_WARN_ON(!IS_ALIGNED(start, PAGE_SIZE) ||
- !IS_ALIGNED(size, PAGE_SIZE));
cur->node = NULL;
cur->start = start;
cur->remaining = size;
diff --git a/drivers/gpu/drm/xe/xe_ring_ops.c b/drivers/gpu/drm/xe/xe_ring_ops.c
index d42b3f33bd7a..0be4f489d3e1 100644
--- a/drivers/gpu/drm/xe/xe_ring_ops.c
+++ b/drivers/gpu/drm/xe/xe_ring_ops.c
@@ -7,9 +7,9 @@
#include <generated/xe_wa_oob.h>
+#include "instructions/xe_gpu_commands.h"
#include "instructions/xe_mi_commands.h"
#include "regs/xe_engine_regs.h"
-#include "regs/xe_gpu_commands.h"
#include "regs/xe_gt_regs.h"
#include "regs/xe_lrc_layout.h"
#include "xe_exec_queue_types.h"
@@ -80,6 +80,16 @@ static int emit_store_imm_ggtt(u32 addr, u32 value, u32 *dw, int i)
return i;
}
+static int emit_flush_dw(u32 *dw, int i)
+{
+ dw[i++] = MI_FLUSH_DW | MI_FLUSH_IMM_DW;
+ dw[i++] = 0;
+ dw[i++] = 0;
+ dw[i++] = 0;
+
+ return i;
+}
+
static int emit_flush_imm_ggtt(u32 addr, u32 value, bool invalidate_tlb,
u32 *dw, int i)
{
@@ -214,6 +224,19 @@ static u32 get_ppgtt_flag(struct xe_sched_job *job)
return job->q->vm ? BIT(8) : 0;
}
+static int emit_copy_timestamp(struct xe_lrc *lrc, u32 *dw, int i)
+{
+ dw[i++] = MI_COPY_MEM_MEM | MI_COPY_MEM_MEM_SRC_GGTT |
+ MI_COPY_MEM_MEM_DST_GGTT;
+ dw[i++] = xe_lrc_ctx_job_timestamp_ggtt_addr(lrc);
+ dw[i++] = 0;
+ dw[i++] = xe_lrc_ctx_timestamp_ggtt_addr(lrc);
+ dw[i++] = 0;
+ dw[i++] = MI_NOOP;
+
+ return i;
+}
+
/* for engines that don't require any special HW handling (no EUs, no aux inval, etc) */
static void __emit_job_gen12_simple(struct xe_sched_job *job, struct xe_lrc *lrc,
u64 batch_addr, u32 seqno)
@@ -222,6 +245,8 @@ static void __emit_job_gen12_simple(struct xe_sched_job *job, struct xe_lrc *lrc
u32 ppgtt_flag = get_ppgtt_flag(job);
struct xe_gt *gt = job->q->gt;
+ i = emit_copy_timestamp(lrc, dw, i);
+
if (job->ring_ops_flush_tlb) {
dw[i++] = preparser_disable(true);
i = emit_flush_imm_ggtt(xe_lrc_start_seqno_ggtt_addr(lrc),
@@ -234,10 +259,12 @@ static void __emit_job_gen12_simple(struct xe_sched_job *job, struct xe_lrc *lrc
i = emit_bb_start(batch_addr, ppgtt_flag, dw, i);
- if (job->user_fence.used)
+ if (job->user_fence.used) {
+ i = emit_flush_dw(dw, i);
i = emit_store_imm_ppgtt_posted(job->user_fence.addr,
job->user_fence.value,
dw, i);
+ }
i = emit_flush_imm_ggtt(xe_lrc_seqno_ggtt_addr(lrc), seqno, false, dw, i);
@@ -271,6 +298,8 @@ static void __emit_job_gen12_video(struct xe_sched_job *job, struct xe_lrc *lrc,
struct xe_device *xe = gt_to_xe(gt);
bool decode = job->q->class == XE_ENGINE_CLASS_VIDEO_DECODE;
+ i = emit_copy_timestamp(lrc, dw, i);
+
dw[i++] = preparser_disable(true);
/* hsdes: 1809175790 */
@@ -293,10 +322,12 @@ static void __emit_job_gen12_video(struct xe_sched_job *job, struct xe_lrc *lrc,
i = emit_bb_start(batch_addr, ppgtt_flag, dw, i);
- if (job->user_fence.used)
+ if (job->user_fence.used) {
+ i = emit_flush_dw(dw, i);
i = emit_store_imm_ppgtt_posted(job->user_fence.addr,
job->user_fence.value,
dw, i);
+ }
i = emit_flush_imm_ggtt(xe_lrc_seqno_ggtt_addr(lrc), seqno, false, dw, i);
@@ -318,6 +349,8 @@ static void __emit_job_gen12_render_compute(struct xe_sched_job *job,
bool lacks_render = !(gt->info.engine_mask & XE_HW_ENGINE_RCS_MASK);
u32 mask_flags = 0;
+ i = emit_copy_timestamp(lrc, dw, i);
+
dw[i++] = preparser_disable(true);
if (lacks_render)
mask_flags = PIPE_CONTROL_3D_ARCH_FLAGS;
@@ -361,12 +394,14 @@ static void emit_migration_job_gen12(struct xe_sched_job *job,
{
u32 dw[MAX_JOB_SIZE_DW], i = 0;
+ i = emit_copy_timestamp(lrc, dw, i);
+
i = emit_store_imm_ggtt(xe_lrc_start_seqno_ggtt_addr(lrc),
seqno, dw, i);
dw[i++] = MI_ARB_ON_OFF | MI_ARB_DISABLE; /* Enabled again below */
- i = emit_bb_start(job->batch_addr[0], BIT(8), dw, i);
+ i = emit_bb_start(job->ptrs[0].batch_addr, BIT(8), dw, i);
if (!IS_SRIOV_VF(gt_to_xe(job->q->gt))) {
/* XXX: Do we need this? Leaving for now. */
@@ -375,7 +410,7 @@ static void emit_migration_job_gen12(struct xe_sched_job *job,
dw[i++] = preparser_disable(false);
}
- i = emit_bb_start(job->batch_addr[1], BIT(8), dw, i);
+ i = emit_bb_start(job->ptrs[1].batch_addr, BIT(8), dw, i);
dw[i++] = MI_FLUSH_DW | MI_INVALIDATE_TLB | job->migrate_flush_flags |
MI_FLUSH_DW_OP_STOREDW | MI_FLUSH_IMM_DW;
@@ -396,9 +431,9 @@ static void emit_job_gen12_gsc(struct xe_sched_job *job)
xe_gt_assert(gt, job->q->width <= 1); /* no parallel submission for GSCCS */
- __emit_job_gen12_simple(job, job->q->lrc,
- job->batch_addr[0],
- xe_sched_job_seqno(job));
+ __emit_job_gen12_simple(job, job->q->lrc[0],
+ job->ptrs[0].batch_addr,
+ xe_sched_job_lrc_seqno(job));
}
static void emit_job_gen12_copy(struct xe_sched_job *job)
@@ -406,15 +441,15 @@ static void emit_job_gen12_copy(struct xe_sched_job *job)
int i;
if (xe_sched_job_is_migration(job->q)) {
- emit_migration_job_gen12(job, job->q->lrc,
- xe_sched_job_seqno(job));
+ emit_migration_job_gen12(job, job->q->lrc[0],
+ xe_sched_job_lrc_seqno(job));
return;
}
for (i = 0; i < job->q->width; ++i)
- __emit_job_gen12_simple(job, job->q->lrc + i,
- job->batch_addr[i],
- xe_sched_job_seqno(job));
+ __emit_job_gen12_simple(job, job->q->lrc[i],
+ job->ptrs[i].batch_addr,
+ xe_sched_job_lrc_seqno(job));
}
static void emit_job_gen12_video(struct xe_sched_job *job)
@@ -423,9 +458,9 @@ static void emit_job_gen12_video(struct xe_sched_job *job)
/* FIXME: Not doing parallel handshake for now */
for (i = 0; i < job->q->width; ++i)
- __emit_job_gen12_video(job, job->q->lrc + i,
- job->batch_addr[i],
- xe_sched_job_seqno(job));
+ __emit_job_gen12_video(job, job->q->lrc[i],
+ job->ptrs[i].batch_addr,
+ xe_sched_job_lrc_seqno(job));
}
static void emit_job_gen12_render_compute(struct xe_sched_job *job)
@@ -433,9 +468,9 @@ static void emit_job_gen12_render_compute(struct xe_sched_job *job)
int i;
for (i = 0; i < job->q->width; ++i)
- __emit_job_gen12_render_compute(job, job->q->lrc + i,
- job->batch_addr[i],
- xe_sched_job_seqno(job));
+ __emit_job_gen12_render_compute(job, job->q->lrc[i],
+ job->ptrs[i].batch_addr,
+ xe_sched_job_lrc_seqno(job));
}
static const struct xe_ring_ops ring_ops_gen12_gsc = {
diff --git a/drivers/gpu/drm/xe/xe_rtp.c b/drivers/gpu/drm/xe/xe_rtp.c
index fb44cc7521d8..02e28274282f 100644
--- a/drivers/gpu/drm/xe/xe_rtp.c
+++ b/drivers/gpu/drm/xe/xe_rtp.c
@@ -13,6 +13,7 @@
#include "xe_gt_topology.h"
#include "xe_macros.h"
#include "xe_reg_sr.h"
+#include "xe_sriov.h"
/**
* DOC: Register Table Processing
@@ -35,11 +36,18 @@ static bool rule_matches(const struct xe_device *xe,
unsigned int n_rules)
{
const struct xe_rtp_rule *r;
- unsigned int i;
+ unsigned int i, rcount = 0;
bool match;
for (r = rules, i = 0; i < n_rules; r = &rules[++i]) {
switch (r->match_type) {
+ case XE_RTP_MATCH_OR:
+ /*
+ * This is only reached if a complete set of
+ * rules passed or none were evaluated. For both cases,
+ * shortcut the other rules and return the proper value.
+ */
+ goto done;
case XE_RTP_MATCH_PLATFORM:
match = xe->info.platform == r->platform;
break;
@@ -56,6 +64,9 @@ static bool rule_matches(const struct xe_device *xe,
xe->info.graphics_verx100 <= r->ver_end &&
(!has_samedia(xe) || !xe_gt_is_media_type(gt));
break;
+ case XE_RTP_MATCH_GRAPHICS_VERSION_ANY_GT:
+ match = xe->info.graphics_verx100 == r->ver_start;
+ break;
case XE_RTP_MATCH_GRAPHICS_STEP:
match = xe->info.step.graphics >= r->step_start &&
xe->info.step.graphics < r->step_end &&
@@ -75,6 +86,9 @@ static bool rule_matches(const struct xe_device *xe,
xe->info.step.media < r->step_end &&
(!has_samedia(xe) || xe_gt_is_media_type(gt));
break;
+ case XE_RTP_MATCH_MEDIA_VERSION_ANY_GT:
+ match = xe->info.media_verx100 == r->ver_start;
+ break;
case XE_RTP_MATCH_INTEGRATED:
match = !xe->info.is_dgfx;
break;
@@ -102,10 +116,27 @@ static bool rule_matches(const struct xe_device *xe,
match = false;
}
- if (!match)
- return false;
+ if (!match) {
+ /*
+ * Advance rules until we find XE_RTP_MATCH_OR to check
+ * if there's another set of conditions to check
+ */
+ while (++i < n_rules && rules[i].match_type != XE_RTP_MATCH_OR)
+ ;
+
+ if (i >= n_rules)
+ return false;
+
+ rcount = 0;
+ } else {
+ rcount++;
+ }
}
+done:
+ if (drm_WARN_ON(&xe->drm, !rcount))
+ return false;
+
return true;
}
@@ -227,6 +258,9 @@ void xe_rtp_process_to_sr(struct xe_rtp_process_ctx *ctx,
rtp_get_context(ctx, &hwe, &gt, &xe);
+ if (IS_SRIOV_VF(xe))
+ return;
+
for (entry = entries; entry && entry->name; entry++) {
bool match = false;
@@ -323,3 +357,4 @@ bool xe_rtp_match_first_gslice_fused_off(const struct xe_gt *gt,
return dss >= dss_per_gslice;
}
+
diff --git a/drivers/gpu/drm/xe/xe_rtp.h b/drivers/gpu/drm/xe/xe_rtp.h
index c56fedd126e6..ad446731192c 100644
--- a/drivers/gpu/drm/xe/xe_rtp.h
+++ b/drivers/gpu/drm/xe/xe_rtp.h
@@ -140,9 +140,23 @@ struct xe_reg_sr;
.ver_start = ver_start__, .ver_end = ver_end__, }
/**
- * XE_RTP_RULE_MEDIA_VERSION - Create rule matching media version
+ * XE_RTP_RULE_GRAPHICS_VERSION_ANY_GT - Create rule matching graphics version on any GT
* @ver__: Graphics IP version to match
*
+ * Like XE_RTP_RULE_GRAPHICS_VERSION, but it matches even if the current GT
+ * being checked is not of the graphics type. It allows to add RTP entries to
+ * another GT when the device contains a Graphics IP with that version.
+ *
+ * Refer to XE_RTP_RULES() for expected usage.
+ */
+#define XE_RTP_RULE_GRAPHICS_VERSION_ANY_GT(ver__) \
+ { .match_type = XE_RTP_MATCH_GRAPHICS_VERSION_ANY_GT, \
+ .ver_start = ver__, }
+
+/**
+ * XE_RTP_RULE_MEDIA_VERSION - Create rule matching media version
+ * @ver__: Media IP version to match
+ *
* Refer to XE_RTP_RULES() for expected usage.
*/
#define XE_RTP_RULE_MEDIA_VERSION(ver__) \
@@ -164,6 +178,20 @@ struct xe_reg_sr;
.ver_start = ver_start__, .ver_end = ver_end__, }
/**
+ * XE_RTP_RULE_MEDIA_VERSION_ANY_GT - Create rule matching media version on any GT
+ * @ver__: Media IP version to match
+ *
+ * Like XE_RTP_RULE_MEDIA_VERSION, but it matches even if the current GT being
+ * checked is not of the media type. It allows to add RTP entries to another
+ * GT when the device contains a Media IP with that version.
+ *
+ * Refer to XE_RTP_RULES() for expected usage.
+ */
+#define XE_RTP_RULE_MEDIA_VERSION_ANY_GT(ver__) \
+ { .match_type = XE_RTP_MATCH_MEDIA_VERSION_ANY_GT, \
+ .ver_start = ver__, }
+
+/**
* XE_RTP_RULE_IS_INTEGRATED - Create a rule matching integrated graphics devices
*
* Refer to XE_RTP_RULES() for expected usage.
@@ -180,6 +208,27 @@ struct xe_reg_sr;
{ .match_type = XE_RTP_MATCH_DISCRETE }
/**
+ * XE_RTP_RULE_OR - Create an OR condition for rtp rules
+ *
+ * RTP rules are AND'ed when evaluated and all of them need to match.
+ * XE_RTP_RULE_OR allows to create set of rules where any of them matching is
+ * sufficient for the action to trigger. Example:
+ *
+ * .. code-block:: c
+ *
+ * const struct xe_rtp_entry_sr entries[] = {
+ * ...
+ * { XE_RTP_NAME("test-entry"),
+ * XE_RTP_RULES(PLATFORM(DG2), OR, PLATFORM(TIGERLAKE)),
+ * ...
+ * },
+ * ...
+ * };
+ */
+#define XE_RTP_RULE_OR \
+ { .match_type = XE_RTP_MATCH_OR }
+
+/**
* XE_RTP_ACTION_WR - Helper to write a value to the register, overriding all
* the bits
* @reg_: Register
@@ -325,7 +374,7 @@ struct xe_reg_sr;
* XE_RTP_RULES - Helper to set multiple rules to a struct xe_rtp_entry_sr entry
* @...: Rules
*
- * At least one rule is needed and up to 4 are supported. Multiple rules are
+ * At least one rule is needed and up to 6 are supported. Multiple rules are
* AND'ed together, i.e. all the rules must evaluate to true for the entry to
* be processed. See XE_RTP_MATCH_* for the possible match rules. Example:
*
@@ -341,7 +390,7 @@ struct xe_reg_sr;
* };
*/
#define XE_RTP_RULES(...) \
- .n_rules = _XE_COUNT_ARGS(__VA_ARGS__), \
+ .n_rules = COUNT_ARGS(__VA_ARGS__), \
.rules = (const struct xe_rtp_rule[]) { \
XE_RTP_PASTE_FOREACH(RULE_, COMMA, (__VA_ARGS__)) \
}
@@ -350,7 +399,7 @@ struct xe_reg_sr;
* XE_RTP_ACTIONS - Helper to set multiple actions to a struct xe_rtp_entry_sr
* @...: Actions to be taken
*
- * At least one action is needed and up to 4 are supported. See XE_RTP_ACTION_*
+ * At least one action is needed and up to 6 are supported. See XE_RTP_ACTION_*
* for the possible actions. Example:
*
* .. code-block:: c
@@ -366,7 +415,7 @@ struct xe_reg_sr;
* };
*/
#define XE_RTP_ACTIONS(...) \
- .n_actions = _XE_COUNT_ARGS(__VA_ARGS__), \
+ .n_actions = COUNT_ARGS(__VA_ARGS__), \
.actions = (const struct xe_rtp_action[]) { \
XE_RTP_PASTE_FOREACH(ACTION_, COMMA, (__VA_ARGS__)) \
}
diff --git a/drivers/gpu/drm/xe/xe_rtp_helpers.h b/drivers/gpu/drm/xe/xe_rtp_helpers.h
index 181b6290fac3..c59e40fd7fff 100644
--- a/drivers/gpu/drm/xe/xe_rtp_helpers.h
+++ b/drivers/gpu/drm/xe/xe_rtp_helpers.h
@@ -10,22 +10,16 @@
#error "This header is supposed to be included by xe_rtp.h only"
#endif
+#include "xe_args.h"
+
/*
* Helper macros - not to be used outside this header.
*/
#define _XE_ESC(...) __VA_ARGS__
-#define _XE_COUNT_ARGS(...) _XE_ESC(__XE_COUNT_ARGS(__VA_ARGS__, 5, 4, 3, 2, 1,))
-#define __XE_COUNT_ARGS(_, _5, _4, _3, _2, X_, ...) X_
-
-#define _XE_FIRST(...) _XE_ESC(__XE_FIRST(__VA_ARGS__,))
-#define __XE_FIRST(x_, ...) x_
-#define _XE_TUPLE_TAIL(...) _XE_ESC(__XE_TUPLE_TAIL(__VA_ARGS__))
-#define __XE_TUPLE_TAIL(x_, ...) (__VA_ARGS__)
-#define _XE_DROP_FIRST(x_, ...) __VA_ARGS__
+#define _XE_TUPLE_TAIL(...) (DROP_FIRST_ARG(__VA_ARGS__))
-#define _XE_RTP_CONCAT(a, b) __XE_RTP_CONCAT(a, b)
-#define __XE_RTP_CONCAT(a, b) XE_RTP_ ## a ## b
+#define _XE_RTP_CONCAT(a, b) CONCATENATE(XE_RTP_, CONCATENATE(a, b))
#define __XE_RTP_PASTE_SEP_COMMA ,
#define __XE_RTP_PASTE_SEP_BITWISE_OR |
@@ -59,11 +53,13 @@
*
* XE_RTP_TEST_FOO BANANA XE_RTP_TEST_BAR
*/
-#define XE_RTP_PASTE_FOREACH(prefix_, sep_, args_) _XE_ESC(_XE_RTP_CONCAT(PASTE_, _XE_COUNT_ARGS args_)(prefix_, sep_, args_))
-#define XE_RTP_PASTE_1(prefix_, sep_, args_) _XE_RTP_CONCAT(prefix_, _XE_FIRST args_)
-#define XE_RTP_PASTE_2(prefix_, sep_, args_) _XE_RTP_CONCAT(prefix_, _XE_FIRST args_) __XE_RTP_PASTE_SEP_ ## sep_ XE_RTP_PASTE_1(prefix_, sep_, _XE_TUPLE_TAIL args_)
-#define XE_RTP_PASTE_3(prefix_, sep_, args_) _XE_RTP_CONCAT(prefix_, _XE_FIRST args_) __XE_RTP_PASTE_SEP_ ## sep_ XE_RTP_PASTE_2(prefix_, sep_, _XE_TUPLE_TAIL args_)
-#define XE_RTP_PASTE_4(prefix_, sep_, args_) _XE_RTP_CONCAT(prefix_, _XE_FIRST args_) __XE_RTP_PASTE_SEP_ ## sep_ XE_RTP_PASTE_3(prefix_, sep_, _XE_TUPLE_TAIL args_)
+#define XE_RTP_PASTE_FOREACH(prefix_, sep_, args_) _XE_RTP_CONCAT(PASTE_, COUNT_ARGS args_)(prefix_, sep_, args_)
+#define XE_RTP_PASTE_1(prefix_, sep_, args_) _XE_RTP_CONCAT(prefix_, FIRST_ARG args_)
+#define XE_RTP_PASTE_2(prefix_, sep_, args_) _XE_RTP_CONCAT(prefix_, FIRST_ARG args_) __XE_RTP_PASTE_SEP_ ## sep_ XE_RTP_PASTE_1(prefix_, sep_, _XE_TUPLE_TAIL args_)
+#define XE_RTP_PASTE_3(prefix_, sep_, args_) _XE_RTP_CONCAT(prefix_, FIRST_ARG args_) __XE_RTP_PASTE_SEP_ ## sep_ XE_RTP_PASTE_2(prefix_, sep_, _XE_TUPLE_TAIL args_)
+#define XE_RTP_PASTE_4(prefix_, sep_, args_) _XE_RTP_CONCAT(prefix_, FIRST_ARG args_) __XE_RTP_PASTE_SEP_ ## sep_ XE_RTP_PASTE_3(prefix_, sep_, _XE_TUPLE_TAIL args_)
+#define XE_RTP_PASTE_5(prefix_, sep_, args_) _XE_RTP_CONCAT(prefix_, FIRST_ARG args_) __XE_RTP_PASTE_SEP_ ## sep_ XE_RTP_PASTE_4(prefix_, sep_, _XE_TUPLE_TAIL args_)
+#define XE_RTP_PASTE_6(prefix_, sep_, args_) _XE_RTP_CONCAT(prefix_, FIRST_ARG args_) __XE_RTP_PASTE_SEP_ ## sep_ XE_RTP_PASTE_5(prefix_, sep_, _XE_TUPLE_TAIL args_)
/*
* XE_RTP_DROP_CAST - Drop cast to convert a compound statement to a initializer
@@ -76,6 +72,6 @@
*
* { .a = 10 }
*/
-#define XE_RTP_DROP_CAST(...) _XE_ESC(_XE_DROP_FIRST _XE_ESC __VA_ARGS__)
+#define XE_RTP_DROP_CAST(...) _XE_ESC(DROP_FIRST_ARG _XE_ESC __VA_ARGS__)
#endif
diff --git a/drivers/gpu/drm/xe/xe_rtp_types.h b/drivers/gpu/drm/xe/xe_rtp_types.h
index 637acc7626a4..1b76b947c706 100644
--- a/drivers/gpu/drm/xe/xe_rtp_types.h
+++ b/drivers/gpu/drm/xe/xe_rtp_types.h
@@ -42,15 +42,18 @@ enum {
XE_RTP_MATCH_SUBPLATFORM,
XE_RTP_MATCH_GRAPHICS_VERSION,
XE_RTP_MATCH_GRAPHICS_VERSION_RANGE,
+ XE_RTP_MATCH_GRAPHICS_VERSION_ANY_GT,
XE_RTP_MATCH_GRAPHICS_STEP,
XE_RTP_MATCH_MEDIA_VERSION,
XE_RTP_MATCH_MEDIA_VERSION_RANGE,
+ XE_RTP_MATCH_MEDIA_VERSION_ANY_GT,
XE_RTP_MATCH_MEDIA_STEP,
XE_RTP_MATCH_INTEGRATED,
XE_RTP_MATCH_DISCRETE,
XE_RTP_MATCH_ENGINE_CLASS,
XE_RTP_MATCH_NOT_ENGINE_CLASS,
XE_RTP_MATCH_FUNC,
+ XE_RTP_MATCH_OR,
};
/** struct xe_rtp_rule - match rule for processing entry */
diff --git a/drivers/gpu/drm/xe/xe_sched_job.c b/drivers/gpu/drm/xe/xe_sched_job.c
index cd8a2fba5438..44d534e362cd 100644
--- a/drivers/gpu/drm/xe/xe_sched_job.c
+++ b/drivers/gpu/drm/xe/xe_sched_job.c
@@ -6,7 +6,7 @@
#include "xe_sched_job.h"
#include <drm/xe_drm.h>
-#include <linux/dma-fence-array.h>
+#include <linux/dma-fence-chain.h>
#include <linux/slab.h>
#include "xe_device.h"
@@ -29,7 +29,7 @@ int __init xe_sched_job_module_init(void)
xe_sched_job_slab =
kmem_cache_create("xe_sched_job",
sizeof(struct xe_sched_job) +
- sizeof(u64), 0,
+ sizeof(struct xe_job_ptrs), 0,
SLAB_HWCACHE_ALIGN, NULL);
if (!xe_sched_job_slab)
return -ENOMEM;
@@ -37,7 +37,7 @@ int __init xe_sched_job_module_init(void)
xe_sched_job_parallel_slab =
kmem_cache_create("xe_sched_job_parallel",
sizeof(struct xe_sched_job) +
- sizeof(u64) *
+ sizeof(struct xe_job_ptrs) *
XE_HW_ENGINE_MAX_INSTANCE, 0,
SLAB_HWCACHE_ALIGN, NULL);
if (!xe_sched_job_parallel_slab) {
@@ -79,26 +79,33 @@ static struct xe_device *job_to_xe(struct xe_sched_job *job)
return gt_to_xe(job->q->gt);
}
+/* Free unused pre-allocated fences */
+static void xe_sched_job_free_fences(struct xe_sched_job *job)
+{
+ int i;
+
+ for (i = 0; i < job->q->width; ++i) {
+ struct xe_job_ptrs *ptrs = &job->ptrs[i];
+
+ if (ptrs->lrc_fence)
+ xe_lrc_free_seqno_fence(ptrs->lrc_fence);
+ if (ptrs->chain_fence)
+ dma_fence_chain_free(ptrs->chain_fence);
+ }
+}
+
struct xe_sched_job *xe_sched_job_create(struct xe_exec_queue *q,
u64 *batch_addr)
{
- struct xe_sched_job *job;
- struct dma_fence **fences;
bool is_migration = xe_sched_job_is_migration(q);
+ struct xe_sched_job *job;
int err;
- int i, j;
+ int i;
u32 width;
/* only a kernel context can submit a vm-less job */
XE_WARN_ON(!q->vm && !(q->flags & EXEC_QUEUE_FLAG_KERNEL));
- /* Migration and kernel engines have their own locking */
- if (!(q->flags & (EXEC_QUEUE_FLAG_KERNEL | EXEC_QUEUE_FLAG_VM))) {
- lockdep_assert_held(&q->vm->lock);
- if (!xe_vm_in_lr_mode(q->vm))
- xe_vm_assert_held(q->vm);
- }
-
job = job_alloc(xe_exec_queue_is_parallel(q) || is_migration);
if (!job)
return ERR_PTR(-ENOMEM);
@@ -111,44 +118,25 @@ struct xe_sched_job *xe_sched_job_create(struct xe_exec_queue *q,
if (err)
goto err_free;
- if (!xe_exec_queue_is_parallel(q)) {
- job->fence = xe_lrc_create_seqno_fence(q->lrc);
- if (IS_ERR(job->fence)) {
- err = PTR_ERR(job->fence);
- goto err_sched_job;
- }
- } else {
- struct dma_fence_array *cf;
+ for (i = 0; i < q->width; ++i) {
+ struct dma_fence *fence = xe_lrc_alloc_seqno_fence();
+ struct dma_fence_chain *chain;
- fences = kmalloc_array(q->width, sizeof(*fences), GFP_KERNEL);
- if (!fences) {
- err = -ENOMEM;
+ if (IS_ERR(fence)) {
+ err = PTR_ERR(fence);
goto err_sched_job;
}
+ job->ptrs[i].lrc_fence = fence;
- for (j = 0; j < q->width; ++j) {
- fences[j] = xe_lrc_create_seqno_fence(q->lrc + j);
- if (IS_ERR(fences[j])) {
- err = PTR_ERR(fences[j]);
- goto err_fences;
- }
- }
+ if (i + 1 == q->width)
+ continue;
- cf = dma_fence_array_create(q->width, fences,
- q->parallel.composite_fence_ctx,
- q->parallel.composite_fence_seqno++,
- false);
- if (!cf) {
- --q->parallel.composite_fence_seqno;
+ chain = dma_fence_chain_alloc();
+ if (!chain) {
err = -ENOMEM;
- goto err_fences;
+ goto err_sched_job;
}
-
- /* Sanity check */
- for (j = 0; j < q->width; ++j)
- xe_assert(job_to_xe(job), cf->base.seqno == fences[j]->seqno);
-
- job->fence = &cf->base;
+ job->ptrs[i].chain_fence = chain;
}
width = q->width;
@@ -156,23 +144,14 @@ struct xe_sched_job *xe_sched_job_create(struct xe_exec_queue *q,
width = 2;
for (i = 0; i < width; ++i)
- job->batch_addr[i] = batch_addr[i];
-
- /* All other jobs require a VM to be open which has a ref */
- if (unlikely(q->flags & EXEC_QUEUE_FLAG_KERNEL))
- xe_pm_runtime_get_noresume(job_to_xe(job));
- xe_device_assert_mem_access(job_to_xe(job));
+ job->ptrs[i].batch_addr = batch_addr[i];
+ xe_pm_runtime_get_noresume(job_to_xe(job));
trace_xe_sched_job_create(job);
return job;
-err_fences:
- for (j = j - 1; j >= 0; --j) {
- --q->lrc[j].fence_ctx.next_seqno;
- dma_fence_put(fences[j]);
- }
- kfree(fences);
err_sched_job:
+ xe_sched_job_free_fences(job);
drm_sched_job_cleanup(&job->drm);
err_free:
xe_exec_queue_put(q);
@@ -191,36 +170,42 @@ void xe_sched_job_destroy(struct kref *ref)
{
struct xe_sched_job *job =
container_of(ref, struct xe_sched_job, refcount);
+ struct xe_device *xe = job_to_xe(job);
- if (unlikely(job->q->flags & EXEC_QUEUE_FLAG_KERNEL))
- xe_pm_runtime_put(job_to_xe(job));
+ xe_sched_job_free_fences(job);
xe_exec_queue_put(job->q);
dma_fence_put(job->fence);
drm_sched_job_cleanup(&job->drm);
job_free(job);
+ xe_pm_runtime_put(xe);
}
-void xe_sched_job_set_error(struct xe_sched_job *job, int error)
+/* Set the error status under the fence to avoid racing with signaling */
+static bool xe_fence_set_error(struct dma_fence *fence, int error)
{
- if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &job->fence->flags))
- return;
+ unsigned long irq_flags;
+ bool signaled;
- dma_fence_set_error(job->fence, error);
+ spin_lock_irqsave(fence->lock, irq_flags);
+ signaled = test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags);
+ if (!signaled)
+ dma_fence_set_error(fence, error);
+ spin_unlock_irqrestore(fence->lock, irq_flags);
- if (dma_fence_is_array(job->fence)) {
- struct dma_fence_array *array =
- to_dma_fence_array(job->fence);
- struct dma_fence **child = array->fences;
- unsigned int nchild = array->num_fences;
+ return signaled;
+}
- do {
- struct dma_fence *current_fence = *child++;
+void xe_sched_job_set_error(struct xe_sched_job *job, int error)
+{
+ if (xe_fence_set_error(job->fence, error))
+ return;
+
+ if (dma_fence_is_chain(job->fence)) {
+ struct dma_fence *iter;
- if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT,
- &current_fence->flags))
- continue;
- dma_fence_set_error(current_fence, error);
- } while (--nchild);
+ dma_fence_chain_for_each(iter, job->fence)
+ xe_fence_set_error(dma_fence_chain_contained(iter),
+ error);
}
trace_xe_sched_job_set_error(job);
@@ -231,30 +216,42 @@ void xe_sched_job_set_error(struct xe_sched_job *job, int error)
bool xe_sched_job_started(struct xe_sched_job *job)
{
- struct xe_lrc *lrc = job->q->lrc;
+ struct xe_lrc *lrc = job->q->lrc[0];
- return !__dma_fence_is_later(xe_sched_job_seqno(job),
+ return !__dma_fence_is_later(xe_sched_job_lrc_seqno(job),
xe_lrc_start_seqno(lrc),
- job->fence->ops);
+ dma_fence_chain_contained(job->fence)->ops);
}
bool xe_sched_job_completed(struct xe_sched_job *job)
{
- struct xe_lrc *lrc = job->q->lrc;
+ struct xe_lrc *lrc = job->q->lrc[0];
/*
* Can safely check just LRC[0] seqno as that is last seqno written when
* parallel handshake is done.
*/
- return !__dma_fence_is_later(xe_sched_job_seqno(job), xe_lrc_seqno(lrc),
- job->fence->ops);
+ return !__dma_fence_is_later(xe_sched_job_lrc_seqno(job),
+ xe_lrc_seqno(lrc),
+ dma_fence_chain_contained(job->fence)->ops);
}
void xe_sched_job_arm(struct xe_sched_job *job)
{
struct xe_exec_queue *q = job->q;
+ struct dma_fence *fence, *prev;
struct xe_vm *vm = q->vm;
+ u64 seqno = 0;
+ int i;
+
+ /* Migration and kernel engines have their own locking */
+ if (IS_ENABLED(CONFIG_LOCKDEP) &&
+ !(q->flags & (EXEC_QUEUE_FLAG_KERNEL | EXEC_QUEUE_FLAG_VM))) {
+ lockdep_assert_held(&q->vm->lock);
+ if (!xe_vm_in_lr_mode(q->vm))
+ xe_vm_assert_held(q->vm);
+ }
if (vm && !xe_sched_job_is_migration(q) && !xe_vm_in_lr_mode(vm) &&
(vm->batch_invalidate_tlb || vm->tlb_flush_seqno != q->tlb_flush_seqno)) {
@@ -263,6 +260,27 @@ void xe_sched_job_arm(struct xe_sched_job *job)
job->ring_ops_flush_tlb = true;
}
+ /* Arm the pre-allocated fences */
+ for (i = 0; i < q->width; prev = fence, ++i) {
+ struct dma_fence_chain *chain;
+
+ fence = job->ptrs[i].lrc_fence;
+ xe_lrc_init_seqno_fence(q->lrc[i], fence);
+ job->ptrs[i].lrc_fence = NULL;
+ if (!i) {
+ job->lrc_seqno = fence->seqno;
+ continue;
+ } else {
+ xe_assert(gt_to_xe(q->gt), job->lrc_seqno == fence->seqno);
+ }
+
+ chain = job->ptrs[i - 1].chain_fence;
+ dma_fence_chain_init(chain, prev, fence, seqno++);
+ job->ptrs[i - 1].chain_fence = NULL;
+ fence = &chain->base;
+ }
+
+ job->fence = fence;
drm_sched_job_arm(&job->drm);
}
@@ -322,7 +340,8 @@ xe_sched_job_snapshot_capture(struct xe_sched_job *job)
snapshot->batch_addr_len = q->width;
for (i = 0; i < q->width; i++)
- snapshot->batch_addr[i] = xe_device_uncanonicalize_addr(xe, job->batch_addr[i]);
+ snapshot->batch_addr[i] =
+ xe_device_uncanonicalize_addr(xe, job->ptrs[i].batch_addr);
return snapshot;
}
@@ -344,3 +363,9 @@ xe_sched_job_snapshot_print(struct xe_sched_job_snapshot *snapshot,
for (i = 0; i < snapshot->batch_addr_len; i++)
drm_printf(p, "batch_addr[%u]: 0x%016llx\n", i, snapshot->batch_addr[i]);
}
+
+int xe_sched_job_add_deps(struct xe_sched_job *job, struct dma_resv *resv,
+ enum dma_resv_usage usage)
+{
+ return drm_sched_job_add_resv_dependencies(&job->drm, resv, usage);
+}
diff --git a/drivers/gpu/drm/xe/xe_sched_job.h b/drivers/gpu/drm/xe/xe_sched_job.h
index c75018f4660d..3dc72c5c1f13 100644
--- a/drivers/gpu/drm/xe/xe_sched_job.h
+++ b/drivers/gpu/drm/xe/xe_sched_job.h
@@ -70,7 +70,12 @@ to_xe_sched_job(struct drm_sched_job *drm)
static inline u32 xe_sched_job_seqno(struct xe_sched_job *job)
{
- return job->fence->seqno;
+ return job->fence ? job->fence->seqno : 0;
+}
+
+static inline u32 xe_sched_job_lrc_seqno(struct xe_sched_job *job)
+{
+ return job->lrc_seqno;
}
static inline void
@@ -85,4 +90,7 @@ struct xe_sched_job_snapshot *xe_sched_job_snapshot_capture(struct xe_sched_job
void xe_sched_job_snapshot_free(struct xe_sched_job_snapshot *snapshot);
void xe_sched_job_snapshot_print(struct xe_sched_job_snapshot *snapshot, struct drm_printer *p);
+int xe_sched_job_add_deps(struct xe_sched_job *job, struct dma_resv *resv,
+ enum dma_resv_usage usage);
+
#endif
diff --git a/drivers/gpu/drm/xe/xe_sched_job_types.h b/drivers/gpu/drm/xe/xe_sched_job_types.h
index 5e12724219fd..0d3f76fb05ce 100644
--- a/drivers/gpu/drm/xe/xe_sched_job_types.h
+++ b/drivers/gpu/drm/xe/xe_sched_job_types.h
@@ -11,6 +11,20 @@
#include <drm/gpu_scheduler.h>
struct xe_exec_queue;
+struct dma_fence;
+struct dma_fence_chain;
+
+/**
+ * struct xe_job_ptrs - Per hw engine instance data
+ */
+struct xe_job_ptrs {
+ /** @lrc_fence: Pre-allocated uinitialized lrc fence.*/
+ struct dma_fence *lrc_fence;
+ /** @chain_fence: Pre-allocated ninitialized fence chain node. */
+ struct dma_fence_chain *chain_fence;
+ /** @batch_addr: Batch buffer address. */
+ u64 batch_addr;
+};
/**
* struct xe_sched_job - XE schedule job (batch buffer tracking)
@@ -37,12 +51,14 @@ struct xe_sched_job {
/** @user_fence.value: write back value */
u64 value;
} user_fence;
+ /** @lrc_seqno: LRC seqno */
+ u32 lrc_seqno;
/** @migrate_flush_flags: Additional flush flags for migration jobs */
u32 migrate_flush_flags;
/** @ring_ops_flush_tlb: The ring ops need to flush TLB before payload. */
bool ring_ops_flush_tlb;
- /** @batch_addr: batch buffer address of job */
- u64 batch_addr[];
+ /** @ptrs: per instance pointers. */
+ struct xe_job_ptrs ptrs[];
};
struct xe_sched_job_snapshot {
diff --git a/drivers/gpu/drm/xe/xe_sriov.c b/drivers/gpu/drm/xe/xe_sriov.c
index 1c3fa84b6adb..a274a5fb1401 100644
--- a/drivers/gpu/drm/xe/xe_sriov.c
+++ b/drivers/gpu/drm/xe/xe_sriov.c
@@ -53,6 +53,7 @@ static bool test_is_vf(struct xe_device *xe)
*/
void xe_sriov_probe_early(struct xe_device *xe)
{
+ struct pci_dev *pdev = to_pci_dev(xe->drm.dev);
enum xe_sriov_mode mode = XE_SRIOV_MODE_NONE;
bool has_sriov = xe->info.has_sriov;
@@ -61,6 +62,16 @@ void xe_sriov_probe_early(struct xe_device *xe)
mode = XE_SRIOV_MODE_VF;
else if (xe_sriov_pf_readiness(xe))
mode = XE_SRIOV_MODE_PF;
+ } else if (pci_sriov_get_totalvfs(pdev)) {
+ /*
+ * Even if we have not enabled SR-IOV support using the
+ * platform specific has_sriov flag, the hardware may still
+ * report SR-IOV capability and the PCI layer may wrongly
+ * advertise driver support to enable VFs. Explicitly reset
+ * the number of supported VFs to zero to avoid confusion.
+ */
+ drm_info(&xe->drm, "Support for SR-IOV is not available\n");
+ pci_sriov_set_totalvfs(pdev, 0);
}
xe_assert(xe, !xe->sriov.__mode);
diff --git a/drivers/gpu/drm/xe/xe_sriov.h b/drivers/gpu/drm/xe/xe_sriov.h
index 486bb21c3256..688fbabf08f1 100644
--- a/drivers/gpu/drm/xe/xe_sriov.h
+++ b/drivers/gpu/drm/xe/xe_sriov.h
@@ -19,18 +19,18 @@ void xe_sriov_probe_early(struct xe_device *xe);
void xe_sriov_print_info(struct xe_device *xe, struct drm_printer *p);
int xe_sriov_init(struct xe_device *xe);
-static inline enum xe_sriov_mode xe_device_sriov_mode(struct xe_device *xe)
+static inline enum xe_sriov_mode xe_device_sriov_mode(const struct xe_device *xe)
{
xe_assert(xe, xe->sriov.__mode);
return xe->sriov.__mode;
}
-static inline bool xe_device_is_sriov_pf(struct xe_device *xe)
+static inline bool xe_device_is_sriov_pf(const struct xe_device *xe)
{
return xe_device_sriov_mode(xe) == XE_SRIOV_MODE_PF;
}
-static inline bool xe_device_is_sriov_vf(struct xe_device *xe)
+static inline bool xe_device_is_sriov_vf(const struct xe_device *xe)
{
return xe_device_sriov_mode(xe) == XE_SRIOV_MODE_VF;
}
diff --git a/drivers/gpu/drm/xe/xe_sync.c b/drivers/gpu/drm/xe/xe_sync.c
index 65f1f1628235..2883d9aca404 100644
--- a/drivers/gpu/drm/xe/xe_sync.c
+++ b/drivers/gpu/drm/xe/xe_sync.c
@@ -339,6 +339,21 @@ err_out:
}
/**
+ * __xe_sync_ufence_get() - Get user fence from user fence
+ * @ufence: input user fence
+ *
+ * Get a user fence reference from user fence
+ *
+ * Return: xe_user_fence pointer with reference
+ */
+struct xe_user_fence *__xe_sync_ufence_get(struct xe_user_fence *ufence)
+{
+ user_fence_get(ufence);
+
+ return ufence;
+}
+
+/**
* xe_sync_ufence_get() - Get user fence from sync
* @sync: input sync
*
diff --git a/drivers/gpu/drm/xe/xe_sync.h b/drivers/gpu/drm/xe/xe_sync.h
index 3e03396af2c6..006dbf780793 100644
--- a/drivers/gpu/drm/xe/xe_sync.h
+++ b/drivers/gpu/drm/xe/xe_sync.h
@@ -37,6 +37,7 @@ static inline bool xe_sync_is_ufence(struct xe_sync_entry *sync)
return !!sync->ufence;
}
+struct xe_user_fence *__xe_sync_ufence_get(struct xe_user_fence *ufence);
struct xe_user_fence *xe_sync_ufence_get(struct xe_sync_entry *sync);
void xe_sync_ufence_put(struct xe_user_fence *ufence);
int xe_sync_ufence_get_status(struct xe_user_fence *ufence);
diff --git a/drivers/gpu/drm/xe/xe_tile_sysfs.c b/drivers/gpu/drm/xe/xe_tile_sysfs.c
index 64661403afcd..b804234a6551 100644
--- a/drivers/gpu/drm/xe/xe_tile_sysfs.c
+++ b/drivers/gpu/drm/xe/xe_tile_sysfs.c
@@ -22,7 +22,7 @@ static const struct kobj_type xe_tile_sysfs_kobj_type = {
.sysfs_ops = &kobj_sysfs_ops,
};
-static void tile_sysfs_fini(struct drm_device *drm, void *arg)
+static void tile_sysfs_fini(void *arg)
{
struct xe_tile *tile = arg;
@@ -55,5 +55,5 @@ int xe_tile_sysfs_init(struct xe_tile *tile)
if (err)
return err;
- return drmm_add_action_or_reset(&xe->drm, tile_sysfs_fini, tile);
+ return devm_add_action_or_reset(xe->drm.dev, tile_sysfs_fini, tile);
}
diff --git a/drivers/gpu/drm/xe/xe_trace.h b/drivers/gpu/drm/xe/xe_trace.h
index 2d56cfc09e42..baba14fb1e32 100644
--- a/drivers/gpu/drm/xe/xe_trace.h
+++ b/drivers/gpu/drm/xe/xe_trace.h
@@ -12,8 +12,6 @@
#include <linux/tracepoint.h>
#include <linux/types.h>
-#include "xe_bo.h"
-#include "xe_bo_types.h"
#include "xe_exec_queue_types.h"
#include "xe_gpu_scheduler_types.h"
#include "xe_gt_tlb_invalidation_types.h"
@@ -22,110 +20,64 @@
#include "xe_sched_job.h"
#include "xe_vm.h"
+#define __dev_name_xe(xe) dev_name((xe)->drm.dev)
+#define __dev_name_gt(gt) __dev_name_xe(gt_to_xe((gt)))
+#define __dev_name_eq(q) __dev_name_gt((q)->gt)
+
DECLARE_EVENT_CLASS(xe_gt_tlb_invalidation_fence,
- TP_PROTO(struct xe_gt_tlb_invalidation_fence *fence),
- TP_ARGS(fence),
+ TP_PROTO(struct xe_device *xe, struct xe_gt_tlb_invalidation_fence *fence),
+ TP_ARGS(xe, fence),
TP_STRUCT__entry(
+ __string(dev, __dev_name_xe(xe))
__field(struct xe_gt_tlb_invalidation_fence *, fence)
__field(int, seqno)
),
TP_fast_assign(
+ __assign_str(dev);
__entry->fence = fence;
__entry->seqno = fence->seqno;
),
- TP_printk("fence=%p, seqno=%d",
- __entry->fence, __entry->seqno)
+ TP_printk("dev=%s, fence=%p, seqno=%d",
+ __get_str(dev), __entry->fence, __entry->seqno)
);
DEFINE_EVENT(xe_gt_tlb_invalidation_fence, xe_gt_tlb_invalidation_fence_create,
- TP_PROTO(struct xe_gt_tlb_invalidation_fence *fence),
- TP_ARGS(fence)
+ TP_PROTO(struct xe_device *xe, struct xe_gt_tlb_invalidation_fence *fence),
+ TP_ARGS(xe, fence)
);
DEFINE_EVENT(xe_gt_tlb_invalidation_fence,
xe_gt_tlb_invalidation_fence_work_func,
- TP_PROTO(struct xe_gt_tlb_invalidation_fence *fence),
- TP_ARGS(fence)
+ TP_PROTO(struct xe_device *xe, struct xe_gt_tlb_invalidation_fence *fence),
+ TP_ARGS(xe, fence)
);
DEFINE_EVENT(xe_gt_tlb_invalidation_fence, xe_gt_tlb_invalidation_fence_cb,
- TP_PROTO(struct xe_gt_tlb_invalidation_fence *fence),
- TP_ARGS(fence)
+ TP_PROTO(struct xe_device *xe, struct xe_gt_tlb_invalidation_fence *fence),
+ TP_ARGS(xe, fence)
);
DEFINE_EVENT(xe_gt_tlb_invalidation_fence, xe_gt_tlb_invalidation_fence_send,
- TP_PROTO(struct xe_gt_tlb_invalidation_fence *fence),
- TP_ARGS(fence)
+ TP_PROTO(struct xe_device *xe, struct xe_gt_tlb_invalidation_fence *fence),
+ TP_ARGS(xe, fence)
);
DEFINE_EVENT(xe_gt_tlb_invalidation_fence, xe_gt_tlb_invalidation_fence_recv,
- TP_PROTO(struct xe_gt_tlb_invalidation_fence *fence),
- TP_ARGS(fence)
+ TP_PROTO(struct xe_device *xe, struct xe_gt_tlb_invalidation_fence *fence),
+ TP_ARGS(xe, fence)
);
DEFINE_EVENT(xe_gt_tlb_invalidation_fence, xe_gt_tlb_invalidation_fence_signal,
- TP_PROTO(struct xe_gt_tlb_invalidation_fence *fence),
- TP_ARGS(fence)
+ TP_PROTO(struct xe_device *xe, struct xe_gt_tlb_invalidation_fence *fence),
+ TP_ARGS(xe, fence)
);
DEFINE_EVENT(xe_gt_tlb_invalidation_fence, xe_gt_tlb_invalidation_fence_timeout,
- TP_PROTO(struct xe_gt_tlb_invalidation_fence *fence),
- TP_ARGS(fence)
-);
-
-DECLARE_EVENT_CLASS(xe_bo,
- TP_PROTO(struct xe_bo *bo),
- TP_ARGS(bo),
-
- TP_STRUCT__entry(
- __field(size_t, size)
- __field(u32, flags)
- __field(struct xe_vm *, vm)
- ),
-
- TP_fast_assign(
- __entry->size = bo->size;
- __entry->flags = bo->flags;
- __entry->vm = bo->vm;
- ),
-
- TP_printk("size=%zu, flags=0x%02x, vm=%p",
- __entry->size, __entry->flags, __entry->vm)
-);
-
-DEFINE_EVENT(xe_bo, xe_bo_cpu_fault,
- TP_PROTO(struct xe_bo *bo),
- TP_ARGS(bo)
-);
-
-TRACE_EVENT(xe_bo_move,
- TP_PROTO(struct xe_bo *bo, uint32_t new_placement, uint32_t old_placement,
- bool move_lacks_source),
- TP_ARGS(bo, new_placement, old_placement, move_lacks_source),
- TP_STRUCT__entry(
- __field(struct xe_bo *, bo)
- __field(size_t, size)
- __field(u32, new_placement)
- __field(u32, old_placement)
- __array(char, device_id, 12)
- __field(bool, move_lacks_source)
- ),
-
- TP_fast_assign(
- __entry->bo = bo;
- __entry->size = bo->size;
- __entry->new_placement = new_placement;
- __entry->old_placement = old_placement;
- strscpy(__entry->device_id, dev_name(xe_bo_device(__entry->bo)->drm.dev), 12);
- __entry->move_lacks_source = move_lacks_source;
- ),
- TP_printk("move_lacks_source:%s, migrate object %p [size %zu] from %s to %s device_id:%s",
- __entry->move_lacks_source ? "yes" : "no", __entry->bo, __entry->size,
- xe_mem_type_to_name[__entry->old_placement],
- xe_mem_type_to_name[__entry->new_placement], __entry->device_id)
+ TP_PROTO(struct xe_device *xe, struct xe_gt_tlb_invalidation_fence *fence),
+ TP_ARGS(xe, fence)
);
DECLARE_EVENT_CLASS(xe_exec_queue,
@@ -133,6 +85,7 @@ DECLARE_EVENT_CLASS(xe_exec_queue,
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)
@@ -143,6 +96,7 @@ DECLARE_EVENT_CLASS(xe_exec_queue,
),
TP_fast_assign(
+ __assign_str(dev);
__entry->class = q->class;
__entry->logical_mask = q->logical_mask;
__entry->gt_id = q->gt->info.id;
@@ -152,8 +106,8 @@ DECLARE_EVENT_CLASS(xe_exec_queue,
__entry->flags = q->flags;
),
- TP_printk("%d:0x%x, gt=%d, width=%d, guc_id=%d, guc_state=0x%x, flags=0x%x",
- __entry->class, __entry->logical_mask,
+ TP_printk("dev=%s, %d:0x%x, gt=%d, width=%d, guc_id=%d, guc_state=0x%x, flags=0x%x",
+ __get_str(dev), __entry->class, __entry->logical_mask,
__entry->gt_id, __entry->width, __entry->guc_id,
__entry->guc_state, __entry->flags)
);
@@ -253,7 +207,9 @@ DECLARE_EVENT_CLASS(xe_sched_job,
TP_ARGS(job),
TP_STRUCT__entry(
+ __string(dev, __dev_name_eq(job->q))
__field(u32, seqno)
+ __field(u32, lrc_seqno)
__field(u16, guc_id)
__field(u32, guc_state)
__field(u32, flags)
@@ -263,18 +219,21 @@ DECLARE_EVENT_CLASS(xe_sched_job,
),
TP_fast_assign(
+ __assign_str(dev);
__entry->seqno = xe_sched_job_seqno(job);
+ __entry->lrc_seqno = xe_sched_job_lrc_seqno(job);
__entry->guc_id = job->q->guc->id;
__entry->guc_state =
atomic_read(&job->q->guc->state);
__entry->flags = job->q->flags;
- __entry->error = job->fence->error;
+ __entry->error = job->fence ? job->fence->error : 0;
__entry->fence = job->fence;
- __entry->batch_addr = (u64)job->batch_addr[0];
+ __entry->batch_addr = (u64)job->ptrs[0].batch_addr;
),
- TP_printk("fence=%p, seqno=%u, guc_id=%d, batch_addr=0x%012llx, guc_state=0x%x, flags=0x%x, error=%d",
- __entry->fence, __entry->seqno, __entry->guc_id,
+ TP_printk("dev=%s, fence=%p, seqno=%u, lrc_seqno=%u, guc_id=%d, batch_addr=0x%012llx, guc_state=0x%x, flags=0x%x, error=%d",
+ __get_str(dev), __entry->fence, __entry->seqno,
+ __entry->lrc_seqno, __entry->guc_id,
__entry->batch_addr, __entry->guc_state,
__entry->flags, __entry->error)
);
@@ -319,17 +278,19 @@ DECLARE_EVENT_CLASS(xe_sched_msg,
TP_ARGS(msg),
TP_STRUCT__entry(
+ __string(dev, __dev_name_eq(((struct xe_exec_queue *)msg->private_data)))
__field(u32, opcode)
__field(u16, guc_id)
),
TP_fast_assign(
+ __assign_str(dev);
__entry->opcode = msg->opcode;
__entry->guc_id =
((struct xe_exec_queue *)msg->private_data)->guc->id;
),
- TP_printk("guc_id=%d, opcode=%u", __entry->guc_id,
+ TP_printk("dev=%s, guc_id=%d, opcode=%u", __get_str(dev), __entry->guc_id,
__entry->opcode)
);
@@ -348,19 +309,21 @@ DECLARE_EVENT_CLASS(xe_hw_fence,
TP_ARGS(fence),
TP_STRUCT__entry(
+ __string(dev, __dev_name_gt(fence->ctx->gt))
__field(u64, ctx)
__field(u32, seqno)
__field(struct xe_hw_fence *, fence)
),
TP_fast_assign(
+ __assign_str(dev);
__entry->ctx = fence->dma.context;
__entry->seqno = fence->dma.seqno;
__entry->fence = fence;
),
- TP_printk("ctx=0x%016llx, fence=%p, seqno=%u",
- __entry->ctx, __entry->fence, __entry->seqno)
+ TP_printk("dev=%s, ctx=0x%016llx, fence=%p, seqno=%u",
+ __get_str(dev), __entry->ctx, __entry->fence, __entry->seqno)
);
DEFINE_EVENT(xe_hw_fence, xe_hw_fence_create,
@@ -378,247 +341,32 @@ DEFINE_EVENT(xe_hw_fence, xe_hw_fence_try_signal,
TP_ARGS(fence)
);
-DEFINE_EVENT(xe_hw_fence, xe_hw_fence_free,
- TP_PROTO(struct xe_hw_fence *fence),
- TP_ARGS(fence)
-);
-
-DECLARE_EVENT_CLASS(xe_vma,
- TP_PROTO(struct xe_vma *vma),
- TP_ARGS(vma),
-
- TP_STRUCT__entry(
- __field(struct xe_vma *, vma)
- __field(u32, asid)
- __field(u64, start)
- __field(u64, end)
- __field(u64, ptr)
- ),
-
- TP_fast_assign(
- __entry->vma = vma;
- __entry->asid = xe_vma_vm(vma)->usm.asid;
- __entry->start = xe_vma_start(vma);
- __entry->end = xe_vma_end(vma) - 1;
- __entry->ptr = xe_vma_userptr(vma);
- ),
-
- TP_printk("vma=%p, asid=0x%05x, start=0x%012llx, end=0x%012llx, userptr=0x%012llx,",
- __entry->vma, __entry->asid, __entry->start,
- __entry->end, __entry->ptr)
-)
-
-DEFINE_EVENT(xe_vma, xe_vma_flush,
- TP_PROTO(struct xe_vma *vma),
- TP_ARGS(vma)
-);
-
-DEFINE_EVENT(xe_vma, xe_vma_pagefault,
- TP_PROTO(struct xe_vma *vma),
- TP_ARGS(vma)
-);
-
-DEFINE_EVENT(xe_vma, xe_vma_acc,
- TP_PROTO(struct xe_vma *vma),
- TP_ARGS(vma)
-);
-
-DEFINE_EVENT(xe_vma, xe_vma_fail,
- TP_PROTO(struct xe_vma *vma),
- TP_ARGS(vma)
-);
-
-DEFINE_EVENT(xe_vma, xe_vma_bind,
- TP_PROTO(struct xe_vma *vma),
- TP_ARGS(vma)
-);
-
-DEFINE_EVENT(xe_vma, xe_vma_pf_bind,
- TP_PROTO(struct xe_vma *vma),
- TP_ARGS(vma)
-);
-
-DEFINE_EVENT(xe_vma, xe_vma_unbind,
- TP_PROTO(struct xe_vma *vma),
- TP_ARGS(vma)
-);
-
-DEFINE_EVENT(xe_vma, xe_vma_userptr_rebind_worker,
- TP_PROTO(struct xe_vma *vma),
- TP_ARGS(vma)
-);
-
-DEFINE_EVENT(xe_vma, xe_vma_userptr_rebind_exec,
- TP_PROTO(struct xe_vma *vma),
- TP_ARGS(vma)
-);
-
-DEFINE_EVENT(xe_vma, xe_vma_rebind_worker,
- TP_PROTO(struct xe_vma *vma),
- TP_ARGS(vma)
-);
-
-DEFINE_EVENT(xe_vma, xe_vma_rebind_exec,
- TP_PROTO(struct xe_vma *vma),
- TP_ARGS(vma)
-);
-
-DEFINE_EVENT(xe_vma, xe_vma_userptr_invalidate,
- TP_PROTO(struct xe_vma *vma),
- TP_ARGS(vma)
-);
-
-DEFINE_EVENT(xe_vma, xe_vma_invalidate,
- TP_PROTO(struct xe_vma *vma),
- TP_ARGS(vma)
-);
-
-DEFINE_EVENT(xe_vma, xe_vma_evict,
- TP_PROTO(struct xe_vma *vma),
- TP_ARGS(vma)
-);
-
-DEFINE_EVENT(xe_vma, xe_vma_userptr_invalidate_complete,
- TP_PROTO(struct xe_vma *vma),
- TP_ARGS(vma)
-);
-
-DECLARE_EVENT_CLASS(xe_vm,
- TP_PROTO(struct xe_vm *vm),
- TP_ARGS(vm),
-
- TP_STRUCT__entry(
- __field(struct xe_vm *, vm)
- __field(u32, asid)
- ),
-
- TP_fast_assign(
- __entry->vm = vm;
- __entry->asid = vm->usm.asid;
- ),
-
- TP_printk("vm=%p, asid=0x%05x", __entry->vm,
- __entry->asid)
-);
-
-DEFINE_EVENT(xe_vm, xe_vm_kill,
- TP_PROTO(struct xe_vm *vm),
- TP_ARGS(vm)
-);
-
-DEFINE_EVENT(xe_vm, xe_vm_create,
- TP_PROTO(struct xe_vm *vm),
- TP_ARGS(vm)
-);
-
-DEFINE_EVENT(xe_vm, xe_vm_free,
- TP_PROTO(struct xe_vm *vm),
- TP_ARGS(vm)
-);
-
-DEFINE_EVENT(xe_vm, xe_vm_cpu_bind,
- TP_PROTO(struct xe_vm *vm),
- TP_ARGS(vm)
-);
-
-DEFINE_EVENT(xe_vm, xe_vm_restart,
- TP_PROTO(struct xe_vm *vm),
- TP_ARGS(vm)
-);
-
-DEFINE_EVENT(xe_vm, xe_vm_rebind_worker_enter,
- TP_PROTO(struct xe_vm *vm),
- TP_ARGS(vm)
-);
-
-DEFINE_EVENT(xe_vm, xe_vm_rebind_worker_retry,
- TP_PROTO(struct xe_vm *vm),
- TP_ARGS(vm)
-);
-
-DEFINE_EVENT(xe_vm, xe_vm_rebind_worker_exit,
- TP_PROTO(struct xe_vm *vm),
- TP_ARGS(vm)
-);
-
-/* GuC */
-DECLARE_EVENT_CLASS(xe_guc_ct_flow_control,
- TP_PROTO(u32 _head, u32 _tail, u32 size, u32 space, u32 len),
- TP_ARGS(_head, _tail, size, space, len),
-
- TP_STRUCT__entry(
- __field(u32, _head)
- __field(u32, _tail)
- __field(u32, size)
- __field(u32, space)
- __field(u32, len)
- ),
-
- TP_fast_assign(
- __entry->_head = _head;
- __entry->_tail = _tail;
- __entry->size = size;
- __entry->space = space;
- __entry->len = len;
- ),
-
- TP_printk("h2g flow control: head=%u, tail=%u, size=%u, space=%u, len=%u",
- __entry->_head, __entry->_tail, __entry->size,
- __entry->space, __entry->len)
-);
-
-DEFINE_EVENT(xe_guc_ct_flow_control, xe_guc_ct_h2g_flow_control,
- TP_PROTO(u32 _head, u32 _tail, u32 size, u32 space, u32 len),
- TP_ARGS(_head, _tail, size, space, len)
-);
-
-DEFINE_EVENT_PRINT(xe_guc_ct_flow_control, xe_guc_ct_g2h_flow_control,
- TP_PROTO(u32 _head, u32 _tail, u32 size, u32 space, u32 len),
- TP_ARGS(_head, _tail, size, space, len),
-
- TP_printk("g2h flow control: head=%u, tail=%u, size=%u, space=%u, len=%u",
- __entry->_head, __entry->_tail, __entry->size,
- __entry->space, __entry->len)
-);
-
-DECLARE_EVENT_CLASS(xe_guc_ctb,
- TP_PROTO(u8 gt_id, u32 action, u32 len, u32 _head, u32 tail),
- TP_ARGS(gt_id, action, len, _head, tail),
-
- TP_STRUCT__entry(
- __field(u8, gt_id)
- __field(u32, action)
- __field(u32, len)
- __field(u32, tail)
- __field(u32, _head)
- ),
-
- TP_fast_assign(
- __entry->gt_id = gt_id;
- __entry->action = action;
- __entry->len = len;
- __entry->tail = tail;
- __entry->_head = _head;
- ),
-
- TP_printk("gt%d: H2G CTB: action=0x%x, len=%d, tail=%d, head=%d\n",
- __entry->gt_id, __entry->action, __entry->len,
- __entry->tail, __entry->_head)
-);
-
-DEFINE_EVENT(xe_guc_ctb, xe_guc_ctb_h2g,
- TP_PROTO(u8 gt_id, u32 action, u32 len, u32 _head, u32 tail),
- TP_ARGS(gt_id, action, len, _head, tail)
-);
-
-DEFINE_EVENT_PRINT(xe_guc_ctb, xe_guc_ctb_g2h,
- TP_PROTO(u8 gt_id, u32 action, u32 len, u32 _head, u32 tail),
- TP_ARGS(gt_id, action, len, _head, tail),
-
- TP_printk("gt%d: G2H CTB: action=0x%x, len=%d, tail=%d, head=%d\n",
- __entry->gt_id, __entry->action, __entry->len,
- __entry->tail, __entry->_head)
-
+TRACE_EVENT(xe_reg_rw,
+ TP_PROTO(struct xe_gt *gt, bool write, u32 reg, u64 val, int len),
+
+ TP_ARGS(gt, write, reg, val, len),
+
+ TP_STRUCT__entry(
+ __string(dev, __dev_name_gt(gt))
+ __field(u64, val)
+ __field(u32, reg)
+ __field(u16, write)
+ __field(u16, len)
+ ),
+
+ TP_fast_assign(
+ __assign_str(dev);
+ __entry->val = val;
+ __entry->reg = reg;
+ __entry->write = write;
+ __entry->len = len;
+ ),
+
+ TP_printk("dev=%s, %s reg=0x%x, len=%d, val=(0x%x, 0x%x)",
+ __get_str(dev), __entry->write ? "write" : "read",
+ __entry->reg, __entry->len,
+ (u32)(__entry->val & 0xffffffff),
+ (u32)(__entry->val >> 32))
);
#endif
diff --git a/drivers/gpu/drm/xe/xe_trace_bo.c b/drivers/gpu/drm/xe/xe_trace_bo.c
new file mode 100644
index 000000000000..6d5e66ce4c50
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_trace_bo.c
@@ -0,0 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright © 2024 Intel Corporation
+ */
+
+#ifndef __CHECKER__
+#define CREATE_TRACE_POINTS
+#include "xe_trace_bo.h"
+#endif
diff --git a/drivers/gpu/drm/xe/xe_trace_bo.h b/drivers/gpu/drm/xe/xe_trace_bo.h
new file mode 100644
index 000000000000..f39f09ed3495
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_trace_bo.h
@@ -0,0 +1,247 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright © 2024 Intel Corporation
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM xe
+
+#if !defined(_XE_TRACE_BO_H_) || defined(TRACE_HEADER_MULTI_READ)
+#define _XE_TRACE_BO_H_
+
+#include <linux/tracepoint.h>
+#include <linux/types.h>
+
+#include "xe_bo.h"
+#include "xe_bo_types.h"
+#include "xe_vm.h"
+
+#define __dev_name_bo(bo) dev_name(xe_bo_device(bo)->drm.dev)
+#define __dev_name_vm(vm) dev_name((vm)->xe->drm.dev)
+#define __dev_name_vma(vma) __dev_name_vm(xe_vma_vm(vma))
+
+DECLARE_EVENT_CLASS(xe_bo,
+ TP_PROTO(struct xe_bo *bo),
+ TP_ARGS(bo),
+
+ TP_STRUCT__entry(
+ __string(dev, __dev_name_bo(bo))
+ __field(size_t, size)
+ __field(u32, flags)
+ __field(struct xe_vm *, vm)
+ ),
+
+ TP_fast_assign(
+ __assign_str(dev);
+ __entry->size = bo->size;
+ __entry->flags = bo->flags;
+ __entry->vm = bo->vm;
+ ),
+
+ TP_printk("dev=%s, size=%zu, flags=0x%02x, vm=%p",
+ __get_str(dev), __entry->size,
+ __entry->flags, __entry->vm)
+);
+
+DEFINE_EVENT(xe_bo, xe_bo_cpu_fault,
+ TP_PROTO(struct xe_bo *bo),
+ TP_ARGS(bo)
+);
+
+TRACE_EVENT(xe_bo_move,
+ TP_PROTO(struct xe_bo *bo, uint32_t new_placement, uint32_t old_placement,
+ bool move_lacks_source),
+ TP_ARGS(bo, new_placement, old_placement, move_lacks_source),
+ TP_STRUCT__entry(
+ __field(struct xe_bo *, bo)
+ __field(size_t, size)
+ __field(u32, new_placement)
+ __field(u32, old_placement)
+ __string(device_id, __dev_name_bo(bo))
+ __field(bool, move_lacks_source)
+ ),
+
+ TP_fast_assign(
+ __entry->bo = bo;
+ __entry->size = bo->size;
+ __entry->new_placement = new_placement;
+ __entry->old_placement = old_placement;
+ __assign_str(device_id);
+ __entry->move_lacks_source = move_lacks_source;
+ ),
+ TP_printk("move_lacks_source:%s, migrate object %p [size %zu] from %s to %s device_id:%s",
+ __entry->move_lacks_source ? "yes" : "no", __entry->bo, __entry->size,
+ xe_mem_type_to_name[__entry->old_placement],
+ xe_mem_type_to_name[__entry->new_placement], __get_str(device_id))
+);
+
+DECLARE_EVENT_CLASS(xe_vma,
+ TP_PROTO(struct xe_vma *vma),
+ TP_ARGS(vma),
+
+ TP_STRUCT__entry(
+ __string(dev, __dev_name_vma(vma))
+ __field(struct xe_vma *, vma)
+ __field(u32, asid)
+ __field(u64, start)
+ __field(u64, end)
+ __field(u64, ptr)
+ ),
+
+ TP_fast_assign(
+ __assign_str(dev);
+ __entry->vma = vma;
+ __entry->asid = xe_vma_vm(vma)->usm.asid;
+ __entry->start = xe_vma_start(vma);
+ __entry->end = xe_vma_end(vma) - 1;
+ __entry->ptr = xe_vma_userptr(vma);
+ ),
+
+ TP_printk("dev=%s, vma=%p, asid=0x%05x, start=0x%012llx, end=0x%012llx, userptr=0x%012llx,",
+ __get_str(dev), __entry->vma, __entry->asid, __entry->start,
+ __entry->end, __entry->ptr)
+)
+
+DEFINE_EVENT(xe_vma, xe_vma_flush,
+ TP_PROTO(struct xe_vma *vma),
+ TP_ARGS(vma)
+);
+
+DEFINE_EVENT(xe_vma, xe_vma_pagefault,
+ TP_PROTO(struct xe_vma *vma),
+ TP_ARGS(vma)
+);
+
+DEFINE_EVENT(xe_vma, xe_vma_acc,
+ TP_PROTO(struct xe_vma *vma),
+ TP_ARGS(vma)
+);
+
+DEFINE_EVENT(xe_vma, xe_vma_fail,
+ TP_PROTO(struct xe_vma *vma),
+ TP_ARGS(vma)
+);
+
+DEFINE_EVENT(xe_vma, xe_vma_bind,
+ TP_PROTO(struct xe_vma *vma),
+ TP_ARGS(vma)
+);
+
+DEFINE_EVENT(xe_vma, xe_vma_pf_bind,
+ TP_PROTO(struct xe_vma *vma),
+ TP_ARGS(vma)
+);
+
+DEFINE_EVENT(xe_vma, xe_vma_unbind,
+ TP_PROTO(struct xe_vma *vma),
+ TP_ARGS(vma)
+);
+
+DEFINE_EVENT(xe_vma, xe_vma_userptr_rebind_worker,
+ TP_PROTO(struct xe_vma *vma),
+ TP_ARGS(vma)
+);
+
+DEFINE_EVENT(xe_vma, xe_vma_userptr_rebind_exec,
+ TP_PROTO(struct xe_vma *vma),
+ TP_ARGS(vma)
+);
+
+DEFINE_EVENT(xe_vma, xe_vma_rebind_worker,
+ TP_PROTO(struct xe_vma *vma),
+ TP_ARGS(vma)
+);
+
+DEFINE_EVENT(xe_vma, xe_vma_rebind_exec,
+ TP_PROTO(struct xe_vma *vma),
+ TP_ARGS(vma)
+);
+
+DEFINE_EVENT(xe_vma, xe_vma_userptr_invalidate,
+ TP_PROTO(struct xe_vma *vma),
+ TP_ARGS(vma)
+);
+
+DEFINE_EVENT(xe_vma, xe_vma_invalidate,
+ TP_PROTO(struct xe_vma *vma),
+ TP_ARGS(vma)
+);
+
+DEFINE_EVENT(xe_vma, xe_vma_evict,
+ TP_PROTO(struct xe_vma *vma),
+ TP_ARGS(vma)
+);
+
+DEFINE_EVENT(xe_vma, xe_vma_userptr_invalidate_complete,
+ TP_PROTO(struct xe_vma *vma),
+ TP_ARGS(vma)
+);
+
+DECLARE_EVENT_CLASS(xe_vm,
+ TP_PROTO(struct xe_vm *vm),
+ TP_ARGS(vm),
+
+ TP_STRUCT__entry(
+ __string(dev, __dev_name_vm(vm))
+ __field(struct xe_vm *, vm)
+ __field(u32, asid)
+ ),
+
+ TP_fast_assign(
+ __assign_str(dev);
+ __entry->vm = vm;
+ __entry->asid = vm->usm.asid;
+ ),
+
+ TP_printk("dev=%s, vm=%p, asid=0x%05x", __get_str(dev),
+ __entry->vm, __entry->asid)
+);
+
+DEFINE_EVENT(xe_vm, xe_vm_kill,
+ TP_PROTO(struct xe_vm *vm),
+ TP_ARGS(vm)
+);
+
+DEFINE_EVENT(xe_vm, xe_vm_create,
+ TP_PROTO(struct xe_vm *vm),
+ TP_ARGS(vm)
+);
+
+DEFINE_EVENT(xe_vm, xe_vm_free,
+ TP_PROTO(struct xe_vm *vm),
+ TP_ARGS(vm)
+);
+
+DEFINE_EVENT(xe_vm, xe_vm_cpu_bind,
+ TP_PROTO(struct xe_vm *vm),
+ TP_ARGS(vm)
+);
+
+DEFINE_EVENT(xe_vm, xe_vm_restart,
+ TP_PROTO(struct xe_vm *vm),
+ TP_ARGS(vm)
+);
+
+DEFINE_EVENT(xe_vm, xe_vm_rebind_worker_enter,
+ TP_PROTO(struct xe_vm *vm),
+ TP_ARGS(vm)
+);
+
+DEFINE_EVENT(xe_vm, xe_vm_rebind_worker_retry,
+ TP_PROTO(struct xe_vm *vm),
+ TP_ARGS(vm)
+);
+
+DEFINE_EVENT(xe_vm, xe_vm_rebind_worker_exit,
+ TP_PROTO(struct xe_vm *vm),
+ TP_ARGS(vm)
+);
+
+#endif
+
+/* This part must be outside protection */
+#undef TRACE_INCLUDE_PATH
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_PATH ../../drivers/gpu/drm/xe
+#define TRACE_INCLUDE_FILE xe_trace_bo
+#include <trace/define_trace.h>
diff --git a/drivers/gpu/drm/xe/xe_trace_guc.c b/drivers/gpu/drm/xe/xe_trace_guc.c
new file mode 100644
index 000000000000..fcdf6888ff2f
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_trace_guc.c
@@ -0,0 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright © 2024 Intel Corporation
+ */
+
+#ifndef __CHECKER__
+#define CREATE_TRACE_POINTS
+#include "xe_trace_guc.h"
+#endif
diff --git a/drivers/gpu/drm/xe/xe_trace_guc.h b/drivers/gpu/drm/xe/xe_trace_guc.h
new file mode 100644
index 000000000000..23abdd55dc62
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_trace_guc.h
@@ -0,0 +1,110 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright © 2024 Intel Corporation
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM xe
+
+#if !defined(_XE_TRACE_GUC_H_) || defined(TRACE_HEADER_MULTI_READ)
+#define _XE_TRACE_GUC_H_
+
+#include <linux/tracepoint.h>
+#include <linux/types.h>
+
+#include "xe_device_types.h"
+#include "xe_guc_exec_queue_types.h"
+
+#define __dev_name_xe(xe) dev_name((xe)->drm.dev)
+
+DECLARE_EVENT_CLASS(xe_guc_ct_flow_control,
+ TP_PROTO(struct xe_device *xe, u32 _head, u32 _tail, u32 size, u32 space, u32 len),
+ TP_ARGS(xe, _head, _tail, size, space, len),
+
+ TP_STRUCT__entry(
+ __string(dev, __dev_name_xe(xe))
+ __field(u32, _head)
+ __field(u32, _tail)
+ __field(u32, size)
+ __field(u32, space)
+ __field(u32, len)
+ ),
+
+ TP_fast_assign(
+ __assign_str(dev);
+ __entry->_head = _head;
+ __entry->_tail = _tail;
+ __entry->size = size;
+ __entry->space = space;
+ __entry->len = len;
+ ),
+
+ TP_printk("h2g flow control: dev=%s, head=%u, tail=%u, size=%u, space=%u, len=%u",
+ __get_str(dev), __entry->_head, __entry->_tail, __entry->size,
+ __entry->space, __entry->len)
+);
+
+DEFINE_EVENT(xe_guc_ct_flow_control, xe_guc_ct_h2g_flow_control,
+ TP_PROTO(struct xe_device *xe, u32 _head, u32 _tail, u32 size, u32 space, u32 len),
+ TP_ARGS(xe, _head, _tail, size, space, len)
+);
+
+DEFINE_EVENT_PRINT(xe_guc_ct_flow_control, xe_guc_ct_g2h_flow_control,
+ TP_PROTO(struct xe_device *xe, u32 _head, u32 _tail, u32 size, u32 space, u32 len),
+ TP_ARGS(xe, _head, _tail, size, space, len),
+
+ TP_printk("g2h flow control: dev=%s, head=%u, tail=%u, size=%u, space=%u, len=%u",
+ __get_str(dev), __entry->_head, __entry->_tail, __entry->size,
+ __entry->space, __entry->len)
+);
+
+DECLARE_EVENT_CLASS(xe_guc_ctb,
+ TP_PROTO(struct xe_device *xe, u8 gt_id, u32 action, u32 len, u32 _head, u32 tail),
+ TP_ARGS(xe, gt_id, action, len, _head, tail),
+
+ TP_STRUCT__entry(
+ __string(dev, __dev_name_xe(xe))
+ __field(u8, gt_id)
+ __field(u32, action)
+ __field(u32, len)
+ __field(u32, tail)
+ __field(u32, _head)
+ ),
+
+ TP_fast_assign(
+ __assign_str(dev);
+ __entry->gt_id = gt_id;
+ __entry->action = action;
+ __entry->len = len;
+ __entry->tail = tail;
+ __entry->_head = _head;
+ ),
+
+ TP_printk("H2G CTB: dev=%s, gt%d: action=0x%x, len=%d, tail=%d, head=%d\n",
+ __get_str(dev), __entry->gt_id, __entry->action, __entry->len,
+ __entry->tail, __entry->_head)
+);
+
+DEFINE_EVENT(xe_guc_ctb, xe_guc_ctb_h2g,
+ TP_PROTO(struct xe_device *xe, u8 gt_id, u32 action, u32 len, u32 _head, u32 tail),
+ TP_ARGS(xe, gt_id, action, len, _head, tail)
+);
+
+DEFINE_EVENT_PRINT(xe_guc_ctb, xe_guc_ctb_g2h,
+ TP_PROTO(struct xe_device *xe, u8 gt_id, u32 action, u32 len, u32 _head, u32 tail),
+ TP_ARGS(xe, gt_id, action, len, _head, tail),
+
+ TP_printk("G2H CTB: dev=%s, gt%d: action=0x%x, len=%d, tail=%d, head=%d\n",
+ __get_str(dev), __entry->gt_id, __entry->action, __entry->len,
+ __entry->tail, __entry->_head)
+
+);
+
+#endif
+
+/* This part must be outside protection */
+#undef TRACE_INCLUDE_PATH
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_PATH ../../drivers/gpu/drm/xe
+#define TRACE_INCLUDE_FILE xe_trace_guc
+#include <trace/define_trace.h>
diff --git a/drivers/gpu/drm/xe/xe_ttm_stolen_mgr.c b/drivers/gpu/drm/xe/xe_ttm_stolen_mgr.c
index f77367329760..f46fd2df84de 100644
--- a/drivers/gpu/drm/xe/xe_ttm_stolen_mgr.c
+++ b/drivers/gpu/drm/xe/xe_ttm_stolen_mgr.c
@@ -13,11 +13,13 @@
#include <generated/xe_wa_oob.h>
+#include "regs/xe_bars.h"
#include "regs/xe_gt_regs.h"
#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"
#include "xe_sriov.h"
diff --git a/drivers/gpu/drm/xe/xe_uc.c b/drivers/gpu/drm/xe/xe_uc.c
index 4feb35c95a1c..0d073a9987c2 100644
--- a/drivers/gpu/drm/xe/xe_uc.c
+++ b/drivers/gpu/drm/xe/xe_uc.c
@@ -5,15 +5,17 @@
#include "xe_uc.h"
+#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"
#include "xe_guc.h"
-#include "xe_guc_db_mgr.h"
#include "xe_guc_pc.h"
-#include "xe_guc_submit.h"
#include "xe_huc.h"
+#include "xe_sriov.h"
#include "xe_uc_fw.h"
#include "xe_wopcm.h"
@@ -51,19 +53,19 @@ int xe_uc_init(struct xe_uc *uc)
goto err;
if (!xe_device_uc_enabled(uc_to_xe(uc)))
- goto err;
+ return 0;
- ret = xe_wopcm_init(&uc->wopcm);
- if (ret)
- goto err;
+ if (IS_SRIOV_VF(uc_to_xe(uc)))
+ return 0;
- ret = xe_guc_submit_init(&uc->guc);
+ ret = xe_wopcm_init(&uc->wopcm);
if (ret)
goto err;
- ret = xe_guc_db_mgr_init(&uc->guc.dbm, ~0);
+ return 0;
err:
+ xe_gt_err(uc_to_gt(uc), "Failed to initialize uC (%pe)\n", ERR_PTR(ret));
return ret;
}
@@ -144,6 +146,31 @@ int xe_uc_init_hwconfig(struct xe_uc *uc)
return 0;
}
+static int vf_uc_init_hw(struct xe_uc *uc)
+{
+ int err;
+
+ err = xe_uc_sanitize_reset(uc);
+ if (err)
+ return err;
+
+ err = xe_guc_enable_communication(&uc->guc);
+ if (err)
+ return err;
+
+ err = xe_gt_sriov_vf_connect(uc_to_gt(uc));
+ if (err)
+ return err;
+
+ uc->guc.submission_state.enabled = true;
+
+ err = xe_gt_record_default_lrcs(uc_to_gt(uc));
+ if (err)
+ return err;
+
+ return 0;
+}
+
/*
* Should be called during driver load, after every GT reset, and after every
* suspend to reload / auth the firmwares.
@@ -156,6 +183,9 @@ int xe_uc_init_hw(struct xe_uc *uc)
if (!xe_device_uc_enabled(uc_to_xe(uc)))
return 0;
+ if (IS_SRIOV_VF(uc_to_xe(uc)))
+ return vf_uc_init_hw(uc);
+
ret = xe_huc_upload(&uc->huc);
if (ret)
return ret;
@@ -215,13 +245,13 @@ void xe_uc_stop_prepare(struct xe_uc *uc)
xe_guc_stop_prepare(&uc->guc);
}
-int xe_uc_stop(struct xe_uc *uc)
+void xe_uc_stop(struct xe_uc *uc)
{
/* GuC submission not enabled, nothing to do */
if (!xe_device_uc_enabled(uc_to_xe(uc)))
- return 0;
+ return;
- return xe_guc_stop(&uc->guc);
+ xe_guc_stop(&uc->guc);
}
int xe_uc_start(struct xe_uc *uc)
@@ -247,17 +277,13 @@ again:
int xe_uc_suspend(struct xe_uc *uc)
{
- int ret;
-
/* GuC submission not enabled, nothing to do */
if (!xe_device_uc_enabled(uc_to_xe(uc)))
return 0;
uc_reset_wait(uc);
- ret = xe_uc_stop(uc);
- if (ret)
- return ret;
+ xe_uc_stop(uc);
return xe_guc_suspend(&uc->guc);
}
@@ -274,3 +300,17 @@ void xe_uc_remove(struct xe_uc *uc)
{
xe_gsc_remove(&uc->gsc);
}
+
+/**
+ * xe_uc_declare_wedged() - Declare UC wedged
+ * @uc: the UC object
+ *
+ * Wedge the UC which stops all submission, saves desired debug state, and
+ * cleans up anything which could timeout.
+ */
+void xe_uc_declare_wedged(struct xe_uc *uc)
+{
+ xe_gt_assert(uc_to_gt(uc), uc_to_xe(uc)->wedged.mode);
+
+ xe_guc_declare_wedged(&uc->guc);
+}
diff --git a/drivers/gpu/drm/xe/xe_uc.h b/drivers/gpu/drm/xe/xe_uc.h
index e4d4e3c99f0e..506517c11333 100644
--- a/drivers/gpu/drm/xe/xe_uc.h
+++ b/drivers/gpu/drm/xe/xe_uc.h
@@ -6,7 +6,7 @@
#ifndef _XE_UC_H_
#define _XE_UC_H_
-#include "xe_uc_types.h"
+struct xe_uc;
int xe_uc_init(struct xe_uc *uc);
int xe_uc_init_hwconfig(struct xe_uc *uc);
@@ -16,10 +16,11 @@ int xe_uc_fini_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_stop_prepare(struct xe_uc *uc);
-int xe_uc_stop(struct xe_uc *uc);
+void xe_uc_stop(struct xe_uc *uc);
int xe_uc_start(struct xe_uc *uc);
int xe_uc_suspend(struct xe_uc *uc);
int xe_uc_sanitize_reset(struct xe_uc *uc);
void xe_uc_remove(struct xe_uc *uc);
+void xe_uc_declare_wedged(struct xe_uc *uc);
#endif
diff --git a/drivers/gpu/drm/xe/xe_uc_fw.c b/drivers/gpu/drm/xe/xe_uc_fw.c
index 186f81640cef..5f23ecd98376 100644
--- a/drivers/gpu/drm/xe/xe_uc_fw.c
+++ b/drivers/gpu/drm/xe/xe_uc_fw.c
@@ -14,6 +14,7 @@
#include "xe_force_wake.h"
#include "xe_gsc.h"
#include "xe_gt.h"
+#include "xe_gt_printk.h"
#include "xe_map.h"
#include "xe_mmio.h"
#include "xe_module.h"
@@ -653,6 +654,10 @@ static int uc_fw_request(struct xe_uc_fw *uc_fw, const struct firmware **firmwar
uc_fw_auto_select(xe, uc_fw);
if (IS_SRIOV_VF(xe)) {
+ /* Only GuC/HuC are supported */
+ if (uc_fw->type != XE_UC_FW_TYPE_GUC &&
+ uc_fw->type != XE_UC_FW_TYPE_HUC)
+ uc_fw->path = NULL;
/* VF will support only firmwares that driver can autoselect */
xe_uc_fw_change_status(uc_fw, uc_fw->path ?
XE_UC_FIRMWARE_PRELOADED :
diff --git a/drivers/gpu/drm/xe/xe_uc_fw.h b/drivers/gpu/drm/xe/xe_uc_fw.h
index 35078038797e..c108e9d08e70 100644
--- a/drivers/gpu/drm/xe/xe_uc_fw.h
+++ b/drivers/gpu/drm/xe/xe_uc_fw.h
@@ -158,7 +158,7 @@ static inline bool xe_uc_fw_is_overridden(const struct xe_uc_fw *uc_fw)
static inline void xe_uc_fw_sanitize(struct xe_uc_fw *uc_fw)
{
- if (xe_uc_fw_is_loaded(uc_fw))
+ if (xe_uc_fw_is_loadable(uc_fw))
xe_uc_fw_change_status(uc_fw, XE_UC_FIRMWARE_LOADABLE);
}
diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c
index 47b737f10e74..c3bdb6362fe9 100644
--- a/drivers/gpu/drm/xe/xe_vm.c
+++ b/drivers/gpu/drm/xe/xe_vm.c
@@ -36,7 +36,7 @@
#include "xe_pt.h"
#include "xe_res_cursor.h"
#include "xe_sync.h"
-#include "xe_trace.h"
+#include "xe_trace_bo.h"
#include "xe_wa.h"
#include "xe_hmm.h"
@@ -83,10 +83,10 @@ static bool preempt_fences_waiting(struct xe_vm *vm)
lockdep_assert_held(&vm->lock);
xe_vm_assert_held(vm);
- list_for_each_entry(q, &vm->preempt.exec_queues, compute.link) {
- if (!q->compute.pfence ||
- (q->compute.pfence && test_bit(DMA_FENCE_FLAG_ENABLE_SIGNAL_BIT,
- &q->compute.pfence->flags))) {
+ list_for_each_entry(q, &vm->preempt.exec_queues, lr.link) {
+ if (!q->lr.pfence ||
+ test_bit(DMA_FENCE_FLAG_ENABLE_SIGNAL_BIT,
+ &q->lr.pfence->flags)) {
return true;
}
}
@@ -129,14 +129,14 @@ static int wait_for_existing_preempt_fences(struct xe_vm *vm)
xe_vm_assert_held(vm);
- list_for_each_entry(q, &vm->preempt.exec_queues, compute.link) {
- if (q->compute.pfence) {
- long timeout = dma_fence_wait(q->compute.pfence, false);
+ list_for_each_entry(q, &vm->preempt.exec_queues, lr.link) {
+ if (q->lr.pfence) {
+ long timeout = dma_fence_wait(q->lr.pfence, false);
if (timeout < 0)
return -ETIME;
- dma_fence_put(q->compute.pfence);
- q->compute.pfence = NULL;
+ dma_fence_put(q->lr.pfence);
+ q->lr.pfence = NULL;
}
}
@@ -148,7 +148,7 @@ static bool xe_vm_is_idle(struct xe_vm *vm)
struct xe_exec_queue *q;
xe_vm_assert_held(vm);
- list_for_each_entry(q, &vm->preempt.exec_queues, compute.link) {
+ list_for_each_entry(q, &vm->preempt.exec_queues, lr.link) {
if (!xe_exec_queue_is_idle(q))
return false;
}
@@ -161,17 +161,17 @@ static void arm_preempt_fences(struct xe_vm *vm, struct list_head *list)
struct list_head *link;
struct xe_exec_queue *q;
- list_for_each_entry(q, &vm->preempt.exec_queues, compute.link) {
+ list_for_each_entry(q, &vm->preempt.exec_queues, lr.link) {
struct dma_fence *fence;
link = list->next;
xe_assert(vm->xe, link != list);
fence = xe_preempt_fence_arm(to_preempt_fence_from_link(link),
- q, q->compute.context,
- ++q->compute.seqno);
- dma_fence_put(q->compute.pfence);
- q->compute.pfence = fence;
+ q, q->lr.context,
+ ++q->lr.seqno);
+ dma_fence_put(q->lr.pfence);
+ q->lr.pfence = fence;
}
}
@@ -180,27 +180,23 @@ static int add_preempt_fences(struct xe_vm *vm, struct xe_bo *bo)
struct xe_exec_queue *q;
int err;
+ xe_bo_assert_held(bo);
+
if (!vm->preempt.num_exec_queues)
return 0;
- err = xe_bo_lock(bo, true);
- if (err)
- return err;
-
err = dma_resv_reserve_fences(bo->ttm.base.resv, vm->preempt.num_exec_queues);
if (err)
- goto out_unlock;
+ return err;
- list_for_each_entry(q, &vm->preempt.exec_queues, compute.link)
- if (q->compute.pfence) {
+ list_for_each_entry(q, &vm->preempt.exec_queues, lr.link)
+ if (q->lr.pfence) {
dma_resv_add_fence(bo->ttm.base.resv,
- q->compute.pfence,
+ q->lr.pfence,
DMA_RESV_USAGE_BOOKKEEP);
}
-out_unlock:
- xe_bo_unlock(bo);
- return err;
+ return 0;
}
static void resume_and_reinstall_preempt_fences(struct xe_vm *vm,
@@ -211,10 +207,10 @@ static void resume_and_reinstall_preempt_fences(struct xe_vm *vm,
lockdep_assert_held(&vm->lock);
xe_vm_assert_held(vm);
- list_for_each_entry(q, &vm->preempt.exec_queues, compute.link) {
+ list_for_each_entry(q, &vm->preempt.exec_queues, lr.link) {
q->ops->resume(q);
- drm_gpuvm_resv_add_fence(&vm->gpuvm, exec, q->compute.pfence,
+ drm_gpuvm_resv_add_fence(&vm->gpuvm, exec, q->lr.pfence,
DMA_RESV_USAGE_BOOKKEEP, DMA_RESV_USAGE_BOOKKEEP);
}
}
@@ -238,16 +234,16 @@ int xe_vm_add_compute_exec_queue(struct xe_vm *vm, struct xe_exec_queue *q)
if (err)
goto out_up_write;
- pfence = xe_preempt_fence_create(q, q->compute.context,
- ++q->compute.seqno);
+ pfence = xe_preempt_fence_create(q, q->lr.context,
+ ++q->lr.seqno);
if (!pfence) {
err = -ENOMEM;
goto out_fini;
}
- list_add(&q->compute.link, &vm->preempt.exec_queues);
+ list_add(&q->lr.link, &vm->preempt.exec_queues);
++vm->preempt.num_exec_queues;
- q->compute.pfence = pfence;
+ q->lr.pfence = pfence;
down_read(&vm->userptr.notifier_lock);
@@ -284,12 +280,12 @@ void xe_vm_remove_compute_exec_queue(struct xe_vm *vm, struct xe_exec_queue *q)
return;
down_write(&vm->lock);
- list_del(&q->compute.link);
+ list_del(&q->lr.link);
--vm->preempt.num_exec_queues;
- if (q->compute.pfence) {
- dma_fence_enable_sw_signaling(q->compute.pfence);
- dma_fence_put(q->compute.pfence);
- q->compute.pfence = NULL;
+ if (q->lr.pfence) {
+ dma_fence_enable_sw_signaling(q->lr.pfence);
+ dma_fence_put(q->lr.pfence);
+ q->lr.pfence = NULL;
}
up_write(&vm->lock);
}
@@ -315,19 +311,23 @@ int __xe_vm_userptr_needs_repin(struct xe_vm *vm)
#define XE_VM_REBIND_RETRY_TIMEOUT_MS 1000
-static void xe_vm_kill(struct xe_vm *vm)
+static void xe_vm_kill(struct xe_vm *vm, bool unlocked)
{
struct xe_exec_queue *q;
lockdep_assert_held(&vm->lock);
- xe_vm_lock(vm, false);
+ if (unlocked)
+ xe_vm_lock(vm, false);
+
vm->flags |= XE_VM_FLAG_BANNED;
trace_xe_vm_kill(vm);
- list_for_each_entry(q, &vm->preempt.exec_queues, compute.link)
+ list_for_each_entry(q, &vm->preempt.exec_queues, lr.link)
q->ops->kill(q);
- xe_vm_unlock(vm);
+
+ if (unlocked)
+ xe_vm_unlock(vm);
/* TODO: Inform user the VM is banned */
}
@@ -557,7 +557,7 @@ out_unlock_outer:
if (err) {
drm_warn(&vm->xe->drm, "VM worker error: %d\n", err);
- xe_vm_kill(vm);
+ xe_vm_kill(vm, true);
}
up_write(&vm->lock);
@@ -708,37 +708,116 @@ int xe_vm_userptr_check_repin(struct xe_vm *vm)
list_empty_careful(&vm->userptr.invalidated)) ? 0 : -EAGAIN;
}
-static struct dma_fence *
-xe_vm_bind_vma(struct xe_vma *vma, struct xe_exec_queue *q,
- struct xe_sync_entry *syncs, u32 num_syncs,
- bool first_op, bool last_op);
+static void xe_vm_populate_rebind(struct xe_vma_op *op, struct xe_vma *vma,
+ u8 tile_mask)
+{
+ INIT_LIST_HEAD(&op->link);
+ op->tile_mask = tile_mask;
+ op->base.op = DRM_GPUVA_OP_MAP;
+ op->base.map.va.addr = vma->gpuva.va.addr;
+ op->base.map.va.range = vma->gpuva.va.range;
+ op->base.map.gem.obj = vma->gpuva.gem.obj;
+ op->base.map.gem.offset = vma->gpuva.gem.offset;
+ op->map.vma = vma;
+ op->map.immediate = true;
+ op->map.dumpable = vma->gpuva.flags & XE_VMA_DUMPABLE;
+ op->map.is_null = xe_vma_is_null(vma);
+}
+
+static int xe_vm_ops_add_rebind(struct xe_vma_ops *vops, struct xe_vma *vma,
+ u8 tile_mask)
+{
+ struct xe_vma_op *op;
+
+ op = kzalloc(sizeof(*op), GFP_KERNEL);
+ if (!op)
+ return -ENOMEM;
+
+ xe_vm_populate_rebind(op, vma, tile_mask);
+ list_add_tail(&op->link, &vops->list);
+
+ return 0;
+}
+
+static struct dma_fence *ops_execute(struct xe_vm *vm,
+ struct xe_vma_ops *vops);
+static void xe_vma_ops_init(struct xe_vma_ops *vops, struct xe_vm *vm,
+ struct xe_exec_queue *q,
+ struct xe_sync_entry *syncs, u32 num_syncs);
int xe_vm_rebind(struct xe_vm *vm, bool rebind_worker)
{
struct dma_fence *fence;
struct xe_vma *vma, *next;
+ struct xe_vma_ops vops;
+ struct xe_vma_op *op, *next_op;
+ int err;
lockdep_assert_held(&vm->lock);
- if (xe_vm_in_lr_mode(vm) && !rebind_worker)
+ if ((xe_vm_in_lr_mode(vm) && !rebind_worker) ||
+ list_empty(&vm->rebind_list))
return 0;
+ xe_vma_ops_init(&vops, vm, NULL, NULL, 0);
+
xe_vm_assert_held(vm);
- list_for_each_entry_safe(vma, next, &vm->rebind_list,
- combined_links.rebind) {
+ list_for_each_entry(vma, &vm->rebind_list, combined_links.rebind) {
xe_assert(vm->xe, vma->tile_present);
- list_del_init(&vma->combined_links.rebind);
if (rebind_worker)
trace_xe_vma_rebind_worker(vma);
else
trace_xe_vma_rebind_exec(vma);
- fence = xe_vm_bind_vma(vma, NULL, NULL, 0, false, false);
- if (IS_ERR(fence))
- return PTR_ERR(fence);
+
+ err = xe_vm_ops_add_rebind(&vops, vma,
+ vma->tile_present);
+ if (err)
+ goto free_ops;
+ }
+
+ fence = ops_execute(vm, &vops);
+ if (IS_ERR(fence)) {
+ err = PTR_ERR(fence);
+ } else {
dma_fence_put(fence);
+ list_for_each_entry_safe(vma, next, &vm->rebind_list,
+ combined_links.rebind)
+ list_del_init(&vma->combined_links.rebind);
+ }
+free_ops:
+ list_for_each_entry_safe(op, next_op, &vops.list, link) {
+ list_del(&op->link);
+ kfree(op);
}
- return 0;
+ return err;
+}
+
+struct dma_fence *xe_vma_rebind(struct xe_vm *vm, struct xe_vma *vma, u8 tile_mask)
+{
+ struct dma_fence *fence = NULL;
+ struct xe_vma_ops vops;
+ struct xe_vma_op *op, *next_op;
+ int err;
+
+ lockdep_assert_held(&vm->lock);
+ xe_vm_assert_held(vm);
+ xe_assert(vm->xe, xe_vm_in_fault_mode(vm));
+
+ xe_vma_ops_init(&vops, vm, NULL, NULL, 0);
+
+ err = xe_vm_ops_add_rebind(&vops, vma, tile_mask);
+ if (err)
+ return ERR_PTR(err);
+
+ fence = ops_execute(vm, &vops);
+
+ list_for_each_entry_safe(op, next_op, &vops.list, link) {
+ list_del(&op->link);
+ kfree(op);
+ }
+
+ return fence;
}
static void xe_vma_free(struct xe_vma *vma)
@@ -805,7 +884,7 @@ static struct xe_vma *xe_vma_create(struct xe_vm *vm,
for_each_tile(tile, vm->xe, id)
vma->tile_mask |= 0x1 << id;
- if (GRAPHICS_VER(vm->xe) >= 20 || vm->xe->info.platform == XE_PVC)
+ if (vm->xe->info.has_atomic_enable_pte_bit)
vma->gpuva.flags |= XE_VMA_ATOMIC_PTE_BIT;
vma->pat_index = pat_index;
@@ -1173,6 +1252,8 @@ static const struct xe_pt_ops xelp_pt_ops = {
.pde_encode_bo = xelp_pde_encode_bo,
};
+static void vm_destroy_work_func(struct work_struct *w);
+
/**
* xe_vm_create_scratch() - Setup a scratch memory pagetable tree for the
* given tile and vm.
@@ -1254,6 +1335,8 @@ struct xe_vm *xe_vm_create(struct xe_device *xe, u32 flags)
ttm_lru_bulk_move_init(&vm->lru_bulk_move);
+ 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 */
@@ -1262,7 +1345,13 @@ struct xe_vm *xe_vm_create(struct xe_device *xe, u32 flags)
vm->pt_ops = &xelp_pt_ops;
- if (!(flags & XE_VM_FLAG_MIGRATION))
+ /*
+ * Long-running workloads are not protected by the scheduler references.
+ * By design, run_job for long-running workloads returns NULL and the
+ * scheduler drops all the references of it, hence protecting the VM
+ * for this case is necessary.
+ */
+ if (flags & XE_VM_FLAG_LR_MODE)
xe_pm_runtime_get_noresume(xe);
vm_resv_obj = drm_gpuvm_resv_object_alloc(&xe->drm);
@@ -1276,7 +1365,7 @@ struct xe_vm *xe_vm_create(struct xe_device *xe, u32 flags)
drm_gem_object_put(vm_resv_obj);
- err = dma_resv_lock_interruptible(xe_vm_resv(vm), NULL);
+ err = xe_vm_lock(vm, true);
if (err)
goto err_close;
@@ -1320,7 +1409,7 @@ struct xe_vm *xe_vm_create(struct xe_device *xe, u32 flags)
xe_pt_populate_empty(tile, vm, vm->pt_root[id]);
}
- dma_resv_unlock(xe_vm_resv(vm));
+ xe_vm_unlock(vm);
/* Kernel migration VM shouldn't have a circular loop.. */
if (!(flags & XE_VM_FLAG_MIGRATION)) {
@@ -1362,7 +1451,7 @@ struct xe_vm *xe_vm_create(struct xe_device *xe, u32 flags)
return vm;
err_unlock_close:
- dma_resv_unlock(xe_vm_resv(vm));
+ xe_vm_unlock(vm);
err_close:
xe_vm_close_and_put(vm);
return ERR_PTR(err);
@@ -1373,7 +1462,7 @@ err_no_resv:
xe_range_fence_tree_fini(&vm->rftree[id]);
ttm_lru_bulk_move_fini(&xe->ttm, &vm->lru_bulk_move);
kfree(vm);
- if (!(flags & XE_VM_FLAG_MIGRATION))
+ if (flags & XE_VM_FLAG_LR_MODE)
xe_pm_runtime_put(xe);
return ERR_PTR(err);
}
@@ -1492,9 +1581,10 @@ void xe_vm_close_and_put(struct xe_vm *vm)
xe_vm_put(vm);
}
-static void xe_vm_free(struct drm_gpuvm *gpuvm)
+static void vm_destroy_work_func(struct work_struct *w)
{
- struct xe_vm *vm = container_of(gpuvm, struct xe_vm, gpuvm);
+ struct xe_vm *vm =
+ container_of(w, struct xe_vm, destroy_work);
struct xe_device *xe = vm->xe;
struct xe_tile *tile;
u8 id;
@@ -1507,7 +1597,7 @@ static void xe_vm_free(struct drm_gpuvm *gpuvm)
mutex_destroy(&vm->snap_mutex);
- if (!(vm->flags & XE_VM_FLAG_MIGRATION))
+ if (vm->flags & XE_VM_FLAG_LR_MODE)
xe_pm_runtime_put(xe);
for_each_tile(tile, xe, id)
@@ -1518,6 +1608,14 @@ static void xe_vm_free(struct drm_gpuvm *gpuvm)
kfree(vm);
}
+static void xe_vm_free(struct drm_gpuvm *gpuvm)
+{
+ struct xe_vm *vm = container_of(gpuvm, struct xe_vm, gpuvm);
+
+ /* To destroy the VM we need to be able to sleep */
+ queue_work(system_unbound_wq, &vm->destroy_work);
+}
+
struct xe_vm *xe_vm_lookup(struct xe_file *xef, u32 id)
{
struct xe_vm *vm;
@@ -1554,23 +1652,13 @@ xe_vm_unbind_vma(struct xe_vma *vma, struct xe_exec_queue *q,
struct dma_fence *fence = NULL;
struct dma_fence **fences = NULL;
struct dma_fence_array *cf = NULL;
- int cur_fence = 0, i;
+ int cur_fence = 0;
int number_tiles = hweight8(vma->tile_present);
int err;
u8 id;
trace_xe_vma_unbind(vma);
- if (vma->ufence) {
- struct xe_user_fence * const f = vma->ufence;
-
- if (!xe_sync_ufence_get_status(f))
- return ERR_PTR(-EBUSY);
-
- vma->ufence = NULL;
- xe_sync_ufence_put(f);
- }
-
if (number_tiles > 1) {
fences = kmalloc_array(number_tiles, sizeof(*fences),
GFP_KERNEL);
@@ -1612,10 +1700,6 @@ next:
fence = cf ? &cf->base : !fence ?
xe_exec_queue_last_fence_get(wait_exec_queue, vm) : fence;
- if (last_op) {
- for (i = 0; i < num_syncs; i++)
- xe_sync_entry_signal(&syncs[i], fence);
- }
return fence;
@@ -1632,15 +1716,15 @@ err_fences:
static struct dma_fence *
xe_vm_bind_vma(struct xe_vma *vma, struct xe_exec_queue *q,
struct xe_sync_entry *syncs, u32 num_syncs,
- bool first_op, bool last_op)
+ u8 tile_mask, bool first_op, bool last_op)
{
struct xe_tile *tile;
struct dma_fence *fence;
struct dma_fence **fences = NULL;
struct dma_fence_array *cf = NULL;
struct xe_vm *vm = xe_vma_vm(vma);
- int cur_fence = 0, i;
- int number_tiles = hweight8(vma->tile_mask);
+ int cur_fence = 0;
+ int number_tiles = hweight8(tile_mask);
int err;
u8 id;
@@ -1654,7 +1738,7 @@ xe_vm_bind_vma(struct xe_vma *vma, struct xe_exec_queue *q,
}
for_each_tile(tile, vm->xe, id) {
- if (!(vma->tile_mask & BIT(id)))
+ if (!(tile_mask & BIT(id)))
goto next;
fence = __xe_pt_bind_vma(tile, vma, q ? q : vm->q[id],
@@ -1686,12 +1770,6 @@ next:
}
}
- if (last_op) {
- for (i = 0; i < num_syncs; i++)
- xe_sync_entry_signal(&syncs[i],
- cf ? &cf->base : fence);
- }
-
return cf ? &cf->base : fence;
err_fences:
@@ -1719,87 +1797,46 @@ find_ufence_get(struct xe_sync_entry *syncs, u32 num_syncs)
return NULL;
}
-static int __xe_vm_bind(struct xe_vm *vm, struct xe_vma *vma,
- struct xe_exec_queue *q, struct xe_sync_entry *syncs,
- u32 num_syncs, bool immediate, bool first_op,
- bool last_op)
+static struct dma_fence *
+xe_vm_bind(struct xe_vm *vm, struct xe_vma *vma, struct xe_exec_queue *q,
+ struct xe_bo *bo, struct xe_sync_entry *syncs, u32 num_syncs,
+ u8 tile_mask, bool immediate, bool first_op, bool last_op)
{
struct dma_fence *fence;
struct xe_exec_queue *wait_exec_queue = to_wait_exec_queue(vm, q);
- struct xe_user_fence *ufence;
xe_vm_assert_held(vm);
-
- ufence = find_ufence_get(syncs, num_syncs);
- if (vma->ufence && ufence)
- xe_sync_ufence_put(vma->ufence);
-
- vma->ufence = ufence ?: vma->ufence;
+ xe_bo_assert_held(bo);
if (immediate) {
- fence = xe_vm_bind_vma(vma, q, syncs, num_syncs, first_op,
- last_op);
+ fence = xe_vm_bind_vma(vma, q, syncs, num_syncs, tile_mask,
+ first_op, last_op);
if (IS_ERR(fence))
- return PTR_ERR(fence);
+ return fence;
} else {
- int i;
-
xe_assert(vm->xe, xe_vm_in_fault_mode(vm));
fence = xe_exec_queue_last_fence_get(wait_exec_queue, vm);
- if (last_op) {
- for (i = 0; i < num_syncs; i++)
- xe_sync_entry_signal(&syncs[i], fence);
- }
}
- if (last_op)
- xe_exec_queue_last_fence_set(wait_exec_queue, vm, fence);
- dma_fence_put(fence);
-
- return 0;
-}
-
-static int xe_vm_bind(struct xe_vm *vm, struct xe_vma *vma, struct xe_exec_queue *q,
- struct xe_bo *bo, struct xe_sync_entry *syncs,
- u32 num_syncs, bool immediate, bool first_op,
- bool last_op)
-{
- int err;
-
- xe_vm_assert_held(vm);
- xe_bo_assert_held(bo);
-
- if (bo && immediate) {
- err = xe_bo_validate(bo, vm, true);
- if (err)
- return err;
- }
-
- return __xe_vm_bind(vm, vma, q, syncs, num_syncs, immediate, first_op,
- last_op);
+ return fence;
}
-static int xe_vm_unbind(struct xe_vm *vm, struct xe_vma *vma,
- struct xe_exec_queue *q, struct xe_sync_entry *syncs,
- u32 num_syncs, bool first_op, bool last_op)
+static struct dma_fence *
+xe_vm_unbind(struct xe_vm *vm, struct xe_vma *vma,
+ struct xe_exec_queue *q, struct xe_sync_entry *syncs,
+ u32 num_syncs, bool first_op, bool last_op)
{
struct dma_fence *fence;
- struct xe_exec_queue *wait_exec_queue = to_wait_exec_queue(vm, q);
xe_vm_assert_held(vm);
xe_bo_assert_held(xe_vma_bo(vma));
fence = xe_vm_unbind_vma(vma, q, syncs, num_syncs, first_op, last_op);
if (IS_ERR(fence))
- return PTR_ERR(fence);
-
- xe_vma_destroy(vma, fence);
- if (last_op)
- xe_exec_queue_last_fence_set(wait_exec_queue, vm, fence);
- dma_fence_put(fence);
+ return fence;
- return 0;
+ return fence;
}
#define ALL_DRM_XE_VM_CREATE_FLAGS (DRM_XE_VM_CREATE_FLAG_SCRATCH_PAGE | \
@@ -1942,40 +1979,18 @@ static const u32 region_to_mem_type[] = {
XE_PL_VRAM1,
};
-static int xe_vm_prefetch(struct xe_vm *vm, struct xe_vma *vma,
- struct xe_exec_queue *q, u32 region,
- struct xe_sync_entry *syncs, u32 num_syncs,
- bool first_op, bool last_op)
+static struct dma_fence *
+xe_vm_prefetch(struct xe_vm *vm, struct xe_vma *vma,
+ struct xe_exec_queue *q, struct xe_sync_entry *syncs,
+ u32 num_syncs, bool first_op, bool last_op)
{
struct xe_exec_queue *wait_exec_queue = to_wait_exec_queue(vm, q);
- int err;
-
- xe_assert(vm->xe, region < ARRAY_SIZE(region_to_mem_type));
-
- if (!xe_vma_has_no_bo(vma)) {
- err = xe_bo_migrate(xe_vma_bo(vma), region_to_mem_type[region]);
- if (err)
- return err;
- }
if (vma->tile_mask != (vma->tile_present & ~vma->tile_invalidated)) {
return xe_vm_bind(vm, vma, q, xe_vma_bo(vma), syncs, num_syncs,
- true, first_op, last_op);
+ vma->tile_mask, true, first_op, last_op);
} else {
- int i;
-
- /* Nothing to do, signal fences now */
- if (last_op) {
- for (i = 0; i < num_syncs; i++) {
- struct dma_fence *fence =
- xe_exec_queue_last_fence_get(wait_exec_queue, vm);
-
- xe_sync_entry_signal(&syncs[i], fence);
- dma_fence_put(fence);
- }
- }
-
- return 0;
+ return xe_exec_queue_last_fence_get(wait_exec_queue, vm);
}
}
@@ -2125,7 +2140,7 @@ static struct xe_vma *new_vma(struct xe_vm *vm, struct drm_gpuva_op_map *op,
struct xe_bo *bo = op->gem.obj ? gem_to_xe_bo(op->gem.obj) : NULL;
struct drm_exec exec;
struct xe_vma *vma;
- int err;
+ int err = 0;
lockdep_assert_held_write(&vm->lock);
@@ -2150,23 +2165,22 @@ static struct xe_vma *new_vma(struct xe_vm *vm, struct drm_gpuva_op_map *op,
vma = xe_vma_create(vm, bo, op->gem.offset,
op->va.addr, op->va.addr +
op->va.range - 1, pat_index, flags);
- if (bo)
- drm_exec_fini(&exec);
+ if (IS_ERR(vma))
+ goto err_unlock;
- if (xe_vma_is_userptr(vma)) {
+ if (xe_vma_is_userptr(vma))
err = xe_vma_userptr_pin_pages(to_userptr_vma(vma));
- if (err) {
- prep_vma_destroy(vm, vma, false);
- xe_vma_destroy_unlocked(vma);
- return ERR_PTR(err);
- }
- } else if (!xe_vma_has_no_bo(vma) && !bo->vm) {
+ else if (!xe_vma_has_no_bo(vma) && !bo->vm)
err = add_preempt_fences(vm, bo);
- if (err) {
- prep_vma_destroy(vm, vma, false);
- xe_vma_destroy_unlocked(vma);
- return ERR_PTR(err);
- }
+
+err_unlock:
+ if (bo)
+ drm_exec_fini(&exec);
+
+ if (err) {
+ prep_vma_destroy(vm, vma, false);
+ xe_vma_destroy_unlocked(vma);
+ vma = ERR_PTR(err);
}
return vma;
@@ -2271,23 +2285,28 @@ static int xe_vma_op_commit(struct xe_vm *vm, struct xe_vma_op *op)
static int vm_bind_ioctl_ops_parse(struct xe_vm *vm, struct xe_exec_queue *q,
struct drm_gpuva_ops *ops,
struct xe_sync_entry *syncs, u32 num_syncs,
- struct list_head *ops_list, bool last)
+ struct xe_vma_ops *vops, bool last)
{
struct xe_device *xe = vm->xe;
struct xe_vma_op *last_op = NULL;
struct drm_gpuva_op *__op;
+ struct xe_tile *tile;
+ u8 id, tile_mask = 0;
int err = 0;
lockdep_assert_held_write(&vm->lock);
+ for_each_tile(tile, vm->xe, id)
+ tile_mask |= 0x1 << id;
+
drm_gpuva_for_each_op(__op, ops) {
struct xe_vma_op *op = gpuva_op_to_vma_op(__op);
struct xe_vma *vma;
- bool first = list_empty(ops_list);
+ bool first = list_empty(&vops->list);
unsigned int flags = 0;
INIT_LIST_HEAD(&op->link);
- list_add_tail(&op->link, ops_list);
+ list_add_tail(&op->link, &vops->list);
if (first) {
op->flags |= XE_VMA_OP_FIRST;
@@ -2296,6 +2315,7 @@ static int vm_bind_ioctl_ops_parse(struct xe_vm *vm, struct xe_exec_queue *q,
}
op->q = q;
+ op->tile_mask = tile_mask;
switch (op->base.op) {
case DRM_GPUVA_OP_MAP:
@@ -2413,12 +2433,11 @@ static int vm_bind_ioctl_ops_parse(struct xe_vm *vm, struct xe_exec_queue *q,
}
/* FIXME: Unhandled corner case */
- XE_WARN_ON(!last_op && last && !list_empty(ops_list));
+ XE_WARN_ON(!last_op && last && !list_empty(&vops->list));
if (!last_op)
return 0;
- last_op->ops = ops;
if (last) {
last_op->flags |= XE_VMA_OP_LAST;
last_op->num_syncs = num_syncs;
@@ -2428,27 +2447,24 @@ static int vm_bind_ioctl_ops_parse(struct xe_vm *vm, struct xe_exec_queue *q,
return 0;
}
-static int op_execute(struct drm_exec *exec, struct xe_vm *vm,
- struct xe_vma *vma, struct xe_vma_op *op)
+static struct dma_fence *op_execute(struct xe_vm *vm, struct xe_vma *vma,
+ struct xe_vma_op *op)
{
- int err;
-
- lockdep_assert_held_write(&vm->lock);
+ struct dma_fence *fence = NULL;
- err = xe_vm_lock_vma(exec, vma);
- if (err)
- return err;
+ lockdep_assert_held(&vm->lock);
xe_vm_assert_held(vm);
xe_bo_assert_held(xe_vma_bo(vma));
switch (op->base.op) {
case DRM_GPUVA_OP_MAP:
- err = xe_vm_bind(vm, vma, op->q, xe_vma_bo(vma),
- op->syncs, op->num_syncs,
- op->map.immediate || !xe_vm_in_fault_mode(vm),
- op->flags & XE_VMA_OP_FIRST,
- op->flags & XE_VMA_OP_LAST);
+ fence = xe_vm_bind(vm, vma, op->q, xe_vma_bo(vma),
+ op->syncs, op->num_syncs,
+ op->tile_mask,
+ op->map.immediate || !xe_vm_in_fault_mode(vm),
+ op->flags & XE_VMA_OP_FIRST,
+ op->flags & XE_VMA_OP_LAST);
break;
case DRM_GPUVA_OP_REMAP:
{
@@ -2458,37 +2474,41 @@ static int op_execute(struct drm_exec *exec, struct xe_vm *vm,
if (!op->remap.unmap_done) {
if (prev || next)
vma->gpuva.flags |= XE_VMA_FIRST_REBIND;
- err = xe_vm_unbind(vm, vma, op->q, op->syncs,
- op->num_syncs,
- op->flags & XE_VMA_OP_FIRST,
- op->flags & XE_VMA_OP_LAST &&
- !prev && !next);
- if (err)
+ fence = xe_vm_unbind(vm, vma, op->q, op->syncs,
+ op->num_syncs,
+ op->flags & XE_VMA_OP_FIRST,
+ op->flags & XE_VMA_OP_LAST &&
+ !prev && !next);
+ if (IS_ERR(fence))
break;
op->remap.unmap_done = true;
}
if (prev) {
op->remap.prev->gpuva.flags |= XE_VMA_LAST_REBIND;
- err = xe_vm_bind(vm, op->remap.prev, op->q,
- xe_vma_bo(op->remap.prev), op->syncs,
- op->num_syncs, true, false,
- op->flags & XE_VMA_OP_LAST && !next);
+ dma_fence_put(fence);
+ fence = xe_vm_bind(vm, op->remap.prev, op->q,
+ xe_vma_bo(op->remap.prev), op->syncs,
+ op->num_syncs,
+ op->remap.prev->tile_mask, true,
+ false,
+ op->flags & XE_VMA_OP_LAST && !next);
op->remap.prev->gpuva.flags &= ~XE_VMA_LAST_REBIND;
- if (err)
+ if (IS_ERR(fence))
break;
op->remap.prev = NULL;
}
if (next) {
op->remap.next->gpuva.flags |= XE_VMA_LAST_REBIND;
- err = xe_vm_bind(vm, op->remap.next, op->q,
- xe_vma_bo(op->remap.next),
- op->syncs, op->num_syncs,
- true, false,
- op->flags & XE_VMA_OP_LAST);
+ dma_fence_put(fence);
+ fence = xe_vm_bind(vm, op->remap.next, op->q,
+ xe_vma_bo(op->remap.next),
+ op->syncs, op->num_syncs,
+ op->remap.next->tile_mask, true,
+ false, op->flags & XE_VMA_OP_LAST);
op->remap.next->gpuva.flags &= ~XE_VMA_LAST_REBIND;
- if (err)
+ if (IS_ERR(fence))
break;
op->remap.next = NULL;
}
@@ -2496,43 +2516,35 @@ static int op_execute(struct drm_exec *exec, struct xe_vm *vm,
break;
}
case DRM_GPUVA_OP_UNMAP:
- err = xe_vm_unbind(vm, vma, op->q, op->syncs,
- op->num_syncs, op->flags & XE_VMA_OP_FIRST,
- op->flags & XE_VMA_OP_LAST);
+ fence = xe_vm_unbind(vm, vma, op->q, op->syncs,
+ op->num_syncs, op->flags & XE_VMA_OP_FIRST,
+ op->flags & XE_VMA_OP_LAST);
break;
case DRM_GPUVA_OP_PREFETCH:
- err = xe_vm_prefetch(vm, vma, op->q, op->prefetch.region,
- op->syncs, op->num_syncs,
- op->flags & XE_VMA_OP_FIRST,
- op->flags & XE_VMA_OP_LAST);
+ fence = xe_vm_prefetch(vm, vma, op->q, op->syncs, op->num_syncs,
+ op->flags & XE_VMA_OP_FIRST,
+ op->flags & XE_VMA_OP_LAST);
break;
default:
drm_warn(&vm->xe->drm, "NOT POSSIBLE");
}
- if (err)
+ if (IS_ERR(fence))
trace_xe_vma_fail(vma);
- return err;
+ return fence;
}
-static int __xe_vma_op_execute(struct xe_vm *vm, struct xe_vma *vma,
- struct xe_vma_op *op)
+static struct dma_fence *
+__xe_vma_op_execute(struct xe_vm *vm, struct xe_vma *vma,
+ struct xe_vma_op *op)
{
- struct drm_exec exec;
+ struct dma_fence *fence;
int err;
retry_userptr:
- drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT, 0);
- drm_exec_until_all_locked(&exec) {
- err = op_execute(&exec, vm, vma, op);
- drm_exec_retry_on_contention(&exec);
- if (err)
- break;
- }
- drm_exec_fini(&exec);
-
- if (err == -EAGAIN) {
+ fence = op_execute(vm, vma, op);
+ if (IS_ERR(fence) && PTR_ERR(fence) == -EAGAIN) {
lockdep_assert_held_write(&vm->lock);
if (op->base.op == DRM_GPUVA_OP_REMAP) {
@@ -2549,22 +2561,24 @@ retry_userptr:
if (!err)
goto retry_userptr;
+ fence = ERR_PTR(err);
trace_xe_vma_fail(vma);
}
}
- return err;
+ return fence;
}
-static int xe_vma_op_execute(struct xe_vm *vm, struct xe_vma_op *op)
+static struct dma_fence *
+xe_vma_op_execute(struct xe_vm *vm, struct xe_vma_op *op)
{
- int ret = 0;
+ struct dma_fence *fence = ERR_PTR(-ENOMEM);
- lockdep_assert_held_write(&vm->lock);
+ lockdep_assert_held(&vm->lock);
switch (op->base.op) {
case DRM_GPUVA_OP_MAP:
- ret = __xe_vma_op_execute(vm, op->map.vma, op);
+ fence = __xe_vma_op_execute(vm, op->map.vma, op);
break;
case DRM_GPUVA_OP_REMAP:
{
@@ -2577,42 +2591,23 @@ static int xe_vma_op_execute(struct xe_vm *vm, struct xe_vma_op *op)
else
vma = op->remap.next;
- ret = __xe_vma_op_execute(vm, vma, op);
+ fence = __xe_vma_op_execute(vm, vma, op);
break;
}
case DRM_GPUVA_OP_UNMAP:
- ret = __xe_vma_op_execute(vm, gpuva_to_vma(op->base.unmap.va),
- op);
+ fence = __xe_vma_op_execute(vm, gpuva_to_vma(op->base.unmap.va),
+ op);
break;
case DRM_GPUVA_OP_PREFETCH:
- ret = __xe_vma_op_execute(vm,
- gpuva_to_vma(op->base.prefetch.va),
- op);
+ fence = __xe_vma_op_execute(vm,
+ gpuva_to_vma(op->base.prefetch.va),
+ op);
break;
default:
drm_warn(&vm->xe->drm, "NOT POSSIBLE");
}
- return ret;
-}
-
-static void xe_vma_op_cleanup(struct xe_vm *vm, struct xe_vma_op *op)
-{
- bool last = op->flags & XE_VMA_OP_LAST;
-
- if (last) {
- while (op->num_syncs--)
- xe_sync_entry_cleanup(&op->syncs[op->num_syncs]);
- kfree(op->syncs);
- if (op->q)
- xe_exec_queue_put(op->q);
- }
- if (!list_empty(&op->link))
- list_del(&op->link);
- if (op->ops)
- drm_gpuva_ops_free(&vm->gpuvm, op->ops);
- if (last)
- xe_vm_put(vm);
+ return fence;
}
static void xe_vma_op_unwind(struct xe_vm *vm, struct xe_vma_op *op,
@@ -2691,34 +2686,223 @@ static void vm_bind_ioctl_ops_unwind(struct xe_vm *vm,
op->flags & XE_VMA_OP_PREV_COMMITTED,
op->flags & XE_VMA_OP_NEXT_COMMITTED);
}
+ }
+}
+
+static int vma_lock_and_validate(struct drm_exec *exec, struct xe_vma *vma,
+ bool validate)
+{
+ struct xe_bo *bo = xe_vma_bo(vma);
+ int err = 0;
- drm_gpuva_ops_free(&vm->gpuvm, __ops);
+ if (bo) {
+ if (!bo->vm)
+ err = drm_exec_lock_obj(exec, &bo->ttm.base);
+ if (!err && validate)
+ err = xe_bo_validate(bo, xe_vma_vm(vma), true);
}
+
+ return err;
}
-static int vm_bind_ioctl_ops_execute(struct xe_vm *vm,
- struct list_head *ops_list)
+static int check_ufence(struct xe_vma *vma)
+{
+ if (vma->ufence) {
+ struct xe_user_fence * const f = vma->ufence;
+
+ if (!xe_sync_ufence_get_status(f))
+ return -EBUSY;
+
+ vma->ufence = NULL;
+ xe_sync_ufence_put(f);
+ }
+
+ return 0;
+}
+
+static int op_lock_and_prep(struct drm_exec *exec, struct xe_vm *vm,
+ struct xe_vma_op *op)
+{
+ int err = 0;
+
+ switch (op->base.op) {
+ case DRM_GPUVA_OP_MAP:
+ err = vma_lock_and_validate(exec, op->map.vma,
+ !xe_vm_in_fault_mode(vm) ||
+ op->map.immediate);
+ break;
+ case DRM_GPUVA_OP_REMAP:
+ err = check_ufence(gpuva_to_vma(op->base.remap.unmap->va));
+ if (err)
+ break;
+
+ err = vma_lock_and_validate(exec,
+ gpuva_to_vma(op->base.remap.unmap->va),
+ false);
+ if (!err && op->remap.prev)
+ err = vma_lock_and_validate(exec, op->remap.prev, true);
+ if (!err && op->remap.next)
+ err = vma_lock_and_validate(exec, op->remap.next, true);
+ break;
+ case DRM_GPUVA_OP_UNMAP:
+ err = check_ufence(gpuva_to_vma(op->base.unmap.va));
+ if (err)
+ break;
+
+ err = vma_lock_and_validate(exec,
+ gpuva_to_vma(op->base.unmap.va),
+ false);
+ break;
+ case DRM_GPUVA_OP_PREFETCH:
+ {
+ struct xe_vma *vma = gpuva_to_vma(op->base.prefetch.va);
+ u32 region = op->prefetch.region;
+
+ xe_assert(vm->xe, region <= ARRAY_SIZE(region_to_mem_type));
+
+ err = vma_lock_and_validate(exec,
+ gpuva_to_vma(op->base.prefetch.va),
+ false);
+ if (!err && !xe_vma_has_no_bo(vma))
+ err = xe_bo_migrate(xe_vma_bo(vma),
+ region_to_mem_type[region]);
+ break;
+ }
+ default:
+ drm_warn(&vm->xe->drm, "NOT POSSIBLE");
+ }
+
+ return err;
+}
+
+static int vm_bind_ioctl_ops_lock_and_prep(struct drm_exec *exec,
+ struct xe_vm *vm,
+ struct xe_vma_ops *vops)
+{
+ struct xe_vma_op *op;
+ int err;
+
+ err = drm_exec_lock_obj(exec, xe_vm_obj(vm));
+ if (err)
+ return err;
+
+ list_for_each_entry(op, &vops->list, link) {
+ err = op_lock_and_prep(exec, vm, op);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static struct dma_fence *ops_execute(struct xe_vm *vm,
+ struct xe_vma_ops *vops)
{
struct xe_vma_op *op, *next;
+ struct dma_fence *fence = NULL;
+
+ list_for_each_entry_safe(op, next, &vops->list, link) {
+ dma_fence_put(fence);
+ fence = xe_vma_op_execute(vm, op);
+ if (IS_ERR(fence)) {
+ drm_warn(&vm->xe->drm, "VM op(%d) failed with %ld",
+ op->base.op, PTR_ERR(fence));
+ fence = ERR_PTR(-ENOSPC);
+ break;
+ }
+ }
+
+ return fence;
+}
+
+static void vma_add_ufence(struct xe_vma *vma, struct xe_user_fence *ufence)
+{
+ if (vma->ufence)
+ xe_sync_ufence_put(vma->ufence);
+ vma->ufence = __xe_sync_ufence_get(ufence);
+}
+
+static void op_add_ufence(struct xe_vm *vm, struct xe_vma_op *op,
+ struct xe_user_fence *ufence)
+{
+ switch (op->base.op) {
+ case DRM_GPUVA_OP_MAP:
+ vma_add_ufence(op->map.vma, ufence);
+ break;
+ case DRM_GPUVA_OP_REMAP:
+ if (op->remap.prev)
+ vma_add_ufence(op->remap.prev, ufence);
+ if (op->remap.next)
+ vma_add_ufence(op->remap.next, ufence);
+ break;
+ case DRM_GPUVA_OP_UNMAP:
+ break;
+ case DRM_GPUVA_OP_PREFETCH:
+ vma_add_ufence(gpuva_to_vma(op->base.prefetch.va), ufence);
+ break;
+ default:
+ drm_warn(&vm->xe->drm, "NOT POSSIBLE");
+ }
+}
+
+static void vm_bind_ioctl_ops_fini(struct xe_vm *vm, struct xe_vma_ops *vops,
+ struct dma_fence *fence)
+{
+ struct xe_exec_queue *wait_exec_queue = to_wait_exec_queue(vm, vops->q);
+ struct xe_user_fence *ufence;
+ struct xe_vma_op *op;
+ int i;
+
+ ufence = find_ufence_get(vops->syncs, vops->num_syncs);
+ list_for_each_entry(op, &vops->list, link) {
+ if (ufence)
+ op_add_ufence(vm, op, ufence);
+
+ if (op->base.op == DRM_GPUVA_OP_UNMAP)
+ xe_vma_destroy(gpuva_to_vma(op->base.unmap.va), fence);
+ else if (op->base.op == DRM_GPUVA_OP_REMAP)
+ xe_vma_destroy(gpuva_to_vma(op->base.remap.unmap->va),
+ fence);
+ }
+ if (ufence)
+ xe_sync_ufence_put(ufence);
+ for (i = 0; i < vops->num_syncs; i++)
+ xe_sync_entry_signal(vops->syncs + i, fence);
+ xe_exec_queue_last_fence_set(wait_exec_queue, vm, fence);
+ dma_fence_put(fence);
+}
+
+static int vm_bind_ioctl_ops_execute(struct xe_vm *vm,
+ struct xe_vma_ops *vops)
+{
+ struct drm_exec exec;
+ struct dma_fence *fence;
int err;
lockdep_assert_held_write(&vm->lock);
- list_for_each_entry_safe(op, next, ops_list, link) {
- err = xe_vma_op_execute(vm, op);
- if (err) {
- drm_warn(&vm->xe->drm, "VM op(%d) failed with %d",
- op->base.op, err);
- /*
- * FIXME: Killing VM rather than proper error handling
- */
- xe_vm_kill(vm);
- return -ENOSPC;
+ drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT |
+ DRM_EXEC_IGNORE_DUPLICATES, 0);
+ drm_exec_until_all_locked(&exec) {
+ err = vm_bind_ioctl_ops_lock_and_prep(&exec, vm, vops);
+ drm_exec_retry_on_contention(&exec);
+ if (err)
+ goto unlock;
+
+ fence = ops_execute(vm, vops);
+ if (IS_ERR(fence)) {
+ err = PTR_ERR(fence);
+ /* FIXME: Killing VM rather than proper error handling */
+ xe_vm_kill(vm, false);
+ goto unlock;
+ } else {
+ vm_bind_ioctl_ops_fini(vm, vops, fence);
}
- xe_vma_op_cleanup(vm, op);
}
- return 0;
+unlock:
+ drm_exec_fini(&exec);
+ return err;
}
#define SUPPORTED_FLAGS \
@@ -2866,6 +3050,58 @@ static int vm_bind_ioctl_signal_fences(struct xe_vm *vm,
return err;
}
+static void xe_vma_ops_init(struct xe_vma_ops *vops, struct xe_vm *vm,
+ struct xe_exec_queue *q,
+ struct xe_sync_entry *syncs, u32 num_syncs)
+{
+ memset(vops, 0, sizeof(*vops));
+ INIT_LIST_HEAD(&vops->list);
+ vops->vm = vm;
+ vops->q = q;
+ vops->syncs = syncs;
+ vops->num_syncs = num_syncs;
+}
+
+static int xe_vm_bind_ioctl_validate_bo(struct xe_device *xe, struct xe_bo *bo,
+ u64 addr, u64 range, u64 obj_offset,
+ u16 pat_index)
+{
+ u16 coh_mode;
+
+ if (XE_IOCTL_DBG(xe, range > bo->size) ||
+ XE_IOCTL_DBG(xe, obj_offset >
+ bo->size - range)) {
+ return -EINVAL;
+ }
+
+ if (bo->flags & XE_BO_FLAG_INTERNAL_64K) {
+ if (XE_IOCTL_DBG(xe, obj_offset &
+ XE_64K_PAGE_MASK) ||
+ XE_IOCTL_DBG(xe, addr & XE_64K_PAGE_MASK) ||
+ XE_IOCTL_DBG(xe, range & XE_64K_PAGE_MASK)) {
+ return -EINVAL;
+ }
+ }
+
+ coh_mode = xe_pat_index_get_coh_mode(xe, pat_index);
+ if (bo->cpu_caching) {
+ if (XE_IOCTL_DBG(xe, coh_mode == XE_COH_NONE &&
+ bo->cpu_caching == DRM_XE_GEM_CPU_CACHING_WB)) {
+ return -EINVAL;
+ }
+ } else if (XE_IOCTL_DBG(xe, coh_mode == XE_COH_NONE)) {
+ /*
+ * Imported dma-buf from a different device should
+ * require 1way or 2way coherency since we don't know
+ * how it was mapped on the CPU. Just assume is it
+ * potentially cached on CPU side.
+ */
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
int xe_vm_bind_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
{
struct xe_device *xe = to_xe_device(dev);
@@ -2879,7 +3115,7 @@ int xe_vm_bind_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
u32 num_syncs, num_ufence = 0;
struct xe_sync_entry *syncs = NULL;
struct drm_xe_vm_bind_op *bind_ops;
- LIST_HEAD(ops_list);
+ struct xe_vma_ops vops;
int err;
int i;
@@ -2949,7 +3185,6 @@ int xe_vm_bind_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
u32 obj = bind_ops[i].obj;
u64 obj_offset = bind_ops[i].obj_offset;
u16 pat_index = bind_ops[i].pat_index;
- u16 coh_mode;
if (!obj)
continue;
@@ -2961,40 +3196,10 @@ int xe_vm_bind_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
}
bos[i] = gem_to_xe_bo(gem_obj);
- if (XE_IOCTL_DBG(xe, range > bos[i]->size) ||
- XE_IOCTL_DBG(xe, obj_offset >
- bos[i]->size - range)) {
- err = -EINVAL;
- goto put_obj;
- }
-
- if (bos[i]->flags & XE_BO_FLAG_INTERNAL_64K) {
- if (XE_IOCTL_DBG(xe, obj_offset &
- XE_64K_PAGE_MASK) ||
- XE_IOCTL_DBG(xe, addr & XE_64K_PAGE_MASK) ||
- XE_IOCTL_DBG(xe, range & XE_64K_PAGE_MASK)) {
- err = -EINVAL;
- goto put_obj;
- }
- }
-
- coh_mode = xe_pat_index_get_coh_mode(xe, pat_index);
- if (bos[i]->cpu_caching) {
- if (XE_IOCTL_DBG(xe, coh_mode == XE_COH_NONE &&
- bos[i]->cpu_caching == DRM_XE_GEM_CPU_CACHING_WB)) {
- err = -EINVAL;
- goto put_obj;
- }
- } else if (XE_IOCTL_DBG(xe, coh_mode == XE_COH_NONE)) {
- /*
- * Imported dma-buf from a different device should
- * require 1way or 2way coherency since we don't know
- * how it was mapped on the CPU. Just assume is it
- * potentially cached on CPU side.
- */
- err = -EINVAL;
+ err = xe_vm_bind_ioctl_validate_bo(xe, bos[i], addr, range,
+ obj_offset, pat_index);
+ if (err)
goto put_obj;
- }
}
if (args->num_syncs) {
@@ -3030,6 +3235,7 @@ int xe_vm_bind_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
goto free_syncs;
}
+ xe_vma_ops_init(&vops, vm, q, syncs, num_syncs);
for (i = 0; i < args->num_binds; ++i) {
u64 range = bind_ops[i].range;
u64 addr = bind_ops[i].addr;
@@ -3049,42 +3255,25 @@ int xe_vm_bind_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
}
err = vm_bind_ioctl_ops_parse(vm, q, ops[i], syncs, num_syncs,
- &ops_list,
- i == args->num_binds - 1);
+ &vops, i == args->num_binds - 1);
if (err)
goto unwind_ops;
}
/* Nothing to do */
- if (list_empty(&ops_list)) {
+ if (list_empty(&vops.list)) {
err = -ENODATA;
goto unwind_ops;
}
- xe_vm_get(vm);
- if (q)
- xe_exec_queue_get(q);
-
- err = vm_bind_ioctl_ops_execute(vm, &ops_list);
-
- up_write(&vm->lock);
-
- if (q)
- xe_exec_queue_put(q);
- xe_vm_put(vm);
-
- for (i = 0; bos && i < args->num_binds; ++i)
- xe_bo_put(bos[i]);
-
- kvfree(bos);
- kvfree(ops);
- if (args->num_binds > 1)
- kvfree(bind_ops);
-
- return err;
+ err = vm_bind_ioctl_ops_execute(vm, &vops);
unwind_ops:
- vm_bind_ioctl_ops_unwind(vm, ops, args->num_binds);
+ if (err && err != -ENODATA)
+ vm_bind_ioctl_ops_unwind(vm, ops, args->num_binds);
+ for (i = args->num_binds - 1; i >= 0; --i)
+ if (ops[i])
+ drm_gpuva_ops_free(&vm->gpuvm, ops[i]);
free_syncs:
if (err == -ENODATA)
err = vm_bind_ioctl_signal_fences(vm, q, syncs, num_syncs);
@@ -3205,55 +3394,6 @@ int xe_vm_invalidate_vma(struct xe_vma *vma)
return 0;
}
-int xe_analyze_vm(struct drm_printer *p, struct xe_vm *vm, int gt_id)
-{
- struct drm_gpuva *gpuva;
- bool is_vram;
- uint64_t addr;
-
- if (!down_read_trylock(&vm->lock)) {
- drm_printf(p, " Failed to acquire VM lock to dump capture");
- return 0;
- }
- if (vm->pt_root[gt_id]) {
- addr = xe_bo_addr(vm->pt_root[gt_id]->bo, 0, XE_PAGE_SIZE);
- is_vram = xe_bo_is_vram(vm->pt_root[gt_id]->bo);
- drm_printf(p, " VM root: A:0x%llx %s\n", addr,
- is_vram ? "VRAM" : "SYS");
- }
-
- drm_gpuvm_for_each_va(gpuva, &vm->gpuvm) {
- struct xe_vma *vma = gpuva_to_vma(gpuva);
- bool is_userptr = xe_vma_is_userptr(vma);
- bool is_null = xe_vma_is_null(vma);
-
- if (is_null) {
- addr = 0;
- } else if (is_userptr) {
- struct sg_table *sg = to_userptr_vma(vma)->userptr.sg;
- struct xe_res_cursor cur;
-
- if (sg) {
- xe_res_first_sg(sg, 0, XE_PAGE_SIZE, &cur);
- addr = xe_res_dma(&cur);
- } else {
- addr = 0;
- }
- } else {
- addr = __xe_bo_addr(xe_vma_bo(vma), 0, XE_PAGE_SIZE);
- is_vram = xe_bo_is_vram(xe_vma_bo(vma));
- }
- drm_printf(p, " [%016llx-%016llx] S:0x%016llx A:%016llx %s\n",
- xe_vma_start(vma), xe_vma_end(vma) - 1,
- xe_vma_size(vma),
- addr, is_null ? "NULL" : is_userptr ? "USR" :
- is_vram ? "VRAM" : "SYS");
- }
- up_read(&vm->lock);
-
- return 0;
-}
-
struct xe_vm_snapshot {
unsigned long num_snaps;
struct {
@@ -3343,7 +3483,7 @@ void xe_vm_snapshot_capture_delayed(struct xe_vm_snapshot *snap)
}
if (bo) {
- dma_resv_lock(bo->ttm.base.resv, NULL);
+ xe_bo_lock(bo, false);
err = ttm_bo_vmap(&bo->ttm, &src);
if (!err) {
xe_map_memcpy_from(xe_bo_device(bo),
@@ -3352,7 +3492,7 @@ void xe_vm_snapshot_capture_delayed(struct xe_vm_snapshot *snap)
snap->snap[i].len);
ttm_bo_vunmap(&bo->ttm, &src);
}
- dma_resv_unlock(bo->ttm.base.resv);
+ xe_bo_unlock(bo);
} else {
void __user *userptr = (void __user *)(size_t)snap->snap[i].bo_ofs;
diff --git a/drivers/gpu/drm/xe/xe_vm.h b/drivers/gpu/drm/xe/xe_vm.h
index 306cd0934a19..b481608b12f1 100644
--- a/drivers/gpu/drm/xe/xe_vm.h
+++ b/drivers/gpu/drm/xe/xe_vm.h
@@ -6,6 +6,7 @@
#ifndef _XE_VM_H_
#define _XE_VM_H_
+#include "xe_assert.h"
#include "xe_bo_types.h"
#include "xe_macros.h"
#include "xe_map.h"
@@ -208,6 +209,8 @@ int __xe_vm_userptr_needs_repin(struct xe_vm *vm);
int xe_vm_userptr_check_repin(struct xe_vm *vm);
int xe_vm_rebind(struct xe_vm *vm, bool rebind_worker);
+struct dma_fence *xe_vma_rebind(struct xe_vm *vm, struct xe_vma *vma,
+ u8 tile_mask);
int xe_vm_invalidate_vma(struct xe_vma *vma);
@@ -240,8 +243,6 @@ int xe_vma_userptr_check_repin(struct xe_userptr_vma *uvma);
bool xe_vm_validate_should_retry(struct drm_exec *exec, int err, ktime_t *end);
-int xe_analyze_vm(struct drm_printer *p, struct xe_vm *vm, int gt_id);
-
int xe_vm_lock_vma(struct drm_exec *exec, struct xe_vma *vma);
int xe_vm_validate_rebind(struct xe_vm *vm, struct drm_exec *exec,
diff --git a/drivers/gpu/drm/xe/xe_vm_doc.h b/drivers/gpu/drm/xe/xe_vm_doc.h
index bdc6659891a5..4d33f310b653 100644
--- a/drivers/gpu/drm/xe/xe_vm_doc.h
+++ b/drivers/gpu/drm/xe/xe_vm_doc.h
@@ -25,7 +25,7 @@
* VM bind (create GPU mapping for a BO or userptr)
* ================================================
*
- * Creates GPU mapings for a BO or userptr within a VM. VM binds uses the same
+ * Creates GPU mappings for a BO or userptr within a VM. VM binds uses the same
* in / out fence interface (struct drm_xe_sync) as execs which allows users to
* think of binds and execs as more or less the same operation.
*
@@ -190,8 +190,8 @@
* Deferred binds in fault mode
* ----------------------------
*
- * In a VM is in fault mode (TODO: link to fault mode), new bind operations that
- * create mappings are by default are deferred to the page fault handler (first
+ * If a VM is in fault mode (TODO: link to fault mode), new bind operations that
+ * create mappings are by default deferred to the page fault handler (first
* use). This behavior can be overriden by setting the flag
* DRM_XE_VM_BIND_FLAG_IMMEDIATE which indicates to creating the mapping
* immediately.
@@ -225,7 +225,7 @@
*
* A VM in compute mode enables long running workloads and ultra low latency
* submission (ULLS). ULLS is implemented via a continuously running batch +
- * semaphores. This enables to the user to insert jump to new batch commands
+ * semaphores. This enables the user to insert jump to new batch commands
* into the continuously running batch. In both cases these batches exceed the
* time a dma fence is allowed to exist for before signaling, as such dma fences
* are not used when a VM is in compute mode. User fences (TODO: link user fence
@@ -244,7 +244,7 @@
* Once all preempt fences are signaled for a VM the kernel can safely move the
* memory and kick the rebind worker which resumes all the engines execution.
*
- * A preempt fence, for every engine using the VM, is installed the VM's
+ * A preempt fence, for every engine using the VM, is installed into the VM's
* dma-resv DMA_RESV_USAGE_PREEMPT_FENCE slot. The same preempt fence, for every
* engine using the VM, is also installed into the same dma-resv slot of every
* external BO mapped in the VM.
@@ -314,7 +314,7 @@
* signaling, and memory allocation is usually required to resolve a page
* fault, but memory allocation is not allowed to gate dma fence signaling. As
* such, dma fences are not allowed when VM is in fault mode. Because dma-fences
- * are not allowed, long running workloads and ULLS are enabled on a faulting
+ * are not allowed, only long running workloads and ULLS are enabled on a faulting
* VM.
*
* Defered VM binds
@@ -399,14 +399,14 @@
* Notice no rebind is issued in the access counter handler as the rebind will
* be issued on next page fault.
*
- * Cavets with eviction / user pointer invalidation
- * ------------------------------------------------
+ * Caveats with eviction / user pointer invalidation
+ * -------------------------------------------------
*
* In the case of eviction and user pointer invalidation on a faulting VM, there
* is no need to issue a rebind rather we just need to blow away the page tables
* for the VMAs and the page fault handler will rebind the VMAs when they fault.
- * The cavet is to update / read the page table structure the VM global lock is
- * neeeed. In both the case of eviction and user pointer invalidation locks are
+ * The caveat is to update / read the page table structure the VM global lock is
+ * needed. In both the case of eviction and user pointer invalidation locks are
* held which make acquiring the VM global lock impossible. To work around this
* every VMA maintains a list of leaf page table entries which should be written
* to zero to blow away the VMA's page tables. After writing zero to these
@@ -427,9 +427,9 @@
* VM global lock (vm->lock) - rw semaphore lock. Outer most lock which protects
* the list of userptrs mapped in the VM, the list of engines using this VM, and
* the array of external BOs mapped in the VM. When adding or removing any of the
- * aforemented state from the VM should acquire this lock in write mode. The VM
+ * aforementioned state from the VM should acquire this lock in write mode. The VM
* bind path also acquires this lock in write while the exec / compute mode
- * rebind worker acquire this lock in read mode.
+ * rebind worker acquires this lock in read mode.
*
* VM dma-resv lock (vm->ttm.base.resv->lock) - WW lock. Protects VM dma-resv
* slots which is shared with any private BO in the VM. Expected to be acquired
diff --git a/drivers/gpu/drm/xe/xe_vm_types.h b/drivers/gpu/drm/xe/xe_vm_types.h
index 72a100671e5d..ce1a63a5e3e7 100644
--- a/drivers/gpu/drm/xe/xe_vm_types.h
+++ b/drivers/gpu/drm/xe/xe_vm_types.h
@@ -178,6 +178,13 @@ struct xe_vm {
struct list_head rebind_list;
/**
+ * @destroy_work: worker to destroy VM, needed as a dma_fence signaling
+ * from an irq context can be last put and the destroy needs to be able
+ * to sleep.
+ */
+ struct work_struct destroy_work;
+
+ /**
* @rftree: range fence tree to track updates to page table structure.
* Used to implement conflict tracking between independent bind engines.
*/
@@ -323,11 +330,6 @@ enum xe_vma_op_flags {
struct xe_vma_op {
/** @base: GPUVA base operation */
struct drm_gpuva_op base;
- /**
- * @ops: GPUVA ops, when set call drm_gpuva_ops_free after this
- * operations is processed
- */
- struct drm_gpuva_ops *ops;
/** @q: exec queue for this operation */
struct xe_exec_queue *q;
/**
@@ -341,6 +343,8 @@ struct xe_vma_op {
struct list_head link;
/** @flags: operation flags */
enum xe_vma_op_flags flags;
+ /** @tile_mask: Tile mask for operation */
+ u8 tile_mask;
union {
/** @map: VMA map operation specific data */
@@ -351,4 +355,19 @@ struct xe_vma_op {
struct xe_vma_op_prefetch prefetch;
};
};
+
+/** struct xe_vma_ops - VMA operations */
+struct xe_vma_ops {
+ /** @list: list of VMA operations */
+ struct list_head list;
+ /** @vm: VM */
+ struct xe_vm *vm;
+ /** @q: exec queue these operations */
+ struct xe_exec_queue *q;
+ /** @syncs: syncs these operation */
+ struct xe_sync_entry *syncs;
+ /** @num_syncs: number of syncs */
+ u32 num_syncs;
+};
+
#endif
diff --git a/drivers/gpu/drm/xe/xe_vram.c b/drivers/gpu/drm/xe/xe_vram.c
new file mode 100644
index 000000000000..5bcd59190353
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_vram.c
@@ -0,0 +1,368 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2021-2024 Intel Corporation
+ */
+
+#include <linux/pci.h>
+
+#include <drm/drm_managed.h>
+#include <drm/drm_print.h>
+
+#include "regs/xe_bars.h"
+#include "regs/xe_gt_regs.h"
+#include "regs/xe_regs.h"
+#include "xe_assert.h"
+#include "xe_device.h"
+#include "xe_force_wake.h"
+#include "xe_gt_mcr.h"
+#include "xe_gt_sriov_vf.h"
+#include "xe_mmio.h"
+#include "xe_module.h"
+#include "xe_sriov.h"
+#include "xe_vram.h"
+
+#define BAR_SIZE_SHIFT 20
+
+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;
+
+ if (pci_resource_len(pdev, resno))
+ pci_release_resource(pdev, resno);
+
+ ret = pci_resize_resource(pdev, resno, bar_size);
+ 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
+ */
+static void resize_vram_bar(struct xe_device *xe)
+{
+ u64 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;
+ u32 bar_size_mask;
+ u32 pci_cmd;
+ int i;
+
+ /* gather some relevant info */
+ current_size = pci_resource_len(pdev, LMEM_BAR);
+ bar_size_mask = pci_rebar_get_possible_sizes(pdev, LMEM_BAR);
+
+ if (!bar_size_mask)
+ return;
+
+ /* set to a specific size? */
+ if (force_vram_bar_size) {
+ u32 bar_size_bit;
+
+ rebar_size = force_vram_bar_size * (resource_size_t)SZ_1M;
+
+ bar_size_bit = bar_size_mask & BIT(pci_rebar_bytes_to_size(rebar_size));
+
+ if (!bar_size_bit) {
+ drm_info(&xe->drm,
+ "Requested size: %lluMiB is not supported by rebar sizes: 0x%x. Leaving default: %lluMiB\n",
+ (u64)rebar_size >> 20, bar_size_mask, (u64)current_size >> 20);
+ return;
+ }
+
+ rebar_size = 1ULL << (__fls(bar_size_bit) + BAR_SIZE_SHIFT);
+
+ if (rebar_size == current_size)
+ return;
+ } else {
+ rebar_size = 1ULL << (__fls(bar_size_mask) + BAR_SIZE_SHIFT);
+
+ /* 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))
+ return false;
+
+ if (pci_resource_flags(pdev, bar) & IORESOURCE_UNSET)
+ return false;
+
+ if (!pci_resource_len(pdev, bar))
+ return false;
+
+ return true;
+}
+
+static int determine_lmem_bar_size(struct xe_device *xe)
+{
+ struct pci_dev *pdev = to_pci_dev(xe->drm.dev);
+
+ if (!resource_is_valid(pdev, LMEM_BAR)) {
+ drm_err(&xe->drm, "pci resource is not valid\n");
+ return -ENXIO;
+ }
+
+ resize_vram_bar(xe);
+
+ xe->mem.vram.io_start = pci_resource_start(pdev, LMEM_BAR);
+ xe->mem.vram.io_size = pci_resource_len(pdev, LMEM_BAR);
+ if (!xe->mem.vram.io_size)
+ return -EIO;
+
+ /* XXX: Need to change when xe link code is ready */
+ xe->mem.vram.dpa_base = 0;
+
+ /* set up a map to the total memory area. */
+ xe->mem.vram.mapping = ioremap_wc(xe->mem.vram.io_start, xe->mem.vram.io_size);
+
+ return 0;
+}
+
+static inline u64 get_flat_ccs_offset(struct xe_gt *gt, u64 tile_size)
+{
+ struct xe_device *xe = gt_to_xe(gt);
+ u64 offset;
+ u32 reg;
+
+ if (GRAPHICS_VER(xe) >= 20) {
+ u64 ccs_size = tile_size / 512;
+ u64 offset_hi, offset_lo;
+ u32 nodes, num_enabled;
+
+ reg = xe_mmio_read32(gt, MIRROR_FUSE3);
+ nodes = REG_FIELD_GET(XE2_NODE_ENABLE_MASK, reg);
+ num_enabled = hweight32(nodes); /* Number of enabled l3 nodes */
+
+ reg = xe_gt_mcr_unicast_read_any(gt, XE2_FLAT_CCS_BASE_RANGE_LOWER);
+ offset_lo = REG_FIELD_GET(XE2_FLAT_CCS_BASE_LOWER_ADDR_MASK, reg);
+
+ reg = xe_gt_mcr_unicast_read_any(gt, XE2_FLAT_CCS_BASE_RANGE_UPPER);
+ offset_hi = REG_FIELD_GET(XE2_FLAT_CCS_BASE_UPPER_ADDR_MASK, reg);
+
+ offset = offset_hi << 32; /* HW view bits 39:32 */
+ offset |= offset_lo << 6; /* HW view bits 31:6 */
+ offset *= num_enabled; /* convert to SW view */
+
+ /* We don't expect any holes */
+ xe_assert_msg(xe, offset == (xe_mmio_read64_2x32(gt, GSMBASE) - ccs_size),
+ "Hole between CCS and GSM.\n");
+ } else {
+ reg = xe_gt_mcr_unicast_read_any(gt, XEHP_FLAT_CCS_BASE_ADDR);
+ offset = (u64)REG_FIELD_GET(XEHP_FLAT_CCS_PTR, reg) * SZ_64K;
+ }
+
+ return offset;
+}
+
+/*
+ * tile_vram_size() - Collect vram size and offset information
+ * @tile: tile to get info for
+ * @vram_size: available vram (size - device reserved portions)
+ * @tile_size: actual vram size
+ * @tile_offset: physical start point in the vram address space
+ *
+ * There are 4 places for size information:
+ * - io size (from pci_resource_len of LMEM bar) (only used for small bar and DG1)
+ * - TILEx size (actual vram size)
+ * - GSMBASE offset (TILEx - "stolen")
+ * - CSSBASE offset (TILEx - CSS space necessary)
+ *
+ * CSSBASE is always a lower/smaller offset then GSMBASE.
+ *
+ * The actual available size of memory is to the CCS or GSM base.
+ * NOTE: multi-tile bases will include the tile offset.
+ *
+ */
+static int tile_vram_size(struct xe_tile *tile, u64 *vram_size,
+ u64 *tile_size, u64 *tile_offset)
+{
+ struct xe_device *xe = tile_to_xe(tile);
+ struct xe_gt *gt = tile->primary_gt;
+ u64 offset;
+ int err;
+ u32 reg;
+
+ if (IS_SRIOV_VF(xe)) {
+ struct xe_tile *t;
+ int id;
+
+ offset = 0;
+ for_each_tile(t, xe, id)
+ for_each_if(t->id < tile->id)
+ offset += xe_gt_sriov_vf_lmem(t->primary_gt);
+
+ *tile_size = xe_gt_sriov_vf_lmem(gt);
+ *vram_size = *tile_size;
+ *tile_offset = offset;
+
+ return 0;
+ }
+
+ err = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
+ if (err)
+ return err;
+
+ /* actual size */
+ if (unlikely(xe->info.platform == XE_DG1)) {
+ *tile_size = pci_resource_len(to_pci_dev(xe->drm.dev), LMEM_BAR);
+ *tile_offset = 0;
+ } else {
+ reg = xe_gt_mcr_unicast_read_any(gt, XEHP_TILE_ADDR_RANGE(gt->info.id));
+ *tile_size = (u64)REG_FIELD_GET(GENMASK(14, 8), reg) * SZ_1G;
+ *tile_offset = (u64)REG_FIELD_GET(GENMASK(7, 1), reg) * SZ_1G;
+ }
+
+ /* minus device usage */
+ if (xe->info.has_flat_ccs) {
+ offset = get_flat_ccs_offset(gt, *tile_size);
+ } else {
+ offset = xe_mmio_read64_2x32(gt, GSMBASE);
+ }
+
+ /* remove the tile offset so we have just the available size */
+ *vram_size = offset - *tile_offset;
+
+ return xe_force_wake_put(gt_to_fw(gt), XE_FW_GT);
+}
+
+static void vram_fini(void *arg)
+{
+ struct xe_device *xe = arg;
+ struct xe_tile *tile;
+ int id;
+
+ if (xe->mem.vram.mapping)
+ iounmap(xe->mem.vram.mapping);
+
+ xe->mem.vram.mapping = NULL;
+
+ for_each_tile(tile, xe, id)
+ tile->mem.vram.mapping = NULL;
+}
+
+/**
+ * xe_vram_probe() - Probe VRAM configuration
+ * @xe: the &xe_device
+ *
+ * Collect VRAM size and offset information for all tiles.
+ *
+ * Return: 0 on success, error code on failure
+ */
+int xe_vram_probe(struct xe_device *xe)
+{
+ struct xe_tile *tile;
+ resource_size_t io_size;
+ u64 available_size = 0;
+ u64 total_size = 0;
+ u64 tile_offset;
+ u64 tile_size;
+ u64 vram_size;
+ int err;
+ u8 id;
+
+ if (!IS_DGFX(xe))
+ return 0;
+
+ /* Get the size of the root tile's vram for later accessibility comparison */
+ tile = xe_device_get_root_tile(xe);
+ err = tile_vram_size(tile, &vram_size, &tile_size, &tile_offset);
+ if (err)
+ return err;
+
+ err = determine_lmem_bar_size(xe);
+ if (err)
+ return err;
+
+ drm_info(&xe->drm, "VISIBLE VRAM: %pa, %pa\n", &xe->mem.vram.io_start,
+ &xe->mem.vram.io_size);
+
+ io_size = xe->mem.vram.io_size;
+
+ /* tile specific ranges */
+ for_each_tile(tile, xe, id) {
+ err = tile_vram_size(tile, &vram_size, &tile_size, &tile_offset);
+ if (err)
+ return err;
+
+ tile->mem.vram.actual_physical_size = tile_size;
+ tile->mem.vram.io_start = xe->mem.vram.io_start + tile_offset;
+ tile->mem.vram.io_size = min_t(u64, vram_size, io_size);
+
+ if (!tile->mem.vram.io_size) {
+ drm_err(&xe->drm, "Tile without any CPU visible VRAM. Aborting.\n");
+ return -ENODEV;
+ }
+
+ tile->mem.vram.dpa_base = xe->mem.vram.dpa_base + tile_offset;
+ tile->mem.vram.usable_size = vram_size;
+ tile->mem.vram.mapping = xe->mem.vram.mapping + tile_offset;
+
+ if (tile->mem.vram.io_size < tile->mem.vram.usable_size)
+ drm_info(&xe->drm, "Small BAR device\n");
+ drm_info(&xe->drm, "VRAM[%u, %u]: Actual physical size %pa, usable size exclude stolen %pa, CPU accessible size %pa\n", id,
+ tile->id, &tile->mem.vram.actual_physical_size, &tile->mem.vram.usable_size, &tile->mem.vram.io_size);
+ drm_info(&xe->drm, "VRAM[%u, %u]: DPA range: [%pa-%llx], io range: [%pa-%llx]\n", id, tile->id,
+ &tile->mem.vram.dpa_base, tile->mem.vram.dpa_base + (u64)tile->mem.vram.actual_physical_size,
+ &tile->mem.vram.io_start, tile->mem.vram.io_start + (u64)tile->mem.vram.io_size);
+
+ /* calculate total size using tile size to get the correct HW sizing */
+ total_size += tile_size;
+ available_size += vram_size;
+
+ if (total_size > xe->mem.vram.io_size) {
+ drm_info(&xe->drm, "VRAM: %pa is larger than resource %pa\n",
+ &total_size, &xe->mem.vram.io_size);
+ }
+
+ io_size -= min_t(u64, tile_size, io_size);
+ }
+
+ xe->mem.vram.actual_physical_size = total_size;
+
+ drm_info(&xe->drm, "Total VRAM: %pa, %pa\n", &xe->mem.vram.io_start,
+ &xe->mem.vram.actual_physical_size);
+ drm_info(&xe->drm, "Available VRAM: %pa, %pa\n", &xe->mem.vram.io_start,
+ &available_size);
+
+ return devm_add_action_or_reset(xe->drm.dev, vram_fini, xe);
+}
diff --git a/drivers/gpu/drm/xe/xe_vram.h b/drivers/gpu/drm/xe/xe_vram.h
new file mode 100644
index 000000000000..e31cc04ec0db
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_vram.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2024 Intel Corporation
+ */
+
+#ifndef _XE_VRAM_H_
+#define _XE_VRAM_H_
+
+struct xe_device;
+
+int xe_vram_probe(struct xe_device *xe);
+
+#endif
diff --git a/drivers/gpu/drm/xe/xe_vram_freq.c b/drivers/gpu/drm/xe/xe_vram_freq.c
index 3e21ddc6e60c..99ff95e408e0 100644
--- a/drivers/gpu/drm/xe/xe_vram_freq.c
+++ b/drivers/gpu/drm/xe/xe_vram_freq.c
@@ -87,7 +87,7 @@ static const struct attribute_group freq_group_attrs = {
.attrs = freq_attrs,
};
-static void vram_freq_sysfs_fini(struct drm_device *drm, void *arg)
+static void vram_freq_sysfs_fini(void *arg)
{
struct kobject *kobj = arg;
@@ -122,5 +122,5 @@ int xe_vram_freq_sysfs_init(struct xe_tile *tile)
return err;
}
- return drmm_add_action_or_reset(&xe->drm, vram_freq_sysfs_fini, kobj);
+ return devm_add_action_or_reset(xe->drm.dev, vram_freq_sysfs_fini, kobj);
}
diff --git a/drivers/gpu/drm/xe/xe_wa.c b/drivers/gpu/drm/xe/xe_wa.c
index dd214d95e4b6..c7bf0862b231 100644
--- a/drivers/gpu/drm/xe/xe_wa.c
+++ b/drivers/gpu/drm/xe/xe_wa.c
@@ -21,6 +21,7 @@
#include "xe_mmio.h"
#include "xe_platform_types.h"
#include "xe_rtp.h"
+#include "xe_sriov.h"
#include "xe_step.h"
/**
@@ -238,11 +239,11 @@ static const struct xe_rtp_entry_sr gt_was[] = {
},
{ XE_RTP_NAME("14020316580"),
XE_RTP_RULES(MEDIA_VERSION(1301)),
- XE_RTP_ACTIONS(CLR(PG_ENABLE,
- VD0_HCP_POWERGATE_ENABLE |
- VD0_MFXVDENC_POWERGATE_ENABLE |
- VD2_HCP_POWERGATE_ENABLE |
- VD2_MFXVDENC_POWERGATE_ENABLE)),
+ 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)),
@@ -533,6 +534,10 @@ static const struct xe_rtp_entry_sr engine_was[] = {
FUNC(xe_rtp_match_first_render_or_compute)),
XE_RTP_ACTIONS(SET(LSC_CHICKEN_BIT_0, WR_REQ_CHAINING_DIS))
},
+ { XE_RTP_NAME("14021402888"),
+ XE_RTP_RULES(GRAPHICS_VERSION(2001), ENGINE_CLASS(RENDER)),
+ XE_RTP_ACTIONS(SET(HALF_SLICE_CHICKEN7, CLEAR_OPTIMIZATION_DISABLE))
+ },
/* Xe2_HPM */
@@ -625,7 +630,7 @@ static const struct xe_rtp_entry_sr lrc_was[] = {
XE_RTP_ACTIONS(SET(CACHE_MODE_1, MSAA_OPTIMIZATION_REDUC_DISABLE))
},
{ XE_RTP_NAME("14019877138"),
- XE_RTP_RULES(GRAPHICS_VERSION_RANGE(1270, 1271), ENGINE_CLASS(RENDER)),
+ XE_RTP_RULES(GRAPHICS_VERSION_RANGE(1270, 1274), ENGINE_CLASS(RENDER)),
XE_RTP_ACTIONS(SET(XEHP_PSS_CHICKEN, FD_END_COLLECT))
},
@@ -668,6 +673,25 @@ static const struct xe_rtp_entry_sr lrc_was[] = {
XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, 2004), ENGINE_CLASS(RENDER)),
XE_RTP_ACTIONS(SET(COMMON_SLICE_CHICKEN1, DISABLE_BOTTOM_CLIP_RECTANGLE_TEST))
},
+ { XE_RTP_NAME("14021567978"),
+ XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, XE_RTP_END_VERSION_UNDEFINED),
+ ENGINE_CLASS(RENDER)),
+ XE_RTP_ACTIONS(SET(CHICKEN_RASTER_2, TBIMR_FAST_CLIP))
+ },
+ { XE_RTP_NAME("14020756599"),
+ XE_RTP_RULES(GRAPHICS_VERSION(2004), ENGINE_CLASS(RENDER), OR,
+ MEDIA_VERSION_ANY_GT(2000), ENGINE_CLASS(RENDER)),
+ XE_RTP_ACTIONS(SET(WM_CHICKEN3, HIZ_PLANE_COMPRESSION_DIS))
+ },
+ { XE_RTP_NAME("14021490052"),
+ XE_RTP_RULES(GRAPHICS_VERSION(2004), ENGINE_CLASS(RENDER)),
+ XE_RTP_ACTIONS(SET(FF_MODE,
+ DIS_MESH_PARTIAL_AUTOSTRIP |
+ DIS_MESH_AUTOSTRIP),
+ SET(VFLSKPD,
+ DIS_PARTIAL_AUTOSTRIP |
+ DIS_AUTOSTRIP))
+ },
/* Xe2_HPG */
{ XE_RTP_NAME("15010599737"),
@@ -682,6 +706,15 @@ static const struct xe_rtp_entry_sr lrc_was[] = {
XE_RTP_RULES(GRAPHICS_VERSION(2001), ENGINE_CLASS(RENDER)),
XE_RTP_ACTIONS(SET(WM_CHICKEN3, HIZ_PLANE_COMPRESSION_DIS))
},
+ { XE_RTP_NAME("14021490052"),
+ XE_RTP_RULES(GRAPHICS_VERSION(2001), ENGINE_CLASS(RENDER)),
+ XE_RTP_ACTIONS(SET(FF_MODE,
+ DIS_MESH_PARTIAL_AUTOSTRIP |
+ DIS_MESH_AUTOSTRIP),
+ SET(VFLSKPD,
+ DIS_PARTIAL_AUTOSTRIP |
+ DIS_AUTOSTRIP))
+ },
{}
};
@@ -833,6 +866,9 @@ void xe_wa_apply_tile_workarounds(struct xe_tile *tile)
{
struct xe_gt *mmio = tile->primary_gt;
+ if (IS_SRIOV_VF(tile->xe))
+ return;
+
if (XE_WA(mmio, 22010954014))
xe_mmio_rmw32(mmio, XEHP_CLOCK_GATE_DIS, 0, SGSI_SIDECLK_DIS);
}
diff --git a/drivers/gpu/drm/xe/xe_wa.h b/drivers/gpu/drm/xe/xe_wa.h
index 1b24d66f9d80..db9ddeaf69bf 100644
--- a/drivers/gpu/drm/xe/xe_wa.h
+++ b/drivers/gpu/drm/xe/xe_wa.h
@@ -17,8 +17,6 @@ void xe_wa_process_gt(struct xe_gt *gt);
void xe_wa_process_engine(struct xe_hw_engine *hwe);
void xe_wa_process_lrc(struct xe_hw_engine *hwe);
void xe_wa_apply_tile_workarounds(struct xe_tile *tile);
-
-void xe_reg_whitelist_process_engine(struct xe_hw_engine *hwe);
void xe_wa_dump(struct xe_gt *gt, struct drm_printer *p);
/**
diff --git a/drivers/gpu/drm/xe/xe_wa_oob.rules b/drivers/gpu/drm/xe/xe_wa_oob.rules
index 12fe88796a49..26066beb4f6f 100644
--- a/drivers/gpu/drm/xe/xe_wa_oob.rules
+++ b/drivers/gpu/drm/xe/xe_wa_oob.rules
@@ -27,3 +27,5 @@
16022287689 GRAPHICS_VERSION(2001)
GRAPHICS_VERSION(2004)
13011645652 GRAPHICS_VERSION(2004)
+22019338487 MEDIA_VERSION(2000)
+ GRAPHICS_VERSION(2001)