diff options
| author | Thomas Zimmermann <tzimmermann@suse.de> | 2026-02-05 10:33:06 +0100 |
|---|---|---|
| committer | Thomas Zimmermann <tzimmermann@suse.de> | 2026-02-05 10:33:06 +0100 |
| commit | 2bebc88d5e37ddcb5ea5039a39f39527662b27f0 (patch) | |
| tree | cd7213334fe604969486b693e44750fe19ecd925 /drivers/gpu/drm/xe | |
| parent | 96f30ee0fb9db1663eb8fd55c12e4c67da8c4a90 (diff) | |
| parent | 3cc9398a9ea69b77de01b370463b706c354e52fb (diff) | |
| download | linux-next-2bebc88d5e37ddcb5ea5039a39f39527662b27f0.tar.gz linux-next-2bebc88d5e37ddcb5ea5039a39f39527662b27f0.zip | |
Merge drm/drm-next into drm-misc-next
Backmerging to get bug fixes from v6.19-rc7.
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Diffstat (limited to 'drivers/gpu/drm/xe')
248 files changed, 9266 insertions, 3458 deletions
diff --git a/drivers/gpu/drm/xe/Kconfig b/drivers/gpu/drm/xe/Kconfig index 4b288eb3f5b0..4d7dcaff2b91 100644 --- a/drivers/gpu/drm/xe/Kconfig +++ b/drivers/gpu/drm/xe/Kconfig @@ -39,7 +39,7 @@ config DRM_XE select DRM_TTM select DRM_TTM_HELPER select DRM_EXEC - select DRM_GPUSVM if !UML && DEVICE_PRIVATE + select DRM_GPUSVM if !UML select DRM_GPUVM select DRM_SCHED select MMU_NOTIFIER @@ -80,8 +80,9 @@ config DRM_XE_GPUSVM bool "Enable CPU to GPU address mirroring" depends on DRM_XE depends on !UML - depends on DEVICE_PRIVATE + depends on ZONE_DEVICE default y + select DEVICE_PRIVATE select DRM_GPUSVM help Enable this option if you want support for CPU to GPU address diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile index 62be4a5227e4..b39cbb756232 100644 --- a/drivers/gpu/drm/xe/Makefile +++ b/drivers/gpu/drm/xe/Makefile @@ -95,8 +95,10 @@ xe-y += xe_bb.o \ xe_oa.o \ xe_observation.o \ xe_pagefault.o \ + xe_page_reclaim.o \ xe_pat.o \ xe_pci.o \ + xe_pci_rebar.o \ xe_pcode.o \ xe_pm.o \ xe_preempt_fence.o \ @@ -115,6 +117,7 @@ xe-y += xe_bb.o \ xe_sa.o \ xe_sched_job.o \ xe_shrinker.o \ + xe_soc_remapper.o \ xe_step.o \ xe_survivability_mode.o \ xe_sync.o \ @@ -173,6 +176,7 @@ xe-$(CONFIG_PCI_IOV) += \ xe_lmtt.o \ xe_lmtt_2l.o \ xe_lmtt_ml.o \ + xe_mert.o \ xe_pci_sriov.o \ xe_sriov_packet.o \ xe_sriov_pf.o \ @@ -195,15 +199,8 @@ endif # i915 Display compat #defines and #includes subdir-ccflags-$(CONFIG_DRM_XE_DISPLAY) += \ - -I$(src)/display/ext \ -I$(src)/compat-i915-headers \ - -I$(srctree)/drivers/gpu/drm/i915/display/ \ - -Ddrm_i915_private=xe_device - -# Rule to build SOC code shared with i915 -$(obj)/i915-soc/%.o: $(srctree)/drivers/gpu/drm/i915/soc/%.c FORCE - $(call cmd,force_checksrc) - $(call if_changed_rule,cc_o_c) + -I$(srctree)/drivers/gpu/drm/i915/display/ # Rule to build display code shared with i915 $(obj)/i915-display/%.o: $(srctree)/drivers/gpu/drm/i915/display/%.c FORCE @@ -212,27 +209,20 @@ $(obj)/i915-display/%.o: $(srctree)/drivers/gpu/drm/i915/display/%.c FORCE # Display code specific to xe xe-$(CONFIG_DRM_XE_DISPLAY) += \ - display/ext/i915_irq.o \ display/intel_bo.o \ display/intel_fb_bo.o \ display/intel_fbdev_fb.o \ display/xe_display.o \ - display/xe_display_misc.o \ display/xe_display_rpm.o \ display/xe_display_wa.o \ display/xe_dsb_buffer.o \ display/xe_fb_pin.o \ display/xe_hdcp_gsc.o \ + display/xe_initial_plane.o \ display/xe_panic.o \ - display/xe_plane_initial.o \ display/xe_stolen.o \ display/xe_tdf.o -# SOC code shared with i915 -xe-$(CONFIG_DRM_XE_DISPLAY) += \ - i915-soc/intel_dram.o \ - i915-soc/intel_rom.o - # Display code shared with i915 xe-$(CONFIG_DRM_XE_DISPLAY) += \ i915-display/icl_dsi.o \ @@ -267,6 +257,7 @@ xe-$(CONFIG_DRM_XE_DISPLAY) += \ i915-display/intel_display_power_map.o \ i915-display/intel_display_power_well.o \ i915-display/intel_display_rpm.o \ + i915-display/intel_display_rps.o \ i915-display/intel_display_trace.o \ i915-display/intel_display_utils.o \ i915-display/intel_display_wa.o \ @@ -283,6 +274,7 @@ xe-$(CONFIG_DRM_XE_DISPLAY) += \ i915-display/intel_dpll.o \ i915-display/intel_dpll_mgr.o \ i915-display/intel_dpt_common.o \ + i915-display/intel_dram.o \ i915-display/intel_drrs.o \ i915-display/intel_dsb.o \ i915-display/intel_dsi.o \ @@ -303,6 +295,7 @@ xe-$(CONFIG_DRM_XE_DISPLAY) += \ i915-display/intel_hotplug.o \ i915-display/intel_hotplug_irq.o \ i915-display/intel_hti.o \ + i915-display/intel_initial_plane.o \ i915-display/intel_link_bw.o \ i915-display/intel_lspcon.o \ i915-display/intel_lt_phy.o \ @@ -310,14 +303,16 @@ xe-$(CONFIG_DRM_XE_DISPLAY) += \ i915-display/intel_modeset_setup.o \ i915-display/intel_modeset_verify.o \ i915-display/intel_panel.o \ + i915-display/intel_parent.o \ + i915-display/intel_pch.o \ i915-display/intel_pfit.o \ i915-display/intel_plane.o \ i915-display/intel_pmdemand.o \ - i915-display/intel_pch.o \ i915-display/intel_pps.o \ i915-display/intel_psr.o \ i915-display/intel_qp_tables.o \ i915-display/intel_quirks.o \ + i915-display/intel_rom.o \ i915-display/intel_snps_hdmi_pll.o \ i915-display/intel_snps_phy.o \ i915-display/intel_tc.o \ @@ -375,7 +370,8 @@ always-$(CONFIG_DRM_XE_WERROR) += \ $(patsubst %.h,%.hdrtest, $(shell cd $(src) && find * -name '*.h' $(hdrtest_find_args))) quiet_cmd_hdrtest = HDRTEST $(patsubst %.hdrtest,%.h,$@) - cmd_hdrtest = $(CC) -DHDRTEST $(filter-out $(CFLAGS_GCOV), $(c_flags)) -S -o /dev/null -x c /dev/null -include $<; touch $@ + cmd_hdrtest = $(CC) $(filter-out $(CFLAGS_GCOV), $(c_flags)) -S -o /dev/null -x c /dev/null -include $< -include $<; \ + $(srctree)/scripts/kernel-doc -none -Werror $<; touch $@ $(obj)/%.hdrtest: $(src)/%.h FORCE $(call if_changed_dep,hdrtest) diff --git a/drivers/gpu/drm/xe/abi/guc_actions_abi.h b/drivers/gpu/drm/xe/abi/guc_actions_abi.h index 47756e4674a1..83a6e7794982 100644 --- a/drivers/gpu/drm/xe/abi/guc_actions_abi.h +++ b/drivers/gpu/drm/xe/abi/guc_actions_abi.h @@ -139,6 +139,10 @@ enum xe_guc_action { XE_GUC_ACTION_DEREGISTER_G2G = 0x4508, XE_GUC_ACTION_DEREGISTER_CONTEXT_DONE = 0x4600, XE_GUC_ACTION_REGISTER_CONTEXT_MULTI_LRC = 0x4601, + XE_GUC_ACTION_REGISTER_CONTEXT_MULTI_QUEUE = 0x4602, + XE_GUC_ACTION_MULTI_QUEUE_CONTEXT_CGP_SYNC = 0x4603, + XE_GUC_ACTION_NOTIFY_MULTI_QUEUE_CONTEXT_CGP_SYNC_DONE = 0x4604, + XE_GUC_ACTION_NOTIFY_MULTI_QUEUE_CGP_CONTEXT_ERROR = 0x4605, XE_GUC_ACTION_CLIENT_SOFT_RESET = 0x5507, XE_GUC_ACTION_SET_ENG_UTIL_BUFF = 0x550A, XE_GUC_ACTION_SET_DEVICE_ENGINE_ACTIVITY_BUFFER = 0x550C, @@ -151,6 +155,8 @@ enum xe_guc_action { XE_GUC_ACTION_TLB_INVALIDATION = 0x7000, XE_GUC_ACTION_TLB_INVALIDATION_DONE = 0x7001, XE_GUC_ACTION_TLB_INVALIDATION_ALL = 0x7002, + XE_GUC_ACTION_PAGE_RECLAMATION = 0x7003, + XE_GUC_ACTION_PAGE_RECLAMATION_DONE = 0x7004, XE_GUC_ACTION_STATE_CAPTURE_NOTIFICATION = 0x8002, XE_GUC_ACTION_NOTIFY_FLUSH_LOG_BUFFER_TO_FILE = 0x8003, XE_GUC_ACTION_NOTIFY_CRASH_DUMP_POSTED = 0x8004, diff --git a/drivers/gpu/drm/xe/abi/guc_actions_sriov_abi.h b/drivers/gpu/drm/xe/abi/guc_actions_sriov_abi.h index 0b28659d94e9..d9f21202e1a9 100644 --- a/drivers/gpu/drm/xe/abi/guc_actions_sriov_abi.h +++ b/drivers/gpu/drm/xe/abi/guc_actions_sriov_abi.h @@ -502,13 +502,17 @@ #define VF2GUC_VF_RESET_RESPONSE_MSG_0_MBZ GUC_HXG_RESPONSE_MSG_0_DATA0 /** - * DOC: VF2GUC_NOTIFY_RESFIX_DONE + * DOC: VF2GUC_RESFIX_DONE * - * This action is used by VF to notify the GuC that the VF KMD has completed - * post-migration recovery steps. + * This action is used by VF to inform the GuC that the VF KMD has completed + * post-migration recovery steps. From GuC VF compatibility 1.27.0 onwards, it + * shall only be sent after posting RESFIX_START and that both @MARKER fields + * must match. * * This message must be sent as `MMIO HXG Message`_. * + * Updated since GuC VF compatibility 1.27.0. + * * +---+-------+--------------------------------------------------------------+ * | | Bits | Description | * +===+=======+==============================================================+ @@ -516,9 +520,11 @@ * | +-------+--------------------------------------------------------------+ * | | 30:28 | TYPE = GUC_HXG_TYPE_REQUEST_ | * | +-------+--------------------------------------------------------------+ - * | | 27:16 | DATA0 = MBZ | + * | | 27:16 | DATA0 = MARKER = MBZ (only prior 1.27.0) | * | +-------+--------------------------------------------------------------+ - * | | 15:0 | ACTION = _`GUC_ACTION_VF2GUC_NOTIFY_RESFIX_DONE` = 0x5508 | + * | | 27:16 | DATA0 = MARKER - can't be zero (1.27.0+) | + * | +-------+--------------------------------------------------------------+ + * | | 15:0 | ACTION = _`GUC_ACTION_VF2GUC_RESFIX_DONE` = 0x5508 | * +---+-------+--------------------------------------------------------------+ * * +---+-------+--------------------------------------------------------------+ @@ -531,13 +537,13 @@ * | | 27:0 | DATA0 = MBZ | * +---+-------+--------------------------------------------------------------+ */ -#define GUC_ACTION_VF2GUC_NOTIFY_RESFIX_DONE 0x5508u +#define GUC_ACTION_VF2GUC_RESFIX_DONE 0x5508u -#define VF2GUC_NOTIFY_RESFIX_DONE_REQUEST_MSG_LEN GUC_HXG_REQUEST_MSG_MIN_LEN -#define VF2GUC_NOTIFY_RESFIX_DONE_REQUEST_MSG_0_MBZ GUC_HXG_REQUEST_MSG_0_DATA0 +#define VF2GUC_RESFIX_DONE_REQUEST_MSG_LEN GUC_HXG_REQUEST_MSG_MIN_LEN +#define VF2GUC_RESFIX_DONE_REQUEST_MSG_0_MARKER GUC_HXG_REQUEST_MSG_0_DATA0 -#define VF2GUC_NOTIFY_RESFIX_DONE_RESPONSE_MSG_LEN GUC_HXG_RESPONSE_MSG_MIN_LEN -#define VF2GUC_NOTIFY_RESFIX_DONE_RESPONSE_MSG_0_MBZ GUC_HXG_RESPONSE_MSG_0_DATA0 +#define VF2GUC_RESFIX_DONE_RESPONSE_MSG_LEN GUC_HXG_RESPONSE_MSG_MIN_LEN +#define VF2GUC_RESFIX_DONE_RESPONSE_MSG_0_MBZ GUC_HXG_RESPONSE_MSG_0_DATA0 /** * DOC: VF2GUC_QUERY_SINGLE_KLV @@ -656,4 +662,45 @@ #define PF2GUC_SAVE_RESTORE_VF_RESPONSE_MSG_LEN GUC_HXG_RESPONSE_MSG_MIN_LEN #define PF2GUC_SAVE_RESTORE_VF_RESPONSE_MSG_0_USED GUC_HXG_RESPONSE_MSG_0_DATA0 +/** + * DOC: VF2GUC_RESFIX_START + * + * This action is used by VF to inform the GuC that the VF KMD will be starting + * post-migration recovery fixups. The @MARKER sent with this action must match + * with the MARKER posted in the VF2GUC_RESFIX_DONE message. + * + * This message must be sent as `MMIO HXG Message`_. + * + * Available since GuC VF compatibility 1.27.0. + * + * +---+-------+--------------------------------------------------------------+ + * | | Bits | Description | + * +===+=======+==============================================================+ + * | 0 | 31 | ORIGIN = GUC_HXG_ORIGIN_HOST_ | + * | +-------+--------------------------------------------------------------+ + * | | 30:28 | TYPE = GUC_HXG_TYPE_REQUEST_ | + * | +-------+--------------------------------------------------------------+ + * | | 27:16 | DATA0 = MARKER - can't be zero | + * | +-------+--------------------------------------------------------------+ + * | | 15:0 | ACTION = _`GUC_ACTION_VF2GUC_RESFIX_START` = 0x550F | + * +---+-------+--------------------------------------------------------------+ + * + * +---+-------+--------------------------------------------------------------+ + * | | Bits | Description | + * +===+=======+==============================================================+ + * | 0 | 31 | ORIGIN = GUC_HXG_ORIGIN_GUC_ | + * | +-------+--------------------------------------------------------------+ + * | | 30:28 | TYPE = GUC_HXG_TYPE_RESPONSE_SUCCESS_ | + * | +-------+--------------------------------------------------------------+ + * | | 27:0 | DATA0 = MBZ | + * +---+-------+--------------------------------------------------------------+ + */ +#define GUC_ACTION_VF2GUC_RESFIX_START 0x550Fu + +#define VF2GUC_RESFIX_START_REQUEST_MSG_LEN GUC_HXG_REQUEST_MSG_MIN_LEN +#define VF2GUC_RESFIX_START_REQUEST_MSG_0_MARKER GUC_HXG_REQUEST_MSG_0_DATA0 + +#define VF2GUC_RESFIX_START_RESPONSE_MSG_LEN GUC_HXG_RESPONSE_MSG_MIN_LEN +#define VF2GUC_RESFIX_START_RESPONSE_MSG_0_MBZ GUC_HXG_RESPONSE_MSG_0_DATA0 + #endif diff --git a/drivers/gpu/drm/xe/abi/guc_klvs_abi.h b/drivers/gpu/drm/xe/abi/guc_klvs_abi.h index 265a135e7061..e33bd622ab44 100644 --- a/drivers/gpu/drm/xe/abi/guc_klvs_abi.h +++ b/drivers/gpu/drm/xe/abi/guc_klvs_abi.h @@ -8,6 +8,8 @@ #include <linux/types.h> +#include "abi/guc_scheduler_abi.h" + /** * DOC: GuC KLV * @@ -46,11 +48,18 @@ * Refers to 32 bit architecture version as reported by the HW IP. * This key is supported on MTL+ platforms only. * Requires GuC ABI 1.2+. + * + * _`GUC_KLV_GLOBAL_CFG_GROUP_SCHEDULING_AVAILABLE` : 0x3001 + * Tells the driver whether scheduler groups are enabled or not. + * Requires GuC ABI 1.26+ */ #define GUC_KLV_GLOBAL_CFG_GMD_ID_KEY 0x3000u #define GUC_KLV_GLOBAL_CFG_GMD_ID_LEN 1u +#define GUC_KLV_GLOBAL_CFG_GROUP_SCHEDULING_AVAILABLE_KEY 0x3001u +#define GUC_KLV_GLOBAL_CFG_GROUP_SCHEDULING_AVAILABLE_LEN 1u + /** * DOC: GuC Self Config KLVs * @@ -200,6 +209,20 @@ enum { * :0: adverse events are not counted (default) * :n: sample period in milliseconds * + * _`GUC_KLV_VGT_POLICY_ENGINE_GROUP_CONFIG` : 0x8004 + * This config allows the PF to split the engines across scheduling groups. + * Each group is independently timesliced across VFs, allowing different + * VFs to be active on the HW at the same time. When enabling this feature, + * all engines must be assigned to a group (and only one group), or they + * will be excluded from scheduling after this KLV is sent. To enable + * the groups, the driver must provide a masks array with + * GUC_MAX_ENGINE_CLASSES entries for each group, with each mask indicating + * which logical instances of that class belong to the group. Therefore, + * the length of this KLV when enabling groups is + * num_groups * GUC_MAX_ENGINE_CLASSES. To disable the groups, the driver + * must send the KLV without any payload (i.e. len = 0). The maximum + * number of groups is 8. + * * _`GUC_KLV_VGT_POLICY_RESET_AFTER_VF_SWITCH` : 0x8D00 * This enum is to reset utilized HW engine after VF Switch (i.e to clean * up Stale HW register left behind by previous VF) @@ -214,6 +237,12 @@ enum { #define GUC_KLV_VGT_POLICY_ADVERSE_SAMPLE_PERIOD_KEY 0x8002 #define GUC_KLV_VGT_POLICY_ADVERSE_SAMPLE_PERIOD_LEN 1u +#define GUC_KLV_VGT_POLICY_ENGINE_GROUP_CONFIG_KEY 0x8004 +#define GUC_KLV_VGT_POLICY_ENGINE_GROUP_MAX_COUNT GUC_MAX_SCHED_GROUPS +#define GUC_KLV_VGT_POLICY_ENGINE_GROUP_CONFIG_MIN_LEN 0 +#define GUC_KLV_VGT_POLICY_ENGINE_GROUP_CONFIG_MAX_LEN \ + (GUC_KLV_VGT_POLICY_ENGINE_GROUP_MAX_COUNT * GUC_MAX_ENGINE_CLASSES) + #define GUC_KLV_VGT_POLICY_RESET_AFTER_VF_SWITCH_KEY 0x8D00 #define GUC_KLV_VGT_POLICY_RESET_AFTER_VF_SWITCH_LEN 1u @@ -268,6 +297,10 @@ enum { * it to take effect. Such cases might typically happen on a 1PF+1VF * Virtualization config enabled for heavier workloads like AI/ML. * + * If scheduling groups are supported, the provided value is applied to all + * groups (even if they've not yet been enabled). Support for this feature + * is available from GuC 70.53.0. + * * The max value for this KLV is 100 seconds, anything exceeding that * will be clamped to the max. * @@ -290,6 +323,10 @@ enum { * on a 1PF+1VF Virtualization config enabled for heavier workloads like * AI/ML. * + * If scheduling groups are supported, the provided value is applied to all + * groups (even if they've not yet been enabled). Support for this feature + * is available from GuC 70.53.0. + * * The max value for this KLV is 100 seconds, anything exceeding that * will be clamped to the max. * @@ -352,6 +389,32 @@ enum { * :1: NORMAL = schedule VF always, irrespective of whether it has work or not * :2: HIGH = schedule VF in the next time-slice after current active * time-slice completes if it has active work + * + * _`GUC_KLV_VF_CFG_THRESHOLD_MULTI_LRC_COUNT` : 0x8A0D + * Given that multi-LRC contexts are incompatible with SRIOV scheduler + * groups and cause the latter to be turned off when registered with the + * GuC, this config allows the PF to set a threshold for multi-LRC context + * registrations by VFs to monitor their behavior. + * + * _`GUC_KLV_VF_CFG_ENGINE_GROUP_EXEC_QUANTUM' : 0x8A0E + * This config sets the VFs-execution-quantum for each scheduling group in + * milliseconds. The driver must provide an array of values, with each of + * them matching the respective group index (first value goes to group 0, + * second to group 1, etc). The setting of group values follows the same + * behavior and rules as setting via GUC_KLV_VF_CFG_EXEC_QUANTUM. Note that + * the GuC always sets the EQ for all groups (even the non-enabled ones), + * so if we provide fewer values than the max the GuC will use 0 for the + * remaining groups. This KLV is available starting from GuC 70.53.0. + * + * _`GUC_KLV_VF_CFG_ENGINE_GROUP_PREEMPT_TIMEOUT' : 0x8A0F + * This config sets the VFs-preemption-timeout for each scheduling group in + * microseconds. The driver must provide an array of values, with each of + * them matching the respective group index (first value goes to group 0, + * second to group 1, etc). The setting of group values follows the same + * behavior and rules as setting via GUC_KLV_VF_CFG_PREEMPT_TIMEOUT. Note + * that the GuC always sets the EQ for all groups (even the non-enabled + * ones), so if we provide fewer values than the max the GuC will use 0 for + * the remaining groups. This KLV is available starting from GuC 70.53.0. */ #define GUC_KLV_VF_CFG_GGTT_START_KEY 0x0001 @@ -410,6 +473,16 @@ enum { #define GUC_SCHED_PRIORITY_NORMAL 1u #define GUC_SCHED_PRIORITY_HIGH 2u +#define GUC_KLV_VF_CFG_THRESHOLD_MULTI_LRC_COUNT_KEY 0x8a0d +#define GUC_KLV_VF_CFG_THRESHOLD_MULTI_LRC_COUNT_LEN 1u + +#define GUC_KLV_VF_CFG_ENGINE_GROUP_EXEC_QUANTUM_KEY 0x8a0e +#define GUC_KLV_VF_CFG_ENGINE_GROUP_EXEC_QUANTUM_MIN_LEN 1u +#define GUC_KLV_VF_CFG_ENGINE_GROUP_EXEC_QUANTUM_MAX_LEN GUC_MAX_SCHED_GROUPS + +#define GUC_KLV_VF_CFG_ENGINE_GROUP_PREEMPT_TIMEOUT_KEY 0x8a0f +#define GUC_KLV_VF_CFG_ENGINE_GROUP_PREEMPT_TIMEOUT_MIN_LEN 1u +#define GUC_KLV_VF_CFG_ENGINE_GROUP_PREEMPT_TIMEOUT_MAX_LEN GUC_MAX_SCHED_GROUPS /* * Workaround keys: */ diff --git a/drivers/gpu/drm/xe/abi/guc_lfd_abi.h b/drivers/gpu/drm/xe/abi/guc_lfd_abi.h new file mode 100644 index 000000000000..5c48459f365f --- /dev/null +++ b/drivers/gpu/drm/xe/abi/guc_lfd_abi.h @@ -0,0 +1,170 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright © 2025 Intel Corporation + */ + +#ifndef _ABI_GUC_LFD_ABI_H_ +#define _ABI_GUC_LFD_ABI_H_ + +#include <linux/types.h> + +#include "guc_lic_abi.h" + +/* The current major version of GuC-Log-File format. */ +#define GUC_LFD_FORMAT_VERSION_MAJOR 0x0001 +/* The current minor version of GuC-Log-File format. */ +#define GUC_LFD_FORMAT_VERSION_MINOR 0x0000 + +/** enum guc_lfd_type - Log format descriptor type */ +enum guc_lfd_type { + /** + * @GUC_LFD_TYPE_FW_REQUIRED_RANGE_START: Start of range for + * required LFDs from GuC + * @GUC_LFD_TYPE_FW_VERSION: GuC Firmware Version structure. + * @GUC_LFD_TYPE_GUC_DEVICE_ID: GuC microcontroller device ID. + * @GUC_LFD_TYPE_TSC_FREQUENCY: Frequency of GuC timestamps. + * @GUC_LFD_TYPE_GMD_ID: HW GMD ID. + * @GUC_LFD_TYPE_BUILD_PLATFORM_ID: GuC build platform ID. + * @GUC_LFD_TYPE_FW_REQUIRED_RANGE_END: End of range for + * required LFDs from GuC + */ + GUC_LFD_TYPE_FW_REQUIRED_RANGE_START = 0x1, + GUC_LFD_TYPE_FW_VERSION = 0x1, + GUC_LFD_TYPE_GUC_DEVICE_ID = 0x2, + GUC_LFD_TYPE_TSC_FREQUENCY = 0x3, + GUC_LFD_TYPE_GMD_ID = 0x4, + GUC_LFD_TYPE_BUILD_PLATFORM_ID = 0x5, + GUC_LFD_TYPE_FW_REQUIRED_RANGE_END = 0x1FFF, + + /** + * @GUC_LFD_TYPE_FW_OPTIONAL_RANGE_START: Start of range for + * optional LFDs from GuC + * @GUC_LFD_TYPE_LOG_EVENTS_BUFFER: Log-event-entries buffer. + * @GUC_LFD_TYPE_FW_CRASH_DUMP: GuC generated crash-dump blob. + * @GUC_LFD_TYPE_FW_OPTIONAL_RANGE_END: End of range for + * optional LFDs from GuC + */ + GUC_LFD_TYPE_FW_OPTIONAL_RANGE_START = 0x2000, + GUC_LFD_TYPE_LOG_EVENTS_BUFFER = 0x2000, + GUC_LFD_TYPE_FW_CRASH_DUMP = 0x2001, + GUC_LFD_TYPE_FW_OPTIONAL_RANGE_END = 0x3FFF, + + /** + * @GUC_LFD_TYPE_KMD_REQUIRED_RANGE_START: Start of range for + * required KMD LFDs + * @GUC_LFD_TYPE_OS_ID: An identifier for the OS. + * @GUC_LFD_TYPE_KMD_REQUIRED_RANGE_END: End of this range for + * required KMD LFDs + */ + GUC_LFD_TYPE_KMD_REQUIRED_RANGE_START = 0x4000, + GUC_LFD_TYPE_OS_ID = 0x4000, + GUC_LFD_TYPE_KMD_REQUIRED_RANGE_END = 0x5FFF, + + /** + * @GUC_LFD_TYPE_KMD_OPTIONAL_RANGE_START: Start of range for + * optional KMD LFDs + * @GUC_LFD_TYPE_BINARY_SCHEMA_FORMAT: Binary representation of + * GuC log-events schema. + * @GUC_LFD_TYPE_HOST_COMMENT: ASCII string containing comments + * from the host/KMD. + * @GUC_LFD_TYPE_TIMESTAMP_ANCHOR: A timestamp anchor, to convert + * between host and GuC timestamp. + * @GUC_LFD_TYPE_TIMESTAMP_ANCHOR_CONFIG: Timestamp anchor + * configuration, definition of timestamp frequency and bit width. + * @GUC_LFD_TYPE_KMD_OPTIONAL_RANGE_END: End of this range for + * optional KMD LFDs + */ + GUC_LFD_TYPE_KMD_OPTIONAL_RANGE_START = 0x6000, + GUC_LFD_TYPE_BINARY_SCHEMA_FORMAT = 0x6000, + GUC_LFD_TYPE_HOST_COMMENT = 0x6001, + GUC_LFD_TYPE_TIMESTAMP_ANCHOR = 0x6002, + GUC_LFD_TYPE_TIMESTAMP_ANCHOR_CONFIG = 0x6003, + GUC_LFD_TYPE_KMD_OPTIONAL_RANGE_END = 0x7FFF, + + /* + * @GUC_LFD_TYPE_RESERVED_RANGE_START: Start of reserved range + * @GUC_LFD_TYPE_RESERVED_RANGE_END: End of reserved range + */ + GUC_LFD_TYPE_RESERVED_RANGE_START = 0x8000, + GUC_LFD_TYPE_RESERVED_RANGE_END = 0xFFFF, +}; + +/** enum guc_lfd_os_type - OS Type LFD-ID */ +enum guc_lfd_os_type { + /** @GUC_LFD_OS_TYPE_OSID_WIN: Windows OS */ + GUC_LFD_OS_TYPE_OSID_WIN = 0x1, + /** @GUC_LFD_OS_TYPE_OSID_LIN: Linux OS */ + GUC_LFD_OS_TYPE_OSID_LIN = 0x2, + /** @GUC_LFD_OS_TYPE_OSID_VMW: VMWare OS */ + GUC_LFD_OS_TYPE_OSID_VMW = 0x3, + /** @GUC_LFD_OS_TYPE_OSID_OTHER: Other */ + GUC_LFD_OS_TYPE_OSID_OTHER = 0x4, +}; + +/** struct guc_lfd_data - A generic header structure for all LFD blocks */ +struct guc_lfd_data { + /** @header: A 32 bits dword, contains multiple bit fields */ + u32 header; + /* LFD type. See guc_lfd_type */ +#define GUC_LFD_DATA_HEADER_MASK_TYPE GENMASK(31, 16) +#define GUC_LFD_DATA_HEADER_MASK_MAGIC GENMASK(15, 0) + + /** @data_count: Number of dwords the `data` field contains. */ + u32 data_count; + /** @data: Data defined by GUC_LFD_DATA_HEADER_MASK_TYPE */ + u32 data[] __counted_by(data_count); +} __packed; + +/** + * struct guc_lfd_data_log_events_buf - GuC Log Events Buffer. + * This is optional fw LFD data + */ +struct guc_lfd_data_log_events_buf { + /** + * @log_events_format_version: version of GuC log format of buffer + */ + u32 log_events_format_version; + /** + * @log_event: The log event data. + * Size in dwords is LFD block size - 1. + */ + u32 log_event[]; +} __packed; + +/** struct guc_lfd_data_os_info - OS Version Information. */ +struct guc_lfd_data_os_info { + /** + * @os_id: enum values to identify the OS brand. + * See guc_lfd_os_type for the range of types + */ + u32 os_id; + /** + * @build_version: ASCII string containing OS build version + * information based on os_id. String is padded with null + * characters to ensure its DWORD aligned. + * Size in dwords is LFD block size - 1. + */ + char build_version[]; +} __packed; + +/** + * struct guc_lfd_file_header - Header of GuC Log Streaming-LFD-File Format. + * This structure encapsulates the layout of the guc-log-file format + */ +struct guc_lfd_file_header { + /** + * @magic: A magic number set by producer of a GuC log file to + * identify that file is a valid guc-log-file containing a stream + * of LFDs. + */ + u64 magic; + /** @version: Version of this file format layout */ + u32 version; +#define GUC_LFD_FILE_HEADER_VERSION_MASK_MAJOR GENMASK(31, 16) +#define GUC_LFD_FILE_HEADER_VERSION_MASK_MINOR GENMASK(15, 0) + + /** @stream: A stream of one or more guc_lfd_data LFD blocks */ + u32 stream[]; +} __packed; + +#endif diff --git a/drivers/gpu/drm/xe/abi/guc_lic_abi.h b/drivers/gpu/drm/xe/abi/guc_lic_abi.h new file mode 100644 index 000000000000..9169644093a2 --- /dev/null +++ b/drivers/gpu/drm/xe/abi/guc_lic_abi.h @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright © 2025 Intel Corporation + */ + +#ifndef _ABI_GUC_LIC_ABI_H_ +#define _ABI_GUC_LIC_ABI_H_ + +#include <linux/types.h> + +/** + * enum guc_lic_type - Log Init Config KLV IDs. + */ +enum guc_lic_type { + /** + * @GUC_LIC_TYPE_GUC_SW_VERSION: GuC firmware version. Value + * is a 32 bit number represented by guc_sw_version. + */ + GUC_LIC_TYPE_GUC_SW_VERSION = 0x1, + /** + * @GUC_LIC_TYPE_GUC_DEVICE_ID: GuC device id. Value is a 32 + * bit. + */ + GUC_LIC_TYPE_GUC_DEVICE_ID = 0x2, + /** + * @GUC_LIC_TYPE_TSC_FREQUENCY: GuC timestamp counter + * frequency. Value is a 32 bit number representing frequency in + * kHz. This timestamp is utilized in log entries, timer and + * for engine utilization tracking. + */ + GUC_LIC_TYPE_TSC_FREQUENCY = 0x3, + /** + * @GUC_LIC_TYPE_GMD_ID: HW GMD ID. Value is a 32 bit number + * representing graphics, media and display HW architecture IDs. + */ + GUC_LIC_TYPE_GMD_ID = 0x4, + /** + * @GUC_LIC_TYPE_BUILD_PLATFORM_ID: GuC build platform ID. + * Value is 32 bits. + */ + GUC_LIC_TYPE_BUILD_PLATFORM_ID = 0x5, +}; + +/** + * struct guc_lic - GuC LIC (Log-Init-Config) structure. + * + * This is populated by the GUC at log init time and is located in the log + * buffer memory allocation. + */ +struct guc_lic { + /** + * @magic: A magic number set by GuC to identify that this + * structure contains valid information: magic = GUC_LIC_MAGIC. + */ + u32 magic; +#define GUC_LIC_MAGIC 0x8086900D + /** + * @version: The version of the this structure. + * Major and minor version number are represented as bit fields. + */ + u32 version; +#define GUC_LIC_VERSION_MASK_MAJOR GENMASK(31, 16) +#define GUC_LIC_VERSION_MASK_MINOR GENMASK(15, 0) + +#define GUC_LIC_VERSION_MAJOR 1u +#define GUC_LIC_VERSION_MINOR 0u + + /** @data_count: Number of dwords the `data` array contains. */ + u32 data_count; + /** + * @data: Array of dwords representing a list of LIC KLVs of + * type guc_klv_generic with keys represented by guc_lic_type + */ + u32 data[] __counted_by(data_count); +} __packed; + +#endif diff --git a/drivers/gpu/drm/xe/abi/guc_log_abi.h b/drivers/gpu/drm/xe/abi/guc_log_abi.h index 554630b7ccd9..fbf212d59a40 100644 --- a/drivers/gpu/drm/xe/abi/guc_log_abi.h +++ b/drivers/gpu/drm/xe/abi/guc_log_abi.h @@ -8,11 +8,45 @@ #include <linux/types.h> +/** + * DOC: GuC Log buffer Layout + * + * The in-memory log buffer layout is as follows:: + * + * +===============================+ 0000h + * | Crash dump state header | ^ + * +-------------------------------+ 32B | + * | Debug state header | | + * +-------------------------------+ 64B 4KB + * | Capture state header | | + * +-------------------------------+ 96B | + * | | v + * +===============================+ <--- EVENT_DATA_OFFSET + * | Event logs(raw data) | ^ + * | | | + * | | EVENT_DATA_BUFFER_SIZE + * | | | + * | | v + * +===============================+ <--- CRASH_DUMP_OFFSET + * | Crash Dump(raw data) | ^ + * | | | + * | | CRASH_DUMP_BUFFER_SIZE + * | | | + * | | v + * +===============================+ <--- STATE_CAPTURE_OFFSET + * | Error state capture(raw data) | ^ + * | | | + * | | STATE_CAPTURE_BUFFER_SIZE + * | | | + * | | v + * +===============================+ Total: GUC_LOG_SIZE + */ + /* GuC logging buffer types */ -enum guc_log_buffer_type { - GUC_LOG_BUFFER_CRASH_DUMP, - GUC_LOG_BUFFER_DEBUG, - GUC_LOG_BUFFER_CAPTURE, +enum guc_log_type { + GUC_LOG_TYPE_EVENT_DATA, + GUC_LOG_TYPE_CRASH_DUMP, + GUC_LOG_TYPE_STATE_CAPTURE, }; #define GUC_LOG_BUFFER_TYPE_MAX 3 diff --git a/drivers/gpu/drm/xe/abi/guc_scheduler_abi.h b/drivers/gpu/drm/xe/abi/guc_scheduler_abi.h new file mode 100644 index 000000000000..513b22a87428 --- /dev/null +++ b/drivers/gpu/drm/xe/abi/guc_scheduler_abi.h @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright © 2025 Intel Corporation + */ + +#ifndef _ABI_GUC_SCHEDULER_ABI_H +#define _ABI_GUC_SCHEDULER_ABI_H + +#include <linux/types.h> + +/** + * Generic defines required for registration with and submissions to the GuC + * scheduler. Includes engine class/instance defines and context attributes + * (id, priority, etc) + */ + +/* Engine classes/instances */ +#define GUC_RENDER_CLASS 0 +#define GUC_VIDEO_CLASS 1 +#define GUC_VIDEOENHANCE_CLASS 2 +#define GUC_BLITTER_CLASS 3 +#define GUC_COMPUTE_CLASS 4 +#define GUC_GSC_OTHER_CLASS 5 +#define GUC_LAST_ENGINE_CLASS GUC_GSC_OTHER_CLASS +#define GUC_MAX_ENGINE_CLASSES 16 +#define GUC_MAX_INSTANCES_PER_CLASS 32 + +/* context priority values */ +#define GUC_CLIENT_PRIORITY_KMD_HIGH 0 +#define GUC_CLIENT_PRIORITY_HIGH 1 +#define GUC_CLIENT_PRIORITY_KMD_NORMAL 2 +#define GUC_CLIENT_PRIORITY_NORMAL 3 +#define GUC_CLIENT_PRIORITY_NUM 4 + +/* Context registration */ +#define GUC_ID_MAX 65535 +#define GUC_ID_UNKNOWN 0xffffffff + +#define CONTEXT_REGISTRATION_FLAG_KMD BIT(0) +#define CONTEXT_REGISTRATION_FLAG_TYPE GENMASK(2, 1) +#define GUC_CONTEXT_NORMAL 0 +#define GUC_CONTEXT_COMPRESSION_SAVE 1 +#define GUC_CONTEXT_COMPRESSION_RESTORE 2 +#define GUC_CONTEXT_COUNT (GUC_CONTEXT_COMPRESSION_RESTORE + 1) + +/* context enable/disable */ +#define GUC_CONTEXT_DISABLE 0 +#define GUC_CONTEXT_ENABLE 1 + +/* scheduler groups */ +#define GUC_MAX_SCHED_GROUPS 8 + +struct guc_sched_group { + u32 engines[GUC_MAX_ENGINE_CLASSES]; +} __packed; + +#endif diff --git a/drivers/gpu/drm/xe/compat-i915-headers/gem/i915_gem_object.h b/drivers/gpu/drm/xe/compat-i915-headers/gem/i915_gem_object.h deleted file mode 100644 index 0548b2e0316f..000000000000 --- a/drivers/gpu/drm/xe/compat-i915-headers/gem/i915_gem_object.h +++ /dev/null @@ -1,13 +0,0 @@ -/* SPDX-License-Identifier: MIT */ -/* Copyright © 2025 Intel Corporation */ - -#ifndef __I915_GEM_OBJECT_H__ -#define __I915_GEM_OBJECT_H__ - -struct dma_fence; - -static inline void i915_gem_fence_wait_priority_display(struct dma_fence *fence) -{ -} - -#endif diff --git a/drivers/gpu/drm/xe/compat-i915-headers/gem/i915_gem_stolen.h b/drivers/gpu/drm/xe/compat-i915-headers/gem/i915_gem_stolen.h deleted file mode 100644 index 48e3256ba37e..000000000000 --- a/drivers/gpu/drm/xe/compat-i915-headers/gem/i915_gem_stolen.h +++ /dev/null @@ -1,40 +0,0 @@ -/* SPDX-License-Identifier: MIT */ -/* - * Copyright © 2024 Intel Corporation - */ - -#ifndef _I915_GEM_STOLEN_H_ -#define _I915_GEM_STOLEN_H_ - -#include <linux/types.h> - -struct drm_device; -struct intel_stolen_node; - -int i915_gem_stolen_insert_node_in_range(struct intel_stolen_node *node, u64 size, - unsigned int align, u64 start, u64 end); - -int i915_gem_stolen_insert_node(struct intel_stolen_node *node, u64 size, - unsigned int align); - -void i915_gem_stolen_remove_node(struct intel_stolen_node *node); - -bool i915_gem_stolen_initialized(struct drm_device *drm); - -bool i915_gem_stolen_node_allocated(const struct intel_stolen_node *node); - -u32 i915_gem_stolen_node_offset(struct intel_stolen_node *node); - -u64 i915_gem_stolen_area_address(struct drm_device *drm); - -u64 i915_gem_stolen_area_size(struct drm_device *drm); - -u64 i915_gem_stolen_node_address(struct intel_stolen_node *node); - -u64 i915_gem_stolen_node_size(const struct intel_stolen_node *node); - -struct intel_stolen_node *i915_gem_stolen_node_alloc(struct drm_device *drm); - -void i915_gem_stolen_node_free(const struct intel_stolen_node *node); - -#endif diff --git a/drivers/gpu/drm/xe/compat-i915-headers/gt/intel_gt_types.h b/drivers/gpu/drm/xe/compat-i915-headers/gt/intel_gt_types.h deleted file mode 100644 index c15806d6c4f7..000000000000 --- a/drivers/gpu/drm/xe/compat-i915-headers/gt/intel_gt_types.h +++ /dev/null @@ -1,11 +0,0 @@ -/* SPDX-License-Identifier: MIT */ -/* - * Copyright © 2023 Intel Corporation - */ - -#ifndef __INTEL_GT_TYPES__ -#define __INTEL_GT_TYPES__ - -#define intel_gt_support_legacy_fencing(gt) 0 - -#endif diff --git a/drivers/gpu/drm/xe/compat-i915-headers/i915_active.h b/drivers/gpu/drm/xe/compat-i915-headers/i915_active.h deleted file mode 100644 index 6f0ab3753563..000000000000 --- a/drivers/gpu/drm/xe/compat-i915-headers/i915_active.h +++ /dev/null @@ -1,22 +0,0 @@ -/* SPDX-License-Identifier: MIT */ -/* - * Copyright © 2022 Intel Corporation - */ - -#ifndef _I915_ACTIVE_H_ -#define _I915_ACTIVE_H_ - -#include "i915_active_types.h" - -static inline void i915_active_init(struct i915_active *ref, - int (*active)(struct i915_active *ref), - void (*retire)(struct i915_active *ref), - unsigned long flags) -{ - (void) active; - (void) retire; -} - -#define i915_active_fini(active) do { } while (0) - -#endif diff --git a/drivers/gpu/drm/xe/compat-i915-headers/i915_active_types.h b/drivers/gpu/drm/xe/compat-i915-headers/i915_active_types.h deleted file mode 100644 index 8c31f9a8b168..000000000000 --- a/drivers/gpu/drm/xe/compat-i915-headers/i915_active_types.h +++ /dev/null @@ -1,13 +0,0 @@ -/* - * SPDX-License-Identifier: MIT - * - * Copyright © 2019 Intel Corporation - */ - -#ifndef _I915_ACTIVE_TYPES_H_ -#define _I915_ACTIVE_TYPES_H_ - -struct i915_active {}; -#define I915_ACTIVE_RETIRE_SLEEPS 0 - -#endif /* _I915_ACTIVE_TYPES_H_ */ diff --git a/drivers/gpu/drm/xe/compat-i915-headers/i915_config.h b/drivers/gpu/drm/xe/compat-i915-headers/i915_config.h index e835bea08d1b..d4522203e2dd 100644 --- a/drivers/gpu/drm/xe/compat-i915-headers/i915_config.h +++ b/drivers/gpu/drm/xe/compat-i915-headers/i915_config.h @@ -8,10 +8,7 @@ #include <linux/sched.h> -struct drm_i915_private; - -static inline unsigned long -i915_fence_timeout(const struct drm_i915_private *i915) +static inline unsigned long i915_fence_timeout(void) { return MAX_SCHEDULE_TIMEOUT; } diff --git a/drivers/gpu/drm/xe/compat-i915-headers/i915_drv.h b/drivers/gpu/drm/xe/compat-i915-headers/i915_drv.h deleted file mode 100644 index 3e79a74ff7de..000000000000 --- a/drivers/gpu/drm/xe/compat-i915-headers/i915_drv.h +++ /dev/null @@ -1,37 +0,0 @@ -/* SPDX-License-Identifier: MIT */ -/* - * Copyright © 2023 Intel Corporation - */ -#ifndef _XE_I915_DRV_H_ -#define _XE_I915_DRV_H_ - -/* - * "Adaptation header" to allow i915 display to also build for xe driver. - * TODO: refactor i915 and xe so this can cease to exist - */ - -#include <drm/drm_drv.h> - -#include "xe_device_types.h" - -static inline struct drm_i915_private *to_i915(const struct drm_device *dev) -{ - return container_of(dev, struct drm_i915_private, drm); -} - -/* compat platform checks only for soc/ usage */ -#define IS_PLATFORM(xe, x) ((xe)->info.platform == x) -#define IS_I915G(dev_priv) (dev_priv && 0) -#define IS_I915GM(dev_priv) (dev_priv && 0) -#define IS_PINEVIEW(dev_priv) (dev_priv && 0) -#define IS_VALLEYVIEW(dev_priv) (dev_priv && 0) -#define IS_CHERRYVIEW(dev_priv) (dev_priv && 0) -#define IS_HASWELL(dev_priv) (dev_priv && 0) -#define IS_BROADWELL(dev_priv) (dev_priv && 0) -#define IS_BROXTON(dev_priv) (dev_priv && 0) -#define IS_GEMINILAKE(dev_priv) (dev_priv && 0) -#define IS_DG2(dev_priv) IS_PLATFORM(dev_priv, XE_DG2) - -#define IS_MOBILE(xe) (xe && 0) - -#endif diff --git a/drivers/gpu/drm/xe/compat-i915-headers/i915_irq.h b/drivers/gpu/drm/xe/compat-i915-headers/i915_irq.h deleted file mode 100644 index 61707a07f91f..000000000000 --- a/drivers/gpu/drm/xe/compat-i915-headers/i915_irq.h +++ /dev/null @@ -1,6 +0,0 @@ -/* SPDX-License-Identifier: MIT */ -/* - * Copyright © 2023 Intel Corporation - */ - -#include "../../i915/i915_irq.h" diff --git a/drivers/gpu/drm/xe/compat-i915-headers/i915_utils.h b/drivers/gpu/drm/xe/compat-i915-headers/i915_utils.h deleted file mode 100644 index bcd441dc0fce..000000000000 --- a/drivers/gpu/drm/xe/compat-i915-headers/i915_utils.h +++ /dev/null @@ -1,13 +0,0 @@ -/* SPDX-License-Identifier: MIT */ -/* - * Copyright © 2023 Intel Corporation - */ - -/* for soc/ */ -#ifndef MISSING_CASE -#define MISSING_CASE(x) WARN(1, "Missing case (%s == %ld)\n", \ - __stringify(x), (long)(x)) -#endif - -/* for a couple of users under i915/display */ -#define i915_inject_probe_failure(unused) ((unused) && 0) diff --git a/drivers/gpu/drm/xe/compat-i915-headers/i915_vgpu.h b/drivers/gpu/drm/xe/compat-i915-headers/i915_vgpu.h deleted file mode 100644 index 4931c7198f13..000000000000 --- a/drivers/gpu/drm/xe/compat-i915-headers/i915_vgpu.h +++ /dev/null @@ -1,18 +0,0 @@ -/* SPDX-License-Identifier: MIT */ -/* - * Copyright © 2023 Intel Corporation - */ - -#ifndef _I915_VGPU_H_ -#define _I915_VGPU_H_ - -#include <linux/types.h> - -struct drm_i915_private; - -static inline bool intel_vgpu_active(struct drm_i915_private *i915) -{ - return false; -} - -#endif /* _I915_VGPU_H_ */ diff --git a/drivers/gpu/drm/xe/compat-i915-headers/i915_vma.h b/drivers/gpu/drm/xe/compat-i915-headers/i915_vma.h index b17e3bab23d5..c4b5adaaa99a 100644 --- a/drivers/gpu/drm/xe/compat-i915-headers/i915_vma.h +++ b/drivers/gpu/drm/xe/compat-i915-headers/i915_vma.h @@ -8,7 +8,7 @@ #include <uapi/drm/i915_drm.h> -#include "xe_ggtt_types.h" +#include "xe_ggtt.h" #include <linux/refcount.h> @@ -30,7 +30,7 @@ struct i915_vma { static inline u32 i915_ggtt_offset(const struct i915_vma *vma) { - return vma->node->base.start; + return xe_ggtt_node_addr(vma->node); } #endif diff --git a/drivers/gpu/drm/xe/compat-i915-headers/intel_uncore.h b/drivers/gpu/drm/xe/compat-i915-headers/intel_uncore.h index d93ddacdf743..c05d4c4292d3 100644 --- a/drivers/gpu/drm/xe/compat-i915-headers/intel_uncore.h +++ b/drivers/gpu/drm/xe/compat-i915-headers/intel_uncore.h @@ -10,8 +10,6 @@ #include "xe_device_types.h" #include "xe_mmio.h" -#define FORCEWAKE_ALL XE_FORCEWAKE_ALL - static inline struct intel_uncore *to_intel_uncore(struct drm_device *drm) { return &to_xe_device(drm)->uncore; @@ -154,9 +152,10 @@ static inline void intel_uncore_write_notrace(struct intel_uncore *uncore, xe_mmio_write32(__compat_uncore_to_mmio(uncore), reg, val); } -#define intel_uncore_forcewake_get(x, y) do { } while (0) -#define intel_uncore_forcewake_put(x, y) do { } while (0) - -#define intel_uncore_arm_unclaimed_mmio_detection(x) do { } while (0) +static inline bool +intel_uncore_arm_unclaimed_mmio_detection(struct intel_uncore *uncore) +{ + return false; +} #endif /* __INTEL_UNCORE_H__ */ diff --git a/drivers/gpu/drm/xe/compat-i915-headers/intel_wakeref.h b/drivers/gpu/drm/xe/compat-i915-headers/intel_wakeref.h deleted file mode 100644 index 2a32faea9db5..000000000000 --- a/drivers/gpu/drm/xe/compat-i915-headers/intel_wakeref.h +++ /dev/null @@ -1,10 +0,0 @@ -/* SPDX-License-Identifier: MIT */ -/* - * Copyright © 2023 Intel Corporation - */ - -#include <linux/types.h> - -typedef struct ref_tracker *intel_wakeref_t; - -#define INTEL_WAKEREF_DEF ERR_PTR(-ENOENT) diff --git a/drivers/gpu/drm/xe/compat-i915-headers/pxp/intel_pxp.h b/drivers/gpu/drm/xe/compat-i915-headers/pxp/intel_pxp.h deleted file mode 100644 index 97fd0ddf0b3a..000000000000 --- a/drivers/gpu/drm/xe/compat-i915-headers/pxp/intel_pxp.h +++ /dev/null @@ -1,29 +0,0 @@ -/* SPDX-License-Identifier: MIT */ -/* - * Copyright © 2023 Intel Corporation - */ - -#ifndef __INTEL_PXP_H__ -#define __INTEL_PXP_H__ - -#include <linux/errno.h> -#include <linux/types.h> - -#include "xe_pxp.h" - -struct drm_gem_object; - -static inline int intel_pxp_key_check(struct drm_gem_object *obj, bool assign) -{ - /* - * The assign variable is used in i915 to assign the key to the BO at - * first submission time. In Xe the key is instead assigned at BO - * creation time, so the assign variable must always be false. - */ - if (assign) - return -EINVAL; - - return xe_pxp_obj_key_check(obj); -} - -#endif diff --git a/drivers/gpu/drm/xe/compat-i915-headers/soc/intel_dram.h b/drivers/gpu/drm/xe/compat-i915-headers/soc/intel_dram.h deleted file mode 100644 index 65707e20c557..000000000000 --- a/drivers/gpu/drm/xe/compat-i915-headers/soc/intel_dram.h +++ /dev/null @@ -1,6 +0,0 @@ -/* SPDX-License-Identifier: MIT */ -/* - * Copyright © 2023 Intel Corporation - */ - -#include "../../../i915/soc/intel_dram.h" diff --git a/drivers/gpu/drm/xe/compat-i915-headers/soc/intel_gmch.h b/drivers/gpu/drm/xe/compat-i915-headers/soc/intel_gmch.h deleted file mode 100644 index 33c5257b3a71..000000000000 --- a/drivers/gpu/drm/xe/compat-i915-headers/soc/intel_gmch.h +++ /dev/null @@ -1,6 +0,0 @@ -/* SPDX-License-Identifier: MIT */ -/* - * Copyright © 2023 Intel Corporation - */ - -#include "../../../i915/soc/intel_gmch.h" diff --git a/drivers/gpu/drm/xe/compat-i915-headers/soc/intel_rom.h b/drivers/gpu/drm/xe/compat-i915-headers/soc/intel_rom.h deleted file mode 100644 index 05cbfb697b2b..000000000000 --- a/drivers/gpu/drm/xe/compat-i915-headers/soc/intel_rom.h +++ /dev/null @@ -1,6 +0,0 @@ -/* SPDX-License-Identifier: MIT */ -/* - * Copyright © 2024 Intel Corporation - */ - -#include "../../../i915/soc/intel_rom.h" diff --git a/drivers/gpu/drm/xe/display/ext/i915_irq.c b/drivers/gpu/drm/xe/display/ext/i915_irq.c deleted file mode 100644 index 3c6bca66ddab..000000000000 --- a/drivers/gpu/drm/xe/display/ext/i915_irq.c +++ /dev/null @@ -1,85 +0,0 @@ -// SPDX-License-Identifier: MIT -/* - * Copyright © 2023 Intel Corporation - */ - -#include "i915_irq.h" -#include "i915_reg.h" -#include "intel_uncore.h" - -void gen2_irq_reset(struct intel_uncore *uncore, struct i915_irq_regs regs) -{ - intel_uncore_write(uncore, regs.imr, 0xffffffff); - intel_uncore_posting_read(uncore, regs.imr); - - intel_uncore_write(uncore, regs.ier, 0); - - /* IIR can theoretically queue up two events. Be paranoid. */ - intel_uncore_write(uncore, regs.iir, 0xffffffff); - intel_uncore_posting_read(uncore, regs.iir); - intel_uncore_write(uncore, regs.iir, 0xffffffff); - intel_uncore_posting_read(uncore, regs.iir); -} - -/* - * We should clear IMR at preinstall/uninstall, and just check at postinstall. - */ -void gen2_assert_iir_is_zero(struct intel_uncore *uncore, i915_reg_t reg) -{ - struct xe_device *xe = container_of(uncore, struct xe_device, uncore); - u32 val = intel_uncore_read(uncore, reg); - - if (val == 0) - return; - - drm_WARN(&xe->drm, 1, - "Interrupt register 0x%x is not zero: 0x%08x\n", - i915_mmio_reg_offset(reg), val); - intel_uncore_write(uncore, reg, 0xffffffff); - intel_uncore_posting_read(uncore, reg); - intel_uncore_write(uncore, reg, 0xffffffff); - intel_uncore_posting_read(uncore, reg); -} - -void gen2_irq_init(struct intel_uncore *uncore, struct i915_irq_regs regs, - u32 imr_val, u32 ier_val) -{ - gen2_assert_iir_is_zero(uncore, regs.iir); - - intel_uncore_write(uncore, regs.ier, ier_val); - intel_uncore_write(uncore, regs.imr, imr_val); - intel_uncore_posting_read(uncore, regs.imr); -} - -void gen2_error_reset(struct intel_uncore *uncore, struct i915_error_regs regs) -{ - intel_uncore_write(uncore, regs.emr, 0xffffffff); - intel_uncore_posting_read(uncore, regs.emr); - - intel_uncore_write(uncore, regs.eir, 0xffffffff); - intel_uncore_posting_read(uncore, regs.eir); - intel_uncore_write(uncore, regs.eir, 0xffffffff); - intel_uncore_posting_read(uncore, regs.eir); -} - -void gen2_error_init(struct intel_uncore *uncore, struct i915_error_regs regs, - u32 emr_val) -{ - intel_uncore_write(uncore, regs.eir, 0xffffffff); - intel_uncore_posting_read(uncore, regs.eir); - intel_uncore_write(uncore, regs.eir, 0xffffffff); - intel_uncore_posting_read(uncore, regs.eir); - - intel_uncore_write(uncore, regs.emr, emr_val); - intel_uncore_posting_read(uncore, regs.emr); -} - -bool intel_irqs_enabled(struct xe_device *xe) -{ - return atomic_read(&xe->irq.enabled); -} - -void intel_synchronize_irq(struct xe_device *xe) -{ - synchronize_irq(to_pci_dev(xe->drm.dev)->irq); -} diff --git a/drivers/gpu/drm/xe/display/intel_bo.c b/drivers/gpu/drm/xe/display/intel_bo.c index bad2243b9114..e8049a255d21 100644 --- a/drivers/gpu/drm/xe/display/intel_bo.c +++ b/drivers/gpu/drm/xe/display/intel_bo.c @@ -3,9 +3,10 @@ #include <drm/drm_gem.h> -#include "xe_bo.h" #include "intel_bo.h" #include "intel_frontbuffer.h" +#include "xe_bo.h" +#include "xe_pxp.h" bool intel_bo_is_tiled(struct drm_gem_object *obj) { @@ -29,6 +30,11 @@ bool intel_bo_is_protected(struct drm_gem_object *obj) return xe_bo_is_protected(gem_to_xe_bo(obj)); } +int intel_bo_key_check(struct drm_gem_object *obj) +{ + return xe_pxp_obj_key_check(obj); +} + int intel_bo_fb_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma) { return drm_gem_prime_mmap(obj, vma); diff --git a/drivers/gpu/drm/xe/display/xe_display.c b/drivers/gpu/drm/xe/display/xe_display.c index 8b0afa270216..f8a831b5dc7d 100644 --- a/drivers/gpu/drm/xe/display/xe_display.c +++ b/drivers/gpu/drm/xe/display/xe_display.c @@ -17,7 +17,6 @@ #include <drm/intel/display_parent_interface.h> #include <uapi/drm/xe_drm.h> -#include "soc/intel_dram.h" #include "intel_acpi.h" #include "intel_audio.h" #include "intel_bw.h" @@ -29,6 +28,7 @@ #include "intel_dmc.h" #include "intel_dmc_wl.h" #include "intel_dp.h" +#include "intel_dram.h" #include "intel_encoder.h" #include "intel_fbdev.h" #include "intel_hdcp.h" @@ -36,7 +36,11 @@ #include "intel_opregion.h" #include "skl_watermark.h" #include "xe_display_rpm.h" +#include "xe_hdcp_gsc.h" +#include "xe_initial_plane.h" #include "xe_module.h" +#include "xe_panic.h" +#include "xe_stolen.h" /* Ensure drm and display members are placed properly. */ INTEL_DISPLAY_MEMBER_STATIC_ASSERT(struct xe_device, drm, display); @@ -122,7 +126,7 @@ int xe_display_init_early(struct xe_device *xe) * Fill the dram structure to get the system dram info. This will be * used for memory latency calculation. */ - err = intel_dram_detect(xe); + err = intel_dram_detect(display); if (err) goto err_opregion; @@ -516,8 +520,30 @@ static void display_device_remove(struct drm_device *dev, void *arg) intel_display_device_remove(display); } +static bool irq_enabled(struct drm_device *drm) +{ + struct xe_device *xe = to_xe_device(drm); + + return atomic_read(&xe->irq.enabled); +} + +static void irq_synchronize(struct drm_device *drm) +{ + synchronize_irq(to_pci_dev(drm->dev)->irq); +} + +static const struct intel_display_irq_interface xe_display_irq_interface = { + .enabled = irq_enabled, + .synchronize = irq_synchronize, +}; + static const struct intel_display_parent_interface parent = { + .hdcp = &xe_display_hdcp_interface, + .initial_plane = &xe_display_initial_plane_interface, + .irq = &xe_display_irq_interface, + .panic = &xe_display_panic_interface, .rpm = &xe_display_rpm_interface, + .stolen = &xe_display_stolen_interface, }; /** diff --git a/drivers/gpu/drm/xe/display/xe_display_misc.c b/drivers/gpu/drm/xe/display/xe_display_misc.c deleted file mode 100644 index 242c2ef4ca93..000000000000 --- a/drivers/gpu/drm/xe/display/xe_display_misc.c +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-License-Identifier: MIT -/* - * Copyright © 2023 Intel Corporation - */ - -#include "intel_display_types.h" - -struct pci_dev; - -unsigned int intel_gmch_vga_set_decode(struct pci_dev *pdev, bool enable_decode); - -unsigned int intel_gmch_vga_set_decode(struct pci_dev *pdev, bool enable_decode) -{ - /* ToDo: Implement the actual handling of vga decode */ - return 0; -} diff --git a/drivers/gpu/drm/xe/display/xe_display_rpm.c b/drivers/gpu/drm/xe/display/xe_display_rpm.c index 340f65884812..b3db40035499 100644 --- a/drivers/gpu/drm/xe/display/xe_display_rpm.c +++ b/drivers/gpu/drm/xe/display/xe_display_rpm.c @@ -9,6 +9,9 @@ #include "xe_device_types.h" #include "xe_pm.h" +/* -ENOENT means we got the ref, but there's no tracking */ +#define INTEL_WAKEREF_DEF ERR_PTR(-ENOENT) + static struct ref_tracker *xe_display_rpm_get(const struct drm_device *drm) { return xe_pm_runtime_resume_and_get(to_xe_device(drm)) ? INTEL_WAKEREF_DEF : NULL; diff --git a/drivers/gpu/drm/xe/display/xe_dsb_buffer.c b/drivers/gpu/drm/xe/display/xe_dsb_buffer.c index 58581d7aaae6..fa0acb11eaad 100644 --- a/drivers/gpu/drm/xe/display/xe_dsb_buffer.c +++ b/drivers/gpu/drm/xe/display/xe_dsb_buffer.c @@ -3,44 +3,49 @@ * Copyright 2023, Intel Corporation. */ -#include "i915_vma.h" -#include "intel_display_types.h" #include "intel_dsb_buffer.h" #include "xe_bo.h" #include "xe_device.h" #include "xe_device_types.h" +struct intel_dsb_buffer { + u32 *cmd_buf; + struct xe_bo *bo; + size_t buf_size; +}; + u32 intel_dsb_buffer_ggtt_offset(struct intel_dsb_buffer *dsb_buf) { - return xe_bo_ggtt_addr(dsb_buf->vma->bo); + return xe_bo_ggtt_addr(dsb_buf->bo); } void intel_dsb_buffer_write(struct intel_dsb_buffer *dsb_buf, u32 idx, u32 val) { - iosys_map_wr(&dsb_buf->vma->bo->vmap, idx * 4, u32, val); + iosys_map_wr(&dsb_buf->bo->vmap, idx * 4, u32, val); } u32 intel_dsb_buffer_read(struct intel_dsb_buffer *dsb_buf, u32 idx) { - return iosys_map_rd(&dsb_buf->vma->bo->vmap, idx * 4, u32); + return iosys_map_rd(&dsb_buf->bo->vmap, idx * 4, u32); } void intel_dsb_buffer_memset(struct intel_dsb_buffer *dsb_buf, u32 idx, u32 val, size_t size) { WARN_ON(idx > (dsb_buf->buf_size - size) / sizeof(*dsb_buf->cmd_buf)); - iosys_map_memset(&dsb_buf->vma->bo->vmap, idx * 4, val, size); + iosys_map_memset(&dsb_buf->bo->vmap, idx * 4, val, size); } -bool intel_dsb_buffer_create(struct intel_crtc *crtc, struct intel_dsb_buffer *dsb_buf, size_t size) +struct intel_dsb_buffer *intel_dsb_buffer_create(struct drm_device *drm, size_t size) { - struct xe_device *xe = to_xe_device(crtc->base.dev); + struct xe_device *xe = to_xe_device(drm); + struct intel_dsb_buffer *dsb_buf; struct xe_bo *obj; - struct i915_vma *vma; + int ret; - vma = kzalloc(sizeof(*vma), GFP_KERNEL); - if (!vma) - return false; + dsb_buf = kzalloc(sizeof(*dsb_buf), GFP_KERNEL); + if (!dsb_buf) + return ERR_PTR(-ENOMEM); /* Set scanout flag for WC mapping */ obj = xe_bo_create_pin_map_novm(xe, xe_device_get_root_tile(xe), @@ -49,26 +54,30 @@ bool intel_dsb_buffer_create(struct intel_crtc *crtc, struct intel_dsb_buffer *d XE_BO_FLAG_VRAM_IF_DGFX(xe_device_get_root_tile(xe)) | XE_BO_FLAG_SCANOUT | XE_BO_FLAG_GGTT, false); if (IS_ERR(obj)) { - kfree(vma); - return false; + ret = PTR_ERR(obj); + goto err_pin_map; } - vma->bo = obj; - dsb_buf->vma = vma; + dsb_buf->bo = obj; dsb_buf->buf_size = size; - return true; + return dsb_buf; + +err_pin_map: + kfree(dsb_buf); + + return ERR_PTR(ret); } void intel_dsb_buffer_cleanup(struct intel_dsb_buffer *dsb_buf) { - xe_bo_unpin_map_no_vm(dsb_buf->vma->bo); - kfree(dsb_buf->vma); + xe_bo_unpin_map_no_vm(dsb_buf->bo); + kfree(dsb_buf); } void intel_dsb_buffer_flush_map(struct intel_dsb_buffer *dsb_buf) { - struct xe_device *xe = dsb_buf->vma->bo->tile->xe; + struct xe_device *xe = dsb_buf->bo->tile->xe; /* * The memory barrier here is to ensure coherency of DSB vs MMIO, diff --git a/drivers/gpu/drm/xe/display/xe_fb_pin.c b/drivers/gpu/drm/xe/display/xe_fb_pin.c index 1fd4a815e784..d2c4e94180fa 100644 --- a/drivers/gpu/drm/xe/display/xe_fb_pin.c +++ b/drivers/gpu/drm/xe/display/xe_fb_pin.c @@ -171,12 +171,13 @@ static int __xe_pin_fb_vma_dpt(const struct intel_framebuffer *fb, } static void -write_ggtt_rotated(struct xe_bo *bo, struct xe_ggtt *ggtt, u32 *ggtt_ofs, u32 bo_ofs, +write_ggtt_rotated(struct xe_ggtt *ggtt, u32 *ggtt_ofs, + u64 pte_flags, + xe_ggtt_set_pte_fn write_pte, + struct xe_bo *bo, u32 bo_ofs, u32 width, u32 height, u32 src_stride, u32 dst_stride) { - struct xe_device *xe = xe_bo_device(bo); u32 column, row; - u64 pte = ggtt->pt_ops->pte_encode_flags(bo, xe->pat.idx[XE_CACHE_NONE]); for (column = 0; column < width; column++) { u32 src_idx = src_stride * (height - 1) + column + bo_ofs; @@ -184,7 +185,7 @@ write_ggtt_rotated(struct xe_bo *bo, struct xe_ggtt *ggtt, u32 *ggtt_ofs, u32 bo for (row = 0; row < height; row++) { u64 addr = xe_bo_addr(bo, src_idx * XE_PAGE_SIZE, XE_PAGE_SIZE); - ggtt->pt_ops->ggtt_set_pte(ggtt, *ggtt_ofs, pte | addr); + write_pte(ggtt, *ggtt_ofs, pte_flags | addr); *ggtt_ofs += XE_PAGE_SIZE; src_idx -= src_stride; } @@ -194,6 +195,28 @@ write_ggtt_rotated(struct xe_bo *bo, struct xe_ggtt *ggtt, u32 *ggtt_ofs, u32 bo } } +struct fb_rotate_args { + const struct i915_gtt_view *view; + struct xe_bo *bo; +}; + +static void write_ggtt_rotated_node(struct xe_ggtt *ggtt, struct xe_ggtt_node *node, + u64 pte_flags, xe_ggtt_set_pte_fn write_pte, void *data) +{ + struct fb_rotate_args *args = data; + struct xe_bo *bo = args->bo; + const struct intel_rotation_info *rot_info = &args->view->rotated; + u32 ggtt_ofs = xe_ggtt_node_addr(node); + + for (u32 i = 0; i < ARRAY_SIZE(rot_info->plane); i++) + write_ggtt_rotated(ggtt, &ggtt_ofs, pte_flags, write_pte, + bo, rot_info->plane[i].offset, + rot_info->plane[i].width, + rot_info->plane[i].height, + rot_info->plane[i].src_stride, + rot_info->plane[i].dst_stride); +} + static int __xe_pin_fb_vma_ggtt(const struct intel_framebuffer *fb, const struct i915_gtt_view *view, struct i915_vma *vma, @@ -204,71 +227,43 @@ static int __xe_pin_fb_vma_ggtt(const struct intel_framebuffer *fb, struct xe_device *xe = to_xe_device(fb->base.dev); struct xe_tile *tile0 = xe_device_get_root_tile(xe); struct xe_ggtt *ggtt = tile0->mem.ggtt; + u64 pte, size; u32 align; - int ret; + int ret = 0; /* TODO: Consider sharing framebuffer mapping? * embed i915_vma inside intel_framebuffer */ - xe_pm_runtime_get_noresume(xe); - ret = mutex_lock_interruptible(&ggtt->lock); - if (ret) - goto out; + guard(xe_pm_runtime_noresume)(xe); align = XE_PAGE_SIZE; - if (xe_bo_is_vram(bo) && ggtt->flags & XE_GGTT_FLAGS_64K) - align = max_t(u32, align, SZ_64K); + if (xe_bo_is_vram(bo) && xe->info.vram_flags & XE_VRAM_FLAGS_NEED64K) + align = max(align, SZ_64K); + /* Fast case, preallocated GGTT view? */ if (bo->ggtt_node[tile0->id] && view->type == I915_GTT_VIEW_NORMAL) { vma->node = bo->ggtt_node[tile0->id]; - } else if (view->type == I915_GTT_VIEW_NORMAL) { - vma->node = xe_ggtt_node_init(ggtt); - if (IS_ERR(vma->node)) { - ret = PTR_ERR(vma->node); - goto out_unlock; - } - - ret = xe_ggtt_node_insert_locked(vma->node, xe_bo_size(bo), align, 0); - if (ret) { - xe_ggtt_node_fini(vma->node); - goto out_unlock; - } - - xe_ggtt_map_bo(ggtt, vma->node, bo, xe->pat.idx[XE_CACHE_NONE]); - } else { - u32 i, ggtt_ofs; - const struct intel_rotation_info *rot_info = &view->rotated; - - /* display seems to use tiles instead of bytes here, so convert it back.. */ - u32 size = intel_rotation_info_size(rot_info) * XE_PAGE_SIZE; - - vma->node = xe_ggtt_node_init(ggtt); - if (IS_ERR(vma->node)) { - ret = PTR_ERR(vma->node); - goto out_unlock; - } - - ret = xe_ggtt_node_insert_locked(vma->node, size, align, 0); - if (ret) { - xe_ggtt_node_fini(vma->node); - goto out_unlock; - } - - ggtt_ofs = vma->node->base.start; - - for (i = 0; i < ARRAY_SIZE(rot_info->plane); i++) - write_ggtt_rotated(bo, ggtt, &ggtt_ofs, - rot_info->plane[i].offset, - rot_info->plane[i].width, - rot_info->plane[i].height, - rot_info->plane[i].src_stride, - rot_info->plane[i].dst_stride); + return 0; } -out_unlock: - mutex_unlock(&ggtt->lock); -out: - xe_pm_runtime_put(xe); + /* TODO: Consider sharing framebuffer mapping? + * embed i915_vma inside intel_framebuffer + */ + if (view->type == I915_GTT_VIEW_NORMAL) + size = xe_bo_size(bo); + else + /* display uses tiles instead of bytes here, so convert it back.. */ + size = intel_rotation_info_size(&view->rotated) * XE_PAGE_SIZE; + + pte = xe_ggtt_encode_pte_flags(ggtt, bo, xe->pat.idx[XE_CACHE_NONE]); + vma->node = xe_ggtt_node_insert_transform(ggtt, bo, pte, + ALIGN(size, align), align, + view->type == I915_GTT_VIEW_NORMAL ? + NULL : write_ggtt_rotated_node, + &(struct fb_rotate_args){view, bo}); + if (IS_ERR(vma->node)) + ret = PTR_ERR(vma->node); + return ret; } @@ -358,7 +353,7 @@ static void __xe_unpin_fb_vma(struct i915_vma *vma) if (vma->dpt) xe_bo_unpin_map_no_vm(vma->dpt); else if (!xe_ggtt_node_allocated(vma->bo->ggtt_node[tile_id]) || - vma->bo->ggtt_node[tile_id]->base.start != vma->node->base.start) + vma->bo->ggtt_node[tile_id] != vma->node) xe_ggtt_node_remove(vma->node, false); ttm_bo_reserve(&vma->bo->ttm, false, false, NULL); @@ -378,7 +373,7 @@ intel_fb_pin_to_ggtt(const struct drm_framebuffer *fb, { *out_flags = 0; - return __xe_pin_fb_vma(to_intel_framebuffer(fb), view, phys_alignment); + return __xe_pin_fb_vma(to_intel_framebuffer(fb), view, alignment); } void intel_fb_unpin_vma(struct i915_vma *vma, unsigned long flags) diff --git a/drivers/gpu/drm/xe/display/xe_hdcp_gsc.c b/drivers/gpu/drm/xe/display/xe_hdcp_gsc.c index 4ae847b628e2..ed1f65f5ef4d 100644 --- a/drivers/gpu/drm/xe/display/xe_hdcp_gsc.c +++ b/drivers/gpu/drm/xe/display/xe_hdcp_gsc.c @@ -3,18 +3,20 @@ * Copyright 2023, Intel Corporation. */ +#include <linux/delay.h> + #include <drm/drm_print.h> +#include <drm/intel/display_parent_interface.h> #include <drm/intel/i915_hdcp_interface.h> -#include <linux/delay.h> #include "abi/gsc_command_header_abi.h" -#include "intel_hdcp_gsc.h" #include "xe_bo.h" #include "xe_device.h" #include "xe_device_types.h" #include "xe_force_wake.h" #include "xe_gsc_proxy.h" #include "xe_gsc_submit.h" +#include "xe_hdcp_gsc.h" #include "xe_map.h" #include "xe_pm.h" #include "xe_uc_fw.h" @@ -30,37 +32,28 @@ struct intel_hdcp_gsc_context { #define HDCP_GSC_HEADER_SIZE sizeof(struct intel_gsc_mtl_header) -bool intel_hdcp_gsc_check_status(struct drm_device *drm) +static bool intel_hdcp_gsc_check_status(struct drm_device *drm) { struct xe_device *xe = to_xe_device(drm); struct xe_tile *tile = xe_device_get_root_tile(xe); struct xe_gt *gt = tile->media_gt; struct xe_gsc *gsc = >->uc.gsc; - bool ret = true; - unsigned int fw_ref; - if (!gsc || !xe_uc_fw_is_enabled(&gsc->fw)) { + if (!gsc || !xe_uc_fw_is_available(&gsc->fw)) { drm_dbg_kms(&xe->drm, "GSC Components not ready for HDCP2.x\n"); return false; } - xe_pm_runtime_get(xe); - fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GSC); - if (!fw_ref) { + guard(xe_pm_runtime)(xe); + CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GSC); + if (!fw_ref.domains) { drm_dbg_kms(&xe->drm, "failed to get forcewake to check proxy status\n"); - ret = false; - goto out; + return false; } - if (!xe_gsc_proxy_init_done(gsc)) - ret = false; - - xe_force_wake_put(gt_to_fw(gt), fw_ref); -out: - xe_pm_runtime_put(xe); - return ret; + return xe_gsc_proxy_init_done(gsc); } /*This function helps allocate memory for the command that we will send to gsc cs */ @@ -96,7 +89,7 @@ out: return ret; } -struct intel_hdcp_gsc_context *intel_hdcp_gsc_context_alloc(struct drm_device *drm) +static struct intel_hdcp_gsc_context *intel_hdcp_gsc_context_alloc(struct drm_device *drm) { struct xe_device *xe = to_xe_device(drm); struct intel_hdcp_gsc_context *gsc_context; @@ -120,7 +113,7 @@ struct intel_hdcp_gsc_context *intel_hdcp_gsc_context_alloc(struct drm_device *d return gsc_context; } -void intel_hdcp_gsc_context_free(struct intel_hdcp_gsc_context *gsc_context) +static void intel_hdcp_gsc_context_free(struct intel_hdcp_gsc_context *gsc_context) { if (!gsc_context) return; @@ -155,9 +148,9 @@ static int xe_gsc_send_sync(struct xe_device *xe, return ret; } -ssize_t intel_hdcp_gsc_msg_send(struct intel_hdcp_gsc_context *gsc_context, - void *msg_in, size_t msg_in_len, - void *msg_out, size_t msg_out_len) +static ssize_t intel_hdcp_gsc_msg_send(struct intel_hdcp_gsc_context *gsc_context, + void *msg_in, size_t msg_in_len, + void *msg_out, size_t msg_out_len) { struct xe_device *xe = gsc_context->xe; const size_t max_msg_size = PAGE_SIZE - HDCP_GSC_HEADER_SIZE; @@ -166,17 +159,15 @@ ssize_t intel_hdcp_gsc_msg_send(struct intel_hdcp_gsc_context *gsc_context, u32 addr_out_off, addr_in_wr_off = 0; int ret, tries = 0; - if (msg_in_len > max_msg_size || msg_out_len > max_msg_size) { - ret = -ENOSPC; - goto out; - } + if (msg_in_len > max_msg_size || msg_out_len > max_msg_size) + return -ENOSPC; msg_size_in = msg_in_len + HDCP_GSC_HEADER_SIZE; msg_size_out = msg_out_len + HDCP_GSC_HEADER_SIZE; addr_out_off = PAGE_SIZE; host_session_id = xe_gsc_create_host_session_id(); - xe_pm_runtime_get_noresume(xe); + guard(xe_pm_runtime_noresume)(xe); addr_in_wr_off = xe_gsc_emit_header(xe, &gsc_context->hdcp_bo->vmap, addr_in_wr_off, HECI_MEADDRESS_HDCP, host_session_id, msg_in_len); @@ -201,13 +192,18 @@ ssize_t intel_hdcp_gsc_msg_send(struct intel_hdcp_gsc_context *gsc_context, } while (++tries < 20); if (ret) - goto out; + return ret; xe_map_memcpy_from(xe, msg_out, &gsc_context->hdcp_bo->vmap, addr_out_off + HDCP_GSC_HEADER_SIZE, msg_out_len); -out: - xe_pm_runtime_put(xe); return ret; } + +const struct intel_display_hdcp_interface xe_display_hdcp_interface = { + .gsc_msg_send = intel_hdcp_gsc_msg_send, + .gsc_check_status = intel_hdcp_gsc_check_status, + .gsc_context_alloc = intel_hdcp_gsc_context_alloc, + .gsc_context_free = intel_hdcp_gsc_context_free, +}; diff --git a/drivers/gpu/drm/xe/display/xe_hdcp_gsc.h b/drivers/gpu/drm/xe/display/xe_hdcp_gsc.h new file mode 100644 index 000000000000..c1062e4b62f7 --- /dev/null +++ b/drivers/gpu/drm/xe/display/xe_hdcp_gsc.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: MIT */ +/* Copyright © 2025 Intel Corporation */ + +#ifndef __XE_HDCP_GSC_H__ +#define __XE_HDCP_GSC_H__ + +extern const struct intel_display_hdcp_interface xe_display_hdcp_interface; + +#endif diff --git a/drivers/gpu/drm/xe/display/xe_initial_plane.c b/drivers/gpu/drm/xe/display/xe_initial_plane.c new file mode 100644 index 000000000000..4cfeafcc158d --- /dev/null +++ b/drivers/gpu/drm/xe/display/xe_initial_plane.c @@ -0,0 +1,189 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright © 2021 Intel Corporation + */ + +/* for ioread64 */ +#include <linux/io-64-nonatomic-lo-hi.h> + +#include <drm/intel/display_parent_interface.h> + +#include "regs/xe_gtt_defs.h" +#include "xe_ggtt.h" +#include "xe_mmio.h" + +#include "i915_vma.h" +#include "intel_crtc.h" +#include "intel_display_regs.h" +#include "intel_display_types.h" +#include "intel_fb.h" +#include "intel_fb_pin.h" +#include "xe_bo.h" +#include "xe_vram_types.h" +#include "xe_wa.h" + +#include <generated/xe_device_wa_oob.h> + +/* Early xe has no irq */ +static void xe_initial_plane_vblank_wait(struct drm_crtc *_crtc) +{ + struct intel_crtc *crtc = to_intel_crtc(_crtc); + struct xe_device *xe = to_xe_device(crtc->base.dev); + struct xe_reg pipe_frmtmstmp = XE_REG(i915_mmio_reg_offset(PIPE_FRMTMSTMP(crtc->pipe))); + u32 timestamp; + int ret; + + timestamp = xe_mmio_read32(xe_root_tile_mmio(xe), pipe_frmtmstmp); + + ret = xe_mmio_wait32_not(xe_root_tile_mmio(xe), pipe_frmtmstmp, ~0U, timestamp, 40000U, ×tamp, false); + if (ret < 0) + drm_warn(&xe->drm, "waiting for early vblank failed with %i\n", ret); +} + +static struct xe_bo * +initial_plane_bo(struct xe_device *xe, + struct intel_initial_plane_config *plane_config) +{ + struct xe_tile *tile0 = xe_device_get_root_tile(xe); + struct xe_bo *bo; + resource_size_t phys_base; + u32 base, size, flags; + u64 page_size = xe->info.vram_flags & XE_VRAM_FLAGS_NEED64K ? SZ_64K : SZ_4K; + + if (plane_config->size == 0) + return NULL; + + flags = XE_BO_FLAG_SCANOUT | XE_BO_FLAG_GGTT; + + base = round_down(plane_config->base, page_size); + if (IS_DGFX(xe)) { + u64 pte = xe_ggtt_read_pte(tile0->mem.ggtt, base); + + if (!(pte & XE_GGTT_PTE_DM)) { + drm_err(&xe->drm, + "Initial plane programming missing DM bit\n"); + return NULL; + } + + phys_base = pte & ~(page_size - 1); + flags |= XE_BO_FLAG_VRAM0; + + /* + * We don't currently expect this to ever be placed in the + * stolen portion. + */ + if (phys_base >= xe_vram_region_usable_size(tile0->mem.vram)) { + drm_err(&xe->drm, + "Initial plane programming using invalid range, phys_base=%pa\n", + &phys_base); + return NULL; + } + + drm_dbg(&xe->drm, + "Using phys_base=%pa, based on initial plane programming\n", + &phys_base); + } else { + struct ttm_resource_manager *stolen = ttm_manager_type(&xe->ttm, XE_PL_STOLEN); + + if (!stolen) + return NULL; + phys_base = base; + flags |= XE_BO_FLAG_STOLEN; + + if (XE_DEVICE_WA(xe, 22019338487_display)) + return NULL; + + /* + * If the FB is too big, just don't use it since fbdev is not very + * important and we should probably use that space with FBC or other + * features. + */ + if (IS_ENABLED(CONFIG_FRAMEBUFFER_CONSOLE) && + plane_config->size * 2 >> PAGE_SHIFT >= stolen->size) + return NULL; + } + + size = round_up(plane_config->base + plane_config->size, + page_size); + size -= base; + + bo = xe_bo_create_pin_map_at_novm(xe, tile0, size, phys_base, + ttm_bo_type_kernel, flags, 0, false); + if (IS_ERR(bo)) { + drm_dbg(&xe->drm, + "Failed to create bo phys_base=%pa size %u with flags %x: %li\n", + &phys_base, size, flags, PTR_ERR(bo)); + return NULL; + } + + return bo; +} + +static struct drm_gem_object * +xe_alloc_initial_plane_obj(struct drm_device *drm, + struct intel_initial_plane_config *plane_config) +{ + struct xe_device *xe = to_xe_device(drm); + struct drm_mode_fb_cmd2 mode_cmd = { 0 }; + struct drm_framebuffer *fb = &plane_config->fb->base; + struct xe_bo *bo; + + mode_cmd.pixel_format = fb->format->format; + mode_cmd.width = fb->width; + mode_cmd.height = fb->height; + mode_cmd.pitches[0] = fb->pitches[0]; + mode_cmd.modifier[0] = fb->modifier; + mode_cmd.flags = DRM_MODE_FB_MODIFIERS; + + bo = initial_plane_bo(xe, plane_config); + if (!bo) + return NULL; + + if (intel_framebuffer_init(to_intel_framebuffer(fb), + &bo->ttm.base, fb->format, &mode_cmd)) { + drm_dbg_kms(&xe->drm, "intel fb init failed\n"); + goto err_bo; + } + /* Reference handed over to fb */ + xe_bo_put(bo); + + return &bo->ttm.base; + +err_bo: + xe_bo_unpin_map_no_vm(bo); + return NULL; +} + +static int +xe_initial_plane_setup(struct drm_plane_state *_plane_state, + struct intel_initial_plane_config *plane_config, + struct drm_framebuffer *fb, + struct i915_vma *_unused) +{ + struct intel_plane_state *plane_state = to_intel_plane_state(_plane_state); + struct i915_vma *vma; + + vma = intel_fb_pin_to_ggtt(fb, &plane_state->view.gtt, + 0, 0, 0, false, &plane_state->flags); + if (IS_ERR(vma)) + return PTR_ERR(vma); + + plane_state->ggtt_vma = vma; + + plane_state->surf = i915_ggtt_offset(plane_state->ggtt_vma); + + plane_config->vma = vma; + + return 0; +} + +static void xe_plane_config_fini(struct intel_initial_plane_config *plane_config) +{ +} + +const struct intel_display_initial_plane_interface xe_display_initial_plane_interface = { + .vblank_wait = xe_initial_plane_vblank_wait, + .alloc_obj = xe_alloc_initial_plane_obj, + .setup = xe_initial_plane_setup, + .config_fini = xe_plane_config_fini, +}; diff --git a/drivers/gpu/drm/xe/display/xe_initial_plane.h b/drivers/gpu/drm/xe/display/xe_initial_plane.h new file mode 100644 index 000000000000..399d15f14441 --- /dev/null +++ b/drivers/gpu/drm/xe/display/xe_initial_plane.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: MIT */ +/* Copyright © 2025 Intel Corporation */ + +#ifndef __XE_INITIAL_PLANE_H__ +#define __XE_INITIAL_PLANE_H__ + +extern const struct intel_display_initial_plane_interface xe_display_initial_plane_interface; + +#endif diff --git a/drivers/gpu/drm/xe/display/xe_panic.c b/drivers/gpu/drm/xe/display/xe_panic.c index df663286092a..e078494dc8ba 100644 --- a/drivers/gpu/drm/xe/display/xe_panic.c +++ b/drivers/gpu/drm/xe/display/xe_panic.c @@ -3,11 +3,12 @@ #include <drm/drm_cache.h> #include <drm/drm_panic.h> +#include <drm/intel/display_parent_interface.h> #include "intel_display_types.h" #include "intel_fb.h" -#include "intel_panic.h" #include "xe_bo.h" +#include "xe_panic.h" #include "xe_res_cursor.h" struct intel_panic { @@ -74,7 +75,7 @@ static void xe_panic_page_set_pixel(struct drm_scanout_buffer *sb, unsigned int iosys_map_wr(&panic->vmap, offset, u32, color); } -struct intel_panic *intel_panic_alloc(void) +static struct intel_panic *xe_panic_alloc(void) { struct intel_panic *panic; @@ -83,7 +84,7 @@ struct intel_panic *intel_panic_alloc(void) return panic; } -int intel_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb) +static int xe_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb) { struct intel_framebuffer *fb = (struct intel_framebuffer *)sb->private; struct xe_bo *bo = gem_to_xe_bo(intel_fb_bo(&fb->base)); @@ -96,7 +97,8 @@ int intel_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb) return 0; } -void intel_panic_finish(struct intel_panic *panic) -{ - xe_panic_kunmap(panic); -} +const struct intel_display_panic_interface xe_display_panic_interface = { + .alloc = xe_panic_alloc, + .setup = xe_panic_setup, + .finish = xe_panic_kunmap, +}; diff --git a/drivers/gpu/drm/xe/display/xe_panic.h b/drivers/gpu/drm/xe/display/xe_panic.h new file mode 100644 index 000000000000..3054b511011b --- /dev/null +++ b/drivers/gpu/drm/xe/display/xe_panic.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: MIT */ +/* Copyright © 2025 Intel Corporation */ + +#ifndef __XE_PANIC_H__ +#define __XE_PANIC_H__ + +extern const struct intel_display_panic_interface xe_display_panic_interface; + +#endif diff --git a/drivers/gpu/drm/xe/display/xe_plane_initial.c b/drivers/gpu/drm/xe/display/xe_plane_initial.c deleted file mode 100644 index 12d25c5290fd..000000000000 --- a/drivers/gpu/drm/xe/display/xe_plane_initial.c +++ /dev/null @@ -1,319 +0,0 @@ -// SPDX-License-Identifier: MIT -/* - * Copyright © 2021 Intel Corporation - */ - -/* for ioread64 */ -#include <linux/io-64-nonatomic-lo-hi.h> - -#include "regs/xe_gtt_defs.h" -#include "xe_ggtt.h" -#include "xe_mmio.h" - -#include "i915_vma.h" -#include "intel_crtc.h" -#include "intel_display.h" -#include "intel_display_core.h" -#include "intel_display_regs.h" -#include "intel_display_types.h" -#include "intel_fb.h" -#include "intel_fb_pin.h" -#include "intel_frontbuffer.h" -#include "intel_plane.h" -#include "intel_plane_initial.h" -#include "xe_bo.h" -#include "xe_vram_types.h" -#include "xe_wa.h" - -#include <generated/xe_device_wa_oob.h> - -void intel_plane_initial_vblank_wait(struct intel_crtc *crtc) -{ - /* Early xe has no irq */ - struct xe_device *xe = to_xe_device(crtc->base.dev); - struct xe_reg pipe_frmtmstmp = XE_REG(i915_mmio_reg_offset(PIPE_FRMTMSTMP(crtc->pipe))); - u32 timestamp; - int ret; - - timestamp = xe_mmio_read32(xe_root_tile_mmio(xe), pipe_frmtmstmp); - - ret = xe_mmio_wait32_not(xe_root_tile_mmio(xe), pipe_frmtmstmp, ~0U, timestamp, 40000U, ×tamp, false); - if (ret < 0) - drm_warn(&xe->drm, "waiting for early vblank failed with %i\n", ret); -} - -static bool -intel_reuse_initial_plane_obj(struct intel_crtc *this, - const struct intel_initial_plane_config plane_configs[], - struct drm_framebuffer **fb) -{ - struct xe_device *xe = to_xe_device(this->base.dev); - struct intel_crtc *crtc; - - for_each_intel_crtc(&xe->drm, crtc) { - struct intel_plane *plane = - to_intel_plane(crtc->base.primary); - const struct intel_plane_state *plane_state = - to_intel_plane_state(plane->base.state); - const struct intel_crtc_state *crtc_state = - to_intel_crtc_state(crtc->base.state); - - if (!crtc_state->uapi.active) - continue; - - if (!plane_state->ggtt_vma) - continue; - - if (plane_configs[this->pipe].base == plane_configs[crtc->pipe].base) { - *fb = plane_state->hw.fb; - return true; - } - } - - return false; -} - -static struct xe_bo * -initial_plane_bo(struct xe_device *xe, - struct intel_initial_plane_config *plane_config) -{ - struct xe_tile *tile0 = xe_device_get_root_tile(xe); - struct xe_bo *bo; - resource_size_t phys_base; - u32 base, size, flags; - u64 page_size = xe->info.vram_flags & XE_VRAM_FLAGS_NEED64K ? SZ_64K : SZ_4K; - - if (plane_config->size == 0) - return NULL; - - flags = XE_BO_FLAG_SCANOUT | XE_BO_FLAG_GGTT; - - base = round_down(plane_config->base, page_size); - if (IS_DGFX(xe)) { - u64 pte = xe_ggtt_read_pte(tile0->mem.ggtt, base); - - if (!(pte & XE_GGTT_PTE_DM)) { - drm_err(&xe->drm, - "Initial plane programming missing DM bit\n"); - return NULL; - } - - phys_base = pte & ~(page_size - 1); - flags |= XE_BO_FLAG_VRAM0; - - /* - * We don't currently expect this to ever be placed in the - * stolen portion. - */ - if (phys_base >= xe_vram_region_usable_size(tile0->mem.vram)) { - drm_err(&xe->drm, - "Initial plane programming using invalid range, phys_base=%pa\n", - &phys_base); - return NULL; - } - - drm_dbg(&xe->drm, - "Using phys_base=%pa, based on initial plane programming\n", - &phys_base); - } else { - struct ttm_resource_manager *stolen = ttm_manager_type(&xe->ttm, XE_PL_STOLEN); - - if (!stolen) - return NULL; - phys_base = base; - flags |= XE_BO_FLAG_STOLEN; - - if (XE_DEVICE_WA(xe, 22019338487_display)) - return NULL; - - /* - * If the FB is too big, just don't use it since fbdev is not very - * important and we should probably use that space with FBC or other - * features. - */ - if (IS_ENABLED(CONFIG_FRAMEBUFFER_CONSOLE) && - plane_config->size * 2 >> PAGE_SHIFT >= stolen->size) - return NULL; - } - - size = round_up(plane_config->base + plane_config->size, - page_size); - size -= base; - - bo = xe_bo_create_pin_map_at_novm(xe, tile0, size, phys_base, - ttm_bo_type_kernel, flags, 0, false); - if (IS_ERR(bo)) { - drm_dbg(&xe->drm, - "Failed to create bo phys_base=%pa size %u with flags %x: %li\n", - &phys_base, size, flags, PTR_ERR(bo)); - return NULL; - } - - return bo; -} - -static bool -intel_alloc_initial_plane_obj(struct intel_crtc *crtc, - struct intel_initial_plane_config *plane_config) -{ - struct xe_device *xe = to_xe_device(crtc->base.dev); - struct drm_mode_fb_cmd2 mode_cmd = { 0 }; - struct drm_framebuffer *fb = &plane_config->fb->base; - struct xe_bo *bo; - - switch (fb->modifier) { - case DRM_FORMAT_MOD_LINEAR: - case I915_FORMAT_MOD_X_TILED: - case I915_FORMAT_MOD_Y_TILED: - case I915_FORMAT_MOD_4_TILED: - break; - default: - drm_dbg_kms(&xe->drm, - "Unsupported modifier for initial FB: 0x%llx\n", - fb->modifier); - return false; - } - - mode_cmd.pixel_format = fb->format->format; - mode_cmd.width = fb->width; - mode_cmd.height = fb->height; - mode_cmd.pitches[0] = fb->pitches[0]; - mode_cmd.modifier[0] = fb->modifier; - mode_cmd.flags = DRM_MODE_FB_MODIFIERS; - - bo = initial_plane_bo(xe, plane_config); - if (!bo) - return false; - - if (intel_framebuffer_init(to_intel_framebuffer(fb), - &bo->ttm.base, fb->format, &mode_cmd)) { - drm_dbg_kms(&xe->drm, "intel fb init failed\n"); - goto err_bo; - } - /* Reference handed over to fb */ - xe_bo_put(bo); - - return true; - -err_bo: - xe_bo_unpin_map_no_vm(bo); - return false; -} - -static void -intel_find_initial_plane_obj(struct intel_crtc *crtc, - struct intel_initial_plane_config plane_configs[]) -{ - struct intel_initial_plane_config *plane_config = - &plane_configs[crtc->pipe]; - struct intel_plane *plane = - to_intel_plane(crtc->base.primary); - struct intel_plane_state *plane_state = - to_intel_plane_state(plane->base.state); - struct drm_framebuffer *fb; - struct i915_vma *vma; - - /* - * TODO: - * Disable planes if get_initial_plane_config() failed. - * Make sure things work if the surface base is not page aligned. - */ - if (!plane_config->fb) - return; - - if (intel_alloc_initial_plane_obj(crtc, plane_config)) - fb = &plane_config->fb->base; - else if (!intel_reuse_initial_plane_obj(crtc, plane_configs, &fb)) - goto nofb; - - plane_state->uapi.rotation = plane_config->rotation; - intel_fb_fill_view(to_intel_framebuffer(fb), - plane_state->uapi.rotation, &plane_state->view); - - vma = intel_fb_pin_to_ggtt(fb, &plane_state->view.gtt, - 0, 0, 0, false, &plane_state->flags); - if (IS_ERR(vma)) - goto nofb; - - plane_state->ggtt_vma = vma; - - plane_state->surf = i915_ggtt_offset(plane_state->ggtt_vma); - - plane_state->uapi.src_x = 0; - plane_state->uapi.src_y = 0; - plane_state->uapi.src_w = fb->width << 16; - plane_state->uapi.src_h = fb->height << 16; - - plane_state->uapi.crtc_x = 0; - plane_state->uapi.crtc_y = 0; - plane_state->uapi.crtc_w = fb->width; - plane_state->uapi.crtc_h = fb->height; - - plane_state->uapi.fb = fb; - drm_framebuffer_get(fb); - - plane_state->uapi.crtc = &crtc->base; - intel_plane_copy_uapi_to_hw_state(plane_state, plane_state, crtc); - - atomic_or(plane->frontbuffer_bit, &to_intel_frontbuffer(fb)->bits); - - plane_config->vma = vma; - return; - -nofb: - /* - * We've failed to reconstruct the BIOS FB. Current display state - * indicates that the primary plane is visible, but has a NULL FB, - * which will lead to problems later if we don't fix it up. The - * simplest solution is to just disable the primary plane now and - * pretend the BIOS never had it enabled. - */ - intel_plane_disable_noatomic(crtc, plane); -} - -static void plane_config_fini(struct intel_initial_plane_config *plane_config) -{ - if (plane_config->fb) { - struct drm_framebuffer *fb = &plane_config->fb->base; - - /* We may only have the stub and not a full framebuffer */ - if (drm_framebuffer_read_refcount(fb)) - drm_framebuffer_put(fb); - else - kfree(fb); - } -} - -void intel_initial_plane_config(struct intel_display *display) -{ - struct intel_initial_plane_config plane_configs[I915_MAX_PIPES] = {}; - struct intel_crtc *crtc; - - for_each_intel_crtc(display->drm, crtc) { - struct intel_initial_plane_config *plane_config = - &plane_configs[crtc->pipe]; - - if (!to_intel_crtc_state(crtc->base.state)->uapi.active) - continue; - - /* - * Note that reserving the BIOS fb up front prevents us - * from stuffing other stolen allocations like the ring - * on top. This prevents some ugliness at boot time, and - * can even allow for smooth boot transitions if the BIOS - * fb is large enough for the active pipe configuration. - */ - display->funcs.display->get_initial_plane_config(crtc, plane_config); - - /* - * If the fb is shared between multiple heads, we'll - * just get the first one. - */ - intel_find_initial_plane_obj(crtc, plane_configs); - - if (display->funcs.display->fixup_initial_plane_config(crtc, plane_config)) - intel_plane_initial_vblank_wait(crtc); - - plane_config_fini(plane_config); - } -} diff --git a/drivers/gpu/drm/xe/display/xe_stolen.c b/drivers/gpu/drm/xe/display/xe_stolen.c index 9f04ba36e930..8dc2f86ec602 100644 --- a/drivers/gpu/drm/xe/display/xe_stolen.c +++ b/drivers/gpu/drm/xe/display/xe_stolen.c @@ -1,8 +1,10 @@ // SPDX-License-Identifier: MIT /* Copyright © 2025 Intel Corporation */ -#include "gem/i915_gem_stolen.h" +#include <drm/intel/display_parent_interface.h> + #include "xe_res_cursor.h" +#include "xe_stolen.h" #include "xe_ttm_stolen_mgr.h" #include "xe_validation.h" @@ -11,8 +13,8 @@ struct intel_stolen_node { struct xe_bo *bo; }; -int i915_gem_stolen_insert_node_in_range(struct intel_stolen_node *node, u64 size, - unsigned int align, u64 start, u64 end) +static int xe_stolen_insert_node_in_range(struct intel_stolen_node *node, u64 size, + unsigned int align, u64 start, u64 end) { struct xe_device *xe = node->xe; @@ -41,33 +43,25 @@ int i915_gem_stolen_insert_node_in_range(struct intel_stolen_node *node, u64 siz return err; } -int i915_gem_stolen_insert_node(struct intel_stolen_node *node, u64 size, unsigned int align) -{ - /* Not used on xe */ - WARN_ON(1); - - return -ENODEV; -} - -void i915_gem_stolen_remove_node(struct intel_stolen_node *node) +static void xe_stolen_remove_node(struct intel_stolen_node *node) { xe_bo_unpin_map_no_vm(node->bo); node->bo = NULL; } -bool i915_gem_stolen_initialized(struct drm_device *drm) +static bool xe_stolen_initialized(struct drm_device *drm) { struct xe_device *xe = to_xe_device(drm); return ttm_manager_type(&xe->ttm, XE_PL_STOLEN); } -bool i915_gem_stolen_node_allocated(const struct intel_stolen_node *node) +static bool xe_stolen_node_allocated(const struct intel_stolen_node *node) { return node->bo; } -u32 i915_gem_stolen_node_offset(struct intel_stolen_node *node) +static u64 xe_stolen_node_offset(const struct intel_stolen_node *node) { struct xe_res_cursor res; @@ -75,35 +69,19 @@ u32 i915_gem_stolen_node_offset(struct intel_stolen_node *node) return res.start; } -/* Used for < gen4. These are not supported by Xe */ -u64 i915_gem_stolen_area_address(struct drm_device *drm) -{ - WARN_ON(1); - - return 0; -} - -/* Used for gen9 specific WA. Gen9 is not supported by Xe */ -u64 i915_gem_stolen_area_size(struct drm_device *drm) -{ - WARN_ON(1); - - return 0; -} - -u64 i915_gem_stolen_node_address(struct intel_stolen_node *node) +static u64 xe_stolen_node_address(const struct intel_stolen_node *node) { struct xe_device *xe = node->xe; - return xe_ttm_stolen_gpu_offset(xe) + i915_gem_stolen_node_offset(node); + return xe_ttm_stolen_gpu_offset(xe) + xe_stolen_node_offset(node); } -u64 i915_gem_stolen_node_size(const struct intel_stolen_node *node) +static u64 xe_stolen_node_size(const struct intel_stolen_node *node) { - return node->bo->ttm.base.size; + return xe_bo_size(node->bo); } -struct intel_stolen_node *i915_gem_stolen_node_alloc(struct drm_device *drm) +static struct intel_stolen_node *xe_stolen_node_alloc(struct drm_device *drm) { struct xe_device *xe = to_xe_device(drm); struct intel_stolen_node *node; @@ -117,7 +95,19 @@ struct intel_stolen_node *i915_gem_stolen_node_alloc(struct drm_device *drm) return node; } -void i915_gem_stolen_node_free(const struct intel_stolen_node *node) +static void xe_stolen_node_free(const struct intel_stolen_node *node) { kfree(node); } + +const struct intel_display_stolen_interface xe_display_stolen_interface = { + .insert_node_in_range = xe_stolen_insert_node_in_range, + .remove_node = xe_stolen_remove_node, + .initialized = xe_stolen_initialized, + .node_allocated = xe_stolen_node_allocated, + .node_offset = xe_stolen_node_offset, + .node_address = xe_stolen_node_address, + .node_size = xe_stolen_node_size, + .node_alloc = xe_stolen_node_alloc, + .node_free = xe_stolen_node_free, +}; diff --git a/drivers/gpu/drm/xe/display/xe_stolen.h b/drivers/gpu/drm/xe/display/xe_stolen.h new file mode 100644 index 000000000000..db86b9e01242 --- /dev/null +++ b/drivers/gpu/drm/xe/display/xe_stolen.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: MIT */ +/* Copyright © 2025 Intel Corporation */ + +#ifndef __XE_STOLEN_H__ +#define __XE_STOLEN_H__ + +extern const struct intel_display_stolen_interface xe_display_stolen_interface; + +#endif diff --git a/drivers/gpu/drm/xe/instructions/xe_gpu_commands.h b/drivers/gpu/drm/xe/instructions/xe_gpu_commands.h index 5d41ca297447..885fcf211e6d 100644 --- a/drivers/gpu/drm/xe/instructions/xe_gpu_commands.h +++ b/drivers/gpu/drm/xe/instructions/xe_gpu_commands.h @@ -47,6 +47,7 @@ #define GFX_OP_PIPE_CONTROL(len) ((0x3<<29)|(0x3<<27)|(0x2<<24)|((len)-2)) +#define PIPE_CONTROL0_QUEUE_DRAIN_MODE BIT(12) #define PIPE_CONTROL0_L3_READ_ONLY_CACHE_INVALIDATE BIT(10) /* gen12 */ #define PIPE_CONTROL0_HDC_PIPELINE_FLUSH BIT(9) /* gen12 */ diff --git a/drivers/gpu/drm/xe/regs/xe_gt_regs.h b/drivers/gpu/drm/xe/regs/xe_gt_regs.h index 917a088c28f2..24fc64fc832e 100644 --- a/drivers/gpu/drm/xe/regs/xe_gt_regs.h +++ b/drivers/gpu/drm/xe/regs/xe_gt_regs.h @@ -89,6 +89,7 @@ #define UNIFIED_COMPRESSION_FORMAT REG_GENMASK(3, 0) #define XE2_GAMREQSTRM_CTRL XE_REG_MCR(0x4194) +#define EN_CMP_1WCOH REG_BIT(15) #define CG_DIS_CNTLBUS REG_BIT(6) #define CCS_AUX_INV XE_REG(0x4208) @@ -101,6 +102,11 @@ #define XE2_LMEM_CFG XE_REG(0x48b0) +#define XE2_GAMWALK_CTRL 0x47e4 +#define XE2_GAMWALK_CTRL_MEDIA XE_REG(XE2_GAMWALK_CTRL + MEDIA_GT_GSI_OFFSET) +#define XE2_GAMWALK_CTRL_3D XE_REG_MCR(XE2_GAMWALK_CTRL) +#define EN_CMP_1WCOH_GW REG_BIT(14) + #define XEHP_FLAT_CCS_BASE_ADDR XE_REG_MCR(0x4910) #define XEHP_FLAT_CCS_PTR REG_GENMASK(31, 8) @@ -227,6 +233,9 @@ #define MIRROR_FUSE1 XE_REG(0x911c) +#define FUSE2 XE_REG(0x9120) +#define PRODUCTION_HW REG_BIT(2) + #define MIRROR_L3BANK_ENABLE XE_REG(0x9130) #define XE3_L3BANK_ENABLE REG_GENMASK(31, 0) diff --git a/drivers/gpu/drm/xe/regs/xe_gtt_defs.h b/drivers/gpu/drm/xe/regs/xe_gtt_defs.h index 4389e5a76f89..4d83461e538b 100644 --- a/drivers/gpu/drm/xe/regs/xe_gtt_defs.h +++ b/drivers/gpu/drm/xe/regs/xe_gtt_defs.h @@ -9,6 +9,7 @@ #define XELPG_GGTT_PTE_PAT0 BIT_ULL(52) #define XELPG_GGTT_PTE_PAT1 BIT_ULL(53) +#define XE_PTE_ADDR_MASK GENMASK_ULL(51, 12) #define GGTT_PTE_VFID GENMASK_ULL(11, 2) #define GUC_GGTT_TOP 0xFEE00000 diff --git a/drivers/gpu/drm/xe/regs/xe_guc_regs.h b/drivers/gpu/drm/xe/regs/xe_guc_regs.h index 2118f7dec287..87984713dd12 100644 --- a/drivers/gpu/drm/xe/regs/xe_guc_regs.h +++ b/drivers/gpu/drm/xe/regs/xe_guc_regs.h @@ -90,6 +90,9 @@ #define GUC_SEND_INTERRUPT XE_REG(0xc4c8) #define GUC_SEND_TRIGGER REG_BIT(0) +#define GUC_INTR_CHICKEN XE_REG(0xc50c) +#define DISABLE_SIGNALING_ENGINES REG_BIT(1) + #define GUC_BCS_RCS_IER XE_REG(0xc550) #define GUC_VCS2_VCS1_IER XE_REG(0xc554) #define GUC_WD_VECS_IER XE_REG(0xc558) diff --git a/drivers/gpu/drm/xe/regs/xe_irq_regs.h b/drivers/gpu/drm/xe/regs/xe_irq_regs.h index 2f97662d958d..9d74f454d3ff 100644 --- a/drivers/gpu/drm/xe/regs/xe_irq_regs.h +++ b/drivers/gpu/drm/xe/regs/xe_irq_regs.h @@ -20,6 +20,7 @@ #define GU_MISC_IRQ REG_BIT(29) #define ERROR_IRQ(x) REG_BIT(26 + (x)) #define DISPLAY_IRQ REG_BIT(16) +#define SOC_H2DMEMINT_IRQ REG_BIT(13) #define I2C_IRQ REG_BIT(12) #define GT_DW_IRQ(x) REG_BIT(x) diff --git a/drivers/gpu/drm/xe/regs/xe_mert_regs.h b/drivers/gpu/drm/xe/regs/xe_mert_regs.h new file mode 100644 index 000000000000..99e5a26da657 --- /dev/null +++ b/drivers/gpu/drm/xe/regs/xe_mert_regs.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright © 2025 Intel Corporation + */ + +#ifndef _XE_MERT_REGS_H_ +#define _XE_MERT_REGS_H_ + +#include "regs/xe_reg_defs.h" + +#define MERT_LMEM_CFG XE_REG(0x1448b0) + +#define MERT_TLB_CT_INTR_ERR_ID_PORT XE_REG(0x145190) +#define CATERR_VFID REG_GENMASK(16, 9) +#define CATERR_CODES REG_GENMASK(5, 0) +#define CATERR_NO_ERROR 0x00 +#define CATERR_UNMAPPED_GGTT 0x01 +#define CATERR_LMTT_FAULT 0x05 + +#define MERT_TLB_INV_DESC_A XE_REG(0x14cf7c) +#define MERT_TLB_INV_DESC_A_VALID REG_BIT(0) + +#endif diff --git a/drivers/gpu/drm/xe/regs/xe_oa_regs.h b/drivers/gpu/drm/xe/regs/xe_oa_regs.h index e693a50706f8..04a729e610aa 100644 --- a/drivers/gpu/drm/xe/regs/xe_oa_regs.h +++ b/drivers/gpu/drm/xe/regs/xe_oa_regs.h @@ -100,4 +100,21 @@ #define OAM_COMPRESSION_T3_CONTROL XE_REG(0x1c2e00) #define OAM_LAT_MEASURE_ENABLE REG_BIT(4) +/* Actual address is MEDIA_GT_GSI_OFFSET + the base addr below */ +#define XE_OAM_SAG_BASE 0x13000 +#define XE_OAM_SCMI_0_BASE 0x14000 +#define XE_OAM_SCMI_1_BASE 0x14800 +#define XE_OAM_SAG_BASE_ADJ (MEDIA_GT_GSI_OFFSET + XE_OAM_SAG_BASE) +#define XE_OAM_SCMI_0_BASE_ADJ (MEDIA_GT_GSI_OFFSET + XE_OAM_SCMI_0_BASE) +#define XE_OAM_SCMI_1_BASE_ADJ (MEDIA_GT_GSI_OFFSET + XE_OAM_SCMI_1_BASE) + +#define OAMERT_CONTROL XE_REG(0x1453a0) +#define OAMERT_DEBUG XE_REG(0x1453a4) +#define OAMERT_STATUS XE_REG(0x1453a8) +#define OAMERT_HEAD_POINTER XE_REG(0x1453ac) +#define OAMERT_TAIL_POINTER XE_REG(0x1453b0) +#define OAMERT_BUFFER XE_REG(0x1453b4) +#define OAMERT_CONTEXT_CONTROL XE_REG(0x1453c8) +#define OAMERT_MMIO_TRG XE_REG(0x1453cc) + #endif diff --git a/drivers/gpu/drm/xe/regs/xe_pcode_regs.h b/drivers/gpu/drm/xe/regs/xe_pcode_regs.h index fb097607b86c..4b3c46eb858f 100644 --- a/drivers/gpu/drm/xe/regs/xe_pcode_regs.h +++ b/drivers/gpu/drm/xe/regs/xe_pcode_regs.h @@ -21,7 +21,10 @@ #define BMG_FAN_1_SPEED XE_REG(0x138140) #define BMG_FAN_2_SPEED XE_REG(0x138170) #define BMG_FAN_3_SPEED XE_REG(0x1381a0) +#define BMG_VRAM_TEMPERATURE_N(n) XE_REG(0x138260 + ((n) * (sizeof(u32)))) #define BMG_VRAM_TEMPERATURE XE_REG(0x1382c0) +#define TEMP_MASK_VRAM_N REG_GENMASK(30, 8) +#define TEMP_SIGN_MASK REG_BIT(31) #define BMG_PACKAGE_TEMPERATURE XE_REG(0x138434) #endif /* _XE_PCODE_REGS_H_ */ diff --git a/drivers/gpu/drm/xe/regs/xe_pmt.h b/drivers/gpu/drm/xe/regs/xe_pmt.h index 0f79c0714454..240d57993ea6 100644 --- a/drivers/gpu/drm/xe/regs/xe_pmt.h +++ b/drivers/gpu/drm/xe/regs/xe_pmt.h @@ -18,9 +18,6 @@ #define BMG_TELEMETRY_BASE_OFFSET 0xE0000 #define BMG_TELEMETRY_OFFSET (SOC_BASE + BMG_TELEMETRY_BASE_OFFSET) -#define SG_REMAP_INDEX1 XE_REG(SOC_BASE + 0x08) -#define SG_REMAP_BITS REG_GENMASK(31, 24) - #define BMG_MODS_RESIDENCY_OFFSET (0x4D0) #define BMG_G2_RESIDENCY_OFFSET (0x530) #define BMG_G6_RESIDENCY_OFFSET (0x538) diff --git a/drivers/gpu/drm/xe/regs/xe_soc_remapper_regs.h b/drivers/gpu/drm/xe/regs/xe_soc_remapper_regs.h new file mode 100644 index 000000000000..be0eb37e73ad --- /dev/null +++ b/drivers/gpu/drm/xe/regs/xe_soc_remapper_regs.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright © 2025 Intel Corporation + */ +#ifndef _XE_SOC_REMAPPER_REGS_H_ +#define _XE_SOC_REMAPPER_REGS_H_ + +#include "xe_regs.h" + +#define SG_REMAP_INDEX1 XE_REG(SOC_BASE + 0x08) +#define SG_REMAP_TELEM_MASK REG_GENMASK(31, 24) +#define SG_REMAP_SYSCTRL_MASK REG_GENMASK(23, 16) + +#endif diff --git a/drivers/gpu/drm/xe/tests/xe_args_test.c b/drivers/gpu/drm/xe/tests/xe_args_test.c index f3fb23aa5d2e..2687a1b054dd 100644 --- a/drivers/gpu/drm/xe/tests/xe_args_test.c +++ b/drivers/gpu/drm/xe/tests/xe_args_test.c @@ -78,6 +78,24 @@ static void pick_arg_example(struct kunit *test) #undef buz } +static void if_args_example(struct kunit *test) +{ + enum { Z = 1, Q }; + +#define foo X, Y +#define bar IF_ARGS(Z, Q, foo) +#define buz IF_ARGS(Z, Q, DROP_FIRST_ARG(FIRST_ARG(foo))) + + KUNIT_EXPECT_EQ(test, bar, Z); + KUNIT_EXPECT_EQ(test, buz, Q); + KUNIT_EXPECT_STREQ(test, __stringify(bar), "Z"); + KUNIT_EXPECT_STREQ(test, __stringify(buz), "Q"); + +#undef foo +#undef bar +#undef buz +} + static void sep_comma_example(struct kunit *test) { #define foo(f) f(X) f(Y) f(Z) f(Q) @@ -198,6 +216,40 @@ static void last_arg_test(struct kunit *test) KUNIT_EXPECT_STREQ(test, __stringify(LAST_ARG(MAX_ARGS)), "-12"); } +static void if_args_test(struct kunit *test) +{ + bool with_args = true; + bool no_args = false; + enum { X = 100 }; + + KUNIT_EXPECT_TRUE(test, IF_ARGS(true, false, FOO_ARGS)); + KUNIT_EXPECT_FALSE(test, IF_ARGS(true, false, NO_ARGS)); + + KUNIT_EXPECT_TRUE(test, CONCATENATE(IF_ARGS(with, no, FOO_ARGS), _args)); + KUNIT_EXPECT_FALSE(test, CONCATENATE(IF_ARGS(with, no, NO_ARGS), _args)); + + KUNIT_EXPECT_STREQ(test, __stringify(IF_ARGS(yes, no, FOO_ARGS)), "yes"); + KUNIT_EXPECT_STREQ(test, __stringify(IF_ARGS(yes, no, NO_ARGS)), "no"); + + KUNIT_EXPECT_EQ(test, IF_ARGS(CALL_ARGS(COUNT_ARGS, FOO_ARGS), -1, FOO_ARGS), 4); + KUNIT_EXPECT_EQ(test, IF_ARGS(CALL_ARGS(COUNT_ARGS, FOO_ARGS), -1, NO_ARGS), -1); + KUNIT_EXPECT_EQ(test, IF_ARGS(CALL_ARGS(COUNT_ARGS, NO_ARGS), -1, FOO_ARGS), 0); + KUNIT_EXPECT_EQ(test, IF_ARGS(CALL_ARGS(COUNT_ARGS, NO_ARGS), -1, NO_ARGS), -1); + + KUNIT_EXPECT_EQ(test, + CALL_ARGS(FIRST_ARG, + CALL_ARGS(CONCATENATE, IF_ARGS(FOO, MAX, FOO_ARGS), _ARGS)), X); + KUNIT_EXPECT_EQ(test, + CALL_ARGS(FIRST_ARG, + CALL_ARGS(CONCATENATE, IF_ARGS(FOO, MAX, NO_ARGS), _ARGS)), -1); + KUNIT_EXPECT_EQ(test, + CALL_ARGS(COUNT_ARGS, + CALL_ARGS(CONCATENATE, IF_ARGS(FOO, MAX, FOO_ARGS), _ARGS)), 4); + KUNIT_EXPECT_EQ(test, + CALL_ARGS(COUNT_ARGS, + CALL_ARGS(CONCATENATE, IF_ARGS(FOO, MAX, NO_ARGS), _ARGS)), 12); +} + static struct kunit_case args_tests[] = { KUNIT_CASE(count_args_test), KUNIT_CASE(call_args_example), @@ -209,6 +261,8 @@ static struct kunit_case args_tests[] = { KUNIT_CASE(last_arg_example), KUNIT_CASE(last_arg_test), KUNIT_CASE(pick_arg_example), + KUNIT_CASE(if_args_example), + KUNIT_CASE(if_args_test), KUNIT_CASE(sep_comma_example), {} }; diff --git a/drivers/gpu/drm/xe/tests/xe_bo.c b/drivers/gpu/drm/xe/tests/xe_bo.c index 2294cf89f3e1..b7d8e45804cf 100644 --- a/drivers/gpu/drm/xe/tests/xe_bo.c +++ b/drivers/gpu/drm/xe/tests/xe_bo.c @@ -18,6 +18,7 @@ #include "tests/xe_test.h" #include "xe_bo_evict.h" +#include "xe_gt.h" #include "xe_pci.h" #include "xe_pm.h" @@ -185,8 +186,7 @@ static int ccs_test_run_device(struct xe_device *xe) return 0; } - xe_pm_runtime_get(xe); - + guard(xe_pm_runtime)(xe); for_each_tile(tile, xe, id) { /* For igfx run only for primary tile */ if (!IS_DGFX(xe) && id > 0) @@ -194,8 +194,6 @@ static int ccs_test_run_device(struct xe_device *xe) ccs_test_run_tile(xe, tile, test); } - xe_pm_runtime_put(xe); - return 0; } @@ -356,13 +354,10 @@ static int evict_test_run_device(struct xe_device *xe) return 0; } - xe_pm_runtime_get(xe); - + guard(xe_pm_runtime)(xe); for_each_tile(tile, xe, id) evict_test_run_tile(xe, tile, test); - xe_pm_runtime_put(xe); - return 0; } diff --git a/drivers/gpu/drm/xe/tests/xe_dma_buf.c b/drivers/gpu/drm/xe/tests/xe_dma_buf.c index c107687ef3c0..0be8440b3976 100644 --- a/drivers/gpu/drm/xe/tests/xe_dma_buf.c +++ b/drivers/gpu/drm/xe/tests/xe_dma_buf.c @@ -265,7 +265,7 @@ static int dma_buf_run_device(struct xe_device *xe) const struct dma_buf_test_params *params; struct kunit *test = kunit_get_current_test(); - xe_pm_runtime_get(xe); + guard(xe_pm_runtime)(xe); for (params = test_params; params->mem_mask; ++params) { struct dma_buf_test_params p = *params; @@ -273,7 +273,6 @@ static int dma_buf_run_device(struct xe_device *xe) test->priv = &p; xe_test_dmabuf_import_same_driver(xe); } - xe_pm_runtime_put(xe); /* A non-zero return would halt iteration over driver devices */ return 0; diff --git a/drivers/gpu/drm/xe/tests/xe_guc_buf_kunit.c b/drivers/gpu/drm/xe/tests/xe_guc_buf_kunit.c index d266882adc0e..acddbedcf17c 100644 --- a/drivers/gpu/drm/xe/tests/xe_guc_buf_kunit.c +++ b/drivers/gpu/drm/xe/tests/xe_guc_buf_kunit.c @@ -67,7 +67,7 @@ static int guc_buf_test_init(struct kunit *test) KUNIT_ASSERT_EQ(test, 0, xe_ggtt_init_kunit(ggtt, DUT_GGTT_START, - DUT_GGTT_START + DUT_GGTT_SIZE)); + DUT_GGTT_SIZE)); kunit_activate_static_stub(test, xe_managed_bo_create_pin_map, replacement_xe_managed_bo_create_pin_map); diff --git a/drivers/gpu/drm/xe/tests/xe_migrate.c b/drivers/gpu/drm/xe/tests/xe_migrate.c index 5904d658d1f2..34e2f0f4631f 100644 --- a/drivers/gpu/drm/xe/tests/xe_migrate.c +++ b/drivers/gpu/drm/xe/tests/xe_migrate.c @@ -344,8 +344,7 @@ static int migrate_test_run_device(struct xe_device *xe) struct xe_tile *tile; int id; - xe_pm_runtime_get(xe); - + guard(xe_pm_runtime)(xe); for_each_tile(tile, xe, id) { struct xe_migrate *m = tile->migrate; struct drm_exec *exec = XE_VALIDATION_OPT_OUT; @@ -356,8 +355,6 @@ static int migrate_test_run_device(struct xe_device *xe) xe_vm_unlock(m->q->vm); } - xe_pm_runtime_put(xe); - return 0; } @@ -759,13 +756,10 @@ static int validate_ccs_test_run_device(struct xe_device *xe) return 0; } - xe_pm_runtime_get(xe); - + guard(xe_pm_runtime)(xe); for_each_tile(tile, xe, id) validate_ccs_test_run_tile(xe, tile, test); - xe_pm_runtime_put(xe); - return 0; } diff --git a/drivers/gpu/drm/xe/tests/xe_mocs.c b/drivers/gpu/drm/xe/tests/xe_mocs.c index 6bb278167aaf..daf3c6836c75 100644 --- a/drivers/gpu/drm/xe/tests/xe_mocs.c +++ b/drivers/gpu/drm/xe/tests/xe_mocs.c @@ -43,14 +43,12 @@ static void read_l3cc_table(struct xe_gt *gt, { struct kunit *test = kunit_get_current_test(); u32 l3cc, l3cc_expected; - unsigned int fw_ref, i; + unsigned int i; u32 reg_val; - fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FORCEWAKE_ALL); - if (!xe_force_wake_ref_has_domain(fw_ref, XE_FORCEWAKE_ALL)) { - xe_force_wake_put(gt_to_fw(gt), fw_ref); + CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FORCEWAKE_ALL); + if (!xe_force_wake_ref_has_domain(fw_ref.domains, XE_FORCEWAKE_ALL)) KUNIT_FAIL_AND_ABORT(test, "Forcewake Failed.\n"); - } for (i = 0; i < info->num_mocs_regs; i++) { if (!(i & 1)) { @@ -74,7 +72,6 @@ static void read_l3cc_table(struct xe_gt *gt, KUNIT_EXPECT_EQ_MSG(test, l3cc_expected, l3cc, "l3cc idx=%u has incorrect val.\n", i); } - xe_force_wake_put(gt_to_fw(gt), fw_ref); } static void read_mocs_table(struct xe_gt *gt, @@ -82,14 +79,14 @@ static void read_mocs_table(struct xe_gt *gt, { struct kunit *test = kunit_get_current_test(); u32 mocs, mocs_expected; - unsigned int fw_ref, i; + unsigned int i; u32 reg_val; KUNIT_EXPECT_TRUE_MSG(test, info->unused_entries_index, "Unused entries index should have been defined\n"); - fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT); - KUNIT_ASSERT_NE_MSG(test, fw_ref, 0, "Forcewake Failed.\n"); + CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT); + KUNIT_ASSERT_NE_MSG(test, fw_ref.domains, 0, "Forcewake Failed.\n"); for (i = 0; i < info->num_mocs_regs; i++) { if (regs_are_mcr(gt)) @@ -106,8 +103,6 @@ static void read_mocs_table(struct xe_gt *gt, KUNIT_EXPECT_EQ_MSG(test, mocs_expected, mocs, "mocs reg 0x%x has incorrect val.\n", i); } - - xe_force_wake_put(gt_to_fw(gt), fw_ref); } static int mocs_kernel_test_run_device(struct xe_device *xe) @@ -120,8 +115,7 @@ static int mocs_kernel_test_run_device(struct xe_device *xe) unsigned int flags; int id; - xe_pm_runtime_get(xe); - + guard(xe_pm_runtime)(xe); for_each_gt(gt, xe, id) { flags = live_mocs_init(&mocs, gt); if (flags & HAS_GLOBAL_MOCS) @@ -130,8 +124,6 @@ static int mocs_kernel_test_run_device(struct xe_device *xe) read_l3cc_table(gt, &mocs.table); } - xe_pm_runtime_put(xe); - return 0; } @@ -155,8 +147,7 @@ static int mocs_reset_test_run_device(struct xe_device *xe) int id; struct kunit *test = kunit_get_current_test(); - xe_pm_runtime_get(xe); - + guard(xe_pm_runtime)(xe); for_each_gt(gt, xe, id) { flags = live_mocs_init(&mocs, gt); kunit_info(test, "mocs_reset_test before reset\n"); @@ -174,8 +165,6 @@ static int mocs_reset_test_run_device(struct xe_device *xe) read_l3cc_table(gt, &mocs.table); } - xe_pm_runtime_put(xe); - return 0; } diff --git a/drivers/gpu/drm/xe/xe_args.h b/drivers/gpu/drm/xe/xe_args.h index 4dbc7e53c624..f550b5e3b993 100644 --- a/drivers/gpu/drm/xe/xe_args.h +++ b/drivers/gpu/drm/xe/xe_args.h @@ -122,6 +122,33 @@ #define PICK_ARG12(args...) PICK_ARG11(DROP_FIRST_ARG(args)) /** + * IF_ARGS() - Make selection based on optional argument list. + * @then: token to return if arguments are present + * @else: token to return if arguments are empty + * @...: arguments to check (optional) + * + * This macro allows to select a token based on the presence of the argument list. + * + * Example: + * + * #define foo X, Y + * #define bar IF_ARGS(Z, Q, foo) + * #define buz IF_ARGS(Z, Q, DROP_FIRST_ARG(FIRST_ARG(foo))) + * + * With above definitions bar expands to Z while buz expands to Q. + */ +#if defined(CONFIG_CC_IS_CLANG) || GCC_VERSION >= 100100 +#define IF_ARGS(then, else, ...) FIRST_ARG(__VA_OPT__(then,) else) +#else +#define IF_ARGS(then, else, ...) _IF_ARGS(then, else, CALL_ARGS(FIRST_ARG, __VA_ARGS__)) +#define _IF_ARGS(then, else, ...) __IF_ARGS(then, else, CALL_ARGS(COUNT_ARGS, __VA_ARGS__)) +#define __IF_ARGS(then, else, n) ___IF_ARGS(then, else, CALL_ARGS(CONCATENATE, ___IF_ARG, n)) +#define ___IF_ARGS(then, else, if) CALL_ARGS(if, then, else) +#define ___IF_ARG1(then, else) then +#define ___IF_ARG0(then, else) else +#endif + +/** * ARGS_SEP_COMMA - Definition of a comma character. * * This definition can be used in cases where any intermediate macro expects diff --git a/drivers/gpu/drm/xe/xe_bb.c b/drivers/gpu/drm/xe/xe_bb.c index 6d20229c11de..8b678297aaa2 100644 --- a/drivers/gpu/drm/xe/xe_bb.c +++ b/drivers/gpu/drm/xe/xe_bb.c @@ -7,10 +7,9 @@ #include "instructions/xe_mi_commands.h" #include "xe_assert.h" -#include "xe_device.h" +#include "xe_device_types.h" #include "xe_exec_queue_types.h" #include "xe_gt.h" -#include "xe_hw_fence.h" #include "xe_sa.h" #include "xe_sched_job.h" #include "xe_vm_types.h" diff --git a/drivers/gpu/drm/xe/xe_bo.c b/drivers/gpu/drm/xe/xe_bo.c index 94712b05edff..8bf16d60b9a5 100644 --- a/drivers/gpu/drm/xe/xe_bo.c +++ b/drivers/gpu/drm/xe/xe_bo.c @@ -26,9 +26,9 @@ #include "xe_dma_buf.h" #include "xe_drm_client.h" #include "xe_ggtt.h" -#include "xe_gt.h" #include "xe_map.h" #include "xe_migrate.h" +#include "xe_pat.h" #include "xe_pm.h" #include "xe_preempt_fence.h" #include "xe_pxp.h" @@ -516,8 +516,7 @@ static struct ttm_tt *xe_ttm_tt_create(struct ttm_buffer_object *ttm_bo, * non-coherent and require a CPU:WC mapping. */ if ((!bo->cpu_caching && bo->flags & XE_BO_FLAG_SCANOUT) || - (xe->info.graphics_verx100 >= 1270 && - bo->flags & XE_BO_FLAG_PAGETABLE)) + (!xe->info.has_cached_pt && bo->flags & XE_BO_FLAG_PAGETABLE)) caching = ttm_write_combined; } @@ -1055,6 +1054,7 @@ static long xe_bo_shrink_purge(struct ttm_operation_ctx *ctx, unsigned long *scanned) { struct xe_device *xe = ttm_to_xe_device(bo->bdev); + struct ttm_tt *tt = bo->ttm; long lret; /* Fake move to system, without copying data. */ @@ -1079,8 +1079,10 @@ static long xe_bo_shrink_purge(struct ttm_operation_ctx *ctx, .writeback = false, .allow_move = false}); - if (lret > 0) + if (lret > 0) { xe_ttm_tt_account_subtract(xe, bo->ttm); + update_global_total_pages(bo->bdev, -(long)tt->num_pages); + } return lret; } @@ -1166,8 +1168,10 @@ long xe_bo_shrink(struct ttm_operation_ctx *ctx, struct ttm_buffer_object *bo, if (needs_rpm) xe_pm_runtime_put(xe); - if (lret > 0) + if (lret > 0) { xe_ttm_tt_account_subtract(xe, tt); + update_global_total_pages(bo->bdev, -(long)tt->num_pages); + } out_unref: xe_bo_put(xe_bo); @@ -1527,7 +1531,7 @@ static bool xe_ttm_bo_lock_in_destructor(struct ttm_buffer_object *ttm_bo) * always succeed here, as long as we hold the lru lock. */ spin_lock(&ttm_bo->bdev->lru_lock); - locked = dma_resv_trylock(ttm_bo->base.resv); + locked = dma_resv_trylock(&ttm_bo->base._resv); spin_unlock(&ttm_bo->bdev->lru_lock); xe_assert(xe, locked); @@ -1547,13 +1551,6 @@ static void xe_ttm_bo_release_notify(struct ttm_buffer_object *ttm_bo) bo = ttm_to_xe_bo(ttm_bo); xe_assert(xe_bo_device(bo), !(bo->created && kref_read(&ttm_bo->base.refcount))); - /* - * Corner case where TTM fails to allocate memory and this BOs resv - * still points the VMs resv - */ - if (ttm_bo->base.resv != &ttm_bo->base._resv) - return; - if (!xe_ttm_bo_lock_in_destructor(ttm_bo)) return; @@ -1563,14 +1560,14 @@ static void xe_ttm_bo_release_notify(struct ttm_buffer_object *ttm_bo) * TODO: Don't do this for external bos once we scrub them after * unbind. */ - dma_resv_for_each_fence(&cursor, ttm_bo->base.resv, + dma_resv_for_each_fence(&cursor, &ttm_bo->base._resv, DMA_RESV_USAGE_BOOKKEEP, fence) { if (xe_fence_is_xe_preempt(fence) && !dma_fence_is_signaled(fence)) { if (!replacement) replacement = dma_fence_get_stub(); - dma_resv_replace_fences(ttm_bo->base.resv, + dma_resv_replace_fences(&ttm_bo->base._resv, fence->context, replacement, DMA_RESV_USAGE_BOOKKEEP); @@ -1578,7 +1575,7 @@ static void xe_ttm_bo_release_notify(struct ttm_buffer_object *ttm_bo) } dma_fence_put(replacement); - dma_resv_unlock(ttm_bo->base.resv); + dma_resv_unlock(&ttm_bo->base._resv); } static void xe_ttm_bo_delete_mem_notify(struct ttm_buffer_object *ttm_bo) @@ -1717,7 +1714,7 @@ static void xe_ttm_bo_destroy(struct ttm_buffer_object *ttm_bo) xe_assert(xe, list_empty(&ttm_bo->base.gpuva.list)); for_each_tile(tile, xe, id) - if (bo->ggtt_node[id] && bo->ggtt_node[id]->base.size) + if (bo->ggtt_node[id]) xe_ggtt_remove_bo(tile->mem.ggtt, bo); #ifdef CONFIG_PROC_FS @@ -2033,13 +2030,9 @@ static int xe_bo_vm_access(struct vm_area_struct *vma, unsigned long addr, struct ttm_buffer_object *ttm_bo = vma->vm_private_data; struct xe_bo *bo = ttm_to_xe_bo(ttm_bo); struct xe_device *xe = xe_bo_device(bo); - int ret; - xe_pm_runtime_get(xe); - ret = ttm_bo_vm_access(vma, addr, buf, len, write); - xe_pm_runtime_put(xe); - - return ret; + guard(xe_pm_runtime)(xe); + return ttm_bo_vm_access(vma, addr, buf, len, write); } /** @@ -3183,7 +3176,8 @@ int xe_gem_create_ioctl(struct drm_device *dev, void *data, if (XE_IOCTL_DBG(xe, args->flags & ~(DRM_XE_GEM_CREATE_FLAG_DEFER_BACKING | DRM_XE_GEM_CREATE_FLAG_SCANOUT | - DRM_XE_GEM_CREATE_FLAG_NEEDS_VISIBLE_VRAM))) + DRM_XE_GEM_CREATE_FLAG_NEEDS_VISIBLE_VRAM | + DRM_XE_GEM_CREATE_FLAG_NO_COMPRESSION))) return -EINVAL; if (XE_IOCTL_DBG(xe, args->handle)) @@ -3205,6 +3199,12 @@ int xe_gem_create_ioctl(struct drm_device *dev, void *data, if (args->flags & DRM_XE_GEM_CREATE_FLAG_SCANOUT) bo_flags |= XE_BO_FLAG_SCANOUT; + if (args->flags & DRM_XE_GEM_CREATE_FLAG_NO_COMPRESSION) { + if (XE_IOCTL_DBG(xe, GRAPHICS_VER(xe) < 20)) + return -EOPNOTSUPP; + bo_flags |= XE_BO_FLAG_NO_COMPRESSION; + } + bo_flags |= args->placement << (ffs(XE_BO_FLAG_SYSTEM) - 1); /* CCS formats need physical placement at a 64K alignment in VRAM. */ @@ -3522,12 +3522,16 @@ bool xe_bo_needs_ccs_pages(struct xe_bo *bo) if (IS_DGFX(xe) && (bo->flags & XE_BO_FLAG_SYSTEM)) return false; + /* Check if userspace explicitly requested no compression */ + if (bo->flags & XE_BO_FLAG_NO_COMPRESSION) + return false; + /* - * Compression implies coh_none, therefore we know for sure that WB - * memory can't currently use compression, which is likely one of the - * common cases. + * For WB (Write-Back) CPU caching mode, check if the device + * supports WB compression with coherency. */ - if (bo->cpu_caching == DRM_XE_GEM_CPU_CACHING_WB) + if (bo->cpu_caching == DRM_XE_GEM_CPU_CACHING_WB && + xe->pat.idx[XE_CACHE_WB_COMPRESSION] == XE_PAT_INVALID_IDX) return false; return true; @@ -3604,8 +3608,8 @@ void xe_bo_put(struct xe_bo *bo) might_lock(&bo->client->bos_lock); #endif for_each_tile(tile, xe_bo_device(bo), id) - if (bo->ggtt_node[id] && bo->ggtt_node[id]->ggtt) - xe_ggtt_might_lock(bo->ggtt_node[id]->ggtt); + if (bo->ggtt_node[id]) + xe_ggtt_might_lock(tile->mem.ggtt); drm_gem_object_put(&bo->ttm.base); } } diff --git a/drivers/gpu/drm/xe/xe_bo.h b/drivers/gpu/drm/xe/xe_bo.h index 911d5b90461a..c914ab719f20 100644 --- a/drivers/gpu/drm/xe/xe_bo.h +++ b/drivers/gpu/drm/xe/xe_bo.h @@ -9,6 +9,7 @@ #include <drm/ttm/ttm_tt.h> #include "xe_bo_types.h" +#include "xe_ggtt.h" #include "xe_macros.h" #include "xe_validation.h" #include "xe_vm_types.h" @@ -50,6 +51,7 @@ #define XE_BO_FLAG_GGTT3 BIT(23) #define XE_BO_FLAG_CPU_ADDR_MIRROR BIT(24) #define XE_BO_FLAG_FORCE_USER_VRAM BIT(25) +#define XE_BO_FLAG_NO_COMPRESSION BIT(26) /* this one is trigger internally only */ #define XE_BO_FLAG_INTERNAL_TEST BIT(30) @@ -251,13 +253,14 @@ static inline u32 __xe_bo_ggtt_addr(struct xe_bo *bo, u8 tile_id) { struct xe_ggtt_node *ggtt_node = bo->ggtt_node[tile_id]; + u64 offset; if (XE_WARN_ON(!ggtt_node)) return 0; - XE_WARN_ON(ggtt_node->base.size > xe_bo_size(bo)); - XE_WARN_ON(ggtt_node->base.start + ggtt_node->base.size > (1ull << 32)); - return ggtt_node->base.start; + offset = xe_ggtt_node_addr(ggtt_node); + XE_WARN_ON(offset + xe_bo_size(bo) > (1ull << 32)); + return offset; } static inline u32 diff --git a/drivers/gpu/drm/xe/xe_debugfs.c b/drivers/gpu/drm/xe/xe_debugfs.c index e91da9589c5f..844cfafe1ec7 100644 --- a/drivers/gpu/drm/xe/xe_debugfs.c +++ b/drivers/gpu/drm/xe/xe_debugfs.c @@ -68,7 +68,7 @@ static int info(struct seq_file *m, void *data) struct xe_gt *gt; u8 id; - xe_pm_runtime_get(xe); + guard(xe_pm_runtime)(xe); drm_printf(&p, "graphics_verx100 %d\n", xe->info.graphics_verx100); drm_printf(&p, "media_verx100 %d\n", xe->info.media_verx100); @@ -93,9 +93,10 @@ static int info(struct seq_file *m, void *data) xe_force_wake_ref(gt_to_fw(gt), XE_FW_GT)); drm_printf(&p, "gt%d engine_mask 0x%llx\n", id, gt->info.engine_mask); + drm_printf(&p, "gt%d multi_queue_engine_class_mask 0x%x\n", id, + gt->info.multi_queue_engine_class_mask); } - xe_pm_runtime_put(xe); return 0; } @@ -110,9 +111,8 @@ static int sriov_info(struct seq_file *m, void *data) static int workarounds(struct xe_device *xe, struct drm_printer *p) { - xe_pm_runtime_get(xe); + guard(xe_pm_runtime)(xe); xe_wa_device_dump(xe, p); - xe_pm_runtime_put(xe); return 0; } @@ -134,7 +134,7 @@ static int dgfx_pkg_residencies_show(struct seq_file *m, void *data) xe = node_to_xe(m->private); p = drm_seq_file_printer(m); - xe_pm_runtime_get(xe); + guard(xe_pm_runtime)(xe); mmio = xe_root_tile_mmio(xe); static const struct { u32 offset; @@ -151,7 +151,6 @@ static int dgfx_pkg_residencies_show(struct seq_file *m, void *data) for (int i = 0; i < ARRAY_SIZE(residencies); i++) read_residency_counter(xe, mmio, residencies[i].offset, residencies[i].name, &p); - xe_pm_runtime_put(xe); return 0; } @@ -163,7 +162,7 @@ static int dgfx_pcie_link_residencies_show(struct seq_file *m, void *data) xe = node_to_xe(m->private); p = drm_seq_file_printer(m); - xe_pm_runtime_get(xe); + guard(xe_pm_runtime)(xe); mmio = xe_root_tile_mmio(xe); static const struct { @@ -178,7 +177,6 @@ static int dgfx_pcie_link_residencies_show(struct seq_file *m, void *data) for (int i = 0; i < ARRAY_SIZE(residencies); i++) read_residency_counter(xe, mmio, residencies[i].offset, residencies[i].name, &p); - xe_pm_runtime_put(xe); return 0; } @@ -256,38 +254,81 @@ static ssize_t wedged_mode_show(struct file *f, char __user *ubuf, return simple_read_from_buffer(ubuf, size, pos, buf, len); } +static int __wedged_mode_set_reset_policy(struct xe_gt *gt, enum xe_wedged_mode mode) +{ + bool enable_engine_reset; + int ret; + + enable_engine_reset = (mode != XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET); + ret = xe_guc_ads_scheduler_policy_toggle_reset(>->uc.guc.ads, + enable_engine_reset); + if (ret) + xe_gt_err(gt, "Failed to update GuC ADS scheduler policy (%pe)\n", ERR_PTR(ret)); + + return ret; +} + +static int wedged_mode_set_reset_policy(struct xe_device *xe, enum xe_wedged_mode mode) +{ + struct xe_gt *gt; + int ret; + u8 id; + + guard(xe_pm_runtime)(xe); + for_each_gt(gt, xe, id) { + ret = __wedged_mode_set_reset_policy(gt, mode); + if (ret) { + if (id > 0) { + xe->wedged.inconsistent_reset = true; + drm_err(&xe->drm, "Inconsistent reset policy state between GTs\n"); + } + return ret; + } + } + + xe->wedged.inconsistent_reset = false; + + return 0; +} + +static bool wedged_mode_needs_policy_update(struct xe_device *xe, enum xe_wedged_mode mode) +{ + if (xe->wedged.inconsistent_reset) + return true; + + if (xe->wedged.mode == mode) + return false; + + if (xe->wedged.mode == XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET || + mode == XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET) + return true; + + return false; +} + static ssize_t wedged_mode_set(struct file *f, const char __user *ubuf, size_t size, loff_t *pos) { struct xe_device *xe = file_inode(f)->i_private; - struct xe_gt *gt; u32 wedged_mode; ssize_t ret; - u8 id; ret = kstrtouint_from_user(ubuf, size, 0, &wedged_mode); if (ret) return ret; - if (wedged_mode > 2) - return -EINVAL; + ret = xe_device_validate_wedged_mode(xe, wedged_mode); + if (ret) + return ret; - if (xe->wedged.mode == wedged_mode) - return size; + if (wedged_mode_needs_policy_update(xe, wedged_mode)) { + ret = wedged_mode_set_reset_policy(xe, wedged_mode); + if (ret) + return ret; + } xe->wedged.mode = wedged_mode; - xe_pm_runtime_get(xe); - for_each_gt(gt, xe, id) { - ret = xe_guc_ads_scheduler_policy_toggle_reset(>->uc.guc.ads); - if (ret) { - xe_gt_err(gt, "Failed to update GuC ADS scheduler policy. GuC may still cause engine reset even with wedged_mode=2\n"); - xe_pm_runtime_put(xe); - return -EIO; - } - } - xe_pm_runtime_put(xe); - return size; } @@ -297,6 +338,39 @@ static const struct file_operations wedged_mode_fops = { .write = wedged_mode_set, }; +static ssize_t page_reclaim_hw_assist_show(struct file *f, char __user *ubuf, + size_t size, loff_t *pos) +{ + struct xe_device *xe = file_inode(f)->i_private; + char buf[8]; + int len; + + len = scnprintf(buf, sizeof(buf), "%d\n", xe->info.has_page_reclaim_hw_assist); + return simple_read_from_buffer(ubuf, size, pos, buf, len); +} + +static ssize_t page_reclaim_hw_assist_set(struct file *f, const char __user *ubuf, + size_t size, loff_t *pos) +{ + struct xe_device *xe = file_inode(f)->i_private; + bool val; + ssize_t ret; + + ret = kstrtobool_from_user(ubuf, size, &val); + if (ret) + return ret; + + xe->info.has_page_reclaim_hw_assist = val; + + return size; +} + +static const struct file_operations page_reclaim_hw_assist_fops = { + .owner = THIS_MODULE, + .read = page_reclaim_hw_assist_show, + .write = page_reclaim_hw_assist_set, +}; + static ssize_t atomic_svm_timeslice_ms_show(struct file *f, char __user *ubuf, size_t size, loff_t *pos) { @@ -332,6 +406,74 @@ static const struct file_operations atomic_svm_timeslice_ms_fops = { .write = atomic_svm_timeslice_ms_set, }; +static ssize_t min_run_period_lr_ms_show(struct file *f, char __user *ubuf, + size_t size, loff_t *pos) +{ + struct xe_device *xe = file_inode(f)->i_private; + char buf[32]; + int len = 0; + + len = scnprintf(buf, sizeof(buf), "%d\n", xe->min_run_period_lr_ms); + + return simple_read_from_buffer(ubuf, size, pos, buf, len); +} + +static ssize_t min_run_period_lr_ms_set(struct file *f, const char __user *ubuf, + size_t size, loff_t *pos) +{ + struct xe_device *xe = file_inode(f)->i_private; + u32 min_run_period_lr_ms; + ssize_t ret; + + ret = kstrtouint_from_user(ubuf, size, 0, &min_run_period_lr_ms); + if (ret) + return ret; + + xe->min_run_period_lr_ms = min_run_period_lr_ms; + + return size; +} + +static const struct file_operations min_run_period_lr_ms_fops = { + .owner = THIS_MODULE, + .read = min_run_period_lr_ms_show, + .write = min_run_period_lr_ms_set, +}; + +static ssize_t min_run_period_pf_ms_show(struct file *f, char __user *ubuf, + size_t size, loff_t *pos) +{ + struct xe_device *xe = file_inode(f)->i_private; + char buf[32]; + int len = 0; + + len = scnprintf(buf, sizeof(buf), "%d\n", xe->min_run_period_pf_ms); + + return simple_read_from_buffer(ubuf, size, pos, buf, len); +} + +static ssize_t min_run_period_pf_ms_set(struct file *f, const char __user *ubuf, + size_t size, loff_t *pos) +{ + struct xe_device *xe = file_inode(f)->i_private; + u32 min_run_period_pf_ms; + ssize_t ret; + + ret = kstrtouint_from_user(ubuf, size, 0, &min_run_period_pf_ms); + if (ret) + return ret; + + xe->min_run_period_pf_ms = min_run_period_pf_ms; + + return size; +} + +static const struct file_operations min_run_period_pf_ms_fops = { + .owner = THIS_MODULE, + .read = min_run_period_pf_ms_show, + .write = min_run_period_pf_ms_set, +}; + static ssize_t disable_late_binding_show(struct file *f, char __user *ubuf, size_t size, loff_t *pos) { @@ -375,7 +517,6 @@ void xe_debugfs_register(struct xe_device *xe) struct ttm_resource_manager *man; struct xe_tile *tile; struct xe_gt *gt; - u32 mem_type; u8 tile_id; u8 id; @@ -400,19 +541,22 @@ void xe_debugfs_register(struct xe_device *xe) debugfs_create_file("atomic_svm_timeslice_ms", 0600, root, xe, &atomic_svm_timeslice_ms_fops); - debugfs_create_file("disable_late_binding", 0600, root, xe, - &disable_late_binding_fops); + debugfs_create_file("min_run_period_lr_ms", 0600, root, xe, + &min_run_period_lr_ms_fops); - for (mem_type = XE_PL_VRAM0; mem_type <= XE_PL_VRAM1; ++mem_type) { - man = ttm_manager_type(bdev, mem_type); + debugfs_create_file("min_run_period_pf_ms", 0600, root, xe, + &min_run_period_pf_ms_fops); - if (man) { - char name[16]; + debugfs_create_file("disable_late_binding", 0600, root, xe, + &disable_late_binding_fops); - snprintf(name, sizeof(name), "vram%d_mm", mem_type - XE_PL_VRAM0); - ttm_resource_manager_create_debugfs(man, root, name); - } - } + /* + * Don't expose page reclaim configuration file if not supported by the + * hardware initially. + */ + if (xe->info.has_page_reclaim_hw_assist) + debugfs_create_file("page_reclaim_hw_assist", 0600, root, xe, + &page_reclaim_hw_assist_fops); man = ttm_manager_type(bdev, XE_PL_TT); ttm_resource_manager_create_debugfs(man, root, "gtt_mm"); diff --git a/drivers/gpu/drm/xe/xe_devcoredump.c b/drivers/gpu/drm/xe/xe_devcoredump.c index d444eda65ca6..cf41bb6d2172 100644 --- a/drivers/gpu/drm/xe/xe_devcoredump.c +++ b/drivers/gpu/drm/xe/xe_devcoredump.c @@ -15,14 +15,13 @@ #include "xe_device.h" #include "xe_exec_queue.h" #include "xe_force_wake.h" -#include "xe_gt.h" #include "xe_gt_printk.h" +#include "xe_gt_types.h" #include "xe_guc_capture.h" #include "xe_guc_ct.h" #include "xe_guc_log.h" #include "xe_guc_submit.h" #include "xe_hw_engine.h" -#include "xe_module.h" #include "xe_pm.h" #include "xe_sched_job.h" #include "xe_vm.h" @@ -276,7 +275,6 @@ static void xe_devcoredump_deferred_snap_work(struct work_struct *work) struct xe_devcoredump_snapshot *ss = container_of(work, typeof(*ss), work); struct xe_devcoredump *coredump = container_of(ss, typeof(*coredump), snapshot); struct xe_device *xe = coredump_to_xe(coredump); - unsigned int fw_ref; /* * NB: Despite passing a GFP_ flags parameter here, more allocations are done @@ -287,15 +285,15 @@ static void xe_devcoredump_deferred_snap_work(struct work_struct *work) xe_devcoredump_read, xe_devcoredump_free, XE_COREDUMP_TIMEOUT_JIFFIES); - xe_pm_runtime_get(xe); + guard(xe_pm_runtime)(xe); /* keep going if fw fails as we still want to save the memory and SW data */ - fw_ref = xe_force_wake_get(gt_to_fw(ss->gt), XE_FORCEWAKE_ALL); - if (!xe_force_wake_ref_has_domain(fw_ref, XE_FORCEWAKE_ALL)) - xe_gt_info(ss->gt, "failed to get forcewake for coredump capture\n"); - xe_vm_snapshot_capture_delayed(ss->vm); - xe_guc_exec_queue_snapshot_capture_delayed(ss->ge); - xe_force_wake_put(gt_to_fw(ss->gt), fw_ref); + xe_with_force_wake(fw_ref, gt_to_fw(ss->gt), XE_FORCEWAKE_ALL) { + if (!xe_force_wake_ref_has_domain(fw_ref.domains, XE_FORCEWAKE_ALL)) + xe_gt_info(ss->gt, "failed to get forcewake for coredump capture\n"); + xe_vm_snapshot_capture_delayed(ss->vm); + xe_guc_exec_queue_snapshot_capture_delayed(ss->ge); + } ss->read.chunk_position = 0; @@ -306,7 +304,7 @@ static void xe_devcoredump_deferred_snap_work(struct work_struct *work) ss->read.buffer = kvmalloc(XE_DEVCOREDUMP_CHUNK_MAX, GFP_USER); if (!ss->read.buffer) - goto put_pm; + return; __xe_devcoredump_read(ss->read.buffer, XE_DEVCOREDUMP_CHUNK_MAX, @@ -314,15 +312,12 @@ static void xe_devcoredump_deferred_snap_work(struct work_struct *work) } else { ss->read.buffer = kvmalloc(ss->read.size, GFP_USER); if (!ss->read.buffer) - goto put_pm; + return; __xe_devcoredump_read(ss->read.buffer, ss->read.size, 0, coredump); xe_devcoredump_snapshot_free(ss); } - -put_pm: - xe_pm_runtime_put(xe); } static void devcoredump_snapshot(struct xe_devcoredump *coredump, @@ -332,7 +327,6 @@ static void devcoredump_snapshot(struct xe_devcoredump *coredump, struct xe_devcoredump_snapshot *ss = &coredump->snapshot; struct xe_guc *guc = exec_queue_to_guc(q); const char *process_name = "no process"; - unsigned int fw_ref; bool cookie; ss->snapshot_time = ktime_get_real(); @@ -348,10 +342,10 @@ static void devcoredump_snapshot(struct xe_devcoredump *coredump, ss->gt = q->gt; INIT_WORK(&ss->work, xe_devcoredump_deferred_snap_work); - cookie = dma_fence_begin_signalling(); - /* keep going if fw fails as we still want to save the memory and SW data */ - fw_ref = xe_force_wake_get(gt_to_fw(q->gt), XE_FORCEWAKE_ALL); + CLASS(xe_force_wake, fw_ref)(gt_to_fw(q->gt), XE_FORCEWAKE_ALL); + + cookie = dma_fence_begin_signalling(); ss->guc.log = xe_guc_log_snapshot_capture(&guc->log, true); ss->guc.ct = xe_guc_ct_snapshot_capture(&guc->ct); @@ -364,7 +358,6 @@ static void devcoredump_snapshot(struct xe_devcoredump *coredump, queue_work(system_unbound_wq, &ss->work); - xe_force_wake_put(gt_to_fw(q->gt), fw_ref); dma_fence_end_signalling(cookie); } diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c index c7d373c70f0f..e575b02008dd 100644 --- a/drivers/gpu/drm/xe/xe_device.c +++ b/drivers/gpu/drm/xe/xe_device.c @@ -8,7 +8,6 @@ #include <linux/aperture.h> #include <linux/delay.h> #include <linux/fault-inject.h> -#include <linux/iopoll.h> #include <linux/units.h> #include <drm/drm_atomic_helper.h> @@ -16,6 +15,7 @@ #include <drm/drm_gem_ttm_helper.h> #include <drm/drm_ioctl.h> #include <drm/drm_managed.h> +#include <drm/drm_pagemap_util.h> #include <drm/drm_print.h> #include <uapi/drm/xe_drm.h> @@ -35,7 +35,6 @@ #include "xe_exec_queue.h" #include "xe_force_wake.h" #include "xe_ggtt.h" -#include "xe_gsc_proxy.h" #include "xe_gt.h" #include "xe_gt_mcr.h" #include "xe_gt_printk.h" @@ -61,8 +60,10 @@ #include "xe_pxp.h" #include "xe_query.h" #include "xe_shrinker.h" +#include "xe_soc_remapper.h" #include "xe_survivability_mode.h" #include "xe_sriov.h" +#include "xe_svm.h" #include "xe_tile.h" #include "xe_ttm_stolen_mgr.h" #include "xe_ttm_sys_mgr.h" @@ -166,7 +167,7 @@ static void xe_file_close(struct drm_device *dev, struct drm_file *file) struct xe_exec_queue *q; unsigned long idx; - xe_pm_runtime_get(xe); + guard(xe_pm_runtime)(xe); /* * No need for exec_queue.lock here as there is no contention for it @@ -184,8 +185,6 @@ static void xe_file_close(struct drm_device *dev, struct drm_file *file) xe_vm_close_and_put(vm); xe_file_put(xef); - - xe_pm_runtime_put(xe); } static const struct drm_ioctl_desc xe_ioctls[] = { @@ -209,6 +208,8 @@ static const struct drm_ioctl_desc xe_ioctls[] = { DRM_IOCTL_DEF_DRV(XE_MADVISE, xe_vm_madvise_ioctl, DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(XE_VM_QUERY_MEM_RANGE_ATTRS, xe_vm_query_vmas_attrs_ioctl, DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(XE_EXEC_QUEUE_SET_PROPERTY, xe_exec_queue_set_property_ioctl, + DRM_RENDER_ALLOW), }; static long xe_drm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) @@ -220,10 +221,10 @@ static long xe_drm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (xe_device_wedged(xe)) return -ECANCELED; - ret = xe_pm_runtime_get_ioctl(xe); + ACQUIRE(xe_pm_runtime_ioctl, pm)(xe); + ret = ACQUIRE_ERR(xe_pm_runtime_ioctl, &pm); if (ret >= 0) ret = drm_ioctl(file, cmd, arg); - xe_pm_runtime_put(xe); return ret; } @@ -238,10 +239,10 @@ static long xe_drm_compat_ioctl(struct file *file, unsigned int cmd, unsigned lo if (xe_device_wedged(xe)) return -ECANCELED; - ret = xe_pm_runtime_get_ioctl(xe); + ACQUIRE(xe_pm_runtime_ioctl, pm)(xe); + ret = ACQUIRE_ERR(xe_pm_runtime_ioctl, &pm); if (ret >= 0) ret = drm_compat_ioctl(file, cmd, arg); - xe_pm_runtime_put(xe); return ret; } @@ -371,6 +372,20 @@ static const struct file_operations xe_driver_fops = { .fop_flags = FOP_UNSIGNED_OFFSET, }; +/** + * xe_is_xe_file() - Is the file an xe device file? + * @file: The file. + * + * Checks whether the file is opened against + * an xe device. + * + * Return: %true if an xe file, %false if not. + */ +bool xe_is_xe_file(const struct file *file) +{ + return file->f_op == &xe_driver_fops; +} + static struct drm_driver driver = { /* Don't use MTRRs here; the Xserver or userspace app should * deal with them for Intel hardware. @@ -455,6 +470,7 @@ struct xe_device *xe_device_create(struct pci_dev *pdev, xe->info.revid = pdev->revision; xe->info.force_execlist = xe_modparam.force_execlist; xe->atomic_svm_timeslice_ms = 5; + xe->min_run_period_lr_ms = 5; err = xe_irq_init(xe); if (err) @@ -466,6 +482,10 @@ struct xe_device *xe_device_create(struct pci_dev *pdev, init_rwsem(&xe->usm.lock); + err = xe_pagemap_shrinker_create(xe); + if (err) + goto err; + xa_init_flags(&xe->usm.asid_to_vm, XA_FLAGS_ALLOC); if (IS_ENABLED(CONFIG_DRM_XE_DEBUG)) { @@ -632,62 +652,14 @@ mask_err: return err; } -static int lmem_initializing(struct xe_device *xe) -{ - if (xe_mmio_read32(xe_root_tile_mmio(xe), GU_CNTL) & LMEM_INIT) - return 0; - - if (signal_pending(current)) - return -EINTR; - - return 1; -} - -static int wait_for_lmem_ready(struct xe_device *xe) +static void assert_lmem_ready(struct xe_device *xe) { - const unsigned long TIMEOUT_SEC = 60; - unsigned long prev_jiffies; - int initializing; - - if (!IS_DGFX(xe)) - return 0; - - if (IS_SRIOV_VF(xe)) - return 0; - - if (!lmem_initializing(xe)) - return 0; - - drm_dbg(&xe->drm, "Waiting for lmem initialization\n"); - prev_jiffies = jiffies; - - /* - * The boot firmware initializes local memory and - * assesses its health. If memory training fails, - * the punit will have been instructed to keep the GT powered - * down.we won't be able to communicate with it - * - * If the status check is done before punit updates the register, - * it can lead to the system being unusable. - * use a timeout and defer the probe to prevent this. - */ - poll_timeout_us(initializing = lmem_initializing(xe), - initializing <= 0, - 20 * USEC_PER_MSEC, TIMEOUT_SEC * USEC_PER_SEC, true); - if (initializing < 0) - return initializing; - - if (initializing) { - drm_dbg(&xe->drm, "lmem not initialized by firmware\n"); - return -EPROBE_DEFER; - } - - drm_dbg(&xe->drm, "lmem ready after %ums", - jiffies_to_msecs(jiffies - prev_jiffies)); + if (!IS_DGFX(xe) || IS_SRIOV_VF(xe)) + return; - return 0; + xe_assert(xe, xe_mmio_read32(xe_root_tile_mmio(xe), GU_CNTL) & + LMEM_INIT); } -ALLOW_ERROR_INJECTION(wait_for_lmem_ready, ERRNO); /* See xe_pci_probe() */ static void vf_update_device_info(struct xe_device *xe) { @@ -742,6 +714,11 @@ int xe_device_probe_early(struct xe_device *xe) if (IS_SRIOV_VF(xe)) vf_update_device_info(xe); + /* + * Check for pcode uncore_init status to confirm if the SoC + * initialization is complete. Until done, any MMIO or lmem access from + * the driver will be blocked + */ err = xe_pcode_probe_early(xe); if (err || xe_survivability_mode_is_requested(xe)) { int save_err = err; @@ -758,11 +735,17 @@ int xe_device_probe_early(struct xe_device *xe) return save_err; } - err = wait_for_lmem_ready(xe); - if (err) - return err; + /* + * Make sure the lmem is initialized and ready to use. xe_pcode_ready() + * is flagged after full initialization is complete. Assert if lmem is + * not initialized. + */ + assert_lmem_ready(xe); - xe->wedged.mode = xe_modparam.wedged_mode; + xe->wedged.mode = xe_device_validate_wedged_mode(xe, xe_modparam.wedged_mode) ? + XE_WEDGED_MODE_DEFAULT : xe_modparam.wedged_mode; + drm_dbg(&xe->drm, "wedged_mode: setting mode (%u) %s\n", + xe->wedged.mode, xe_wedged_mode_to_string(xe->wedged.mode)); err = xe_device_vram_alloc(xe); if (err) @@ -775,7 +758,6 @@ ALLOW_ERROR_INJECTION(xe_device_probe_early, ERRNO); /* See xe_pci_probe() */ static int probe_has_flat_ccs(struct xe_device *xe) { struct xe_gt *gt; - unsigned int fw_ref; u32 reg; /* Always enabled/disabled, no runtime check to do */ @@ -786,8 +768,8 @@ static int probe_has_flat_ccs(struct xe_device *xe) if (!gt) return 0; - fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT); - if (!fw_ref) + CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT); + if (!fw_ref.domains) return -ETIMEDOUT; reg = xe_gt_mcr_unicast_read_any(gt, XE2_FLAT_CCS_BASE_RANGE_LOWER); @@ -797,11 +779,64 @@ static int probe_has_flat_ccs(struct xe_device *xe) drm_dbg(&xe->drm, "Flat CCS has been disabled in bios, May lead to performance impact"); - xe_force_wake_put(gt_to_fw(gt), fw_ref); - return 0; } +/* + * Detect if the driver is being run on pre-production hardware. We don't + * keep workarounds for pre-production hardware long term, so print an + * error and add taint if we're being loaded on a pre-production platform + * for which the pre-prod workarounds have already been removed. + * + * The general policy is that we'll remove any workarounds that only apply to + * pre-production hardware around the time force_probe restrictions are lifted + * for a platform of the next major IP generation (for example, Xe2 pre-prod + * workarounds should be removed around the time the first Xe3 platforms have + * force_probe lifted). + */ +static void detect_preproduction_hw(struct xe_device *xe) +{ + struct xe_gt *gt; + int id; + + /* + * SR-IOV VFs don't have access to the FUSE2 register, so we can't + * check pre-production status there. But the host OS will notice + * and report the pre-production status, which should be enough to + * help us catch mistaken use of pre-production hardware. + */ + if (IS_SRIOV_VF(xe)) + return; + + /* + * The "SW_CAP" fuse contains a bit indicating whether the device is a + * production or pre-production device. This fuse is reflected through + * the GT "FUSE2" register, even though the contents of the fuse are + * not GT-specific. Every GT's reflection of this fuse should show the + * same value, so we'll just use the first available GT for lookup. + */ + for_each_gt(gt, xe, id) + break; + + if (!gt) + return; + + CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT); + if (!xe_force_wake_ref_has_domain(fw_ref.domains, XE_FW_GT)) { + xe_gt_err(gt, "Forcewake failure; cannot determine production/pre-production hw status.\n"); + return; + } + + if (xe_mmio_read32(>->mmio, FUSE2) & PRODUCTION_HW) + return; + + xe_info(xe, "Pre-production hardware detected.\n"); + if (!xe->info.has_pre_prod_wa) { + xe_err(xe, "Pre-production workarounds for this platform have already been removed.\n"); + add_taint(TAINT_MACHINE_CHECK, LOCKDEP_STILL_OK); + } +} + int xe_device_probe(struct xe_device *xe) { struct xe_tile *tile; @@ -911,6 +946,10 @@ int xe_device_probe(struct xe_device *xe) xe_nvm_init(xe); + err = xe_soc_remapper_init(xe); + if (err) + return err; + err = xe_heci_gsc_init(xe); if (err) return err; @@ -972,10 +1011,13 @@ int xe_device_probe(struct xe_device *xe) if (err) goto err_unregister_display; + detect_preproduction_hw(xe); + return devm_add_action_or_reset(xe->drm.dev, xe_device_sanitize, xe); err_unregister_display: xe_display_unregister(xe); + drm_dev_unregister(&xe->drm); return err; } @@ -1034,7 +1076,6 @@ void xe_device_wmb(struct xe_device *xe) */ static void tdf_request_sync(struct xe_device *xe) { - unsigned int fw_ref; struct xe_gt *gt; u8 id; @@ -1042,8 +1083,8 @@ static void tdf_request_sync(struct xe_device *xe) if (xe_gt_is_media_type(gt)) continue; - fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT); - if (!fw_ref) + CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT); + if (!fw_ref.domains) return; xe_mmio_write32(>->mmio, XE2_TDF_CTRL, TRANSIENT_FLUSH_REQUEST); @@ -1056,17 +1097,14 @@ static void tdf_request_sync(struct xe_device *xe) * transient and need to be flushed.. */ if (xe_mmio_wait32(>->mmio, XE2_TDF_CTRL, TRANSIENT_FLUSH_REQUEST, 0, - 150, NULL, false)) + 300, NULL, false)) xe_gt_err_once(gt, "TD flush timeout\n"); - - xe_force_wake_put(gt_to_fw(gt), fw_ref); } } void xe_device_l2_flush(struct xe_device *xe) { struct xe_gt *gt; - unsigned int fw_ref; gt = xe_root_mmio_gt(xe); if (!gt) @@ -1075,8 +1113,8 @@ void xe_device_l2_flush(struct xe_device *xe) if (!XE_GT_WA(gt, 16023588340)) return; - fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT); - if (!fw_ref) + CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT); + if (!fw_ref.domains) return; spin_lock(>->global_invl_lock); @@ -1086,8 +1124,6 @@ void xe_device_l2_flush(struct xe_device *xe) xe_gt_err_once(gt, "Global invalidation timeout\n"); spin_unlock(>->global_invl_lock); - - xe_force_wake_put(gt_to_fw(gt), fw_ref); } /** @@ -1193,10 +1229,10 @@ static void xe_device_wedged_fini(struct drm_device *drm, void *arg) * DOC: Xe Device Wedging * * Xe driver uses drm device wedged uevent as documented in Documentation/gpu/drm-uapi.rst. - * When device is in wedged state, every IOCTL will be blocked and GT cannot be - * used. Certain critical errors like gt reset failure, firmware failures can cause - * the device to be wedged. The default recovery method for a wedged state - * is rebind/bus-reset. + * When device is in wedged state, every IOCTL will be blocked and GT cannot + * be used. The conditions under which the driver declares the device wedged + * depend on the wedged mode configuration (see &enum xe_wedged_mode). The + * default recovery method for a wedged state is rebind/bus-reset. * * Another recovery method is vendor-specific. Below are the cases that send * ``WEDGED=vendor-specific`` recovery method in drm device wedged uevent. @@ -1261,7 +1297,7 @@ void xe_device_declare_wedged(struct xe_device *xe) struct xe_gt *gt; u8 id; - if (xe->wedged.mode == 0) { + if (xe->wedged.mode == XE_WEDGED_MODE_NEVER) { drm_dbg(&xe->drm, "Wedged mode is forcibly disabled\n"); return; } @@ -1295,3 +1331,48 @@ void xe_device_declare_wedged(struct xe_device *xe) drm_dev_wedged_event(&xe->drm, xe->wedged.method, NULL); } } + +/** + * xe_device_validate_wedged_mode - Check if given mode is supported + * @xe: the &xe_device + * @mode: requested mode to validate + * + * Check whether the provided wedged mode is supported. + * + * Return: 0 if mode is supported, error code otherwise. + */ +int xe_device_validate_wedged_mode(struct xe_device *xe, unsigned int mode) +{ + if (mode > XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET) { + drm_dbg(&xe->drm, "wedged_mode: invalid value (%u)\n", mode); + return -EINVAL; + } else if (mode == XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET && (IS_SRIOV_VF(xe) || + (IS_SRIOV_PF(xe) && !IS_ENABLED(CONFIG_DRM_XE_DEBUG)))) { + drm_dbg(&xe->drm, "wedged_mode: (%u) %s mode is not supported for %s\n", + mode, xe_wedged_mode_to_string(mode), + xe_sriov_mode_to_string(xe_device_sriov_mode(xe))); + return -EPERM; + } + + return 0; +} + +/** + * xe_wedged_mode_to_string - Convert enum value to string. + * @mode: the &xe_wedged_mode to convert + * + * Returns: wedged mode as a user friendly string. + */ +const char *xe_wedged_mode_to_string(enum xe_wedged_mode mode) +{ + switch (mode) { + case XE_WEDGED_MODE_NEVER: + return "never"; + case XE_WEDGED_MODE_UPON_CRITICAL_ERROR: + return "upon-critical-error"; + case XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET: + return "upon-any-hang-no-reset"; + default: + return "<invalid>"; + } +} diff --git a/drivers/gpu/drm/xe/xe_device.h b/drivers/gpu/drm/xe/xe_device.h index 32cc6323b7f6..3740143790db 100644 --- a/drivers/gpu/drm/xe/xe_device.h +++ b/drivers/gpu/drm/xe/xe_device.h @@ -172,6 +172,11 @@ static inline bool xe_device_has_lmtt(struct xe_device *xe) return IS_DGFX(xe); } +static inline bool xe_device_has_mert(struct xe_device *xe) +{ + return xe->info.has_mert; +} + u32 xe_device_ccs_bytes(struct xe_device *xe, u64 size); void xe_device_snapshot_print(struct xe_device *xe, struct drm_printer *p); @@ -189,12 +194,16 @@ static inline bool xe_device_wedged(struct xe_device *xe) void xe_device_set_wedged_method(struct xe_device *xe, unsigned long method); void xe_device_declare_wedged(struct xe_device *xe); +int xe_device_validate_wedged_mode(struct xe_device *xe, unsigned int mode); +const char *xe_wedged_mode_to_string(enum xe_wedged_mode mode); struct xe_file *xe_file_get(struct xe_file *xef); void xe_file_put(struct xe_file *xef); int xe_is_injection_active(void); +bool xe_is_xe_file(const struct file *file); + /* * Occasionally it is seen that the G2H worker starts running after a delay of more than * a second even after being queued and activated by the Linux workqueue subsystem. This diff --git a/drivers/gpu/drm/xe/xe_device_sysfs.c b/drivers/gpu/drm/xe/xe_device_sysfs.c index ec9c06b06fb5..a73e0e957cb0 100644 --- a/drivers/gpu/drm/xe/xe_device_sysfs.c +++ b/drivers/gpu/drm/xe/xe_device_sysfs.c @@ -57,9 +57,8 @@ vram_d3cold_threshold_store(struct device *dev, struct device_attribute *attr, drm_dbg(&xe->drm, "vram_d3cold_threshold: %u\n", vram_d3cold_threshold); - xe_pm_runtime_get(xe); + guard(xe_pm_runtime)(xe); ret = xe_pm_set_vram_threshold(xe, vram_d3cold_threshold); - xe_pm_runtime_put(xe); return ret ?: count; } @@ -84,33 +83,31 @@ lb_fan_control_version_show(struct device *dev, struct device_attribute *attr, c u16 major = 0, minor = 0, hotfix = 0, build = 0; int ret; - xe_pm_runtime_get(xe); + guard(xe_pm_runtime)(xe); ret = xe_pcode_read(root, PCODE_MBOX(PCODE_LATE_BINDING, GET_CAPABILITY_STATUS, 0), &cap, NULL); if (ret) - goto out; + return ret; if (REG_FIELD_GET(V1_FAN_PROVISIONED, cap)) { ret = xe_pcode_read(root, PCODE_MBOX(PCODE_LATE_BINDING, GET_VERSION_LOW, 0), &ver_low, NULL); if (ret) - goto out; + return ret; ret = xe_pcode_read(root, PCODE_MBOX(PCODE_LATE_BINDING, GET_VERSION_HIGH, 0), &ver_high, NULL); if (ret) - goto out; + return ret; major = REG_FIELD_GET(MAJOR_VERSION_MASK, ver_low); minor = REG_FIELD_GET(MINOR_VERSION_MASK, ver_low); hotfix = REG_FIELD_GET(HOTFIX_VERSION_MASK, ver_high); build = REG_FIELD_GET(BUILD_VERSION_MASK, ver_high); } -out: - xe_pm_runtime_put(xe); - return ret ?: sysfs_emit(buf, "%u.%u.%u.%u\n", major, minor, hotfix, build); + return sysfs_emit(buf, "%u.%u.%u.%u\n", major, minor, hotfix, build); } static DEVICE_ATTR_ADMIN_RO(lb_fan_control_version); @@ -123,33 +120,31 @@ lb_voltage_regulator_version_show(struct device *dev, struct device_attribute *a u16 major = 0, minor = 0, hotfix = 0, build = 0; int ret; - xe_pm_runtime_get(xe); + guard(xe_pm_runtime)(xe); ret = xe_pcode_read(root, PCODE_MBOX(PCODE_LATE_BINDING, GET_CAPABILITY_STATUS, 0), &cap, NULL); if (ret) - goto out; + return ret; if (REG_FIELD_GET(VR_PARAMS_PROVISIONED, cap)) { ret = xe_pcode_read(root, PCODE_MBOX(PCODE_LATE_BINDING, GET_VERSION_LOW, 0), &ver_low, NULL); if (ret) - goto out; + return ret; ret = xe_pcode_read(root, PCODE_MBOX(PCODE_LATE_BINDING, GET_VERSION_HIGH, 0), &ver_high, NULL); if (ret) - goto out; + return ret; major = REG_FIELD_GET(MAJOR_VERSION_MASK, ver_low); minor = REG_FIELD_GET(MINOR_VERSION_MASK, ver_low); hotfix = REG_FIELD_GET(HOTFIX_VERSION_MASK, ver_high); build = REG_FIELD_GET(BUILD_VERSION_MASK, ver_high); } -out: - xe_pm_runtime_put(xe); - return ret ?: sysfs_emit(buf, "%u.%u.%u.%u\n", major, minor, hotfix, build); + return sysfs_emit(buf, "%u.%u.%u.%u\n", major, minor, hotfix, build); } static DEVICE_ATTR_ADMIN_RO(lb_voltage_regulator_version); @@ -233,9 +228,8 @@ auto_link_downgrade_capable_show(struct device *dev, struct device_attribute *at struct xe_device *xe = pdev_to_xe_device(pdev); u32 cap, val; - xe_pm_runtime_get(xe); + guard(xe_pm_runtime)(xe); val = xe_mmio_read32(xe_root_tile_mmio(xe), BMG_PCIE_CAP); - xe_pm_runtime_put(xe); cap = REG_FIELD_GET(LINK_DOWNGRADE, val); return sysfs_emit(buf, "%u\n", cap == DOWNGRADE_CAPABLE); @@ -251,11 +245,10 @@ auto_link_downgrade_status_show(struct device *dev, struct device_attribute *att u32 val = 0; int ret; - xe_pm_runtime_get(xe); + guard(xe_pm_runtime)(xe); ret = xe_pcode_read(xe_device_get_root_tile(xe), PCODE_MBOX(DGFX_PCODE_STATUS, DGFX_GET_INIT_STATUS, 0), &val, NULL); - xe_pm_runtime_put(xe); return ret ?: sysfs_emit(buf, "%u\n", REG_FIELD_GET(DGFX_LINK_DOWNGRADE_STATUS, val)); } diff --git a/drivers/gpu/drm/xe/xe_device_types.h b/drivers/gpu/drm/xe/xe_device_types.h index 0b2fa7c56d38..f689766adcb1 100644 --- a/drivers/gpu/drm/xe/xe_device_types.h +++ b/drivers/gpu/drm/xe/xe_device_types.h @@ -17,6 +17,7 @@ #include "xe_late_bind_fw_types.h" #include "xe_lmtt_types.h" #include "xe_memirq_types.h" +#include "xe_mert.h" #include "xe_oa_types.h" #include "xe_pagefault_types.h" #include "xe_platform_types.h" @@ -36,6 +37,7 @@ #endif struct dram_info; +struct drm_pagemap_shrinker; struct intel_display; struct intel_dg_nvm_dev; struct xe_ggtt; @@ -44,6 +46,25 @@ struct xe_pat_ops; struct xe_pxp; struct xe_vram_region; +/** + * enum xe_wedged_mode - possible wedged modes + * @XE_WEDGED_MODE_NEVER: Device will never be declared wedged. + * @XE_WEDGED_MODE_UPON_CRITICAL_ERROR: Device will be declared wedged only + * when critical error occurs like GT reset failure or firmware failure. + * This is the default mode. + * @XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET: Device will be declared wedged on + * any hang. In this mode, engine resets are disabled to avoid automatic + * recovery attempts. This mode is primarily intended for debugging hangs. + */ +enum xe_wedged_mode { + XE_WEDGED_MODE_NEVER = 0, + XE_WEDGED_MODE_UPON_CRITICAL_ERROR = 1, + XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET = 2, +}; + +#define XE_WEDGED_MODE_DEFAULT XE_WEDGED_MODE_UPON_CRITICAL_ERROR +#define XE_WEDGED_MODE_DEFAULT_STR "upon-critical-error" + #define XE_BO_INVALID_OFFSET LONG_MAX #define GRAPHICS_VER(xe) ((xe)->info.graphics_verx100 / 100) @@ -184,6 +205,13 @@ struct xe_tile { * Media GT shares a pool with its primary GT. */ struct xe_sa_manager *kernel_bb_pool; + + /** + * @mem.reclaim_pool: Pool for PRLs allocated. + * + * Only main GT has page reclaim list allocations. + */ + struct xe_sa_manager *reclaim_pool; } mem; /** @sriov: tile level virtualization data */ @@ -220,6 +248,9 @@ struct xe_tile { /** @debugfs: debugfs directory associated with this tile */ struct dentry *debugfs; + + /** @mert: MERT-related data */ + struct xe_mert mert; }; /** @@ -286,6 +317,8 @@ struct xe_device { u8 has_asid:1; /** @info.has_atomic_enable_pte_bit: Device has atomic enable PTE bit */ u8 has_atomic_enable_pte_bit:1; + /** @info.has_cached_pt: Supports caching pagetable */ + u8 has_cached_pt:1; /** @info.has_device_atomics_on_smem: Supports device atomics on SMEM */ u8 has_device_atomics_on_smem:1; /** @info.has_fan_control: Device supports fan control */ @@ -298,6 +331,8 @@ struct xe_device { u8 has_heci_cscfi:1; /** @info.has_heci_gscfi: device has heci gscfi */ u8 has_heci_gscfi:1; + /** @info.has_i2c: Device has I2C controller */ + u8 has_i2c:1; /** @info.has_late_bind: Device has firmware late binding support */ u8 has_late_bind:1; /** @info.has_llc: Device has a shared CPU+GPU last level cache */ @@ -306,12 +341,24 @@ struct xe_device { * pcode mailbox commands. */ u8 has_mbx_power_limits:1; + /** @info.has_mbx_thermal_info: Device supports thermal mailbox commands */ + u8 has_mbx_thermal_info:1; /** @info.has_mem_copy_instr: Device supports MEM_COPY instruction */ u8 has_mem_copy_instr:1; + /** @info.has_mert: Device has standalone MERT */ + u8 has_mert:1; + /** @info.has_page_reclaim_hw_assist: Device supports page reclamation feature */ + u8 has_page_reclaim_hw_assist:1; + /** @info.has_pre_prod_wa: Pre-production workarounds still present in driver */ + u8 has_pre_prod_wa:1; /** @info.has_pxp: Device has PXP support */ u8 has_pxp:1; /** @info.has_range_tlb_inval: Has range based TLB invalidations */ u8 has_range_tlb_inval:1; + /** @info.has_soc_remapper_sysctrl: Has SoC remapper system controller */ + u8 has_soc_remapper_sysctrl:1; + /** @info.has_soc_remapper_telem: Has SoC remapper telemetry support */ + u8 has_soc_remapper_telem:1; /** @info.has_sriov: Supports SR-IOV */ u8 has_sriov:1; /** @info.has_usm: Device has unified shared memory support */ @@ -429,6 +476,10 @@ struct xe_device { #define XE_PAGEFAULT_QUEUE_COUNT 4 /** @usm.pf_queue: Page fault queues */ struct xe_pagefault_queue pf_queue[XE_PAGEFAULT_QUEUE_COUNT]; +#if IS_ENABLED(CONFIG_DRM_XE_PAGEMAP) + /** @usm.pagemap_shrinker: Shrinker for unused pagemaps */ + struct drm_pagemap_shrinker *dpagemap_shrinker; +#endif } usm; /** @pinned: pinned BO state */ @@ -552,6 +603,18 @@ struct xe_device { struct mutex lock; } pmt; + /** @soc_remapper: SoC remapper object */ + struct { + /** @soc_remapper.lock: Serialize access to SoC Remapper's index registers */ + spinlock_t lock; + + /** @soc_remapper.set_telem_region: Set telemetry index */ + void (*set_telem_region)(struct xe_device *xe, u32 index); + + /** @soc_remapper.set_sysctrl_region: Set system controller index */ + void (*set_sysctrl_region)(struct xe_device *xe, u32 index); + } soc_remapper; + /** * @pm_callback_task: Track the active task that is running in either * the runtime_suspend or runtime_resume callbacks. @@ -584,9 +647,11 @@ struct xe_device { /** @wedged.flag: Xe device faced a critical error and is now blocked. */ atomic_t flag; /** @wedged.mode: Mode controlled by kernel parameter and debugfs */ - int mode; + enum xe_wedged_mode mode; /** @wedged.method: Recovery method to be sent in the drm device wedged uevent */ unsigned long method; + /** @wedged.inconsistent_reset: Inconsistent reset policy state between GTs */ + bool inconsistent_reset; } wedged; /** @bo_device: Struct to control async free of BOs */ @@ -606,6 +671,12 @@ struct xe_device { /** @atomic_svm_timeslice_ms: Atomic SVM fault timeslice MS */ u32 atomic_svm_timeslice_ms; + /** @min_run_period_lr_ms: LR VM (preempt fence mode) timeslice */ + u32 min_run_period_lr_ms; + + /** @min_run_period_pf_ms: LR VM (page fault mode) timeslice */ + u32 min_run_period_pf_ms; + #ifdef TEST_VM_OPS_ERROR /** * @vm_inject_error_position: inject errors at different places in VM @@ -648,13 +719,6 @@ struct xe_device { * drm_i915_private during build. After cleanup these should go away, * migrating to the right sub-structs */ - const struct dram_info *dram_info; - - /* - * edram size in MB. - * Cannot be determined by PCIID. You must always read a register. - */ - u32 edram_size_mb; struct intel_uncore { spinlock_t lock; diff --git a/drivers/gpu/drm/xe/xe_dma_buf.c b/drivers/gpu/drm/xe/xe_dma_buf.c index 4e3a968bc6a0..ea370cd373e9 100644 --- a/drivers/gpu/drm/xe/xe_dma_buf.c +++ b/drivers/gpu/drm/xe/xe_dma_buf.c @@ -120,7 +120,7 @@ static struct sg_table *xe_dma_buf_map(struct dma_buf_attachment *attach, case XE_PL_TT: sgt = drm_prime_pages_to_sg(obj->dev, bo->ttm.ttm->pages, - bo->ttm.ttm->num_pages); + obj->size >> PAGE_SHIFT); if (IS_ERR(sgt)) return sgt; diff --git a/drivers/gpu/drm/xe/xe_drm_client.c b/drivers/gpu/drm/xe/xe_drm_client.c index f931ff9b1ec0..2787bbb36141 100644 --- a/drivers/gpu/drm/xe/xe_drm_client.c +++ b/drivers/gpu/drm/xe/xe_drm_client.c @@ -285,32 +285,31 @@ static struct xe_hw_engine *any_engine(struct xe_device *xe) return NULL; } -static bool force_wake_get_any_engine(struct xe_device *xe, - struct xe_hw_engine **phwe, - unsigned int *pfw_ref) +/* + * Pick any engine and grab its forcewake. On error phwe will be NULL and + * the returned forcewake reference will be invalid. Callers should check + * phwe against NULL. + */ +static struct xe_force_wake_ref force_wake_get_any_engine(struct xe_device *xe, + struct xe_hw_engine **phwe) { enum xe_force_wake_domains domain; - unsigned int fw_ref; + struct xe_force_wake_ref fw_ref = {}; struct xe_hw_engine *hwe; - struct xe_force_wake *fw; + + *phwe = NULL; hwe = any_engine(xe); if (!hwe) - return false; + return fw_ref; /* will be invalid */ domain = xe_hw_engine_to_fw_domain(hwe); - fw = gt_to_fw(hwe->gt); - - fw_ref = xe_force_wake_get(fw, domain); - if (!xe_force_wake_ref_has_domain(fw_ref, domain)) { - xe_force_wake_put(fw, fw_ref); - return false; - } - *phwe = hwe; - *pfw_ref = fw_ref; + fw_ref = xe_force_wake_constructor(gt_to_fw(hwe->gt), domain); + if (xe_force_wake_ref_has_domain(fw_ref.domains, domain)) + *phwe = hwe; /* valid forcewake */ - return true; + return fw_ref; } static void show_run_ticks(struct drm_printer *p, struct drm_file *file) @@ -322,7 +321,6 @@ static void show_run_ticks(struct drm_printer *p, struct drm_file *file) struct xe_hw_engine *hwe; struct xe_exec_queue *q; u64 gpu_timestamp; - unsigned int fw_ref; /* * RING_TIMESTAMP registers are inaccessible in VF mode. @@ -339,29 +337,26 @@ static void show_run_ticks(struct drm_printer *p, struct drm_file *file) wait_var_event(&xef->exec_queue.pending_removal, !atomic_read(&xef->exec_queue.pending_removal)); - xe_pm_runtime_get(xe); - if (!force_wake_get_any_engine(xe, &hwe, &fw_ref)) { - xe_pm_runtime_put(xe); - return; - } - - /* Accumulate all the exec queues from this client */ - mutex_lock(&xef->exec_queue.lock); - xa_for_each(&xef->exec_queue.xa, i, q) { - xe_exec_queue_get(q); - mutex_unlock(&xef->exec_queue.lock); - - xe_exec_queue_update_run_ticks(q); + scoped_guard(xe_pm_runtime, xe) { + CLASS(xe_force_wake_release_only, fw_ref)(force_wake_get_any_engine(xe, &hwe)); + if (!hwe) + return; + /* Accumulate all the exec queues from this client */ mutex_lock(&xef->exec_queue.lock); - xe_exec_queue_put(q); - } - mutex_unlock(&xef->exec_queue.lock); + xa_for_each(&xef->exec_queue.xa, i, q) { + xe_exec_queue_get(q); + mutex_unlock(&xef->exec_queue.lock); - gpu_timestamp = xe_hw_engine_read_timestamp(hwe); + xe_exec_queue_update_run_ticks(q); - xe_force_wake_put(gt_to_fw(hwe->gt), fw_ref); - xe_pm_runtime_put(xe); + mutex_lock(&xef->exec_queue.lock); + xe_exec_queue_put(q); + } + mutex_unlock(&xef->exec_queue.lock); + + gpu_timestamp = xe_hw_engine_read_timestamp(hwe); + } for (class = 0; class < XE_ENGINE_CLASS_MAX; class++) { const char *class_name; diff --git a/drivers/gpu/drm/xe/xe_eu_stall.c b/drivers/gpu/drm/xe/xe_eu_stall.c index 97dfb7945b7a..a5c36a317a70 100644 --- a/drivers/gpu/drm/xe/xe_eu_stall.c +++ b/drivers/gpu/drm/xe/xe_eu_stall.c @@ -315,7 +315,7 @@ static int xe_eu_stall_user_ext_set_property(struct xe_device *xe, u64 extension return -EFAULT; if (XE_IOCTL_DBG(xe, ext.property >= ARRAY_SIZE(xe_set_eu_stall_property_funcs)) || - XE_IOCTL_DBG(xe, ext.pad)) + XE_IOCTL_DBG(xe, !ext.property) || XE_IOCTL_DBG(xe, ext.pad)) return -EINVAL; idx = array_index_nospec(ext.property, ARRAY_SIZE(xe_set_eu_stall_property_funcs)); diff --git a/drivers/gpu/drm/xe/xe_exec.c b/drivers/gpu/drm/xe/xe_exec.c index 4d81210e41f5..a5485fe6e3f1 100644 --- a/drivers/gpu/drm/xe/xe_exec.c +++ b/drivers/gpu/drm/xe/xe_exec.c @@ -11,7 +11,6 @@ #include <uapi/drm/xe_drm.h> #include <linux/delay.h> -#include "xe_bo.h" #include "xe_device.h" #include "xe_exec_queue.h" #include "xe_hw_engine_group.h" @@ -121,7 +120,7 @@ int xe_exec_ioctl(struct drm_device *dev, void *data, struct drm_file *file) u64 addresses[XE_HW_ENGINE_MAX_INSTANCE]; struct drm_gpuvm_exec vm_exec = {.extra.fn = xe_exec_fn}; struct drm_exec *exec = &vm_exec.exec; - u32 i, num_syncs, num_ufence = 0; + u32 i, num_syncs, num_in_sync = 0, num_ufence = 0; struct xe_validation_ctx ctx; struct xe_sched_job *job; struct xe_vm *vm; @@ -132,7 +131,8 @@ int xe_exec_ioctl(struct drm_device *dev, void *data, struct drm_file *file) if (XE_IOCTL_DBG(xe, args->extensions) || XE_IOCTL_DBG(xe, args->pad[0] || args->pad[1] || args->pad[2]) || - XE_IOCTL_DBG(xe, args->reserved[0] || args->reserved[1])) + XE_IOCTL_DBG(xe, args->reserved[0] || args->reserved[1]) || + XE_IOCTL_DBG(xe, args->num_syncs > DRM_XE_MAX_SYNCS)) return -EINVAL; q = xe_exec_queue_lookup(xef, args->exec_queue_id); @@ -182,6 +182,9 @@ int xe_exec_ioctl(struct drm_device *dev, void *data, struct drm_file *file) if (xe_sync_is_ufence(&syncs[num_syncs])) num_ufence++; + + if (!num_in_sync && xe_sync_needs_wait(&syncs[num_syncs])) + num_in_sync++; } if (XE_IOCTL_DBG(xe, num_ufence > 1)) { @@ -202,7 +205,9 @@ int xe_exec_ioctl(struct drm_device *dev, void *data, struct drm_file *file) mode = xe_hw_engine_group_find_exec_mode(q); if (mode == EXEC_MODE_DMA_FENCE) { - err = xe_hw_engine_group_get_mode(group, mode, &previous_mode); + err = xe_hw_engine_group_get_mode(group, mode, &previous_mode, + syncs, num_in_sync ? + num_syncs : 0); if (err) goto err_syncs; } diff --git a/drivers/gpu/drm/xe/xe_exec_queue.c b/drivers/gpu/drm/xe/xe_exec_queue.c index 8724f8de67e2..9b6311f7fd4f 100644 --- a/drivers/gpu/drm/xe/xe_exec_queue.c +++ b/drivers/gpu/drm/xe/xe_exec_queue.c @@ -13,19 +13,19 @@ #include <drm/drm_syncobj.h> #include <uapi/drm/xe_drm.h> +#include "xe_bo.h" #include "xe_dep_scheduler.h" #include "xe_device.h" #include "xe_gt.h" +#include "xe_gt_sriov_pf.h" #include "xe_gt_sriov_vf.h" #include "xe_hw_engine_class_sysfs.h" #include "xe_hw_engine_group.h" -#include "xe_hw_fence.h" #include "xe_irq.h" #include "xe_lrc.h" #include "xe_macros.h" #include "xe_migrate.h" #include "xe_pm.h" -#include "xe_ring_ops_types.h" #include "xe_trace.h" #include "xe_vm.h" #include "xe_pxp.h" @@ -53,6 +53,53 @@ * the ring operations the different engine classes support. */ +/** + * DOC: Multi Queue Group + * + * Multi Queue Group is another mode of execution supported by the compute + * and blitter copy command streamers (CCS and BCS, respectively). It is + * an enhancement of the existing hardware architecture and leverages the + * same submission model. It enables support for efficient, parallel + * execution of multiple queues within a single shared context. The multi + * queue group functionality is only supported with GuC submission backend. + * All the queues of a group must use the same address space (VM). + * + * The DRM_XE_EXEC_QUEUE_SET_PROPERTY_MULTI_QUEUE execution queue property + * supports creating a multi queue group and adding queues to a queue group. + * + * The XE_EXEC_QUEUE_CREATE ioctl call with above property with value field + * set to DRM_XE_MULTI_GROUP_CREATE, will create a new multi queue group with + * the queue being created as the primary queue (aka q0) of the group. To add + * secondary queues to the group, they need to be created with the above + * property with id of the primary queue as the value. The properties of + * the primary queue (like priority, time slice) applies to the whole group. + * So, these properties can't be set for secondary queues of a group. + * + * The hardware does not support removing a queue from a multi-queue group. + * However, queues can be dynamically added to the group. A group can have + * up to 64 queues. To support this, XeKMD holds references to LRCs of the + * queues even after the queues are destroyed by the user until the whole + * group is destroyed. The secondary queues hold a reference to the primary + * queue thus preventing the group from being destroyed when user destroys + * the primary queue. Once the primary queue is destroyed, secondary queues + * can't be added to the queue group and new job submissions on existing + * secondary queues are not allowed. + * + * The queues of a multi queue group can set their priority within the group + * through the DRM_XE_EXEC_QUEUE_SET_PROPERTY_MULTI_QUEUE_PRIORITY property. + * This multi queue priority can also be set dynamically through the + * XE_EXEC_QUEUE_SET_PROPERTY ioctl. This is the only other property + * supported by the secondary queues of a multi queue group, other than + * DRM_XE_EXEC_QUEUE_SET_PROPERTY_MULTI_QUEUE. + * + * When GuC reports an error on any of the queues of a multi queue group, + * the queue cleanup mechanism is invoked for all the queues of the group + * as hardware cannot make progress on the multi queue context. + * + * Refer :ref:`multi-queue-group-guc-interface` for multi queue group GuC + * interface. + */ + enum xe_exec_queue_sched_prop { XE_EXEC_QUEUE_JOB_TIMEOUT = 0, XE_EXEC_QUEUE_TIMESLICE = 1, @@ -61,7 +108,35 @@ enum xe_exec_queue_sched_prop { }; static int exec_queue_user_extensions(struct xe_device *xe, struct xe_exec_queue *q, - u64 extensions, int ext_number); + u64 extensions); + +static void xe_exec_queue_group_cleanup(struct xe_exec_queue *q) +{ + struct xe_exec_queue_group *group = q->multi_queue.group; + struct xe_lrc *lrc; + unsigned long idx; + + if (xe_exec_queue_is_multi_queue_secondary(q)) { + /* + * Put pairs with get from xe_exec_queue_lookup() call + * in xe_exec_queue_group_validate(). + */ + xe_exec_queue_put(xe_exec_queue_multi_queue_primary(q)); + return; + } + + if (!group) + return; + + /* Primary queue cleanup */ + xa_for_each(&group->xa, idx, lrc) + xe_lrc_put(lrc); + + xa_destroy(&group->xa); + mutex_destroy(&group->list_lock); + xe_bo_unpin_map_no_vm(group->cgp_bo); + kfree(group); +} static void __xe_exec_queue_free(struct xe_exec_queue *q) { @@ -73,12 +148,17 @@ static void __xe_exec_queue_free(struct xe_exec_queue *q) if (xe_exec_queue_uses_pxp(q)) xe_pxp_exec_queue_remove(gt_to_xe(q->gt)->pxp, q); + + if (xe_exec_queue_is_multi_queue(q)) + xe_exec_queue_group_cleanup(q); + if (q->vm) xe_vm_put(q->vm); if (q->xef) xe_file_put(q->xef); + kvfree(q->replay_state); kfree(q); } @@ -147,6 +227,7 @@ static struct xe_exec_queue *__xe_exec_queue_alloc(struct xe_device *xe, INIT_LIST_HEAD(&q->multi_gt_link); INIT_LIST_HEAD(&q->hw_engine_group_link); INIT_LIST_HEAD(&q->pxp.link); + q->multi_queue.priority = XE_MULTI_QUEUE_PRIORITY_NORMAL; q->sched_props.timeslice_us = hwe->eclass->sched_props.timeslice_us; q->sched_props.preempt_timeout_us = @@ -175,7 +256,7 @@ static struct xe_exec_queue *__xe_exec_queue_alloc(struct xe_device *xe, * may set q->usm, must come before xe_lrc_create(), * may overwrite q->sched_props, must come before q->ops->init() */ - err = exec_queue_user_extensions(xe, q, extensions, 0); + err = exec_queue_user_extensions(xe, q, extensions); if (err) { __xe_exec_queue_free(q); return ERR_PTR(err); @@ -225,8 +306,8 @@ static int __xe_exec_queue_init(struct xe_exec_queue *q, u32 exec_queue_flags) struct xe_lrc *lrc; xe_gt_sriov_vf_wait_valid_ggtt(q->gt); - lrc = xe_lrc_create(q->hwe, q->vm, xe_lrc_ring_size(), - q->msix_vec, flags); + lrc = xe_lrc_create(q->hwe, q->vm, q->replay_state, + xe_lrc_ring_size(), q->msix_vec, flags); if (IS_ERR(lrc)) { err = PTR_ERR(lrc); goto err_lrc; @@ -328,6 +409,7 @@ struct xe_exec_queue *xe_exec_queue_create_class(struct xe_device *xe, struct xe * @xe: Xe device. * @tile: tile which bind exec queue belongs to. * @flags: exec queue creation flags + * @user_vm: The user VM which this exec queue belongs to * @extensions: exec queue creation extensions * * Normalize bind exec queue creation. Bind exec queue is tied to migration VM @@ -341,6 +423,7 @@ struct xe_exec_queue *xe_exec_queue_create_class(struct xe_device *xe, struct xe */ struct xe_exec_queue *xe_exec_queue_create_bind(struct xe_device *xe, struct xe_tile *tile, + struct xe_vm *user_vm, u32 flags, u64 extensions) { struct xe_gt *gt = tile->primary_gt; @@ -377,6 +460,9 @@ struct xe_exec_queue *xe_exec_queue_create_bind(struct xe_device *xe, xe_exec_queue_put(q); return ERR_PTR(err); } + + if (user_vm) + q->user_vm = xe_vm_get(user_vm); } return q; @@ -407,6 +493,11 @@ void xe_exec_queue_destroy(struct kref *ref) xe_exec_queue_put(eq); } + if (q->user_vm) { + xe_vm_put(q->user_vm); + q->user_vm = NULL; + } + q->ops->destroy(q); } @@ -567,6 +658,194 @@ exec_queue_set_pxp_type(struct xe_device *xe, struct xe_exec_queue *q, u64 value return xe_pxp_exec_queue_set_type(xe->pxp, q, DRM_XE_PXP_TYPE_HWDRM); } +static int exec_queue_set_hang_replay_state(struct xe_device *xe, + struct xe_exec_queue *q, + u64 value) +{ + size_t size = xe_gt_lrc_hang_replay_size(q->gt, q->class); + u64 __user *address = u64_to_user_ptr(value); + void *ptr; + + ptr = vmemdup_user(address, size); + if (XE_IOCTL_DBG(xe, IS_ERR(ptr))) + return PTR_ERR(ptr); + + q->replay_state = ptr; + + return 0; +} + +static int xe_exec_queue_group_init(struct xe_device *xe, struct xe_exec_queue *q) +{ + struct xe_tile *tile = gt_to_tile(q->gt); + struct xe_exec_queue_group *group; + struct xe_bo *bo; + + group = kzalloc(sizeof(*group), GFP_KERNEL); + if (!group) + return -ENOMEM; + + bo = xe_bo_create_pin_map_novm(xe, tile, SZ_4K, ttm_bo_type_kernel, + XE_BO_FLAG_VRAM_IF_DGFX(tile) | + XE_BO_FLAG_PINNED_LATE_RESTORE | + XE_BO_FLAG_FORCE_USER_VRAM | + XE_BO_FLAG_GGTT_INVALIDATE | + XE_BO_FLAG_GGTT, false); + if (IS_ERR(bo)) { + drm_err(&xe->drm, "CGP bo allocation for queue group failed: %ld\n", + PTR_ERR(bo)); + kfree(group); + return PTR_ERR(bo); + } + + xe_map_memset(xe, &bo->vmap, 0, 0, SZ_4K); + + group->primary = q; + group->cgp_bo = bo; + INIT_LIST_HEAD(&group->list); + xa_init_flags(&group->xa, XA_FLAGS_ALLOC1); + mutex_init(&group->list_lock); + q->multi_queue.group = group; + + /* group->list_lock is used in submission backend */ + if (IS_ENABLED(CONFIG_LOCKDEP)) { + fs_reclaim_acquire(GFP_KERNEL); + might_lock(&group->list_lock); + fs_reclaim_release(GFP_KERNEL); + } + + return 0; +} + +static inline bool xe_exec_queue_supports_multi_queue(struct xe_exec_queue *q) +{ + return q->gt->info.multi_queue_engine_class_mask & BIT(q->class); +} + +static int xe_exec_queue_group_validate(struct xe_device *xe, struct xe_exec_queue *q, + u32 primary_id) +{ + struct xe_exec_queue_group *group; + struct xe_exec_queue *primary; + int ret; + + /* + * Get from below xe_exec_queue_lookup() pairs with put + * in xe_exec_queue_group_cleanup(). + */ + primary = xe_exec_queue_lookup(q->vm->xef, primary_id); + if (XE_IOCTL_DBG(xe, !primary)) + return -ENOENT; + + if (XE_IOCTL_DBG(xe, !xe_exec_queue_is_multi_queue_primary(primary)) || + XE_IOCTL_DBG(xe, q->vm != primary->vm) || + XE_IOCTL_DBG(xe, q->logical_mask != primary->logical_mask)) { + ret = -EINVAL; + goto put_primary; + } + + group = primary->multi_queue.group; + q->multi_queue.valid = true; + q->multi_queue.group = group; + + return 0; +put_primary: + xe_exec_queue_put(primary); + return ret; +} + +#define XE_MAX_GROUP_SIZE 64 +static int xe_exec_queue_group_add(struct xe_device *xe, struct xe_exec_queue *q) +{ + struct xe_exec_queue_group *group = q->multi_queue.group; + u32 pos; + int err; + + xe_assert(xe, xe_exec_queue_is_multi_queue_secondary(q)); + + /* Primary queue holds a reference to LRCs of all secondary queues */ + err = xa_alloc(&group->xa, &pos, xe_lrc_get(q->lrc[0]), + XA_LIMIT(1, XE_MAX_GROUP_SIZE - 1), GFP_KERNEL); + if (XE_IOCTL_DBG(xe, err)) { + xe_lrc_put(q->lrc[0]); + + /* It is invalid if queue group limit is exceeded */ + if (err == -EBUSY) + err = -EINVAL; + + return err; + } + + q->multi_queue.pos = pos; + + return 0; +} + +static void xe_exec_queue_group_delete(struct xe_device *xe, struct xe_exec_queue *q) +{ + struct xe_exec_queue_group *group = q->multi_queue.group; + struct xe_lrc *lrc; + + xe_assert(xe, xe_exec_queue_is_multi_queue_secondary(q)); + + lrc = xa_erase(&group->xa, q->multi_queue.pos); + xe_assert(xe, lrc); + xe_lrc_put(lrc); +} + +static int exec_queue_set_multi_group(struct xe_device *xe, struct xe_exec_queue *q, + u64 value) +{ + if (XE_IOCTL_DBG(xe, !xe_exec_queue_supports_multi_queue(q))) + return -ENODEV; + + if (XE_IOCTL_DBG(xe, !xe_device_uc_enabled(xe))) + return -EOPNOTSUPP; + + if (XE_IOCTL_DBG(xe, !q->vm->xef)) + return -EINVAL; + + if (XE_IOCTL_DBG(xe, xe_exec_queue_is_parallel(q))) + return -EINVAL; + + if (XE_IOCTL_DBG(xe, xe_exec_queue_is_multi_queue(q))) + return -EINVAL; + + if (value & DRM_XE_MULTI_GROUP_CREATE) { + if (XE_IOCTL_DBG(xe, value & ~DRM_XE_MULTI_GROUP_CREATE)) + return -EINVAL; + + q->multi_queue.valid = true; + q->multi_queue.is_primary = true; + q->multi_queue.pos = 0; + return 0; + } + + /* While adding secondary queues, the upper 32 bits must be 0 */ + if (XE_IOCTL_DBG(xe, value & (~0ull << 32))) + return -EINVAL; + + return xe_exec_queue_group_validate(xe, q, value); +} + +static int exec_queue_set_multi_queue_priority(struct xe_device *xe, struct xe_exec_queue *q, + u64 value) +{ + if (XE_IOCTL_DBG(xe, value > XE_MULTI_QUEUE_PRIORITY_HIGH)) + return -EINVAL; + + /* For queue creation time (!q->xef) setting, just store the priority value */ + if (!q->xef) { + q->multi_queue.priority = value; + return 0; + } + + if (!xe_exec_queue_is_multi_queue(q)) + return -EINVAL; + + return q->ops->set_multi_queue_priority(q, value); +} + typedef int (*xe_exec_queue_set_property_fn)(struct xe_device *xe, struct xe_exec_queue *q, u64 value); @@ -575,11 +854,76 @@ static const xe_exec_queue_set_property_fn exec_queue_set_property_funcs[] = { [DRM_XE_EXEC_QUEUE_SET_PROPERTY_PRIORITY] = exec_queue_set_priority, [DRM_XE_EXEC_QUEUE_SET_PROPERTY_TIMESLICE] = exec_queue_set_timeslice, [DRM_XE_EXEC_QUEUE_SET_PROPERTY_PXP_TYPE] = exec_queue_set_pxp_type, + [DRM_XE_EXEC_QUEUE_SET_HANG_REPLAY_STATE] = exec_queue_set_hang_replay_state, + [DRM_XE_EXEC_QUEUE_SET_PROPERTY_MULTI_GROUP] = exec_queue_set_multi_group, + [DRM_XE_EXEC_QUEUE_SET_PROPERTY_MULTI_QUEUE_PRIORITY] = + exec_queue_set_multi_queue_priority, }; +int xe_exec_queue_set_property_ioctl(struct drm_device *dev, void *data, + struct drm_file *file) +{ + struct xe_device *xe = to_xe_device(dev); + struct xe_file *xef = to_xe_file(file); + struct drm_xe_exec_queue_set_property *args = data; + struct xe_exec_queue *q; + int ret; + u32 idx; + + if (XE_IOCTL_DBG(xe, args->reserved[0] || args->reserved[1])) + return -EINVAL; + + if (XE_IOCTL_DBG(xe, args->property != + DRM_XE_EXEC_QUEUE_SET_PROPERTY_MULTI_QUEUE_PRIORITY)) + return -EINVAL; + + q = xe_exec_queue_lookup(xef, args->exec_queue_id); + if (XE_IOCTL_DBG(xe, !q)) + return -ENOENT; + + idx = array_index_nospec(args->property, + ARRAY_SIZE(exec_queue_set_property_funcs)); + ret = exec_queue_set_property_funcs[idx](xe, q, args->value); + if (XE_IOCTL_DBG(xe, ret)) + goto err_post_lookup; + + xe_exec_queue_put(q); + return 0; + + err_post_lookup: + xe_exec_queue_put(q); + return ret; +} + +static int exec_queue_user_ext_check(struct xe_exec_queue *q, u64 properties) +{ + u64 secondary_queue_valid_props = BIT_ULL(DRM_XE_EXEC_QUEUE_SET_PROPERTY_MULTI_GROUP) | + BIT_ULL(DRM_XE_EXEC_QUEUE_SET_PROPERTY_MULTI_QUEUE_PRIORITY); + + /* + * Only MULTI_QUEUE_PRIORITY property is valid for secondary queues of a + * multi-queue group. + */ + if (xe_exec_queue_is_multi_queue_secondary(q) && + properties & ~secondary_queue_valid_props) + return -EINVAL; + + return 0; +} + +static int exec_queue_user_ext_check_final(struct xe_exec_queue *q, u64 properties) +{ + /* MULTI_QUEUE_PRIORITY only applies to multi-queue group queues */ + if ((properties & BIT_ULL(DRM_XE_EXEC_QUEUE_SET_PROPERTY_MULTI_QUEUE_PRIORITY)) && + !(properties & BIT_ULL(DRM_XE_EXEC_QUEUE_SET_PROPERTY_MULTI_GROUP))) + return -EINVAL; + + return 0; +} + static int exec_queue_user_ext_set_property(struct xe_device *xe, struct xe_exec_queue *q, - u64 extension) + u64 extension, u64 *properties) { u64 __user *address = u64_to_user_ptr(extension); struct drm_xe_ext_set_property ext; @@ -595,27 +939,35 @@ static int exec_queue_user_ext_set_property(struct xe_device *xe, XE_IOCTL_DBG(xe, ext.pad) || XE_IOCTL_DBG(xe, ext.property != DRM_XE_EXEC_QUEUE_SET_PROPERTY_PRIORITY && ext.property != DRM_XE_EXEC_QUEUE_SET_PROPERTY_TIMESLICE && - ext.property != DRM_XE_EXEC_QUEUE_SET_PROPERTY_PXP_TYPE)) + ext.property != DRM_XE_EXEC_QUEUE_SET_PROPERTY_PXP_TYPE && + ext.property != DRM_XE_EXEC_QUEUE_SET_HANG_REPLAY_STATE && + ext.property != DRM_XE_EXEC_QUEUE_SET_PROPERTY_MULTI_GROUP && + ext.property != DRM_XE_EXEC_QUEUE_SET_PROPERTY_MULTI_QUEUE_PRIORITY)) return -EINVAL; idx = array_index_nospec(ext.property, ARRAY_SIZE(exec_queue_set_property_funcs)); if (!exec_queue_set_property_funcs[idx]) return -EINVAL; + *properties |= BIT_ULL(idx); + err = exec_queue_user_ext_check(q, *properties); + if (XE_IOCTL_DBG(xe, err)) + return err; + return exec_queue_set_property_funcs[idx](xe, q, ext.value); } typedef int (*xe_exec_queue_user_extension_fn)(struct xe_device *xe, struct xe_exec_queue *q, - u64 extension); + u64 extension, u64 *properties); static const xe_exec_queue_user_extension_fn exec_queue_user_extension_funcs[] = { [DRM_XE_EXEC_QUEUE_EXTENSION_SET_PROPERTY] = exec_queue_user_ext_set_property, }; #define MAX_USER_EXTENSIONS 16 -static int exec_queue_user_extensions(struct xe_device *xe, struct xe_exec_queue *q, - u64 extensions, int ext_number) +static int __exec_queue_user_extensions(struct xe_device *xe, struct xe_exec_queue *q, + u64 extensions, int ext_number, u64 *properties) { u64 __user *address = u64_to_user_ptr(extensions); struct drm_xe_user_extension ext; @@ -636,13 +988,36 @@ static int exec_queue_user_extensions(struct xe_device *xe, struct xe_exec_queue idx = array_index_nospec(ext.name, ARRAY_SIZE(exec_queue_user_extension_funcs)); - err = exec_queue_user_extension_funcs[idx](xe, q, extensions); + err = exec_queue_user_extension_funcs[idx](xe, q, extensions, properties); if (XE_IOCTL_DBG(xe, err)) return err; if (ext.next_extension) - return exec_queue_user_extensions(xe, q, ext.next_extension, - ++ext_number); + return __exec_queue_user_extensions(xe, q, ext.next_extension, + ++ext_number, properties); + + return 0; +} + +static int exec_queue_user_extensions(struct xe_device *xe, struct xe_exec_queue *q, + u64 extensions) +{ + u64 properties = 0; + int err; + + err = __exec_queue_user_extensions(xe, q, extensions, 0, &properties); + if (XE_IOCTL_DBG(xe, err)) + return err; + + err = exec_queue_user_ext_check_final(q, properties); + if (XE_IOCTL_DBG(xe, err)) + return err; + + if (xe_exec_queue_is_multi_queue_primary(q)) { + err = xe_exec_queue_group_init(xe, q); + if (XE_IOCTL_DBG(xe, err)) + return err; + } return 0; } @@ -698,6 +1073,17 @@ static u32 calc_validate_logical_mask(struct xe_device *xe, return return_mask; } +static bool has_sched_groups(struct xe_gt *gt) +{ + if (IS_SRIOV_PF(gt_to_xe(gt)) && xe_gt_sriov_pf_sched_groups_enabled(gt)) + return true; + + if (IS_SRIOV_VF(gt_to_xe(gt)) && xe_gt_sriov_vf_sched_groups_enabled(gt)) + return true; + + return false; +} + int xe_exec_queue_create_ioctl(struct drm_device *dev, void *data, struct drm_file *file) { @@ -742,6 +1128,22 @@ int xe_exec_queue_create_ioctl(struct drm_device *dev, void *data, XE_IOCTL_DBG(xe, eci[0].engine_instance != 0)) return -EINVAL; + vm = xe_vm_lookup(xef, args->vm_id); + if (XE_IOCTL_DBG(xe, !vm)) + return -ENOENT; + + err = down_read_interruptible(&vm->lock); + if (err) { + xe_vm_put(vm); + return err; + } + + if (XE_IOCTL_DBG(xe, xe_vm_is_closed_or_banned(vm))) { + up_read(&vm->lock); + xe_vm_put(vm); + return -ENOENT; + } + for_each_tile(tile, xe, id) { struct xe_exec_queue *new; @@ -749,9 +1151,11 @@ int xe_exec_queue_create_ioctl(struct drm_device *dev, void *data, if (id) flags |= EXEC_QUEUE_FLAG_BIND_ENGINE_CHILD; - new = xe_exec_queue_create_bind(xe, tile, flags, + new = xe_exec_queue_create_bind(xe, tile, vm, flags, args->extensions); if (IS_ERR(new)) { + up_read(&vm->lock); + xe_vm_put(vm); err = PTR_ERR(new); if (q) goto put_exec_queue; @@ -763,6 +1167,8 @@ int xe_exec_queue_create_ioctl(struct drm_device *dev, void *data, list_add_tail(&new->multi_gt_list, &q->multi_gt_link); } + up_read(&vm->lock); + xe_vm_put(vm); } else { logical_mask = calc_validate_logical_mask(xe, eci, args->width, @@ -790,6 +1196,13 @@ int xe_exec_queue_create_ioctl(struct drm_device *dev, void *data, return -ENOENT; } + /* SRIOV sched groups are not compatible with multi-lrc */ + if (XE_IOCTL_DBG(xe, args->width > 1 && has_sched_groups(hwe->gt))) { + up_read(&vm->lock); + xe_vm_put(vm); + return -EINVAL; + } + q = xe_exec_queue_create(xe, vm, logical_mask, args->width, hwe, flags, args->extensions); @@ -798,12 +1211,18 @@ int xe_exec_queue_create_ioctl(struct drm_device *dev, void *data, if (IS_ERR(q)) return PTR_ERR(q); + if (xe_exec_queue_is_multi_queue_secondary(q)) { + err = xe_exec_queue_group_add(xe, q); + if (XE_IOCTL_DBG(xe, err)) + goto put_exec_queue; + } + if (xe_vm_in_preempt_fence_mode(vm)) { q->lr.context = dma_fence_context_alloc(1); err = xe_vm_add_compute_exec_queue(vm, q); if (XE_IOCTL_DBG(xe, err)) - goto put_exec_queue; + goto delete_queue_group; } if (q->vm && q->hwe->hw_engine_group) { @@ -826,6 +1245,9 @@ int xe_exec_queue_create_ioctl(struct drm_device *dev, void *data, kill_exec_queue: xe_exec_queue_kill(q); +delete_queue_group: + if (xe_exec_queue_is_multi_queue_secondary(q)) + xe_exec_queue_group_delete(xe, q); put_exec_queue: xe_exec_queue_put(q); return err; diff --git a/drivers/gpu/drm/xe/xe_exec_queue.h b/drivers/gpu/drm/xe/xe_exec_queue.h index fda4d4f9bda8..c9e3a7c2d249 100644 --- a/drivers/gpu/drm/xe/xe_exec_queue.h +++ b/drivers/gpu/drm/xe/xe_exec_queue.h @@ -28,6 +28,7 @@ struct xe_exec_queue *xe_exec_queue_create_class(struct xe_device *xe, struct xe u32 flags, u64 extensions); struct xe_exec_queue *xe_exec_queue_create_bind(struct xe_device *xe, struct xe_tile *tile, + struct xe_vm *user_vm, u32 flags, u64 extensions); void xe_exec_queue_fini(struct xe_exec_queue *q); @@ -66,6 +67,53 @@ static inline bool xe_exec_queue_uses_pxp(struct xe_exec_queue *q) return q->pxp.type; } +/** + * xe_exec_queue_is_multi_queue() - Whether an exec_queue is part of a queue group. + * @q: The exec_queue + * + * Return: True if the exec_queue is part of a queue group, false otherwise. + */ +static inline bool xe_exec_queue_is_multi_queue(struct xe_exec_queue *q) +{ + return q->multi_queue.valid; +} + +/** + * xe_exec_queue_is_multi_queue_primary() - Whether an exec_queue is primary queue + * of a multi queue group. + * @q: The exec_queue + * + * Return: True if @q is primary queue of a queue group, false otherwise. + */ +static inline bool xe_exec_queue_is_multi_queue_primary(struct xe_exec_queue *q) +{ + return q->multi_queue.is_primary; +} + +/** + * xe_exec_queue_is_multi_queue_secondary() - Whether an exec_queue is secondary queue + * of a multi queue group. + * @q: The exec_queue + * + * Return: True if @q is secondary queue of a queue group, false otherwise. + */ +static inline bool xe_exec_queue_is_multi_queue_secondary(struct xe_exec_queue *q) +{ + return xe_exec_queue_is_multi_queue(q) && !xe_exec_queue_is_multi_queue_primary(q); +} + +/** + * xe_exec_queue_multi_queue_primary() - Get multi queue group's primary queue + * @q: The exec_queue + * + * If @q belongs to a multi queue group, then the primary queue of the group will + * be returned. Otherwise, @q will be returned. + */ +static inline struct xe_exec_queue *xe_exec_queue_multi_queue_primary(struct xe_exec_queue *q) +{ + return xe_exec_queue_is_multi_queue(q) ? q->multi_queue.group->primary : q; +} + bool xe_exec_queue_is_lr(struct xe_exec_queue *q); bool xe_exec_queue_is_idle(struct xe_exec_queue *q); @@ -78,6 +126,8 @@ int xe_exec_queue_destroy_ioctl(struct drm_device *dev, void *data, struct drm_file *file); int xe_exec_queue_get_property_ioctl(struct drm_device *dev, void *data, struct drm_file *file); +int xe_exec_queue_set_property_ioctl(struct drm_device *dev, void *data, + struct drm_file *file); enum xe_exec_queue_priority xe_exec_queue_device_get_max_priority(struct xe_device *xe); void xe_exec_queue_last_fence_put(struct xe_exec_queue *e, struct xe_vm *vm); @@ -111,4 +161,21 @@ int xe_exec_queue_contexts_hwsp_rebase(struct xe_exec_queue *q, void *scratch); struct xe_lrc *xe_exec_queue_lrc(struct xe_exec_queue *q); +/** + * xe_exec_queue_idle_skip_suspend() - Can exec queue skip suspend + * @q: The exec_queue + * + * If an exec queue is not parallel and is idle, the suspend steps can be + * skipped in the submission backend immediatley signaling the suspend fence. + * Parallel queues cannot skip this step due to limitations in the submission + * backend. + * + * Return: True if exec queue is idle and can skip suspend steps, False + * otherwise + */ +static inline bool xe_exec_queue_idle_skip_suspend(struct xe_exec_queue *q) +{ + return !xe_exec_queue_is_parallel(q) && xe_exec_queue_is_idle(q); +} + #endif diff --git a/drivers/gpu/drm/xe/xe_exec_queue_types.h b/drivers/gpu/drm/xe/xe_exec_queue_types.h index 771ffe35cd0c..f1142e5d3e3e 100644 --- a/drivers/gpu/drm/xe/xe_exec_queue_types.h +++ b/drivers/gpu/drm/xe/xe_exec_queue_types.h @@ -33,6 +33,42 @@ enum xe_exec_queue_priority { }; /** + * enum xe_multi_queue_priority - Multi Queue priority values + * + * The priority values of the queues within the multi queue group. + */ +enum xe_multi_queue_priority { + /** @XE_MULTI_QUEUE_PRIORITY_LOW: Priority low */ + XE_MULTI_QUEUE_PRIORITY_LOW = 0, + /** @XE_MULTI_QUEUE_PRIORITY_NORMAL: Priority normal */ + XE_MULTI_QUEUE_PRIORITY_NORMAL, + /** @XE_MULTI_QUEUE_PRIORITY_HIGH: Priority high */ + XE_MULTI_QUEUE_PRIORITY_HIGH, +}; + +/** + * struct xe_exec_queue_group - Execution multi queue group + * + * Contains multi queue group information. + */ +struct xe_exec_queue_group { + /** @primary: Primary queue of this group */ + struct xe_exec_queue *primary; + /** @cgp_bo: BO for the Context Group Page */ + struct xe_bo *cgp_bo; + /** @xa: xarray to store LRCs */ + struct xarray xa; + /** @list: List of all secondary queues in the group */ + struct list_head list; + /** @list_lock: Secondary queue list lock */ + struct mutex list_lock; + /** @sync_pending: CGP_SYNC_DONE g2h response pending */ + bool sync_pending; + /** @banned: Group banned */ + bool banned; +}; + +/** * struct xe_exec_queue - Execution queue * * Contains all state necessary for submissions. Can either be a user object or @@ -54,6 +90,12 @@ struct xe_exec_queue { struct kref refcount; /** @vm: VM (address space) for this exec queue */ struct xe_vm *vm; + /** + * @user_vm: User VM (address space) for this exec queue (bind queues + * only) + */ + struct xe_vm *user_vm; + /** @class: class of this exec queue */ enum xe_engine_class class; /** @@ -111,6 +153,22 @@ struct xe_exec_queue { struct xe_guc_exec_queue *guc; }; + /** @multi_queue: Multi queue information */ + struct { + /** @multi_queue.group: Queue group information */ + struct xe_exec_queue_group *group; + /** @multi_queue.link: Link into group's secondary queues list */ + struct list_head link; + /** @multi_queue.priority: Queue priority within the multi-queue group */ + enum xe_multi_queue_priority priority; + /** @multi_queue.pos: Position of queue within the multi-queue group */ + u8 pos; + /** @multi_queue.valid: Queue belongs to a multi queue group */ + u8 valid:1; + /** @multi_queue.is_primary: Is primary queue (Q0) of the group */ + u8 is_primary:1; + } multi_queue; + /** @sched_props: scheduling properties */ struct { /** @sched_props.timeslice_us: timeslice period in micro-seconds */ @@ -167,6 +225,9 @@ struct xe_exec_queue { /** @ufence_timeline_value: User fence timeline value */ u64 ufence_timeline_value; + /** @replay_state: GPU hang replay state */ + void *replay_state; + /** @ops: submission backend exec queue operations */ const struct xe_exec_queue_ops *ops; @@ -213,6 +274,9 @@ struct xe_exec_queue_ops { int (*set_timeslice)(struct xe_exec_queue *q, u32 timeslice_us); /** @set_preempt_timeout: Set preemption timeout for exec queue */ int (*set_preempt_timeout)(struct xe_exec_queue *q, u32 preempt_timeout_us); + /** @set_multi_queue_priority: Set multi queue priority */ + int (*set_multi_queue_priority)(struct xe_exec_queue *q, + enum xe_multi_queue_priority priority); /** * @suspend: Suspend exec queue from executing, allowed to be called * multiple times in a row before resume with the caveat that diff --git a/drivers/gpu/drm/xe/xe_execlist.c b/drivers/gpu/drm/xe/xe_execlist.c index 769d05517f93..8bf330aeaec0 100644 --- a/drivers/gpu/drm/xe/xe_execlist.c +++ b/drivers/gpu/drm/xe/xe_execlist.c @@ -15,8 +15,7 @@ #include "xe_bo.h" #include "xe_device.h" #include "xe_exec_queue.h" -#include "xe_gt.h" -#include "xe_hw_fence.h" +#include "xe_gt_types.h" #include "xe_irq.h" #include "xe_lrc.h" #include "xe_macros.h" @@ -269,7 +268,7 @@ struct xe_execlist_port *xe_execlist_port_create(struct xe_device *xe, port->hwe = hwe; - port->lrc = xe_lrc_create(hwe, NULL, SZ_16K, XE_IRQ_DEFAULT_MSIX, 0); + port->lrc = xe_lrc_create(hwe, NULL, NULL, SZ_16K, XE_IRQ_DEFAULT_MSIX, 0); if (IS_ERR(port->lrc)) { err = PTR_ERR(port->lrc); goto err; diff --git a/drivers/gpu/drm/xe/xe_force_wake.c b/drivers/gpu/drm/xe/xe_force_wake.c index c59a9b330697..76e054f314ee 100644 --- a/drivers/gpu/drm/xe/xe_force_wake.c +++ b/drivers/gpu/drm/xe/xe_force_wake.c @@ -166,6 +166,13 @@ static int domain_sleep_wait(struct xe_gt *gt, * xe_force_wake_ref_has_domain() function. Caller must call * xe_force_wake_put() function to decrease incremented refcounts. * + * When possible, scope-based forcewake (through CLASS(xe_force_wake, ...) or + * xe_with_force_wake()) should be used instead of direct calls to this + * function. Direct usage of get/put should only be used when the function + * has goto-based flows that can interfere with scope-based cleanup, or when + * the lifetime of the forcewake reference does not match a specific scope + * (e.g., forcewake obtained in one function and released in a different one). + * * Return: opaque reference to woken domains or zero if none of requested * domains were awake. */ diff --git a/drivers/gpu/drm/xe/xe_force_wake.h b/drivers/gpu/drm/xe/xe_force_wake.h index 0e3e84bfa51c..1e2198f6a007 100644 --- a/drivers/gpu/drm/xe/xe_force_wake.h +++ b/drivers/gpu/drm/xe/xe_force_wake.h @@ -61,4 +61,44 @@ xe_force_wake_ref_has_domain(unsigned int fw_ref, enum xe_force_wake_domains dom return fw_ref & domain; } +struct xe_force_wake_ref { + struct xe_force_wake *fw; + unsigned int domains; +}; + +static struct xe_force_wake_ref +xe_force_wake_constructor(struct xe_force_wake *fw, unsigned int domains) +{ + struct xe_force_wake_ref fw_ref = { .fw = fw }; + + fw_ref.domains = xe_force_wake_get(fw, domains); + + return fw_ref; +} + +DEFINE_CLASS(xe_force_wake, struct xe_force_wake_ref, + xe_force_wake_put(_T.fw, _T.domains), + xe_force_wake_constructor(fw, domains), + struct xe_force_wake *fw, unsigned int domains); + +/* + * Scoped helper for the forcewake class, using the same trick as scoped_guard() + * to bind the lifetime to the next statement/block. + */ +#define __xe_with_force_wake(ref, fw, domains, done) \ + for (CLASS(xe_force_wake, ref)(fw, domains), *(done) = NULL; \ + !(done); (done) = (void *)1) + +#define xe_with_force_wake(ref, fw, domains) \ + __xe_with_force_wake(ref, fw, domains, __UNIQUE_ID(done)) + +/* + * Used when xe_force_wake_constructor() has already been called by another + * function and the current function is responsible for releasing the forcewake + * reference in all possible cases and error paths. + */ +DEFINE_CLASS(xe_force_wake_release_only, struct xe_force_wake_ref, + if (_T.fw) xe_force_wake_put(_T.fw, _T.domains), fw_ref, + struct xe_force_wake_ref fw_ref); + #endif diff --git a/drivers/gpu/drm/xe/xe_gen_wa_oob.c b/drivers/gpu/drm/xe/xe_gen_wa_oob.c index 247e41c1c48d..e7a50b1348b7 100644 --- a/drivers/gpu/drm/xe/xe_gen_wa_oob.c +++ b/drivers/gpu/drm/xe/xe_gen_wa_oob.c @@ -195,7 +195,8 @@ int main(int argc, const char *argv[]) } } - fprintf(args[ARGS_CHEADER].f, HEADER, args[ARGS_INPUT].fn, prefix, prefix); + fprintf(args[ARGS_CHEADER].f, HEADER, xbasename(args[ARGS_INPUT].fn), + prefix, prefix); ret = parse(args[ARGS_INPUT].f, args[ARGS_CSOURCE].f, args[ARGS_CHEADER].f, prefix); diff --git a/drivers/gpu/drm/xe/xe_ggtt.c b/drivers/gpu/drm/xe/xe_ggtt.c index ef481b334af4..60665ad1415b 100644 --- a/drivers/gpu/drm/xe/xe_ggtt.c +++ b/drivers/gpu/drm/xe/xe_ggtt.c @@ -20,9 +20,8 @@ #include "regs/xe_regs.h" #include "xe_assert.h" #include "xe_bo.h" -#include "xe_device.h" -#include "xe_gt.h" #include "xe_gt_printk.h" +#include "xe_gt_types.h" #include "xe_map.h" #include "xe_mmio.h" #include "xe_pm.h" @@ -67,6 +66,24 @@ * give us the correct placement for free. */ +/** + * struct xe_ggtt_node - A node in GGTT. + * + * This struct needs to be initialized (only-once) with xe_ggtt_node_init() before any node + * insertion, reservation, or 'ballooning'. + * It will, then, be finalized by either xe_ggtt_node_remove() or xe_ggtt_node_deballoon(). + */ +struct xe_ggtt_node { + /** @ggtt: Back pointer to xe_ggtt where this region will be inserted at */ + struct xe_ggtt *ggtt; + /** @base: A drm_mm_node */ + struct drm_mm_node base; + /** @delayed_removal_work: The work struct for the delayed removal */ + struct work_struct delayed_removal_work; + /** @invalidate_on_remove: If it needs invalidation upon removal */ + bool invalidate_on_remove; +}; + static u64 xelp_ggtt_pte_flags(struct xe_bo *bo, u16 pat_index) { u64 pte = XE_PAGE_PRESENT; @@ -137,10 +154,32 @@ static void ggtt_update_access_counter(struct xe_ggtt *ggtt) } } +/** + * xe_ggtt_start - Get starting offset of GGTT. + * @ggtt: &xe_ggtt + * + * Returns: Starting offset for this &xe_ggtt. + */ +u64 xe_ggtt_start(struct xe_ggtt *ggtt) +{ + return ggtt->start; +} + +/** + * xe_ggtt_size - Get size of GGTT. + * @ggtt: &xe_ggtt + * + * Returns: Total usable size of this &xe_ggtt. + */ +u64 xe_ggtt_size(struct xe_ggtt *ggtt) +{ + return ggtt->size; +} + static void xe_ggtt_set_pte(struct xe_ggtt *ggtt, u64 addr, u64 pte) { xe_tile_assert(ggtt->tile, !(addr & XE_PTE_MASK)); - xe_tile_assert(ggtt->tile, addr < ggtt->size); + xe_tile_assert(ggtt->tile, addr < ggtt->start + ggtt->size); writeq(pte, &ggtt->gsm[addr >> XE_PTE_SHIFT]); } @@ -256,16 +295,16 @@ static const struct xe_ggtt_pt_ops xelpg_pt_wa_ops = { .ggtt_get_pte = xe_ggtt_get_pte, }; -static void __xe_ggtt_init_early(struct xe_ggtt *ggtt, u32 reserved) +static void __xe_ggtt_init_early(struct xe_ggtt *ggtt, u64 start, u64 size) { - drm_mm_init(&ggtt->mm, reserved, - ggtt->size - reserved); + ggtt->start = start; + ggtt->size = size; + drm_mm_init(&ggtt->mm, start, size); } -int xe_ggtt_init_kunit(struct xe_ggtt *ggtt, u32 reserved, u32 size) +int xe_ggtt_init_kunit(struct xe_ggtt *ggtt, u32 start, u32 size) { - ggtt->size = size; - __xe_ggtt_init_early(ggtt, reserved); + __xe_ggtt_init_early(ggtt, start, size); return 0; } EXPORT_SYMBOL_IF_KUNIT(xe_ggtt_init_kunit); @@ -293,26 +332,32 @@ int xe_ggtt_init_early(struct xe_ggtt *ggtt) struct xe_device *xe = tile_to_xe(ggtt->tile); struct pci_dev *pdev = to_pci_dev(xe->drm.dev); unsigned int gsm_size; + u64 ggtt_start, wopcm = xe_wopcm_size(xe), ggtt_size; int err; - if (IS_SRIOV_VF(xe) || GRAPHICS_VERx100(xe) >= 1250) - gsm_size = SZ_8M; /* GGTT is expected to be 4GiB */ - else - gsm_size = probe_gsm_size(pdev); - - if (gsm_size == 0) { - xe_tile_err(ggtt->tile, "Hardware reported no preallocated GSM\n"); - return -ENOMEM; + if (!IS_SRIOV_VF(xe)) { + if (GRAPHICS_VERx100(xe) >= 1250) + gsm_size = SZ_8M; /* GGTT is expected to be 4GiB */ + else + gsm_size = probe_gsm_size(pdev); + if (gsm_size == 0) { + xe_tile_err(ggtt->tile, "Hardware reported no preallocated GSM\n"); + return -ENOMEM; + } + ggtt_start = wopcm; + ggtt_size = (gsm_size / 8) * (u64)XE_PAGE_SIZE - ggtt_start; + } else { + /* GGTT is expected to be 4GiB */ + ggtt_start = wopcm; + ggtt_size = SZ_4G - ggtt_start; } ggtt->gsm = ggtt->tile->mmio.regs + SZ_8M; - ggtt->size = (gsm_size / 8) * (u64) XE_PAGE_SIZE; - if (IS_DGFX(xe) && xe->info.vram_flags & XE_VRAM_FLAGS_NEED64K) ggtt->flags |= XE_GGTT_FLAGS_64K; - if (ggtt->size > GUC_GGTT_TOP) - ggtt->size = GUC_GGTT_TOP; + if (ggtt_size + ggtt_start > GUC_GGTT_TOP) + ggtt_size = GUC_GGTT_TOP - ggtt_start; if (GRAPHICS_VERx100(xe) >= 1270) ggtt->pt_ops = @@ -322,11 +367,11 @@ int xe_ggtt_init_early(struct xe_ggtt *ggtt) else ggtt->pt_ops = &xelp_pt_ops; - ggtt->wq = alloc_workqueue("xe-ggtt-wq", 0, WQ_MEM_RECLAIM); + ggtt->wq = alloc_workqueue("xe-ggtt-wq", WQ_MEM_RECLAIM, 0); if (!ggtt->wq) return -ENOMEM; - __xe_ggtt_init_early(ggtt, xe_wopcm_size(xe)); + __xe_ggtt_init_early(ggtt, ggtt_start, ggtt_size); err = drmm_add_action_or_reset(&xe->drm, ggtt_fini_early, ggtt); if (err) @@ -396,9 +441,8 @@ static void ggtt_node_remove_work_func(struct work_struct *work) delayed_removal_work); struct xe_device *xe = tile_to_xe(node->ggtt->tile); - xe_pm_runtime_get(xe); + guard(xe_pm_runtime)(xe); ggtt_node_remove(node); - xe_pm_runtime_put(xe); } /** @@ -564,11 +608,9 @@ void xe_ggtt_node_remove_balloon_locked(struct xe_ggtt_node *node) static void xe_ggtt_assert_fit(struct xe_ggtt *ggtt, u64 start, u64 size) { struct xe_tile *tile = ggtt->tile; - struct xe_device *xe = tile_to_xe(tile); - u64 __maybe_unused wopcm = xe_wopcm_size(xe); - xe_tile_assert(tile, start >= wopcm); - xe_tile_assert(tile, start + size < ggtt->size - wopcm); + xe_tile_assert(tile, start >= ggtt->start); + xe_tile_assert(tile, start + size <= ggtt->start + ggtt->size); } /** @@ -611,20 +653,8 @@ void xe_ggtt_shift_nodes_locked(struct xe_ggtt *ggtt, s64 shift) } } -/** - * xe_ggtt_node_insert_locked - Locked version to insert a &xe_ggtt_node into the GGTT - * @node: the &xe_ggtt_node to be inserted - * @size: size of the node - * @align: alignment constrain of the node - * @mm_flags: flags to control the node behavior - * - * It cannot be called without first having called xe_ggtt_init() once. - * To be used in cases where ggtt->lock is already taken. - * - * Return: 0 on success or a negative error code on failure. - */ -int xe_ggtt_node_insert_locked(struct xe_ggtt_node *node, - u32 size, u32 align, u32 mm_flags) +static int xe_ggtt_node_insert_locked(struct xe_ggtt_node *node, + u32 size, u32 align, u32 mm_flags) { return drm_mm_insert_node_generic(&node->ggtt->mm, &node->base, size, align, 0, mm_flags); @@ -662,9 +692,11 @@ int xe_ggtt_node_insert(struct xe_ggtt_node *node, u32 size, u32 align) * This function will allocate the struct %xe_ggtt_node and return its pointer. * This struct will then be freed after the node removal upon xe_ggtt_node_remove() * or xe_ggtt_node_remove_balloon_locked(). - * Having %xe_ggtt_node struct allocated doesn't mean that the node is already allocated - * in GGTT. Only the xe_ggtt_node_insert(), xe_ggtt_node_insert_locked(), - * xe_ggtt_node_insert_balloon_locked() will ensure the node is inserted or reserved in GGTT. + * + * Having %xe_ggtt_node struct allocated doesn't mean that the node is already + * allocated in GGTT. Only xe_ggtt_node_insert(), allocation through + * xe_ggtt_node_insert_transform(), or xe_ggtt_node_insert_balloon_locked() will ensure the node is inserted or reserved + * in GGTT. * * Return: A pointer to %xe_ggtt_node struct on success. An ERR_PTR otherwise. **/ @@ -727,13 +759,12 @@ size_t xe_ggtt_node_pt_size(const struct xe_ggtt_node *node) * @ggtt: the &xe_ggtt where node will be mapped * @node: the &xe_ggtt_node where this BO is mapped * @bo: the &xe_bo to be mapped - * @pat_index: Which pat_index to use. + * @pte: The pte flags to append. */ -void xe_ggtt_map_bo(struct xe_ggtt *ggtt, struct xe_ggtt_node *node, - struct xe_bo *bo, u16 pat_index) +static void xe_ggtt_map_bo(struct xe_ggtt *ggtt, struct xe_ggtt_node *node, + struct xe_bo *bo, u64 pte) { - - u64 start, pte, end; + u64 start, end; struct xe_res_cursor cur; if (XE_WARN_ON(!node)) @@ -742,7 +773,6 @@ void xe_ggtt_map_bo(struct xe_ggtt *ggtt, struct xe_ggtt_node *node, start = node->base.start; end = start + xe_bo_size(bo); - pte = ggtt->pt_ops->pte_encode_flags(bo, pat_index); if (!xe_bo_is_vram(bo) && !xe_bo_is_stolen(bo)) { xe_assert(xe_bo_device(bo), bo->ttm.ttm); @@ -772,12 +802,65 @@ void xe_ggtt_map_bo_unlocked(struct xe_ggtt *ggtt, struct xe_bo *bo) { u16 cache_mode = bo->flags & XE_BO_FLAG_NEEDS_UC ? XE_CACHE_NONE : XE_CACHE_WB; u16 pat_index = tile_to_xe(ggtt->tile)->pat.idx[cache_mode]; + u64 pte; mutex_lock(&ggtt->lock); - xe_ggtt_map_bo(ggtt, bo->ggtt_node[ggtt->tile->id], bo, pat_index); + pte = ggtt->pt_ops->pte_encode_flags(bo, pat_index); + xe_ggtt_map_bo(ggtt, bo->ggtt_node[ggtt->tile->id], bo, pte); mutex_unlock(&ggtt->lock); } +/** + * xe_ggtt_node_insert_transform - Insert a newly allocated &xe_ggtt_node into the GGTT + * @ggtt: the &xe_ggtt where the node will inserted/reserved. + * @bo: The bo to be transformed + * @pte_flags: The extra GGTT flags to add to mapping. + * @size: size of the node + * @align: required alignment for node + * @transform: transformation function that will populate the GGTT node, or NULL for linear mapping. + * @arg: Extra argument to pass to the transformation function. + * + * This function allows inserting a GGTT node with a custom transformation function. + * This is useful for display to allow inserting rotated framebuffers to GGTT. + * + * Return: A pointer to %xe_ggtt_node struct on success. An ERR_PTR otherwise. + */ +struct xe_ggtt_node *xe_ggtt_node_insert_transform(struct xe_ggtt *ggtt, + struct xe_bo *bo, u64 pte_flags, + u64 size, u32 align, + xe_ggtt_transform_cb transform, void *arg) +{ + struct xe_ggtt_node *node; + int ret; + + node = xe_ggtt_node_init(ggtt); + if (IS_ERR(node)) + return ERR_CAST(node); + + if (mutex_lock_interruptible(&ggtt->lock) < 0) { + ret = -ERESTARTSYS; + goto err; + } + + ret = xe_ggtt_node_insert_locked(node, size, align, 0); + if (ret) + goto err_unlock; + + if (transform) + transform(ggtt, node, pte_flags, ggtt->pt_ops->ggtt_set_pte, arg); + else + xe_ggtt_map_bo(ggtt, node, bo, pte_flags); + + mutex_unlock(&ggtt->lock); + return node; + +err_unlock: + mutex_unlock(&ggtt->lock); +err: + xe_ggtt_node_fini(node); + return ERR_PTR(ret); +} + static int __xe_ggtt_insert_bo_at(struct xe_ggtt *ggtt, struct xe_bo *bo, u64 start, u64 end, struct drm_exec *exec) { @@ -816,8 +899,9 @@ static int __xe_ggtt_insert_bo_at(struct xe_ggtt *ggtt, struct xe_bo *bo, } else { u16 cache_mode = bo->flags & XE_BO_FLAG_NEEDS_UC ? XE_CACHE_NONE : XE_CACHE_WB; u16 pat_index = tile_to_xe(ggtt->tile)->pat.idx[cache_mode]; + u64 pte = ggtt->pt_ops->pte_encode_flags(bo, pat_index); - xe_ggtt_map_bo(ggtt, bo->ggtt_node[tile_id], bo, pat_index); + xe_ggtt_map_bo(ggtt, bo->ggtt_node[tile_id], bo, pte); } mutex_unlock(&ggtt->lock); @@ -891,14 +975,12 @@ u64 xe_ggtt_largest_hole(struct xe_ggtt *ggtt, u64 alignment, u64 *spare) { const struct drm_mm *mm = &ggtt->mm; const struct drm_mm_node *entry; - u64 hole_min_start = xe_wopcm_size(tile_to_xe(ggtt->tile)); u64 hole_start, hole_end, hole_size; u64 max_hole = 0; mutex_lock(&ggtt->lock); - drm_mm_for_each_hole(entry, mm, hole_start, hole_end) { - hole_start = max(hole_start, hole_min_start); + hole_start = max(hole_start, ggtt->start); hole_start = ALIGN(hole_start, alignment); hole_end = ALIGN_DOWN(hole_end, alignment); if (hole_start >= hole_end) @@ -1070,15 +1152,13 @@ u64 xe_ggtt_print_holes(struct xe_ggtt *ggtt, u64 alignment, struct drm_printer { const struct drm_mm *mm = &ggtt->mm; const struct drm_mm_node *entry; - u64 hole_min_start = xe_wopcm_size(tile_to_xe(ggtt->tile)); u64 hole_start, hole_end, hole_size; u64 total = 0; char buf[10]; mutex_lock(&ggtt->lock); - drm_mm_for_each_hole(entry, mm, hole_start, hole_end) { - hole_start = max(hole_start, hole_min_start); + hole_start = max(hole_start, ggtt->start); hole_start = ALIGN(hole_start, alignment); hole_end = ALIGN_DOWN(hole_end, alignment); if (hole_start >= hole_end) @@ -1122,3 +1202,25 @@ u64 xe_ggtt_read_pte(struct xe_ggtt *ggtt, u64 offset) { return ioread64(ggtt->gsm + (offset / XE_PAGE_SIZE)); } + +/** + * xe_ggtt_node_addr - Get @node offset in GGTT. + * @node: &xe_ggtt_node + * + * Get the GGTT offset for allocated node. + */ +u64 xe_ggtt_node_addr(const struct xe_ggtt_node *node) +{ + return node->base.start; +} + +/** + * xe_ggtt_node_size - Get @node allocation size. + * @node: &xe_ggtt_node + * + * Get the allocated node's size. + */ +u64 xe_ggtt_node_size(const struct xe_ggtt_node *node) +{ + return node->base.size; +} diff --git a/drivers/gpu/drm/xe/xe_ggtt.h b/drivers/gpu/drm/xe/xe_ggtt.h index 93fea4b6079c..70d5e07ac4b6 100644 --- a/drivers/gpu/drm/xe/xe_ggtt.h +++ b/drivers/gpu/drm/xe/xe_ggtt.h @@ -23,15 +23,18 @@ int xe_ggtt_node_insert_balloon_locked(struct xe_ggtt_node *node, u64 start, u64 size); void xe_ggtt_node_remove_balloon_locked(struct xe_ggtt_node *node); void xe_ggtt_shift_nodes_locked(struct xe_ggtt *ggtt, s64 shift); +u64 xe_ggtt_start(struct xe_ggtt *ggtt); +u64 xe_ggtt_size(struct xe_ggtt *ggtt); int xe_ggtt_node_insert(struct xe_ggtt_node *node, u32 size, u32 align); -int xe_ggtt_node_insert_locked(struct xe_ggtt_node *node, - u32 size, u32 align, u32 mm_flags); +struct xe_ggtt_node * +xe_ggtt_node_insert_transform(struct xe_ggtt *ggtt, + struct xe_bo *bo, u64 pte, + u64 size, u32 align, + xe_ggtt_transform_cb transform, void *arg); void xe_ggtt_node_remove(struct xe_ggtt_node *node, bool invalidate); bool xe_ggtt_node_allocated(const struct xe_ggtt_node *node); size_t xe_ggtt_node_pt_size(const struct xe_ggtt_node *node); -void xe_ggtt_map_bo(struct xe_ggtt *ggtt, struct xe_ggtt_node *node, - struct xe_bo *bo, u16 pat_index); void xe_ggtt_map_bo_unlocked(struct xe_ggtt *ggtt, struct xe_bo *bo); int xe_ggtt_insert_bo(struct xe_ggtt *ggtt, struct xe_bo *bo, struct drm_exec *exec); int xe_ggtt_insert_bo_at(struct xe_ggtt *ggtt, struct xe_bo *bo, @@ -58,4 +61,7 @@ void xe_ggtt_might_lock(struct xe_ggtt *ggtt); u64 xe_ggtt_encode_pte_flags(struct xe_ggtt *ggtt, struct xe_bo *bo, u16 pat_index); u64 xe_ggtt_read_pte(struct xe_ggtt *ggtt, u64 offset); +u64 xe_ggtt_node_addr(const struct xe_ggtt_node *node); +u64 xe_ggtt_node_size(const struct xe_ggtt_node *node); + #endif diff --git a/drivers/gpu/drm/xe/xe_ggtt_types.h b/drivers/gpu/drm/xe/xe_ggtt_types.h index dacd796f8184..d82b71a198bc 100644 --- a/drivers/gpu/drm/xe/xe_ggtt_types.h +++ b/drivers/gpu/drm/xe/xe_ggtt_types.h @@ -11,6 +11,7 @@ #include "xe_pt_types.h" struct xe_bo; +struct xe_ggtt_node; struct xe_gt; /** @@ -22,7 +23,9 @@ struct xe_gt; struct xe_ggtt { /** @tile: Back pointer to tile where this GGTT belongs */ struct xe_tile *tile; - /** @size: Total size of this GGTT */ + /** @start: Start offset of GGTT */ + u64 start; + /** @size: Total usable size of this GGTT */ u64 size; #define XE_GGTT_FLAGS_64K BIT(0) @@ -51,24 +54,11 @@ struct xe_ggtt { struct workqueue_struct *wq; }; -/** - * struct xe_ggtt_node - A node in GGTT. - * - * This struct needs to be initialized (only-once) with xe_ggtt_node_init() before any node - * insertion, reservation, or 'ballooning'. - * It will, then, be finalized by either xe_ggtt_node_remove() or xe_ggtt_node_deballoon(). - */ -struct xe_ggtt_node { - /** @ggtt: Back pointer to xe_ggtt where this region will be inserted at */ - struct xe_ggtt *ggtt; - /** @base: A drm_mm_node */ - struct drm_mm_node base; - /** @delayed_removal_work: The work struct for the delayed removal */ - struct work_struct delayed_removal_work; - /** @invalidate_on_remove: If it needs invalidation upon removal */ - bool invalidate_on_remove; -}; - +typedef void (*xe_ggtt_set_pte_fn)(struct xe_ggtt *ggtt, u64 addr, u64 pte); +typedef void (*xe_ggtt_transform_cb)(struct xe_ggtt *ggtt, + struct xe_ggtt_node *node, + u64 pte_flags, + xe_ggtt_set_pte_fn set_pte, void *arg); /** * struct xe_ggtt_pt_ops - GGTT Page table operations * Which can vary from platform to platform. @@ -76,8 +66,10 @@ struct xe_ggtt_node { struct xe_ggtt_pt_ops { /** @pte_encode_flags: Encode PTE flags for a given BO */ u64 (*pte_encode_flags)(struct xe_bo *bo, u16 pat_index); + /** @ggtt_set_pte: Directly write into GGTT's PTE */ - void (*ggtt_set_pte)(struct xe_ggtt *ggtt, u64 addr, u64 pte); + xe_ggtt_set_pte_fn ggtt_set_pte; + /** @ggtt_get_pte: Directly read from GGTT's PTE */ u64 (*ggtt_get_pte)(struct xe_ggtt *ggtt, u64 addr); }; diff --git a/drivers/gpu/drm/xe/xe_gpu_scheduler.c b/drivers/gpu/drm/xe/xe_gpu_scheduler.c index f91e06d03511..9c8004d5dd91 100644 --- a/drivers/gpu/drm/xe/xe_gpu_scheduler.c +++ b/drivers/gpu/drm/xe/xe_gpu_scheduler.c @@ -7,7 +7,7 @@ static void xe_sched_process_msg_queue(struct xe_gpu_scheduler *sched) { - if (!READ_ONCE(sched->base.pause_submit)) + if (!drm_sched_is_stopped(&sched->base)) queue_work(sched->base.submit_wq, &sched->work_process_msg); } @@ -43,7 +43,7 @@ static void xe_sched_process_msg_work(struct work_struct *w) container_of(w, struct xe_gpu_scheduler, work_process_msg); struct xe_sched_msg *msg; - if (READ_ONCE(sched->base.pause_submit)) + if (drm_sched_is_stopped(&sched->base)) return; msg = xe_sched_get_msg(sched); @@ -77,6 +77,7 @@ int xe_sched_init(struct xe_gpu_scheduler *sched, }; sched->ops = xe_ops; + spin_lock_init(&sched->msg_lock); INIT_LIST_HEAD(&sched->msgs); INIT_WORK(&sched->work_process_msg, xe_sched_process_msg_work); @@ -117,7 +118,7 @@ void xe_sched_add_msg(struct xe_gpu_scheduler *sched, void xe_sched_add_msg_locked(struct xe_gpu_scheduler *sched, struct xe_sched_msg *msg) { - lockdep_assert_held(&sched->base.job_list_lock); + lockdep_assert_held(&sched->msg_lock); list_add_tail(&msg->link, &sched->msgs); xe_sched_process_msg_queue(sched); @@ -131,7 +132,7 @@ void xe_sched_add_msg_locked(struct xe_gpu_scheduler *sched, void xe_sched_add_msg_head(struct xe_gpu_scheduler *sched, struct xe_sched_msg *msg) { - lockdep_assert_held(&sched->base.job_list_lock); + lockdep_assert_held(&sched->msg_lock); list_add(&msg->link, &sched->msgs); xe_sched_process_msg_queue(sched); diff --git a/drivers/gpu/drm/xe/xe_gpu_scheduler.h b/drivers/gpu/drm/xe/xe_gpu_scheduler.h index c7a77a3a9681..664c2db56af3 100644 --- a/drivers/gpu/drm/xe/xe_gpu_scheduler.h +++ b/drivers/gpu/drm/xe/xe_gpu_scheduler.h @@ -33,12 +33,12 @@ void xe_sched_add_msg_head(struct xe_gpu_scheduler *sched, static inline void xe_sched_msg_lock(struct xe_gpu_scheduler *sched) { - spin_lock(&sched->base.job_list_lock); + spin_lock(&sched->msg_lock); } static inline void xe_sched_msg_unlock(struct xe_gpu_scheduler *sched) { - spin_unlock(&sched->base.job_list_lock); + spin_unlock(&sched->msg_lock); } static inline void xe_sched_stop(struct xe_gpu_scheduler *sched) @@ -56,12 +56,9 @@ static inline void xe_sched_resubmit_jobs(struct xe_gpu_scheduler *sched) struct drm_sched_job *s_job; bool restore_replay = false; - list_for_each_entry(s_job, &sched->base.pending_list, list) { - struct drm_sched_fence *s_fence = s_job->s_fence; - struct dma_fence *hw_fence = s_fence->parent; - + drm_sched_for_each_pending_job(s_job, &sched->base, NULL) { restore_replay |= to_xe_sched_job(s_job)->restore_replay; - if (restore_replay || (hw_fence && !dma_fence_is_signaled(hw_fence))) + if (restore_replay || !drm_sched_job_is_signaled(s_job)) sched->base.ops->run_job(s_job); } } @@ -72,14 +69,6 @@ xe_sched_invalidate_job(struct xe_sched_job *job, int threshold) return drm_sched_invalidate_job(&job->drm, threshold); } -static inline void xe_sched_add_pending_job(struct xe_gpu_scheduler *sched, - struct xe_sched_job *job) -{ - spin_lock(&sched->base.job_list_lock); - list_add(&job->drm.list, &sched->base.pending_list); - spin_unlock(&sched->base.job_list_lock); -} - /** * xe_sched_first_pending_job() - Find first pending job which is unsignaled * @sched: Xe GPU scheduler @@ -89,21 +78,13 @@ static inline void xe_sched_add_pending_job(struct xe_gpu_scheduler *sched, static inline struct xe_sched_job *xe_sched_first_pending_job(struct xe_gpu_scheduler *sched) { - struct xe_sched_job *job, *r_job = NULL; - - spin_lock(&sched->base.job_list_lock); - list_for_each_entry(job, &sched->base.pending_list, drm.list) { - struct drm_sched_fence *s_fence = job->drm.s_fence; - struct dma_fence *hw_fence = s_fence->parent; + struct drm_sched_job *job; - if (hw_fence && !dma_fence_is_signaled(hw_fence)) { - r_job = job; - break; - } - } - spin_unlock(&sched->base.job_list_lock); + drm_sched_for_each_pending_job(job, &sched->base, NULL) + if (!drm_sched_job_is_signaled(job)) + return to_xe_sched_job(job); - return r_job; + return NULL; } static inline int diff --git a/drivers/gpu/drm/xe/xe_gpu_scheduler_types.h b/drivers/gpu/drm/xe/xe_gpu_scheduler_types.h index 6731b13da8bb..63d9bf92583c 100644 --- a/drivers/gpu/drm/xe/xe_gpu_scheduler_types.h +++ b/drivers/gpu/drm/xe/xe_gpu_scheduler_types.h @@ -47,6 +47,8 @@ struct xe_gpu_scheduler { const struct xe_sched_backend_ops *ops; /** @msgs: list of messages to be processed in @work_process_msg */ struct list_head msgs; + /** @msg_lock: Message lock */ + spinlock_t msg_lock; /** @work_process_msg: processes messages */ struct work_struct work_process_msg; }; diff --git a/drivers/gpu/drm/xe/xe_gsc.c b/drivers/gpu/drm/xe/xe_gsc.c index dd69cb834f8e..e5c234f3d795 100644 --- a/drivers/gpu/drm/xe/xe_gsc.c +++ b/drivers/gpu/drm/xe/xe_gsc.c @@ -352,7 +352,6 @@ static void gsc_work(struct work_struct *work) struct xe_gsc *gsc = container_of(work, typeof(*gsc), work); struct xe_gt *gt = gsc_to_gt(gsc); struct xe_device *xe = gt_to_xe(gt); - unsigned int fw_ref; u32 actions; int ret; @@ -361,13 +360,12 @@ static void gsc_work(struct work_struct *work) gsc->work_actions = 0; spin_unlock_irq(&gsc->lock); - xe_pm_runtime_get(xe); - fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GSC); + guard(xe_pm_runtime)(xe); + CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GSC); if (actions & GSC_ACTION_ER_COMPLETE) { - ret = gsc_er_complete(gt); - if (ret) - goto out; + if (gsc_er_complete(gt)) + return; } if (actions & GSC_ACTION_FW_LOAD) { @@ -380,10 +378,6 @@ static void gsc_work(struct work_struct *work) if (actions & GSC_ACTION_SW_PROXY) xe_gsc_proxy_request_handler(gsc); - -out: - xe_force_wake_put(gt_to_fw(gt), fw_ref); - xe_pm_runtime_put(xe); } void xe_gsc_hwe_irq_handler(struct xe_hw_engine *hwe, u16 intr_vec) @@ -420,15 +414,16 @@ int xe_gsc_init(struct xe_gsc *gsc) } /* - * Some platforms can have GuC but not GSC. That would cause - * xe_uc_fw_init(gsc) to return a "not supported" failure code and abort - * all firmware loading. So check for GSC being enabled before - * propagating the failure back up. That way the higher level will keep - * going and load GuC as appropriate. + * Starting from BMG the GSC is no longer needed for MC6 entry, so the + * only missing features if the FW is lacking would be the content + * protection ones. This is acceptable, so we allow the driver load to + * continue if the GSC FW is missing. */ ret = xe_uc_fw_init(&gsc->fw); if (!xe_uc_fw_is_enabled(&gsc->fw)) return 0; + else if (gt_to_xe(gt)->info.platform >= XE_BATTLEMAGE && !xe_uc_fw_is_available(&gsc->fw)) + return 0; else if (ret) goto out; @@ -615,17 +610,16 @@ void xe_gsc_print_info(struct xe_gsc *gsc, struct drm_printer *p) { struct xe_gt *gt = gsc_to_gt(gsc); struct xe_mmio *mmio = >->mmio; - unsigned int fw_ref; xe_uc_fw_print(&gsc->fw, p); drm_printf(p, "\tfound security version %u\n", gsc->security_version); - if (!xe_uc_fw_is_enabled(&gsc->fw)) + if (!xe_uc_fw_is_available(&gsc->fw)) return; - fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GSC); - if (!fw_ref) + CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GSC); + if (!fw_ref.domains) return; drm_printf(p, "\nHECI1 FWSTS: 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", @@ -635,6 +629,4 @@ void xe_gsc_print_info(struct xe_gsc *gsc, struct drm_printer *p) xe_mmio_read32(mmio, HECI_FWSTS4(MTL_GSC_HECI1_BASE)), xe_mmio_read32(mmio, HECI_FWSTS5(MTL_GSC_HECI1_BASE)), xe_mmio_read32(mmio, HECI_FWSTS6(MTL_GSC_HECI1_BASE))); - - xe_force_wake_put(gt_to_fw(gt), fw_ref); } diff --git a/drivers/gpu/drm/xe/xe_gsc_debugfs.c b/drivers/gpu/drm/xe/xe_gsc_debugfs.c index 461d7e99c2b3..d4977e666946 100644 --- a/drivers/gpu/drm/xe/xe_gsc_debugfs.c +++ b/drivers/gpu/drm/xe/xe_gsc_debugfs.c @@ -7,11 +7,10 @@ #include <drm/drm_debugfs.h> #include <drm/drm_managed.h> +#include <drm/drm_print.h> -#include "xe_device.h" -#include "xe_gt.h" +#include "xe_gt_types.h" #include "xe_gsc.h" -#include "xe_macros.h" #include "xe_pm.h" static struct xe_gt * @@ -37,9 +36,8 @@ static int gsc_info(struct seq_file *m, void *data) struct xe_device *xe = gsc_to_xe(gsc); struct drm_printer p = drm_seq_file_printer(m); - xe_pm_runtime_get(xe); + guard(xe_pm_runtime)(xe); xe_gsc_print_info(gsc, &p); - xe_pm_runtime_put(xe); return 0; } diff --git a/drivers/gpu/drm/xe/xe_gsc_proxy.c b/drivers/gpu/drm/xe/xe_gsc_proxy.c index 464282a89eef..42438b21f235 100644 --- a/drivers/gpu/drm/xe/xe_gsc_proxy.c +++ b/drivers/gpu/drm/xe/xe_gsc_proxy.c @@ -18,8 +18,8 @@ #include "xe_force_wake.h" #include "xe_gsc.h" #include "xe_gsc_submit.h" -#include "xe_gt.h" #include "xe_gt_printk.h" +#include "xe_gt_types.h" #include "xe_map.h" #include "xe_mmio.h" #include "xe_pm.h" @@ -440,22 +440,19 @@ static void xe_gsc_proxy_remove(void *arg) struct xe_gsc *gsc = arg; struct xe_gt *gt = gsc_to_gt(gsc); struct xe_device *xe = gt_to_xe(gt); - unsigned int fw_ref = 0; if (!gsc->proxy.component_added) return; /* disable HECI2 IRQs */ - xe_pm_runtime_get(xe); - fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GSC); - if (!fw_ref) - xe_gt_err(gt, "failed to get forcewake to disable GSC interrupts\n"); + scoped_guard(xe_pm_runtime, xe) { + CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GSC); + if (!fw_ref.domains) + xe_gt_err(gt, "failed to get forcewake to disable GSC interrupts\n"); - /* try do disable irq even if forcewake failed */ - gsc_proxy_irq_toggle(gsc, false); - - xe_force_wake_put(gt_to_fw(gt), fw_ref); - xe_pm_runtime_put(xe); + /* try do disable irq even if forcewake failed */ + gsc_proxy_irq_toggle(gsc, false); + } xe_gsc_wait_for_worker_completion(gsc); diff --git a/drivers/gpu/drm/xe/xe_gsc_submit.c b/drivers/gpu/drm/xe/xe_gsc_submit.c index 9ede483d37ef..08082b596501 100644 --- a/drivers/gpu/drm/xe/xe_gsc_submit.c +++ b/drivers/gpu/drm/xe/xe_gsc_submit.c @@ -11,12 +11,10 @@ #include "xe_assert.h" #include "xe_bb.h" #include "xe_exec_queue.h" -#include "xe_gt_printk.h" #include "xe_gt_types.h" #include "xe_map.h" #include "xe_sched_job.h" #include "instructions/xe_gsc_commands.h" -#include "regs/xe_gsc_regs.h" #define GSC_HDR_SIZE (sizeof(struct intel_gsc_mtl_header)) /* shorthand define */ diff --git a/drivers/gpu/drm/xe/xe_gt.c b/drivers/gpu/drm/xe/xe_gt.c index dbb5e7a9bc6a..9d090d0f2438 100644 --- a/drivers/gpu/drm/xe/xe_gt.c +++ b/drivers/gpu/drm/xe/xe_gt.c @@ -13,13 +13,11 @@ #include <generated/xe_wa_oob.h> #include "instructions/xe_alu_commands.h" -#include "instructions/xe_gfxpipe_commands.h" #include "instructions/xe_mi_commands.h" #include "regs/xe_engine_regs.h" #include "regs/xe_gt_regs.h" #include "xe_assert.h" #include "xe_bb.h" -#include "xe_bo.h" #include "xe_device.h" #include "xe_eu_stall.h" #include "xe_exec_queue.h" @@ -103,14 +101,13 @@ void xe_gt_sanitize(struct xe_gt *gt) static void xe_gt_enable_host_l2_vram(struct xe_gt *gt) { - unsigned int fw_ref; u32 reg; if (!XE_GT_WA(gt, 16023588340)) return; - fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT); - if (!fw_ref) + CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT); + if (!fw_ref.domains) return; if (xe_gt_is_main_type(gt)) { @@ -120,12 +117,10 @@ static void xe_gt_enable_host_l2_vram(struct xe_gt *gt) } xe_gt_mcr_multicast_write(gt, XEHPC_L3CLOS_MASK(3), 0xF); - xe_force_wake_put(gt_to_fw(gt), fw_ref); } static void xe_gt_disable_host_l2_vram(struct xe_gt *gt) { - unsigned int fw_ref; u32 reg; if (!XE_GT_WA(gt, 16023588340)) @@ -134,15 +129,43 @@ static void xe_gt_disable_host_l2_vram(struct xe_gt *gt) if (xe_gt_is_media_type(gt)) return; - fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT); - if (!fw_ref) + CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT); + if (!fw_ref.domains) return; reg = xe_gt_mcr_unicast_read_any(gt, XE2_GAMREQSTRM_CTRL); reg &= ~CG_DIS_CNTLBUS; xe_gt_mcr_multicast_write(gt, XE2_GAMREQSTRM_CTRL, reg); +} - xe_force_wake_put(gt_to_fw(gt), fw_ref); +static void xe_gt_enable_comp_1wcoh(struct xe_gt *gt) +{ + struct xe_device *xe = gt_to_xe(gt); + unsigned int fw_ref; + u32 reg; + + if (IS_SRIOV_VF(xe)) + return; + + if (GRAPHICS_VER(xe) >= 30 && xe->info.has_flat_ccs) { + fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT); + if (!fw_ref) + return; + + reg = xe_gt_mcr_unicast_read_any(gt, XE2_GAMREQSTRM_CTRL); + reg |= EN_CMP_1WCOH; + xe_gt_mcr_multicast_write(gt, XE2_GAMREQSTRM_CTRL, reg); + + if (xe_gt_is_media_type(gt)) { + xe_mmio_rmw32(>->mmio, XE2_GAMWALK_CTRL_MEDIA, 0, EN_CMP_1WCOH_GW); + } else { + reg = xe_gt_mcr_unicast_read_any(gt, XE2_GAMWALK_CTRL_3D); + reg |= EN_CMP_1WCOH_GW; + xe_gt_mcr_multicast_write(gt, XE2_GAMWALK_CTRL_3D, reg); + } + + xe_force_wake_put(gt_to_fw(gt), fw_ref); + } } static void gt_reset_worker(struct work_struct *w); @@ -389,7 +412,6 @@ put_exec_queue: int xe_gt_init_early(struct xe_gt *gt) { - unsigned int fw_ref; int err; if (IS_SRIOV_PF(gt_to_xe(gt))) { @@ -436,13 +458,12 @@ int xe_gt_init_early(struct xe_gt *gt) if (err) return err; - fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT); - if (!fw_ref) + CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT); + if (!fw_ref.domains) return -ETIMEDOUT; xe_gt_mcr_init_early(gt); xe_pat_init(gt); - xe_force_wake_put(gt_to_fw(gt), fw_ref); return 0; } @@ -460,25 +481,25 @@ static void dump_pat_on_error(struct xe_gt *gt) static int gt_init_with_gt_forcewake(struct xe_gt *gt) { - unsigned int fw_ref; int err; - fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT); - if (!fw_ref) + CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT); + if (!fw_ref.domains) return -ETIMEDOUT; err = xe_uc_init(>->uc); if (err) - goto err_force_wake; + return err; xe_gt_topology_init(gt); xe_gt_mcr_init(gt); xe_gt_enable_host_l2_vram(gt); + xe_gt_enable_comp_1wcoh(gt); if (xe_gt_is_main_type(gt)) { err = xe_ggtt_init(gt_to_tile(gt)->mem.ggtt); if (err) - goto err_force_wake; + return err; if (IS_SRIOV_PF(gt_to_xe(gt))) xe_lmtt_init(>_to_tile(gt)->sriov.pf.lmtt); } @@ -492,17 +513,17 @@ static int gt_init_with_gt_forcewake(struct xe_gt *gt) err = xe_hw_engines_init_early(gt); if (err) { dump_pat_on_error(gt); - goto err_force_wake; + return err; } err = xe_hw_engine_class_sysfs_init(gt); if (err) - goto err_force_wake; + return err; /* Initialize CCS mode sysfs after early initialization of HW engines */ err = xe_gt_ccs_mode_sysfs_init(gt); if (err) - goto err_force_wake; + return err; /* * Stash hardware-reported version. Since this register does not exist @@ -510,25 +531,16 @@ static int gt_init_with_gt_forcewake(struct xe_gt *gt) */ gt->info.gmdid = xe_mmio_read32(>->mmio, GMD_ID); - xe_force_wake_put(gt_to_fw(gt), fw_ref); return 0; - -err_force_wake: - xe_force_wake_put(gt_to_fw(gt), fw_ref); - - return err; } static int gt_init_with_all_forcewake(struct xe_gt *gt) { - unsigned int fw_ref; int err; - fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FORCEWAKE_ALL); - if (!xe_force_wake_ref_has_domain(fw_ref, XE_FORCEWAKE_ALL)) { - err = -ETIMEDOUT; - goto err_force_wake; - } + CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FORCEWAKE_ALL); + if (!xe_force_wake_ref_has_domain(fw_ref.domains, XE_FORCEWAKE_ALL)) + return -ETIMEDOUT; xe_gt_mcr_set_implicit_defaults(gt); xe_wa_process_gt(gt); @@ -537,20 +549,20 @@ static int gt_init_with_all_forcewake(struct xe_gt *gt) err = xe_gt_clock_init(gt); if (err) - goto err_force_wake; + return err; xe_mocs_init(gt); err = xe_execlist_init(gt); if (err) - goto err_force_wake; + return err; err = xe_hw_engines_init(gt); if (err) - goto err_force_wake; + return err; err = xe_uc_init_post_hwconfig(>->uc); if (err) - goto err_force_wake; + return err; if (xe_gt_is_main_type(gt)) { /* @@ -561,10 +573,8 @@ static int gt_init_with_all_forcewake(struct xe_gt *gt) gt->usm.bb_pool = xe_sa_bo_manager_init(gt_to_tile(gt), IS_DGFX(xe) ? SZ_1M : SZ_512K, 16); - if (IS_ERR(gt->usm.bb_pool)) { - err = PTR_ERR(gt->usm.bb_pool); - goto err_force_wake; - } + if (IS_ERR(gt->usm.bb_pool)) + return PTR_ERR(gt->usm.bb_pool); } } @@ -573,12 +583,12 @@ static int gt_init_with_all_forcewake(struct xe_gt *gt) err = xe_migrate_init(tile->migrate); if (err) - goto err_force_wake; + return err; } err = xe_uc_load_hw(>->uc); if (err) - goto err_force_wake; + return err; /* Configure default CCS mode of 1 engine with all resources */ if (xe_gt_ccs_mode_enabled(gt)) { @@ -592,14 +602,7 @@ static int gt_init_with_all_forcewake(struct xe_gt *gt) if (IS_SRIOV_PF(gt_to_xe(gt))) xe_gt_sriov_pf_init_hw(gt); - xe_force_wake_put(gt_to_fw(gt), fw_ref); - return 0; - -err_force_wake: - xe_force_wake_put(gt_to_fw(gt), fw_ref); - - return err; } static void xe_gt_fini(void *arg) @@ -771,6 +774,7 @@ static int do_gt_restart(struct xe_gt *gt) xe_pat_init(gt); xe_gt_enable_host_l2_vram(gt); + xe_gt_enable_comp_1wcoh(gt); xe_gt_mcr_set_implicit_defaults(gt); xe_reg_sr_apply_mmio(>->reg_sr, gt); @@ -797,9 +801,6 @@ static int do_gt_restart(struct xe_gt *gt) xe_gt_sriov_pf_init_hw(gt); xe_mocs_init(gt); - err = xe_uc_start(>->uc); - if (err) - return err; for_each_hw_engine(hwe, gt, id) xe_reg_sr_apply_mmio(&hwe->reg_sr, gt); @@ -807,6 +808,10 @@ static int do_gt_restart(struct xe_gt *gt) /* Get CCS mode in sync between sw/hw */ xe_gt_apply_ccs_mode(gt); + err = xe_uc_start(>->uc); + if (err) + return err; + /* Restore GT freq to expected values */ xe_gt_sanitize_freq(gt); @@ -901,56 +906,42 @@ void xe_gt_reset_async(struct xe_gt *gt) void xe_gt_suspend_prepare(struct xe_gt *gt) { - unsigned int fw_ref; - - fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FORCEWAKE_ALL); - + CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FORCEWAKE_ALL); xe_uc_suspend_prepare(>->uc); - - xe_force_wake_put(gt_to_fw(gt), fw_ref); } int xe_gt_suspend(struct xe_gt *gt) { - unsigned int fw_ref; int err; xe_gt_dbg(gt, "suspending\n"); xe_gt_sanitize(gt); - fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FORCEWAKE_ALL); - if (!xe_force_wake_ref_has_domain(fw_ref, XE_FORCEWAKE_ALL)) - goto err_msg; + CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FORCEWAKE_ALL); + if (!xe_force_wake_ref_has_domain(fw_ref.domains, XE_FORCEWAKE_ALL)) { + xe_gt_err(gt, "suspend failed (%pe)\n", ERR_PTR(-ETIMEDOUT)); + return -ETIMEDOUT; + } err = xe_uc_suspend(>->uc); - if (err) - goto err_force_wake; + if (err) { + xe_gt_err(gt, "suspend failed (%pe)\n", ERR_PTR(err)); + return err; + } xe_gt_idle_disable_pg(gt); xe_gt_disable_host_l2_vram(gt); - xe_force_wake_put(gt_to_fw(gt), fw_ref); xe_gt_dbg(gt, "suspended\n"); return 0; - -err_msg: - err = -ETIMEDOUT; -err_force_wake: - xe_force_wake_put(gt_to_fw(gt), fw_ref); - xe_gt_err(gt, "suspend failed (%pe)\n", ERR_PTR(err)); - - return err; } void xe_gt_shutdown(struct xe_gt *gt) { - unsigned int fw_ref; - - fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FORCEWAKE_ALL); + CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FORCEWAKE_ALL); do_gt_reset(gt); - xe_force_wake_put(gt_to_fw(gt), fw_ref); } /** @@ -975,32 +966,72 @@ int xe_gt_sanitize_freq(struct xe_gt *gt) int xe_gt_resume(struct xe_gt *gt) { - unsigned int fw_ref; int err; xe_gt_dbg(gt, "resuming\n"); - fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FORCEWAKE_ALL); - if (!xe_force_wake_ref_has_domain(fw_ref, XE_FORCEWAKE_ALL)) - goto err_msg; + CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FORCEWAKE_ALL); + if (!xe_force_wake_ref_has_domain(fw_ref.domains, XE_FORCEWAKE_ALL)) { + xe_gt_err(gt, "resume failed (%pe)\n", ERR_PTR(-ETIMEDOUT)); + return -ETIMEDOUT; + } err = do_gt_restart(gt); if (err) - goto err_force_wake; + return err; xe_gt_idle_enable_pg(gt); - xe_force_wake_put(gt_to_fw(gt), fw_ref); xe_gt_dbg(gt, "resumed\n"); return 0; +} + +/** + * xe_gt_runtime_suspend() - GT runtime suspend + * @gt: the GT object + * + * Return: 0 on success, negative error code otherwise. + */ +int xe_gt_runtime_suspend(struct xe_gt *gt) +{ + xe_gt_dbg(gt, "runtime suspending\n"); + + CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FORCEWAKE_ALL); + if (!xe_force_wake_ref_has_domain(fw_ref.domains, XE_FORCEWAKE_ALL)) { + xe_gt_err(gt, "runtime suspend failed (%pe)\n", ERR_PTR(-ETIMEDOUT)); + return -ETIMEDOUT; + } + + xe_uc_runtime_suspend(>->uc); + xe_gt_disable_host_l2_vram(gt); + + xe_gt_dbg(gt, "runtime suspended\n"); + + return 0; +} -err_msg: - err = -ETIMEDOUT; -err_force_wake: - xe_force_wake_put(gt_to_fw(gt), fw_ref); - xe_gt_err(gt, "resume failed (%pe)\n", ERR_PTR(err)); +/** + * xe_gt_runtime_resume() - GT runtime resume + * @gt: the GT object + * + * Return: 0 on success, negative error code otherwise. + */ +int xe_gt_runtime_resume(struct xe_gt *gt) +{ + xe_gt_dbg(gt, "runtime resuming\n"); - return err; + CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FORCEWAKE_ALL); + if (!xe_force_wake_ref_has_domain(fw_ref.domains, XE_FORCEWAKE_ALL)) { + xe_gt_err(gt, "runtime resume failed (%pe)\n", ERR_PTR(-ETIMEDOUT)); + return -ETIMEDOUT; + } + + xe_gt_enable_host_l2_vram(gt); + xe_uc_runtime_resume(>->uc); + + xe_gt_dbg(gt, "runtime resumed\n"); + + return 0; } struct xe_hw_engine *xe_gt_hw_engine(struct xe_gt *gt, diff --git a/drivers/gpu/drm/xe/xe_gt.h b/drivers/gpu/drm/xe/xe_gt.h index 9d710049da45..de7e47763411 100644 --- a/drivers/gpu/drm/xe/xe_gt.h +++ b/drivers/gpu/drm/xe/xe_gt.h @@ -20,7 +20,17 @@ for_each_if(((hwe__) = (gt__)->hw_engines + (id__)) && \ xe_hw_engine_is_valid((hwe__))) -#define CCS_MASK(gt) (((gt)->info.engine_mask & XE_HW_ENGINE_CCS_MASK) >> XE_HW_ENGINE_CCS0) +#define XE_ENGINE_INSTANCES_FROM_MASK(gt, NAME) \ + (((gt)->info.engine_mask & XE_HW_ENGINE_##NAME##_MASK) >> XE_HW_ENGINE_##NAME##0) + +#define RCS_INSTANCES(gt) XE_ENGINE_INSTANCES_FROM_MASK(gt, RCS) +#define VCS_INSTANCES(gt) XE_ENGINE_INSTANCES_FROM_MASK(gt, VCS) +#define VECS_INSTANCES(gt) XE_ENGINE_INSTANCES_FROM_MASK(gt, VECS) +#define CCS_INSTANCES(gt) XE_ENGINE_INSTANCES_FROM_MASK(gt, CCS) +#define GSCCS_INSTANCES(gt) XE_ENGINE_INSTANCES_FROM_MASK(gt, GSCCS) + +/* Our devices have up to 4 media slices */ +#define MAX_MEDIA_SLICES 4 #define GT_VER(gt) ({ \ typeof(gt) gt_ = (gt); \ @@ -58,6 +68,8 @@ int xe_gt_suspend(struct xe_gt *gt); void xe_gt_shutdown(struct xe_gt *gt); int xe_gt_resume(struct xe_gt *gt); void xe_gt_reset_async(struct xe_gt *gt); +int xe_gt_runtime_resume(struct xe_gt *gt); +int xe_gt_runtime_suspend(struct xe_gt *gt); void xe_gt_sanitize(struct xe_gt *gt); int xe_gt_sanitize_freq(struct xe_gt *gt); diff --git a/drivers/gpu/drm/xe/xe_gt_ccs_mode.c b/drivers/gpu/drm/xe/xe_gt_ccs_mode.c index 50fffc9ebf62..91ac22ef5703 100644 --- a/drivers/gpu/drm/xe/xe_gt_ccs_mode.c +++ b/drivers/gpu/drm/xe/xe_gt_ccs_mode.c @@ -17,7 +17,7 @@ static void __xe_gt_apply_ccs_mode(struct xe_gt *gt, u32 num_engines) { u32 mode = CCS_MODE_CSLICE_0_3_MASK; /* disable all by default */ - int num_slices = hweight32(CCS_MASK(gt)); + int num_slices = hweight32(CCS_INSTANCES(gt)); struct xe_device *xe = gt_to_xe(gt); int width, cslice = 0; u32 config = 0; @@ -59,7 +59,7 @@ static void __xe_gt_apply_ccs_mode(struct xe_gt *gt, u32 num_engines) config |= BIT(hwe->instance) << XE_HW_ENGINE_CCS0; /* If a slice is fused off, leave disabled */ - while ((CCS_MASK(gt) & BIT(cslice)) == 0) + while ((CCS_INSTANCES(gt) & BIT(cslice)) == 0) cslice++; mode &= ~CCS_MODE_CSLICE(cslice, CCS_MODE_CSLICE_MASK); @@ -94,7 +94,7 @@ num_cslices_show(struct device *kdev, { struct xe_gt *gt = kobj_to_gt(&kdev->kobj); - return sysfs_emit(buf, "%u\n", hweight32(CCS_MASK(gt))); + return sysfs_emit(buf, "%u\n", hweight32(CCS_INSTANCES(gt))); } static DEVICE_ATTR_RO(num_cslices); @@ -131,7 +131,7 @@ ccs_mode_store(struct device *kdev, struct device_attribute *attr, * Ensure number of engines specified is valid and there is an * exact multiple of engines for slices. */ - num_slices = hweight32(CCS_MASK(gt)); + num_slices = hweight32(CCS_INSTANCES(gt)); if (!num_engines || num_engines > num_slices || num_slices % num_engines) { xe_gt_dbg(gt, "Invalid compute config, %d engines %d slices\n", num_engines, num_slices); diff --git a/drivers/gpu/drm/xe/xe_gt_ccs_mode.h b/drivers/gpu/drm/xe/xe_gt_ccs_mode.h index f8779852cf0d..ef3b853f5c8c 100644 --- a/drivers/gpu/drm/xe/xe_gt_ccs_mode.h +++ b/drivers/gpu/drm/xe/xe_gt_ccs_mode.h @@ -17,7 +17,7 @@ int xe_gt_ccs_mode_sysfs_init(struct xe_gt *gt); static inline bool xe_gt_ccs_mode_enabled(const struct xe_gt *gt) { /* Check if there are more than one compute engines available */ - return hweight32(CCS_MASK(gt)) > 1; + return hweight32(CCS_INSTANCES(gt)) > 1; } #endif diff --git a/drivers/gpu/drm/xe/xe_gt_clock.c b/drivers/gpu/drm/xe/xe_gt_clock.c index bfc25c46f798..53b3835192da 100644 --- a/drivers/gpu/drm/xe/xe_gt_clock.c +++ b/drivers/gpu/drm/xe/xe_gt_clock.c @@ -8,12 +8,8 @@ #include "xe_gt_clock.h" #include "regs/xe_gt_regs.h" -#include "regs/xe_regs.h" -#include "xe_assert.h" -#include "xe_device.h" -#include "xe_gt.h" +#include "xe_gt_types.h" #include "xe_gt_printk.h" -#include "xe_macros.h" #include "xe_mmio.h" #define f19_2_mhz 19200000 diff --git a/drivers/gpu/drm/xe/xe_gt_debugfs.c b/drivers/gpu/drm/xe/xe_gt_debugfs.c index e4fd632f43cf..4363bc9c3606 100644 --- a/drivers/gpu/drm/xe/xe_gt_debugfs.c +++ b/drivers/gpu/drm/xe/xe_gt_debugfs.c @@ -22,7 +22,6 @@ #include "xe_guc_hwconfig.h" #include "xe_hw_engine.h" #include "xe_lrc.h" -#include "xe_macros.h" #include "xe_mocs.h" #include "xe_pat.h" #include "xe_pm.h" @@ -105,35 +104,24 @@ int xe_gt_debugfs_show_with_rpm(struct seq_file *m, void *data) struct drm_info_node *node = m->private; struct xe_gt *gt = node_to_gt(node); struct xe_device *xe = gt_to_xe(gt); - int ret; - - xe_pm_runtime_get(xe); - ret = xe_gt_debugfs_simple_show(m, data); - xe_pm_runtime_put(xe); - return ret; + guard(xe_pm_runtime)(xe); + return xe_gt_debugfs_simple_show(m, data); } static int hw_engines(struct xe_gt *gt, struct drm_printer *p) { struct xe_hw_engine *hwe; enum xe_hw_engine_id id; - unsigned int fw_ref; - int ret = 0; - fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FORCEWAKE_ALL); - if (!xe_force_wake_ref_has_domain(fw_ref, XE_FORCEWAKE_ALL)) { - ret = -ETIMEDOUT; - goto fw_put; - } + CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FORCEWAKE_ALL); + if (!xe_force_wake_ref_has_domain(fw_ref.domains, XE_FORCEWAKE_ALL)) + return -ETIMEDOUT; for_each_hw_engine(hwe, gt, id) xe_hw_engine_print(hwe, p); -fw_put: - xe_force_wake_put(gt_to_fw(gt), fw_ref); - - return ret; + return 0; } static int steering(struct xe_gt *gt, struct drm_printer *p) @@ -220,6 +208,7 @@ static const struct drm_info_list vf_safe_debugfs_list[] = { { "default_lrc_vcs", .show = xe_gt_debugfs_show_with_rpm, .data = vcs_default_lrc }, { "default_lrc_vecs", .show = xe_gt_debugfs_show_with_rpm, .data = vecs_default_lrc }, { "hwconfig", .show = xe_gt_debugfs_show_with_rpm, .data = hwconfig }, + { "pat_sw_config", .show = xe_gt_debugfs_simple_show, .data = xe_pat_dump_sw_config }, }; /* everything else should be added here */ @@ -269,9 +258,8 @@ static void force_reset(struct xe_gt *gt) { struct xe_device *xe = gt_to_xe(gt); - xe_pm_runtime_get(xe); + guard(xe_pm_runtime)(xe); xe_gt_reset_async(gt); - xe_pm_runtime_put(xe); } static ssize_t force_reset_write(struct file *file, @@ -297,9 +285,8 @@ static void force_reset_sync(struct xe_gt *gt) { struct xe_device *xe = gt_to_xe(gt); - xe_pm_runtime_get(xe); + guard(xe_pm_runtime)(xe); xe_gt_reset(gt); - xe_pm_runtime_put(xe); } static ssize_t force_reset_sync_write(struct file *file, diff --git a/drivers/gpu/drm/xe/xe_gt_freq.c b/drivers/gpu/drm/xe/xe_gt_freq.c index 849ea6c86e8e..a40dd074106f 100644 --- a/drivers/gpu/drm/xe/xe_gt_freq.c +++ b/drivers/gpu/drm/xe/xe_gt_freq.c @@ -70,9 +70,8 @@ static ssize_t act_freq_show(struct kobject *kobj, struct xe_guc_pc *pc = dev_to_pc(dev); u32 freq; - xe_pm_runtime_get(dev_to_xe(dev)); + guard(xe_pm_runtime)(dev_to_xe(dev)); freq = xe_guc_pc_get_act_freq(pc); - xe_pm_runtime_put(dev_to_xe(dev)); return sysfs_emit(buf, "%d\n", freq); } @@ -86,9 +85,8 @@ static ssize_t cur_freq_show(struct kobject *kobj, u32 freq; ssize_t ret; - xe_pm_runtime_get(dev_to_xe(dev)); + guard(xe_pm_runtime)(dev_to_xe(dev)); ret = xe_guc_pc_get_cur_freq(pc, &freq); - xe_pm_runtime_put(dev_to_xe(dev)); if (ret) return ret; @@ -113,9 +111,8 @@ static ssize_t rpe_freq_show(struct kobject *kobj, struct xe_guc_pc *pc = dev_to_pc(dev); u32 freq; - xe_pm_runtime_get(dev_to_xe(dev)); + guard(xe_pm_runtime)(dev_to_xe(dev)); freq = xe_guc_pc_get_rpe_freq(pc); - xe_pm_runtime_put(dev_to_xe(dev)); return sysfs_emit(buf, "%d\n", freq); } @@ -128,9 +125,8 @@ static ssize_t rpa_freq_show(struct kobject *kobj, struct xe_guc_pc *pc = dev_to_pc(dev); u32 freq; - xe_pm_runtime_get(dev_to_xe(dev)); + guard(xe_pm_runtime)(dev_to_xe(dev)); freq = xe_guc_pc_get_rpa_freq(pc); - xe_pm_runtime_put(dev_to_xe(dev)); return sysfs_emit(buf, "%d\n", freq); } @@ -154,9 +150,8 @@ static ssize_t min_freq_show(struct kobject *kobj, u32 freq; ssize_t ret; - xe_pm_runtime_get(dev_to_xe(dev)); + guard(xe_pm_runtime)(dev_to_xe(dev)); ret = xe_guc_pc_get_min_freq(pc, &freq); - xe_pm_runtime_put(dev_to_xe(dev)); if (ret) return ret; @@ -175,9 +170,8 @@ static ssize_t min_freq_store(struct kobject *kobj, if (ret) return ret; - xe_pm_runtime_get(dev_to_xe(dev)); + guard(xe_pm_runtime)(dev_to_xe(dev)); ret = xe_guc_pc_set_min_freq(pc, freq); - xe_pm_runtime_put(dev_to_xe(dev)); if (ret) return ret; @@ -193,9 +187,8 @@ static ssize_t max_freq_show(struct kobject *kobj, u32 freq; ssize_t ret; - xe_pm_runtime_get(dev_to_xe(dev)); + guard(xe_pm_runtime)(dev_to_xe(dev)); ret = xe_guc_pc_get_max_freq(pc, &freq); - xe_pm_runtime_put(dev_to_xe(dev)); if (ret) return ret; @@ -214,9 +207,8 @@ static ssize_t max_freq_store(struct kobject *kobj, if (ret) return ret; - xe_pm_runtime_get(dev_to_xe(dev)); + guard(xe_pm_runtime)(dev_to_xe(dev)); ret = xe_guc_pc_set_max_freq(pc, freq); - xe_pm_runtime_put(dev_to_xe(dev)); if (ret) return ret; @@ -243,9 +235,8 @@ static ssize_t power_profile_store(struct kobject *kobj, struct xe_guc_pc *pc = dev_to_pc(dev); int err; - xe_pm_runtime_get(dev_to_xe(dev)); + guard(xe_pm_runtime)(dev_to_xe(dev)); err = xe_guc_pc_set_power_profile(pc, buff); - xe_pm_runtime_put(dev_to_xe(dev)); return err ?: count; } @@ -293,8 +284,10 @@ int xe_gt_freq_init(struct xe_gt *gt) return -ENOMEM; err = sysfs_create_files(gt->freq, freq_attrs); - if (err) + if (err) { + kobject_put(gt->freq); return err; + } err = devm_add_action_or_reset(xe->drm.dev, freq_fini, gt->freq); if (err) diff --git a/drivers/gpu/drm/xe/xe_gt_idle.c b/drivers/gpu/drm/xe/xe_gt_idle.c index bdc9d9877ec4..7a569e1730a4 100644 --- a/drivers/gpu/drm/xe/xe_gt_idle.c +++ b/drivers/gpu/drm/xe/xe_gt_idle.c @@ -5,6 +5,7 @@ #include <drm/drm_managed.h> +#include <generated/xe_wa_oob.h> #include "xe_force_wake.h" #include "xe_device.h" #include "xe_gt.h" @@ -12,10 +13,10 @@ #include "xe_gt_sysfs.h" #include "xe_guc_pc.h" #include "regs/xe_gt_regs.h" -#include "xe_macros.h" #include "xe_mmio.h" #include "xe_pm.h" #include "xe_sriov.h" +#include "xe_wa.h" /** * DOC: Xe GT Idle @@ -103,7 +104,6 @@ void xe_gt_idle_enable_pg(struct xe_gt *gt) struct xe_gt_idle *gtidle = >->gtidle; struct xe_mmio *mmio = >->mmio; u32 vcs_mask, vecs_mask; - unsigned int fw_ref; int i, j; if (IS_SRIOV_VF(xe)) @@ -135,7 +135,7 @@ void xe_gt_idle_enable_pg(struct xe_gt *gt) } } - fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT); + CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT); if (xe->info.skip_guc_pc) { /* * GuC sets the hysteresis value when GuC PC is enabled @@ -145,14 +145,18 @@ void xe_gt_idle_enable_pg(struct xe_gt *gt) xe_mmio_write32(mmio, RENDER_POWERGATE_IDLE_HYSTERESIS, 25); } + if (XE_GT_WA(gt, 14020316580)) + gtidle->powergate_enable &= ~(VDN_HCP_POWERGATE_ENABLE(0) | + VDN_MFXVDENC_POWERGATE_ENABLE(0) | + VDN_HCP_POWERGATE_ENABLE(2) | + VDN_MFXVDENC_POWERGATE_ENABLE(2)); + xe_mmio_write32(mmio, POWERGATE_ENABLE, gtidle->powergate_enable); - xe_force_wake_put(gt_to_fw(gt), fw_ref); } void xe_gt_idle_disable_pg(struct xe_gt *gt) { struct xe_gt_idle *gtidle = >->gtidle; - unsigned int fw_ref; if (IS_SRIOV_VF(gt_to_xe(gt))) return; @@ -160,9 +164,8 @@ void xe_gt_idle_disable_pg(struct xe_gt *gt) xe_device_assert_mem_access(gt_to_xe(gt)); gtidle->powergate_enable = 0; - fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT); + CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT); xe_mmio_write32(>->mmio, POWERGATE_ENABLE, gtidle->powergate_enable); - xe_force_wake_put(gt_to_fw(gt), fw_ref); } /** @@ -181,7 +184,6 @@ int xe_gt_idle_pg_print(struct xe_gt *gt, struct drm_printer *p) enum xe_gt_idle_state state; u32 pg_enabled, pg_status = 0; u32 vcs_mask, vecs_mask; - unsigned int fw_ref; int n; /* * Media Slices @@ -218,14 +220,12 @@ int xe_gt_idle_pg_print(struct xe_gt *gt, struct drm_printer *p) /* Do not wake the GT to read powergating status */ if (state != GT_IDLE_C6) { - fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT); - if (!fw_ref) + CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT); + if (!fw_ref.domains) return -ETIMEDOUT; pg_enabled = xe_mmio_read32(>->mmio, POWERGATE_ENABLE); pg_status = xe_mmio_read32(>->mmio, POWERGATE_DOMAIN_STATUS); - - xe_force_wake_put(gt_to_fw(gt), fw_ref); } if (gt->info.engine_mask & XE_HW_ENGINE_RCS_MASK) { @@ -263,13 +263,9 @@ static ssize_t name_show(struct kobject *kobj, struct device *dev = kobj_to_dev(kobj); struct xe_gt_idle *gtidle = dev_to_gtidle(dev); struct xe_guc_pc *pc = gtidle_to_pc(gtidle); - ssize_t ret; - - xe_pm_runtime_get(pc_to_xe(pc)); - ret = sysfs_emit(buff, "%s\n", gtidle->name); - xe_pm_runtime_put(pc_to_xe(pc)); - return ret; + guard(xe_pm_runtime)(pc_to_xe(pc)); + return sysfs_emit(buff, "%s\n", gtidle->name); } static struct kobj_attribute name_attr = __ATTR_RO(name); @@ -281,9 +277,8 @@ static ssize_t idle_status_show(struct kobject *kobj, struct xe_guc_pc *pc = gtidle_to_pc(gtidle); enum xe_gt_idle_state state; - xe_pm_runtime_get(pc_to_xe(pc)); - state = gtidle->idle_status(pc); - xe_pm_runtime_put(pc_to_xe(pc)); + scoped_guard(xe_pm_runtime, pc_to_xe(pc)) + state = gtidle->idle_status(pc); return sysfs_emit(buff, "%s\n", gt_idle_state_to_string(state)); } @@ -311,9 +306,8 @@ static ssize_t idle_residency_ms_show(struct kobject *kobj, struct xe_guc_pc *pc = gtidle_to_pc(gtidle); u64 residency; - xe_pm_runtime_get(pc_to_xe(pc)); - residency = xe_gt_idle_residency_msec(gtidle); - xe_pm_runtime_put(pc_to_xe(pc)); + scoped_guard(xe_pm_runtime, pc_to_xe(pc)) + residency = xe_gt_idle_residency_msec(gtidle); return sysfs_emit(buff, "%llu\n", residency); } @@ -396,21 +390,17 @@ void xe_gt_idle_enable_c6(struct xe_gt *gt) int xe_gt_idle_disable_c6(struct xe_gt *gt) { - unsigned int fw_ref; - xe_device_assert_mem_access(gt_to_xe(gt)); if (IS_SRIOV_VF(gt_to_xe(gt))) return 0; - fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT); - if (!fw_ref) + CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT); + if (!fw_ref.domains) return -ETIMEDOUT; xe_mmio_write32(>->mmio, RC_CONTROL, 0); xe_mmio_write32(>->mmio, RC_STATE, 0); - xe_force_wake_put(gt_to_fw(gt), fw_ref); - return 0; } diff --git a/drivers/gpu/drm/xe/xe_gt_mcr.c b/drivers/gpu/drm/xe/xe_gt_mcr.c index 164010860664..7c1fe9ac120d 100644 --- a/drivers/gpu/drm/xe/xe_gt_mcr.c +++ b/drivers/gpu/drm/xe/xe_gt_mcr.c @@ -7,7 +7,6 @@ #include "regs/xe_gt_regs.h" #include "xe_assert.h" -#include "xe_gt.h" #include "xe_gt_printk.h" #include "xe_gt_topology.h" #include "xe_gt_types.h" diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf.c b/drivers/gpu/drm/xe/xe_gt_sriov_pf.c index 0714c758b9c1..fb5c9101e275 100644 --- a/drivers/gpu/drm/xe/xe_gt_sriov_pf.c +++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf.c @@ -14,6 +14,7 @@ #include "xe_gt_sriov_pf_control.h" #include "xe_gt_sriov_pf_helpers.h" #include "xe_gt_sriov_pf_migration.h" +#include "xe_gt_sriov_pf_policy.h" #include "xe_gt_sriov_pf_service.h" #include "xe_gt_sriov_printk.h" #include "xe_guc_submit.h" @@ -123,6 +124,8 @@ int xe_gt_sriov_pf_init(struct xe_gt *gt) if (err) return err; + xe_gt_sriov_pf_policy_init(gt); + err = xe_gt_sriov_pf_migration_init(gt); if (err) return err; @@ -281,3 +284,20 @@ int xe_gt_sriov_pf_wait_ready(struct xe_gt *gt) pf_flush_restart(gt); return 0; } + +/** + * xe_gt_sriov_pf_sched_groups_enabled - Check if multiple scheduler groups are + * enabled + * @gt: the &xe_gt + * + * This function is for PF use only. + * + * Return: true if shed groups were enabled, false otherwise. + */ +bool xe_gt_sriov_pf_sched_groups_enabled(struct xe_gt *gt) +{ + xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt))); + + return xe_gt_sriov_pf_policy_sched_groups_enabled(gt); +} + diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf.h b/drivers/gpu/drm/xe/xe_gt_sriov_pf.h index e7fde3f9937a..1ccfc7137b98 100644 --- a/drivers/gpu/drm/xe/xe_gt_sriov_pf.h +++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf.h @@ -6,6 +6,8 @@ #ifndef _XE_GT_SRIOV_PF_H_ #define _XE_GT_SRIOV_PF_H_ +#include <linux/types.h> + struct xe_gt; #ifdef CONFIG_PCI_IOV @@ -16,6 +18,7 @@ void xe_gt_sriov_pf_init_hw(struct xe_gt *gt); void xe_gt_sriov_pf_sanitize_hw(struct xe_gt *gt, unsigned int vfid); void xe_gt_sriov_pf_stop_prepare(struct xe_gt *gt); void xe_gt_sriov_pf_restart(struct xe_gt *gt); +bool xe_gt_sriov_pf_sched_groups_enabled(struct xe_gt *gt); #else static inline int xe_gt_sriov_pf_init_early(struct xe_gt *gt) { @@ -38,6 +41,11 @@ static inline void xe_gt_sriov_pf_stop_prepare(struct xe_gt *gt) static inline void xe_gt_sriov_pf_restart(struct xe_gt *gt) { } + +static inline bool xe_gt_sriov_pf_sched_groups_enabled(struct xe_gt *gt) +{ + return false; +} #endif #endif diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_config.c b/drivers/gpu/drm/xe/xe_gt_sriov_pf_config.c index 59c5c6b4d994..23601ce79348 100644 --- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_config.c +++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_config.c @@ -24,13 +24,11 @@ #include "xe_guc_buf.h" #include "xe_guc_ct.h" #include "xe_guc_db_mgr.h" -#include "xe_guc_fwif.h" #include "xe_guc_id_mgr.h" #include "xe_guc_klv_helpers.h" #include "xe_guc_klv_thresholds_set.h" #include "xe_guc_submit.h" #include "xe_lmtt.h" -#include "xe_map.h" #include "xe_migrate.h" #include "xe_sriov.h" #include "xe_ttm_vram_mgr.h" @@ -195,6 +193,25 @@ static int pf_push_vf_cfg_dbs(struct xe_gt *gt, unsigned int vfid, u32 begin, u3 return pf_push_vf_cfg_klvs(gt, vfid, 2, klvs, ARRAY_SIZE(klvs)); } +static int pf_push_vf_grp_cfg_u32(struct xe_gt *gt, unsigned int vfid, + u16 key, const u32 *values, u32 count) +{ + CLASS(xe_guc_buf, buf)(>->uc.guc.buf, GUC_KLV_LEN_MIN + GUC_MAX_SCHED_GROUPS); + u32 *klv; + + xe_gt_assert(gt, count && count <= GUC_MAX_SCHED_GROUPS); + + if (!xe_guc_buf_is_valid(buf)) + return -ENOBUFS; + + klv = xe_guc_buf_cpu_ptr(buf); + + klv[0] = FIELD_PREP(GUC_KLV_0_KEY, key) | FIELD_PREP(GUC_KLV_0_LEN, count); + memcpy(&klv[1], values, count * sizeof(u32)); + + return pf_push_vf_buf_klvs(gt, vfid, 1, buf, GUC_KLV_LEN_MIN + count); +} + static int pf_push_vf_cfg_exec_quantum(struct xe_gt *gt, unsigned int vfid, u32 *exec_quantum) { /* GuC will silently clamp values exceeding max */ @@ -265,11 +282,43 @@ static u32 encode_config_ggtt(u32 *cfg, const struct xe_gt_sriov_config *config, if (!xe_ggtt_node_allocated(node)) return 0; - return encode_ggtt(cfg, node->base.start, node->base.size, details); + return encode_ggtt(cfg, xe_ggtt_node_addr(node), xe_ggtt_node_size(node), details); +} + +static u32 encode_config_sched(struct xe_gt *gt, u32 *cfg, u32 n, + const struct xe_gt_sriov_config *config) +{ + int i; + + if (xe_sriov_gt_pf_policy_has_multi_group_modes(gt)) { + BUILD_BUG_ON(ARRAY_SIZE(config->exec_quantum) > + GUC_KLV_VF_CFG_ENGINE_GROUP_EXEC_QUANTUM_MAX_LEN); + BUILD_BUG_ON(ARRAY_SIZE(config->preempt_timeout) > + GUC_KLV_VF_CFG_ENGINE_GROUP_PREEMPT_TIMEOUT_MAX_LEN); + + cfg[n++] = PREP_GUC_KLV_CONST(GUC_KLV_VF_CFG_ENGINE_GROUP_EXEC_QUANTUM_KEY, + ARRAY_SIZE(config->exec_quantum)); + for (i = 0; i < ARRAY_SIZE(config->exec_quantum); i++) + cfg[n++] = config->exec_quantum[i]; + + cfg[n++] = PREP_GUC_KLV_CONST(GUC_KLV_VF_CFG_ENGINE_GROUP_PREEMPT_TIMEOUT_KEY, + ARRAY_SIZE(config->preempt_timeout)); + for (i = 0; i < ARRAY_SIZE(config->preempt_timeout); i++) + cfg[n++] = config->preempt_timeout[i]; + } else { + cfg[n++] = PREP_GUC_KLV_TAG(VF_CFG_EXEC_QUANTUM); + cfg[n++] = config->exec_quantum[0]; + + cfg[n++] = PREP_GUC_KLV_TAG(VF_CFG_PREEMPT_TIMEOUT); + cfg[n++] = config->preempt_timeout[0]; + } + + return n; } /* Return: number of configuration dwords written */ -static u32 encode_config(u32 *cfg, const struct xe_gt_sriov_config *config, bool details) +static u32 encode_config(struct xe_gt *gt, u32 *cfg, const struct xe_gt_sriov_config *config, + bool details) { u32 n = 0; @@ -297,15 +346,13 @@ static u32 encode_config(u32 *cfg, const struct xe_gt_sriov_config *config, bool cfg[n++] = upper_32_bits(xe_bo_size(config->lmem_obj)); } - cfg[n++] = PREP_GUC_KLV_TAG(VF_CFG_EXEC_QUANTUM); - cfg[n++] = config->exec_quantum; + n = encode_config_sched(gt, cfg, n, config); - cfg[n++] = PREP_GUC_KLV_TAG(VF_CFG_PREEMPT_TIMEOUT); - cfg[n++] = config->preempt_timeout; - -#define encode_threshold_config(TAG, ...) ({ \ - cfg[n++] = PREP_GUC_KLV_TAG(VF_CFG_THRESHOLD_##TAG); \ - cfg[n++] = config->thresholds[MAKE_XE_GUC_KLV_THRESHOLD_INDEX(TAG)]; \ +#define encode_threshold_config(TAG, NAME, VER...) ({ \ + if (IF_ARGS(GUC_FIRMWARE_VER_AT_LEAST(>->uc.guc, VER), true, VER)) { \ + cfg[n++] = PREP_GUC_KLV_TAG(VF_CFG_THRESHOLD_##TAG); \ + cfg[n++] = config->thresholds[MAKE_XE_GUC_KLV_THRESHOLD_INDEX(TAG)]; \ + } \ }); MAKE_XE_GUC_KLV_THRESHOLDS_SET(encode_threshold_config); @@ -328,7 +375,7 @@ static int pf_push_full_vf_config(struct xe_gt *gt, unsigned int vfid) return -ENOBUFS; cfg = xe_guc_buf_cpu_ptr(buf); - num_dwords = encode_config(cfg, config, true); + num_dwords = encode_config(gt, cfg, config, true); xe_gt_assert(gt, num_dwords <= max_cfg_dwords); if (xe_gt_is_media_type(gt)) { @@ -344,8 +391,8 @@ static int pf_push_full_vf_config(struct xe_gt *gt, unsigned int vfid) xe_gt_assert(gt, num_dwords <= max_cfg_dwords); if (vfid == PFID) { - u64 ggtt_start = xe_wopcm_size(gt_to_xe(gt)); - u64 ggtt_size = gt_to_tile(gt)->mem.ggtt->size - ggtt_start; + u64 ggtt_start = xe_ggtt_start(gt_to_tile(gt)->mem.ggtt); + u64 ggtt_size = xe_ggtt_size(gt_to_tile(gt)->mem.ggtt); /* plain PF config data will never include a real GGTT region */ xe_gt_assert(gt, !encode_config_ggtt(cfg + num_dwords, config, true)); @@ -496,9 +543,9 @@ static int pf_provision_vf_ggtt(struct xe_gt *gt, unsigned int vfid, u64 size) xe_ggtt_assign(node, vfid); xe_gt_sriov_dbg_verbose(gt, "VF%u assigned GGTT %llx-%llx\n", - vfid, node->base.start, node->base.start + node->base.size - 1); + vfid, xe_ggtt_node_addr(node), xe_ggtt_node_addr(node) + size - 1); - err = pf_distribute_config_ggtt(gt->tile, vfid, node->base.start, node->base.size); + err = pf_distribute_config_ggtt(gt->tile, vfid, xe_ggtt_node_addr(node), size); if (unlikely(err)) goto err; @@ -515,7 +562,7 @@ static u64 pf_get_vf_config_ggtt(struct xe_gt *gt, unsigned int vfid) struct xe_ggtt_node *node = config->ggtt_region; xe_gt_assert(gt, xe_gt_is_main_type(gt)); - return xe_ggtt_node_allocated(node) ? node->base.size : 0; + return xe_ggtt_node_allocated(node) ? xe_ggtt_node_size(node) : 0; } /** @@ -973,6 +1020,33 @@ static int pf_config_set_u32_done(struct xe_gt *gt, unsigned int vfid, u32 value return 0; } +static char *to_group_name(const char *what, u8 group, char *buf, size_t size) +{ + snprintf(buf, size, "group%u%s%s", group, what ? " " : "", what ?: ""); + return buf; +} + +static int +pf_groups_cfg_set_u32_done(struct xe_gt *gt, unsigned int vfid, u32 *values, u32 count, + void (*get_actual)(struct xe_gt *, unsigned int, u32 *, u32), + const char *what, const char *(*unit)(u32), int err) +{ + u32 actual[GUC_MAX_SCHED_GROUPS]; + char group_name[32]; + u8 g; + + xe_gt_assert(gt, count <= ARRAY_SIZE(actual)); + + get_actual(gt, vfid, actual, count); + + for (g = 0; g < count; g++) + pf_config_set_u32_done(gt, vfid, values[g], actual[g], + to_group_name(what, g, group_name, sizeof(group_name)), + unit, err); + + return err; +} + /** * xe_gt_sriov_pf_config_set_ctxs - Configure GuC contexts IDs quota for the VF. * @gt: the &xe_gt @@ -1857,12 +1931,15 @@ static int pf_provision_exec_quantum(struct xe_gt *gt, unsigned int vfid, { struct xe_gt_sriov_config *config = pf_pick_vf_config(gt, vfid); int err; + int i; err = pf_push_vf_cfg_exec_quantum(gt, vfid, &exec_quantum); if (unlikely(err)) return err; - config->exec_quantum = exec_quantum; + for (i = 0; i < ARRAY_SIZE(config->exec_quantum); i++) + config->exec_quantum[i] = exec_quantum; + return 0; } @@ -1870,7 +1947,7 @@ static u32 pf_get_exec_quantum(struct xe_gt *gt, unsigned int vfid) { struct xe_gt_sriov_config *config = pf_pick_vf_config(gt, vfid); - return config->exec_quantum; + return config->exec_quantum[0]; } /** @@ -1977,6 +2054,88 @@ int xe_gt_sriov_pf_config_bulk_set_exec_quantum_locked(struct xe_gt *gt, u32 exe exec_quantum_unit, n, err); } +static int pf_provision_groups_exec_quantums(struct xe_gt *gt, unsigned int vfid, + const u32 *exec_quantums, u32 count) +{ + struct xe_gt_sriov_config *config = pf_pick_vf_config(gt, vfid); + int err; + int i; + + err = pf_push_vf_grp_cfg_u32(gt, vfid, GUC_KLV_VF_CFG_ENGINE_GROUP_EXEC_QUANTUM_KEY, + exec_quantums, count); + if (unlikely(err)) + return err; + + /* + * GuC silently clamps values exceeding the max and zeroes out the + * quantum for groups not in the klv payload + */ + for (i = 0; i < ARRAY_SIZE(config->exec_quantum); i++) { + if (i < count) + config->exec_quantum[i] = min_t(u32, exec_quantums[i], + GUC_KLV_VF_CFG_EXEC_QUANTUM_MAX_VALUE); + else + config->exec_quantum[i] = 0; + } + + return 0; +} + +static void pf_get_groups_exec_quantums(struct xe_gt *gt, unsigned int vfid, + u32 *exec_quantums, u32 max_count) +{ + struct xe_gt_sriov_config *config = pf_pick_vf_config(gt, vfid); + u32 count = min_t(u32, max_count, ARRAY_SIZE(config->exec_quantum)); + + memcpy(exec_quantums, config->exec_quantum, sizeof(u32) * count); +} + +/** + * xe_gt_sriov_pf_config_set_groups_exec_quantums() - Configure PF/VF EQs for sched groups. + * @gt: the &xe_gt + * @vfid: the PF or VF identifier + * @exec_quantums: array of requested EQs in milliseconds (0 is infinity) + * @count: number of entries in the array + * + * This function can only be called on PF. + * It will log the provisioned value or an error in case of the failure. + * + * Return: 0 on success or a negative error code on failure. + */ +int xe_gt_sriov_pf_config_set_groups_exec_quantums(struct xe_gt *gt, unsigned int vfid, + u32 *exec_quantums, u32 count) +{ + int err; + + guard(mutex)(xe_gt_sriov_pf_master_mutex(gt)); + + err = pf_provision_groups_exec_quantums(gt, vfid, exec_quantums, count); + + return pf_groups_cfg_set_u32_done(gt, vfid, exec_quantums, count, + pf_get_groups_exec_quantums, + "execution quantum", + exec_quantum_unit, err); +} + +/** + * xe_gt_sriov_pf_config_get_groups_exec_quantums() - Get PF/VF sched groups EQs + * @gt: the &xe_gt + * @vfid: the PF or VF identifier + * @exec_quantums: array in which to store the execution quantums values + * @count: maximum number of entries to store + * + * This function can only be called on PF. + */ +void xe_gt_sriov_pf_config_get_groups_exec_quantums(struct xe_gt *gt, unsigned int vfid, + u32 *exec_quantums, u32 count) +{ + guard(mutex)(xe_gt_sriov_pf_master_mutex(gt)); + + xe_gt_assert(gt, count <= GUC_MAX_SCHED_GROUPS); + + pf_get_groups_exec_quantums(gt, vfid, exec_quantums, count); +} + static const char *preempt_timeout_unit(u32 preempt_timeout) { return preempt_timeout ? "us" : "(infinity)"; @@ -1987,12 +2146,14 @@ static int pf_provision_preempt_timeout(struct xe_gt *gt, unsigned int vfid, { struct xe_gt_sriov_config *config = pf_pick_vf_config(gt, vfid); int err; + int i; err = pf_push_vf_cfg_preempt_timeout(gt, vfid, &preempt_timeout); if (unlikely(err)) return err; - config->preempt_timeout = preempt_timeout; + for (i = 0; i < ARRAY_SIZE(config->preempt_timeout); i++) + config->preempt_timeout[i] = preempt_timeout; return 0; } @@ -2001,7 +2162,7 @@ static u32 pf_get_preempt_timeout(struct xe_gt *gt, unsigned int vfid) { struct xe_gt_sriov_config *config = pf_pick_vf_config(gt, vfid); - return config->preempt_timeout; + return config->preempt_timeout[0]; } /** @@ -2107,6 +2268,89 @@ int xe_gt_sriov_pf_config_bulk_set_preempt_timeout_locked(struct xe_gt *gt, u32 preempt_timeout_unit, n, err); } +static int pf_provision_groups_preempt_timeouts(struct xe_gt *gt, unsigned int vfid, + const u32 *preempt_timeouts, u32 count) +{ + struct xe_gt_sriov_config *config = pf_pick_vf_config(gt, vfid); + int err; + int i; + + err = pf_push_vf_grp_cfg_u32(gt, vfid, GUC_KLV_VF_CFG_ENGINE_GROUP_PREEMPT_TIMEOUT_KEY, + preempt_timeouts, count); + if (unlikely(err)) + return err; + + /* + * GuC silently clamps values exceeding the max and zeroes out the + * quantum for groups not in the klv payload + */ + for (i = 0; i < ARRAY_SIZE(config->preempt_timeout); i++) { + if (i < count) + config->preempt_timeout[i] = + min_t(u32, preempt_timeouts[i], + GUC_KLV_VF_CFG_PREEMPT_TIMEOUT_MAX_VALUE); + else + config->preempt_timeout[i] = 0; + } + + return 0; +} + +static void pf_get_groups_preempt_timeouts(struct xe_gt *gt, unsigned int vfid, + u32 *preempt_timeouts, u32 max_count) +{ + struct xe_gt_sriov_config *config = pf_pick_vf_config(gt, vfid); + u32 count = min_t(u32, max_count, ARRAY_SIZE(config->preempt_timeout)); + + memcpy(preempt_timeouts, config->preempt_timeout, sizeof(u32) * count); +} + +/** + * xe_gt_sriov_pf_config_set_groups_preempt_timeouts() - Configure PF/VF PTs for sched groups. + * @gt: the &xe_gt + * @vfid: the PF or VF identifier + * @preempt_timeouts: array of requested PTs in microseconds (0 is infinity) + * @count: number of entries in the array + * + * This function can only be called on PF. + * It will log the provisioned value or an error in case of the failure. + * + * Return: 0 on success or a negative error code on failure. + */ +int xe_gt_sriov_pf_config_set_groups_preempt_timeouts(struct xe_gt *gt, unsigned int vfid, + u32 *preempt_timeouts, u32 count) +{ + int err; + + guard(mutex)(xe_gt_sriov_pf_master_mutex(gt)); + + err = pf_provision_groups_preempt_timeouts(gt, vfid, preempt_timeouts, count); + + return pf_groups_cfg_set_u32_done(gt, vfid, preempt_timeouts, count, + pf_get_groups_preempt_timeouts, + "preempt_timeout", + preempt_timeout_unit, err); +} + +/** + * xe_gt_sriov_pf_config_get_groups_preempt_timeouts() - Get PF/VF sched groups PTs + * @gt: the &xe_gt + * @vfid: the PF or VF identifier + * @preempt_timeouts: array in which to store the preemption timeouts values + * @count: maximum number of entries to store + * + * This function can only be called on PF. + */ +void xe_gt_sriov_pf_config_get_groups_preempt_timeouts(struct xe_gt *gt, unsigned int vfid, + u32 *preempt_timeouts, u32 count) +{ + guard(mutex)(xe_gt_sriov_pf_master_mutex(gt)); + + xe_gt_assert(gt, count <= GUC_MAX_SCHED_GROUPS); + + pf_get_groups_preempt_timeouts(gt, vfid, preempt_timeouts, count); +} + static const char *sched_priority_unit(u32 priority) { return priority == GUC_SCHED_PRIORITY_LOW ? "(low)" : @@ -2180,10 +2424,14 @@ u32 xe_gt_sriov_pf_config_get_sched_priority(struct xe_gt *gt, unsigned int vfid static void pf_reset_config_sched(struct xe_gt *gt, struct xe_gt_sriov_config *config) { + int i; + lockdep_assert_held(xe_gt_sriov_pf_master_mutex(gt)); - config->exec_quantum = 0; - config->preempt_timeout = 0; + for (i = 0; i < ARRAY_SIZE(config->exec_quantum); i++) { + config->exec_quantum[i] = 0; + config->preempt_timeout[i] = 0; + } } static int pf_provision_threshold(struct xe_gt *gt, unsigned int vfid, @@ -2518,7 +2766,7 @@ ssize_t xe_gt_sriov_pf_config_save(struct xe_gt *gt, unsigned int vfid, void *bu ret = -ENOBUFS; } else { config = pf_pick_vf_config(gt, vfid); - ret = encode_config(buf, config, false) * sizeof(u32); + ret = encode_config(gt, buf, config, false) * sizeof(u32); } } mutex_unlock(xe_gt_sriov_pf_master_mutex(gt)); @@ -2545,17 +2793,29 @@ static int pf_restore_vf_config_klv(struct xe_gt *gt, unsigned int vfid, return -EBADMSG; return pf_provision_exec_quantum(gt, vfid, value[0]); + case GUC_KLV_VF_CFG_ENGINE_GROUP_EXEC_QUANTUM_KEY: + if (len > GUC_KLV_VF_CFG_ENGINE_GROUP_EXEC_QUANTUM_MAX_LEN) + return -EBADMSG; + return pf_provision_groups_exec_quantums(gt, vfid, value, len); + + case GUC_KLV_VF_CFG_ENGINE_GROUP_PREEMPT_TIMEOUT_KEY: + if (len > GUC_KLV_VF_CFG_ENGINE_GROUP_PREEMPT_TIMEOUT_MAX_LEN) + return -EBADMSG; + return pf_provision_groups_preempt_timeouts(gt, vfid, value, len); + case GUC_KLV_VF_CFG_PREEMPT_TIMEOUT_KEY: if (len != GUC_KLV_VF_CFG_PREEMPT_TIMEOUT_LEN) return -EBADMSG; return pf_provision_preempt_timeout(gt, vfid, value[0]); /* auto-generate case statements */ -#define define_threshold_key_to_provision_case(TAG, ...) \ +#define define_threshold_key_to_provision_case(TAG, NAME, VER...) \ case MAKE_GUC_KLV_VF_CFG_THRESHOLD_KEY(TAG): \ BUILD_BUG_ON(MAKE_GUC_KLV_VF_CFG_THRESHOLD_LEN(TAG) != 1u); \ if (len != MAKE_GUC_KLV_VF_CFG_THRESHOLD_LEN(TAG)) \ return -EBADMSG; \ + if (IF_ARGS(!GUC_FIRMWARE_VER_AT_LEAST(>->uc.guc, VER), false, VER)) \ + return -EKEYREJECTED; \ return pf_provision_threshold(gt, vfid, \ MAKE_XE_GUC_KLV_THRESHOLD_INDEX(TAG), \ value[0]); @@ -2778,11 +3038,12 @@ int xe_gt_sriov_pf_config_print_ggtt(struct xe_gt *gt, struct drm_printer *p) if (!xe_ggtt_node_allocated(config->ggtt_region)) continue; - string_get_size(config->ggtt_region->base.size, 1, STRING_UNITS_2, + string_get_size(xe_ggtt_node_size(config->ggtt_region), 1, STRING_UNITS_2, buf, sizeof(buf)); drm_printf(p, "VF%u:\t%#0llx-%#llx\t(%s)\n", - n, config->ggtt_region->base.start, - config->ggtt_region->base.start + config->ggtt_region->base.size - 1, + n, xe_ggtt_node_addr(config->ggtt_region), + xe_ggtt_node_addr(config->ggtt_region) + + xe_ggtt_node_size(config->ggtt_region) - 1, buf); } diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_config.h b/drivers/gpu/drm/xe/xe_gt_sriov_pf_config.h index 4975730423d7..3c6c8b6655af 100644 --- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_config.h +++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_config.h @@ -46,6 +46,11 @@ int xe_gt_sriov_pf_config_set_exec_quantum_locked(struct xe_gt *gt, unsigned int u32 exec_quantum); int xe_gt_sriov_pf_config_bulk_set_exec_quantum_locked(struct xe_gt *gt, u32 exec_quantum); +void xe_gt_sriov_pf_config_get_groups_exec_quantums(struct xe_gt *gt, unsigned int vfid, + u32 *exec_quantum, u32 max_count); +int xe_gt_sriov_pf_config_set_groups_exec_quantums(struct xe_gt *gt, unsigned int vfid, + u32 *exec_quantum, u32 count); + u32 xe_gt_sriov_pf_config_get_preempt_timeout(struct xe_gt *gt, unsigned int vfid); int xe_gt_sriov_pf_config_set_preempt_timeout(struct xe_gt *gt, unsigned int vfid, u32 preempt_timeout); @@ -55,6 +60,11 @@ int xe_gt_sriov_pf_config_set_preempt_timeout_locked(struct xe_gt *gt, unsigned u32 preempt_timeout); int xe_gt_sriov_pf_config_bulk_set_preempt_timeout_locked(struct xe_gt *gt, u32 preempt_timeout); +void xe_gt_sriov_pf_config_get_groups_preempt_timeouts(struct xe_gt *gt, unsigned int vfid, + u32 *preempt_timeout, u32 max_count); +int xe_gt_sriov_pf_config_set_groups_preempt_timeouts(struct xe_gt *gt, unsigned int vfid, + u32 *preempt_timeout, u32 count); + u32 xe_gt_sriov_pf_config_get_sched_priority(struct xe_gt *gt, unsigned int vfid); int xe_gt_sriov_pf_config_set_sched_priority(struct xe_gt *gt, unsigned int vfid, u32 priority); diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_config_types.h b/drivers/gpu/drm/xe/xe_gt_sriov_pf_config_types.h index 686c7b3b6d7a..75a48d0fa859 100644 --- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_config_types.h +++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_config_types.h @@ -6,6 +6,7 @@ #ifndef _XE_GT_SRIOV_PF_CONFIG_TYPES_H_ #define _XE_GT_SRIOV_PF_CONFIG_TYPES_H_ +#include "abi/guc_scheduler_abi.h" #include "xe_ggtt_types.h" #include "xe_guc_klv_thresholds_set_types.h" @@ -30,9 +31,9 @@ struct xe_gt_sriov_config { /** @begin_db: start index of GuC doorbell ID range. */ u16 begin_db; /** @exec_quantum: execution-quantum in milliseconds. */ - u32 exec_quantum; + u32 exec_quantum[GUC_MAX_SCHED_GROUPS]; /** @preempt_timeout: preemption timeout in microseconds. */ - u32 preempt_timeout; + u32 preempt_timeout[GUC_MAX_SCHED_GROUPS]; /** @sched_priority: scheduling priority. */ u32 sched_priority; /** @thresholds: GuC thresholds for adverse events notifications. */ diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_debugfs.c b/drivers/gpu/drm/xe/xe_gt_sriov_pf_debugfs.c index 5278ea4fd655..f97abb02aebd 100644 --- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_debugfs.c +++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_debugfs.c @@ -8,7 +8,6 @@ #include <drm/drm_print.h> #include <drm/drm_debugfs.h> -#include "xe_bo.h" #include "xe_debugfs.h" #include "xe_device.h" #include "xe_gt.h" @@ -21,6 +20,7 @@ #include "xe_gt_sriov_pf_monitor.h" #include "xe_gt_sriov_pf_policy.h" #include "xe_gt_sriov_pf_service.h" +#include "xe_guc.h" #include "xe_pm.h" #include "xe_sriov_pf.h" #include "xe_sriov_pf_provision.h" @@ -123,11 +123,10 @@ static int POLICY##_set(void *data, u64 val) \ if (val > (TYPE)~0ull) \ return -EOVERFLOW; \ \ - xe_pm_runtime_get(xe); \ + guard(xe_pm_runtime)(xe); \ err = xe_gt_sriov_pf_policy_set_##POLICY(gt, val); \ if (!err) \ xe_sriov_pf_provision_set_custom_mode(xe); \ - xe_pm_runtime_put(xe); \ \ return err; \ } \ @@ -160,6 +159,299 @@ static void pf_add_policy_attrs(struct xe_gt *gt, struct dentry *parent) * /sys/kernel/debug/dri/BDF/ * ├── sriov * : ├── pf + * : ├── tile0 + * : ├── gt0 + * : ├── sched_groups_mode + * ├── sched_groups_exec_quantums_ms + * ├── sched_groups_preempt_timeout_us + * ├── sched_groups + * : ├── group0 + * : + * : └── groupN + * ├── vf1 + * : ├── tile0 + * : ├── gt0 + * : ├── sched_groups_exec_quantums_ms + * ├── sched_groups_preempt_timeout_us + * : + */ + +static const char *sched_group_mode_to_string(enum xe_sriov_sched_group_modes mode) +{ + switch (mode) { + case XE_SRIOV_SCHED_GROUPS_DISABLED: + return "disabled"; + case XE_SRIOV_SCHED_GROUPS_MEDIA_SLICES: + return "media_slices"; + case XE_SRIOV_SCHED_GROUPS_MODES_COUNT: + /* dummy mode to make the compiler happy */ + break; + } + + return "unknown"; +} + +static int sched_groups_info(struct seq_file *m, void *data) +{ + struct drm_printer p = drm_seq_file_printer(m); + struct xe_gt *gt = extract_gt(m->private); + enum xe_sriov_sched_group_modes current_mode = + gt->sriov.pf.policy.guc.sched_groups.current_mode; + enum xe_sriov_sched_group_modes mode; + + for (mode = XE_SRIOV_SCHED_GROUPS_DISABLED; + mode < XE_SRIOV_SCHED_GROUPS_MODES_COUNT; + mode++) { + if (!xe_sriov_gt_pf_policy_has_sched_group_mode(gt, mode)) + continue; + + drm_printf(&p, "%s%s%s%s", + mode == XE_SRIOV_SCHED_GROUPS_DISABLED ? "" : " ", + mode == current_mode ? "[" : "", + sched_group_mode_to_string(mode), + mode == current_mode ? "]" : ""); + } + + drm_puts(&p, "\n"); + + return 0; +} + +static int sched_groups_open(struct inode *inode, struct file *file) +{ + return single_open(file, sched_groups_info, inode->i_private); +} + +static ssize_t sched_groups_write(struct file *file, const char __user *ubuf, + size_t size, loff_t *pos) +{ + struct xe_gt *gt = extract_gt(file_inode(file)->i_private); + enum xe_sriov_sched_group_modes mode; + char name[32]; + int ret; + + if (*pos) + return -ESPIPE; + + if (!size) + return -ENODATA; + + if (size > sizeof(name) - 1) + return -EINVAL; + + ret = simple_write_to_buffer(name, sizeof(name) - 1, pos, ubuf, size); + if (ret < 0) + return ret; + name[ret] = '\0'; + + for (mode = XE_SRIOV_SCHED_GROUPS_DISABLED; + mode < XE_SRIOV_SCHED_GROUPS_MODES_COUNT; + mode++) + if (sysfs_streq(name, sched_group_mode_to_string(mode))) + break; + + if (mode == XE_SRIOV_SCHED_GROUPS_MODES_COUNT) + return -EINVAL; + + guard(xe_pm_runtime)(gt_to_xe(gt)); + ret = xe_gt_sriov_pf_policy_set_sched_groups_mode(gt, mode); + + return ret < 0 ? ret : size; +} + +static const struct file_operations sched_groups_fops = { + .owner = THIS_MODULE, + .open = sched_groups_open, + .read = seq_read, + .write = sched_groups_write, + .llseek = seq_lseek, + .release = single_release, +}; + +static int sched_groups_config_show(struct seq_file *m, void *data, + void (*get)(struct xe_gt *, unsigned int, u32 *, u32)) +{ + struct drm_printer p = drm_seq_file_printer(m); + unsigned int vfid = extract_vfid(m->private); + struct xe_gt *gt = extract_gt(m->private); + u32 values[GUC_MAX_SCHED_GROUPS]; + bool first = true; + u8 group; + + get(gt, vfid, values, ARRAY_SIZE(values)); + + for (group = 0; group < ARRAY_SIZE(values); group++) { + drm_printf(&p, "%s%u", first ? "" : ",", values[group]); + + first = false; + } + + drm_puts(&p, "\n"); + + return 0; +} + +static ssize_t sched_groups_config_write(struct file *file, const char __user *ubuf, + size_t size, loff_t *pos, + int (*set)(struct xe_gt *, unsigned int, u32 *, u32)) +{ + struct dentry *parent = file_inode(file)->i_private; + unsigned int vfid = extract_vfid(parent); + struct xe_gt *gt = extract_gt(parent); + u32 values[GUC_MAX_SCHED_GROUPS]; + int *input __free(kfree) = NULL; + u32 count; + int ret; + int i; + + if (*pos) + return -ESPIPE; + + if (!size) + return -ENODATA; + + ret = parse_int_array_user(ubuf, min(size, GUC_MAX_SCHED_GROUPS * sizeof(u32)), &input); + if (ret) + return ret; + + count = input[0]; + if (count > GUC_MAX_SCHED_GROUPS) + return -E2BIG; + + for (i = 0; i < count; i++) { + if (input[i + 1] < 0 || input[i + 1] > S32_MAX) + return -EINVAL; + + values[i] = input[i + 1]; + } + + guard(xe_pm_runtime)(gt_to_xe(gt)); + ret = set(gt, vfid, values, count); + + return ret < 0 ? ret : size; +} + +#define DEFINE_SRIOV_GT_GRP_CFG_DEBUGFS_ATTRIBUTE(CONFIG) \ +static int sched_groups_##CONFIG##_show(struct seq_file *m, void *data) \ +{ \ + return sched_groups_config_show(m, data, \ + xe_gt_sriov_pf_config_get_groups_##CONFIG); \ +} \ + \ +static int sched_groups_##CONFIG##_open(struct inode *inode, struct file *file) \ +{ \ + return single_open(file, sched_groups_##CONFIG##_show, \ + inode->i_private); \ +} \ + \ +static ssize_t sched_groups_##CONFIG##_write(struct file *file, \ + const char __user *ubuf, \ + size_t size, loff_t *pos) \ +{ \ + return sched_groups_config_write(file, ubuf, size, pos, \ + xe_gt_sriov_pf_config_set_groups_##CONFIG); \ +} \ + \ +static const struct file_operations sched_groups_##CONFIG##_fops = { \ + .owner = THIS_MODULE, \ + .open = sched_groups_##CONFIG##_open, \ + .read = seq_read, \ + .llseek = seq_lseek, \ + .write = sched_groups_##CONFIG##_write, \ + .release = single_release, \ +} + +DEFINE_SRIOV_GT_GRP_CFG_DEBUGFS_ATTRIBUTE(exec_quantums); +DEFINE_SRIOV_GT_GRP_CFG_DEBUGFS_ATTRIBUTE(preempt_timeouts); + +static ssize_t sched_group_engines_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct dentry *dent = file_dentry(file); + struct xe_gt *gt = extract_gt(dent->d_parent->d_parent); + struct xe_gt_sriov_scheduler_groups *info = >->sriov.pf.policy.guc.sched_groups; + struct guc_sched_group *groups = info->modes[info->current_mode].groups; + u32 num_groups = info->modes[info->current_mode].num_groups; + unsigned int group = (uintptr_t)extract_priv(dent); + struct xe_hw_engine *hwe; + enum xe_hw_engine_id id; + char engines[128]; + + engines[0] = '\0'; + + if (group < num_groups) { + for_each_hw_engine(hwe, gt, id) { + u8 guc_class = xe_engine_class_to_guc_class(hwe->class); + u32 mask = groups[group].engines[guc_class]; + + if (mask & BIT(hwe->logical_instance)) { + strlcat(engines, hwe->name, sizeof(engines)); + strlcat(engines, " ", sizeof(engines)); + } + } + strlcat(engines, "\n", sizeof(engines)); + } + + return simple_read_from_buffer(buf, count, ppos, engines, strlen(engines)); +} + +static const struct file_operations sched_group_engines_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .read = sched_group_engines_read, + .llseek = default_llseek, +}; + +static void pf_add_sched_groups(struct xe_gt *gt, struct dentry *parent, unsigned int vfid) +{ + struct dentry *groups; + u8 group; + + xe_gt_assert(gt, gt == extract_gt(parent)); + xe_gt_assert(gt, vfid == extract_vfid(parent)); + + /* + * TODO: we currently call this function before we initialize scheduler + * groups, so at this point in time we don't know if there are any + * valid groups on the GT and we can't selectively register the debugfs + * only if there are any. Therefore, we always register the debugfs + * files if we're on a platform that has support for groups. + * We should rework the flow so that debugfs is registered after the + * policy init, so that we check if there are valid groups before + * adding the debugfs files. + * Similarly, instead of using GUC_MAX_SCHED_GROUPS we could use + * gt->sriov.pf.policy.guc.sched_groups.max_number_of_groups. + */ + if (!xe_sriov_gt_pf_policy_has_sched_groups_support(gt)) + return; + + debugfs_create_file("sched_groups_exec_quantums_ms", 0644, parent, parent, + &sched_groups_exec_quantums_fops); + debugfs_create_file("sched_groups_preempt_timeouts_us", 0644, parent, parent, + &sched_groups_preempt_timeouts_fops); + + if (vfid != PFID) + return; + + debugfs_create_file("sched_groups_mode", 0644, parent, parent, &sched_groups_fops); + + groups = debugfs_create_dir("sched_groups", parent); + if (IS_ERR(groups)) + return; + + for (group = 0; group < GUC_MAX_SCHED_GROUPS; group++) { + char name[10]; + + snprintf(name, sizeof(name), "group%u", group); + debugfs_create_file(name, 0644, groups, (void *)(uintptr_t)group, + &sched_group_engines_fops); + } +} + +/* + * /sys/kernel/debug/dri/BDF/ + * ├── sriov + * : ├── pf * │ ├── tile0 * │ : ├── gt0 * │ : ├── doorbells_spare @@ -189,12 +481,11 @@ static int CONFIG##_set(void *data, u64 val) \ if (val > (TYPE)~0ull) \ return -EOVERFLOW; \ \ - xe_pm_runtime_get(xe); \ + guard(xe_pm_runtime)(xe); \ err = xe_sriov_pf_wait_ready(xe) ?: \ xe_gt_sriov_pf_config_set_##CONFIG(gt, vfid, val); \ if (!err) \ xe_sriov_pf_provision_set_custom_mode(xe); \ - xe_pm_runtime_put(xe); \ \ return err; \ } \ @@ -249,11 +540,10 @@ static int set_threshold(void *data, u64 val, enum xe_guc_klv_threshold_index in if (val > (u32)~0ull) return -EOVERFLOW; - xe_pm_runtime_get(xe); + guard(xe_pm_runtime)(xe); err = xe_gt_sriov_pf_config_set_threshold(gt, vfid, index, val); if (!err) xe_sriov_pf_provision_set_custom_mode(xe); - xe_pm_runtime_put(xe); return err; } @@ -304,9 +594,11 @@ static void pf_add_config_attrs(struct xe_gt *gt, struct dentry *parent, unsigne &sched_priority_fops); /* register all threshold attributes */ -#define register_threshold_attribute(TAG, NAME, ...) \ - debugfs_create_file_unsafe("threshold_" #NAME, 0644, parent, parent, \ - &NAME##_fops); +#define register_threshold_attribute(TAG, NAME, VER...) ({ \ + if (IF_ARGS(GUC_FIRMWARE_VER_AT_LEAST(>->uc.guc, VER), true, VER)) \ + debugfs_create_file_unsafe("threshold_" #NAME, 0644, parent, parent, \ + &NAME##_fops); \ +}); MAKE_XE_GUC_KLV_THRESHOLDS_SET(register_threshold_attribute) #undef register_threshold_attribute } @@ -358,9 +650,8 @@ static ssize_t control_write(struct file *file, const char __user *buf, size_t c xe_gt_assert(gt, sizeof(cmd) > strlen(control_cmds[n].cmd)); if (sysfs_streq(cmd, control_cmds[n].cmd)) { - xe_pm_runtime_get(xe); + guard(xe_pm_runtime)(xe); ret = control_cmds[n].fn ? (*control_cmds[n].fn)(gt, vfid) : 0; - xe_pm_runtime_put(xe); break; } } @@ -519,6 +810,7 @@ static void pf_populate_gt(struct xe_gt *gt, struct dentry *dent, unsigned int v if (vfid) { pf_add_config_attrs(gt, dent, vfid); + pf_add_sched_groups(gt, dent, vfid); debugfs_create_file("control", 0600, dent, NULL, &control_ops); @@ -532,6 +824,7 @@ static void pf_populate_gt(struct xe_gt *gt, struct dentry *dent, unsigned int v } else { pf_add_config_attrs(gt, dent, PFID); pf_add_policy_attrs(gt, dent); + pf_add_sched_groups(gt, dent, PFID); drm_debugfs_create_files(pf_info, ARRAY_SIZE(pf_info), dent, minor); } diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_migration.c b/drivers/gpu/drm/xe/xe_gt_sriov_pf_migration.c index 3174a8dee779..87a164efcc33 100644 --- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_migration.c +++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_migration.c @@ -14,7 +14,6 @@ #include "xe_gt_sriov_pf.h" #include "xe_gt_sriov_pf_config.h" #include "xe_gt_sriov_pf_control.h" -#include "xe_gt_sriov_pf_helpers.h" #include "xe_gt_sriov_pf_migration.h" #include "xe_gt_sriov_printk.h" #include "xe_guc.h" @@ -25,6 +24,7 @@ #include "xe_sriov.h" #include "xe_sriov_packet.h" #include "xe_sriov_packet_types.h" +#include "xe_sriov_pf_helpers.h" #include "xe_sriov_pf_migration.h" #define XE_GT_SRIOV_PF_MIGRATION_RING_SIZE 5 @@ -1026,7 +1026,7 @@ static void action_ring_cleanup(void *arg) static void pf_gt_migration_check_support(struct xe_gt *gt) { - if (GUC_FIRMWARE_VER(>->uc.guc) < MAKE_GUC_VER(70, 54, 0)) + if (!GUC_FIRMWARE_VER_AT_LEAST(>->uc.guc, 70, 54)) xe_sriov_pf_migration_disable(gt_to_xe(gt), "requires GuC version >= 70.54.0"); } diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_policy.c b/drivers/gpu/drm/xe/xe_gt_sriov_pf_policy.c index 4445f660e6d1..848e24926ecd 100644 --- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_policy.c +++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_policy.c @@ -3,16 +3,19 @@ * Copyright © 2023-2024 Intel Corporation */ +#include <drm/drm_managed.h> + #include "abi/guc_actions_sriov_abi.h" -#include "xe_bo.h" #include "xe_gt.h" #include "xe_gt_sriov_pf_helpers.h" #include "xe_gt_sriov_pf_policy.h" #include "xe_gt_sriov_printk.h" +#include "xe_guc.h" #include "xe_guc_buf.h" #include "xe_guc_ct.h" #include "xe_guc_klv_helpers.h" +#include "xe_guc_submit.h" #include "xe_pm.h" /* @@ -94,6 +97,23 @@ static int pf_push_policy_u32(struct xe_gt *gt, u16 key, u32 value) return pf_push_policy_klvs(gt, 1, klv, ARRAY_SIZE(klv)); } +static int pf_push_policy_payload(struct xe_gt *gt, u16 key, void *payload, u32 num_dwords) +{ + CLASS(xe_guc_buf, buf)(>->uc.guc.buf, GUC_KLV_LEN_MIN + num_dwords); + u32 *klv; + + if (!xe_guc_buf_is_valid(buf)) + return -ENOBUFS; + + klv = xe_guc_buf_cpu_ptr(buf); + + klv[0] = PREP_GUC_KLV(key, num_dwords); + if (num_dwords) + memcpy(&klv[1], payload, num_dwords * sizeof(u32)); + + return pf_push_policy_buf_klvs(gt, 1, buf, GUC_KLV_LEN_MIN + num_dwords); +} + static int pf_update_policy_bool(struct xe_gt *gt, u16 key, bool *policy, bool value) { int err; @@ -351,11 +371,306 @@ u32 xe_gt_sriov_pf_policy_get_sample_period(struct xe_gt *gt) return value; } +static void pf_sched_group_media_slices(struct xe_gt *gt, struct guc_sched_group **groups, + u32 *num_groups) +{ + u8 slice_to_group[MAX_MEDIA_SLICES]; + u32 vecs_mask = VECS_INSTANCES(gt); + u32 gsc_mask = GSCCS_INSTANCES(gt); + u32 vcs_mask = VCS_INSTANCES(gt); + struct guc_sched_group *values; + struct xe_hw_engine *hwe; + enum xe_hw_engine_id id; + int group = 0; + int slice; + + xe_gt_assert(gt, xe_gt_is_media_type(gt)); + + /* + * Post-BMG the matching of video engines to slices changes, so for now + * we don't allow this mode on those platforms. + */ + if (gt_to_xe(gt)->info.platform > XE_BATTLEMAGE) + return; + + /* + * On BMG and older platforms a media slice has 2 VCS and a VECS. We + * bundle the GSC with the first slice. + */ + for (slice = 0; slice < MAX_MEDIA_SLICES; slice++) { + if ((vcs_mask & 0x3) || (vecs_mask & 0x1) || (gsc_mask & 0x1)) + slice_to_group[slice] = group++; + + vcs_mask >>= 2; + vecs_mask >>= 1; + gsc_mask >>= 1; + } + + xe_gt_assert(gt, !vcs_mask); + xe_gt_assert(gt, !vecs_mask); + xe_gt_assert(gt, !gsc_mask); + + /* We need at least 2 slices to split them up */ + if (group < 2) + return; + + /* + * If we have more groups than the GuC can support then we don't want to + * expose this specific mode, because the GuC will return an error if we + * try to enable it. + */ + if (group > gt->sriov.pf.policy.guc.sched_groups.max_groups) { + xe_gt_sriov_notice(gt, "media_slice mode has too many groups: %u vs %u\n", + group, gt->sriov.pf.policy.guc.sched_groups.max_groups); + return; + } + + /* The GuC expects an array with a guc_sched_group entry for each group */ + values = drmm_kcalloc(>_to_xe(gt)->drm, group, sizeof(struct guc_sched_group), + GFP_KERNEL); + if (!values) + return; + + for_each_hw_engine(hwe, gt, id) { + u8 guc_class = xe_engine_class_to_guc_class(hwe->class); + + switch (hwe->class) { + case XE_ENGINE_CLASS_VIDEO_DECODE: + slice = hwe->instance / 2; + break; + case XE_ENGINE_CLASS_VIDEO_ENHANCE: + slice = hwe->instance; + break; + case XE_ENGINE_CLASS_OTHER: + slice = 0; + break; + default: + xe_gt_assert_msg(gt, false, + "unknown media gt class %u (%s) during EGS setup\n", + hwe->class, hwe->name); + slice = 0; + } + + values[slice_to_group[slice]].engines[guc_class] |= BIT(hwe->logical_instance); + } + + *groups = values; + *num_groups = group; +} + +/** + * xe_sriov_gt_pf_policy_has_sched_groups_support() - Checks whether scheduler + * groups are supported. + * @gt: the &xe_gt + * + * This function can only be called on PF. + * + * Return: true if scheduler groups are supported, false otherwise. + */ +bool xe_sriov_gt_pf_policy_has_sched_groups_support(struct xe_gt *gt) +{ + xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt))); + + /* + * The GuC supports scheduler groups from v70.53.0, but a fix for it has + * been merged in v70.55.1, so we require the latter. The feature is + * also only enabled on BMG and newer FW. + */ + return GUC_FIRMWARE_VER_AT_LEAST(>->uc.guc, 70, 55, 1) && + gt_to_xe(gt)->info.platform >= XE_BATTLEMAGE; +} + +static void pf_init_sched_groups(struct xe_gt *gt) +{ + enum xe_sriov_sched_group_modes m; + + if (!xe_sriov_gt_pf_policy_has_sched_groups_support(gt)) + return; + + /* + * The GuC interface supports up to 8 groups. However, the GuC only + * fully allocates resources for a subset of groups, based on the number + * of engines and expected usage. The plan is for this to become + * queryable via H2G, but for now GuC FW for all devices supports a + * maximum of 2 groups so we can just hardcode that. + */ + gt->sriov.pf.policy.guc.sched_groups.max_groups = 2; + + for (m = XE_SRIOV_SCHED_GROUPS_DISABLED + 1; m < XE_SRIOV_SCHED_GROUPS_MODES_COUNT; m++) { + u32 *num_groups = >->sriov.pf.policy.guc.sched_groups.modes[m].num_groups; + struct guc_sched_group **groups = + >->sriov.pf.policy.guc.sched_groups.modes[m].groups; + + switch (m) { + case XE_SRIOV_SCHED_GROUPS_MEDIA_SLICES: + /* this mode only has groups on the media GT */ + if (xe_gt_is_media_type(gt)) + pf_sched_group_media_slices(gt, groups, num_groups); + break; + case XE_SRIOV_SCHED_GROUPS_DISABLED: + case XE_SRIOV_SCHED_GROUPS_MODES_COUNT: + /* + * By defining m of type enum xe_sriov_sched_group_modes + * we can get the compiler to automatically flag + * missing cases if new enum entries are added. However, + * to keep the compiler happy we also need to add the + * cases that are excluded from the loop. + */ + xe_gt_assert(gt, false); + break; + } + + xe_gt_assert(gt, *num_groups < GUC_MAX_SCHED_GROUPS); + + if (*num_groups) + gt->sriov.pf.policy.guc.sched_groups.supported_modes |= BIT(m); + } +} + +/** + * xe_sriov_gt_pf_policy_has_multi_group_modes() - check whether the GT supports + * any scheduler modes that have multiple groups + * @gt: the &xe_gt to check + * + * This function can only be called on PF. + * + * Return: true if the GT supports modes with multiple groups, false otherwise. + */ +bool xe_sriov_gt_pf_policy_has_multi_group_modes(struct xe_gt *gt) +{ + return gt->sriov.pf.policy.guc.sched_groups.supported_modes; +} + +/** + * xe_sriov_gt_pf_policy_has_sched_group_mode() - check whether the GT supports + * a specific scheduler group mode + * @gt: the &xe_gt to check + * @mode: the mode to check + * + * This function can only be called on PF. + * + * Return: true if the GT supports the specified mode, false otherwise. + */ +bool xe_sriov_gt_pf_policy_has_sched_group_mode(struct xe_gt *gt, + enum xe_sriov_sched_group_modes mode) +{ + if (mode == XE_SRIOV_SCHED_GROUPS_DISABLED) + return true; + + return gt->sriov.pf.policy.guc.sched_groups.supported_modes & BIT(mode); +} + +static int __pf_provision_sched_groups(struct xe_gt *gt, enum xe_sriov_sched_group_modes mode) +{ + struct guc_sched_group *groups = gt->sriov.pf.policy.guc.sched_groups.modes[mode].groups; + u32 num_groups = gt->sriov.pf.policy.guc.sched_groups.modes[mode].num_groups; + + return pf_push_policy_payload(gt, GUC_KLV_VGT_POLICY_ENGINE_GROUP_CONFIG_KEY, + groups, num_groups * GUC_MAX_ENGINE_CLASSES); +} + +static int pf_provision_sched_groups(struct xe_gt *gt, enum xe_sriov_sched_group_modes mode) +{ + int err; + + xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt))); + lockdep_assert_held(xe_gt_sriov_pf_master_mutex(gt)); + + if (!xe_sriov_gt_pf_policy_has_sched_group_mode(gt, mode)) + return -EINVAL; + + /* already in the desired mode */ + if (gt->sriov.pf.policy.guc.sched_groups.current_mode == mode) + return 0; + + /* + * We don't allow changing this with VFs active since it is hard for + * VFs to check. + */ + if (xe_sriov_pf_num_vfs(gt_to_xe(gt))) + return -EBUSY; + + /* + * The GuC silently ignores the setting if any MLRC contexts are + * registered. We expect the admin to make sure that all apps that use + * MLRC are terminated before scheduler groups are enabled, so this + * check is just to make sure that the exec_queue destruction has been + * completed. + */ + if (mode != XE_SRIOV_SCHED_GROUPS_DISABLED && + xe_guc_has_registered_mlrc_queues(>->uc.guc)) { + xe_gt_sriov_notice(gt, "can't enable sched groups with active MLRC queues\n"); + return -EPERM; + } + + err = __pf_provision_sched_groups(gt, mode); + if (err) + return err; + + gt->sriov.pf.policy.guc.sched_groups.current_mode = mode; + + return 0; +} + +static int pf_reprovision_sched_groups(struct xe_gt *gt) +{ + xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt))); + lockdep_assert_held(xe_gt_sriov_pf_master_mutex(gt)); + + /* We only have something to provision if we have possible groups */ + if (!xe_sriov_gt_pf_policy_has_multi_group_modes(gt)) + return 0; + + return __pf_provision_sched_groups(gt, gt->sriov.pf.policy.guc.sched_groups.current_mode); +} + +static void pf_sanitize_sched_groups(struct xe_gt *gt) +{ + xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt))); + lockdep_assert_held(xe_gt_sriov_pf_master_mutex(gt)); + + gt->sriov.pf.policy.guc.sched_groups.current_mode = XE_SRIOV_SCHED_GROUPS_DISABLED; +} + +/** + * xe_gt_sriov_pf_policy_set_sched_groups_mode() - Control the 'sched_groups' policy. + * @gt: the &xe_gt where to apply the policy + * @mode: the sched_group mode to be activated + * + * This function can only be called on PF. + * + * Return: 0 on success or a negative error code on failure. + */ +int xe_gt_sriov_pf_policy_set_sched_groups_mode(struct xe_gt *gt, + enum xe_sriov_sched_group_modes mode) +{ + if (!xe_sriov_gt_pf_policy_has_multi_group_modes(gt)) + return -ENODEV; + + guard(mutex)(xe_gt_sriov_pf_master_mutex(gt)); + return pf_provision_sched_groups(gt, mode); +} + +/** + * xe_gt_sriov_pf_policy_sched_groups_enabled() - check whether the GT has + * multiple scheduler groups enabled + * @gt: the &xe_gt to check + * + * This function can only be called on PF. + * + * Return: true if the GT has multiple groups enabled, false otherwise. + */ +bool xe_gt_sriov_pf_policy_sched_groups_enabled(struct xe_gt *gt) +{ + return gt->sriov.pf.policy.guc.sched_groups.current_mode != XE_SRIOV_SCHED_GROUPS_DISABLED; +} + static void pf_sanitize_guc_policies(struct xe_gt *gt) { pf_sanitize_sched_if_idle(gt); pf_sanitize_reset_engine(gt); pf_sanitize_sample_period(gt); + pf_sanitize_sched_groups(gt); } /** @@ -394,6 +709,7 @@ int xe_gt_sriov_pf_policy_reprovision(struct xe_gt *gt, bool reset) err |= pf_reprovision_sched_if_idle(gt); err |= pf_reprovision_reset_engine(gt); err |= pf_reprovision_sample_period(gt); + err |= pf_reprovision_sched_groups(gt); mutex_unlock(xe_gt_sriov_pf_master_mutex(gt)); xe_pm_runtime_put(gt_to_xe(gt)); @@ -401,6 +717,18 @@ int xe_gt_sriov_pf_policy_reprovision(struct xe_gt *gt, bool reset) return err ? -ENXIO : 0; } +/** + * xe_gt_sriov_pf_policy_init() - Initializes the SW state of the PF policies. + * @gt: the &xe_gt + * + * This function can only be called on PF. This function does not touch the HW, + * but must be called after the engines have been initialized. + */ +void xe_gt_sriov_pf_policy_init(struct xe_gt *gt) +{ + pf_init_sched_groups(gt); +} + static void print_guc_policies(struct drm_printer *p, struct xe_gt_sriov_guc_policies *policy) { drm_printf(p, "%s:\t%s\n", diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_policy.h b/drivers/gpu/drm/xe/xe_gt_sriov_pf_policy.h index 2a5dc33dc6d7..bd73aa58f9ca 100644 --- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_policy.h +++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_policy.h @@ -8,6 +8,8 @@ #include <linux/types.h> +#include "xe_gt_sriov_pf_policy_types.h" + struct drm_printer; struct xe_gt; @@ -17,7 +19,15 @@ int xe_gt_sriov_pf_policy_set_reset_engine(struct xe_gt *gt, bool enable); bool xe_gt_sriov_pf_policy_get_reset_engine(struct xe_gt *gt); int xe_gt_sriov_pf_policy_set_sample_period(struct xe_gt *gt, u32 value); u32 xe_gt_sriov_pf_policy_get_sample_period(struct xe_gt *gt); +bool xe_sriov_gt_pf_policy_has_sched_groups_support(struct xe_gt *gt); +bool xe_sriov_gt_pf_policy_has_multi_group_modes(struct xe_gt *gt); +bool xe_sriov_gt_pf_policy_has_sched_group_mode(struct xe_gt *gt, + enum xe_sriov_sched_group_modes mode); +int xe_gt_sriov_pf_policy_set_sched_groups_mode(struct xe_gt *gt, + enum xe_sriov_sched_group_modes mode); +bool xe_gt_sriov_pf_policy_sched_groups_enabled(struct xe_gt *gt); +void xe_gt_sriov_pf_policy_init(struct xe_gt *gt); void xe_gt_sriov_pf_policy_sanitize(struct xe_gt *gt); int xe_gt_sriov_pf_policy_reprovision(struct xe_gt *gt, bool reset); int xe_gt_sriov_pf_policy_print(struct xe_gt *gt, struct drm_printer *p); diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_policy_types.h b/drivers/gpu/drm/xe/xe_gt_sriov_pf_policy_types.h index 4de532af135e..97d278190521 100644 --- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_policy_types.h +++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_policy_types.h @@ -8,16 +8,55 @@ #include <linux/types.h> +#include "abi/guc_scheduler_abi.h" + +/** + * enum xe_sriov_sched_group_modes - list of possible scheduler group modes + * @XE_SRIOV_SCHED_GROUPS_DISABLED: no separate groups (i.e., all engines in group 0) + * @XE_SRIOV_SCHED_GROUPS_MEDIA_SLICES: separate groups for each media slice + * @XE_SRIOV_SCHED_GROUPS_MODES_COUNT: number of valid modes + */ +enum xe_sriov_sched_group_modes { + XE_SRIOV_SCHED_GROUPS_DISABLED = 0, + XE_SRIOV_SCHED_GROUPS_MEDIA_SLICES, + XE_SRIOV_SCHED_GROUPS_MODES_COUNT /* must be last */ +}; + +/** + * struct xe_gt_sriov_scheduler_groups - Scheduler groups policy info + * @max_groups: max number of groups supported by the GuC for the platform + * @supported_modes: mask of supported modes + * @current_mode: active scheduler groups mode + * @modes: array of masks and their number for each mode + * @modes.groups: array of engine instance groups in given mode, with each group + * consisting of GUC_MAX_ENGINE_CLASSES engine instances masks. A + * A NULL value indicates that all the engines are in the same + * group for this mode on this GT. + * @modes.num_groups: number of groups in given mode, zero if all the engines + * are in the same group. + */ +struct xe_gt_sriov_scheduler_groups { + u8 max_groups; + u32 supported_modes; + enum xe_sriov_sched_group_modes current_mode; + struct { + struct guc_sched_group *groups; + u32 num_groups; + } modes[XE_SRIOV_SCHED_GROUPS_MODES_COUNT]; +}; + /** * struct xe_gt_sriov_guc_policies - GuC SR-IOV policies. * @sched_if_idle: controls strict scheduling policy. * @reset_engine: controls engines reset on VF switch policy. * @sample_period: adverse events sampling period (in milliseconds). + * @sched_groups: available scheduling group configurations. */ struct xe_gt_sriov_guc_policies { bool sched_if_idle; bool reset_engine; u32 sample_period; + struct xe_gt_sriov_scheduler_groups sched_groups; }; /** diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_service.c b/drivers/gpu/drm/xe/xe_gt_sriov_pf_service.c index 2eb21610e5a0..b5e0a5b7723e 100644 --- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_service.c +++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_service.c @@ -5,20 +5,19 @@ #include <drm/drm_managed.h> -#include "abi/guc_actions_sriov_abi.h" #include "abi/guc_relay_actions_abi.h" #include "regs/xe_gt_regs.h" #include "regs/xe_guc_regs.h" -#include "regs/xe_regs.h" +#include "xe_assert.h" #include "xe_mmio.h" #include "xe_gt_sriov_printk.h" -#include "xe_gt_sriov_pf_helpers.h" #include "xe_gt_sriov_pf_service.h" #include "xe_gt_sriov_pf_service_types.h" #include "xe_guc_ct.h" #include "xe_guc_hxg_helpers.h" +#include "xe_sriov.h" #include "xe_sriov_pf_service.h" static const struct xe_reg tgl_runtime_regs[] = { diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_vf.c b/drivers/gpu/drm/xe/xe_gt_sriov_vf.c index 4c73a077d314..30e8c2cf5f09 100644 --- a/drivers/gpu/drm/xe/xe_gt_sriov_vf.c +++ b/drivers/gpu/drm/xe/xe_gt_sriov_vf.c @@ -5,6 +5,7 @@ #include <linux/bitfield.h> #include <linux/bsearch.h> +#include <linux/delay.h> #include <drm/drm_managed.h> #include <drm/drm_print.h> @@ -14,7 +15,6 @@ #include "abi/guc_klvs_abi.h" #include "abi/guc_relay_actions_abi.h" #include "regs/xe_gt_regs.h" -#include "regs/xe_gtt_defs.h" #include "xe_assert.h" #include "xe_device.h" @@ -41,6 +41,37 @@ #define make_u64_from_u32(hi, lo) ((u64)((u64)(u32)(hi) << 32 | (u32)(lo))) +#ifdef CONFIG_DRM_XE_DEBUG +enum VF_MIGRATION_WAIT_POINTS { + VF_MIGRATION_WAIT_RESFIX_START = BIT(0), + VF_MIGRATION_WAIT_FIXUPS = BIT(1), + VF_MIGRATION_WAIT_RESTART_JOBS = BIT(2), + VF_MIGRATION_WAIT_RESFIX_DONE = BIT(3), +}; + +#define VF_MIGRATION_WAIT_DELAY_IN_MS 1000 +static void vf_post_migration_inject_wait(struct xe_gt *gt, + enum VF_MIGRATION_WAIT_POINTS wait) +{ + while (gt->sriov.vf.migration.debug.resfix_stoppers & wait) { + xe_gt_dbg(gt, + "*TESTING* injecting %u ms delay due to resfix_stoppers=%#x, to continue clear %#x\n", + VF_MIGRATION_WAIT_DELAY_IN_MS, + gt->sriov.vf.migration.debug.resfix_stoppers, wait); + + msleep(VF_MIGRATION_WAIT_DELAY_IN_MS); + } +} + +#define VF_MIGRATION_INJECT_WAIT(gt, _POS) ({ \ + struct xe_gt *__gt = (gt); \ + vf_post_migration_inject_wait(__gt, VF_MIGRATION_WAIT_##_POS); \ + }) + +#else +#define VF_MIGRATION_INJECT_WAIT(_gt, ...) typecheck(struct xe_gt *, (_gt)) +#endif + static int guc_action_vf_reset(struct xe_guc *guc) { u32 request[GUC_HXG_REQUEST_MSG_MIN_LEN] = { @@ -299,12 +330,13 @@ void xe_gt_sriov_vf_guc_versions(struct xe_gt *gt, *found = gt->sriov.vf.guc_version; } -static int guc_action_vf_notify_resfix_done(struct xe_guc *guc) +static int guc_action_vf_resfix_start(struct xe_guc *guc, u16 marker) { u32 request[GUC_HXG_REQUEST_MSG_MIN_LEN] = { FIELD_PREP(GUC_HXG_MSG_0_ORIGIN, GUC_HXG_ORIGIN_HOST) | FIELD_PREP(GUC_HXG_MSG_0_TYPE, GUC_HXG_TYPE_REQUEST) | - FIELD_PREP(GUC_HXG_REQUEST_MSG_0_ACTION, GUC_ACTION_VF2GUC_NOTIFY_RESFIX_DONE), + FIELD_PREP(GUC_HXG_REQUEST_MSG_0_ACTION, GUC_ACTION_VF2GUC_RESFIX_START) | + FIELD_PREP(VF2GUC_RESFIX_START_REQUEST_MSG_0_MARKER, marker), }; int ret; @@ -313,28 +345,43 @@ static int guc_action_vf_notify_resfix_done(struct xe_guc *guc) return ret > 0 ? -EPROTO : ret; } -/** - * vf_notify_resfix_done - Notify GuC about resource fixups apply completed. - * @gt: the &xe_gt struct instance linked to target GuC - * - * Returns: 0 if the operation completed successfully, or a negative error - * code otherwise. - */ -static int vf_notify_resfix_done(struct xe_gt *gt) +static int vf_resfix_start(struct xe_gt *gt, u16 marker) { struct xe_guc *guc = >->uc.guc; - int err; xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt))); - err = guc_action_vf_notify_resfix_done(guc); - if (unlikely(err)) - xe_gt_sriov_err(gt, "Failed to notify GuC about resource fixup done (%pe)\n", - ERR_PTR(err)); - else - xe_gt_sriov_dbg_verbose(gt, "sent GuC resource fixup done\n"); + VF_MIGRATION_INJECT_WAIT(gt, RESFIX_START); - return err; + xe_gt_sriov_dbg_verbose(gt, "Sending resfix start marker %u\n", marker); + + return guc_action_vf_resfix_start(guc, marker); +} + +static int guc_action_vf_resfix_done(struct xe_guc *guc, u16 marker) +{ + u32 request[GUC_HXG_REQUEST_MSG_MIN_LEN] = { + FIELD_PREP(GUC_HXG_MSG_0_ORIGIN, GUC_HXG_ORIGIN_HOST) | + FIELD_PREP(GUC_HXG_MSG_0_TYPE, GUC_HXG_TYPE_REQUEST) | + FIELD_PREP(GUC_HXG_REQUEST_MSG_0_ACTION, GUC_ACTION_VF2GUC_RESFIX_DONE) | + FIELD_PREP(VF2GUC_RESFIX_DONE_REQUEST_MSG_0_MARKER, marker), + }; + int ret; + + ret = xe_guc_mmio_send(guc, request, ARRAY_SIZE(request)); + + return ret > 0 ? -EPROTO : ret; +} + +static int vf_resfix_done(struct xe_gt *gt, u16 marker) +{ + struct xe_guc *guc = >->uc.guc; + + xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt))); + + xe_gt_sriov_dbg_verbose(gt, "Sending resfix done marker %u\n", marker); + + return guc_action_vf_resfix_done(guc, marker); } static int guc_action_query_single_klv(struct xe_guc *guc, u32 key, @@ -564,6 +611,52 @@ static void vf_cache_gmdid(struct xe_gt *gt) gt->sriov.vf.runtime.gmdid = xe_gt_sriov_vf_gmdid(gt); } +static int vf_query_sched_groups(struct xe_gt *gt) +{ + struct xe_guc *guc = >->uc.guc; + struct xe_uc_fw_version guc_version; + u32 value = 0; + int err; + + xe_gt_sriov_vf_guc_versions(gt, NULL, &guc_version); + + if (MAKE_GUC_VER_STRUCT(guc_version) < MAKE_GUC_VER(1, 26, 0)) + return 0; + + err = guc_action_query_single_klv32(guc, + GUC_KLV_GLOBAL_CFG_GROUP_SCHEDULING_AVAILABLE_KEY, + &value); + if (unlikely(err)) { + xe_gt_sriov_err(gt, "Failed to obtain sched groups status (%pe)\n", + ERR_PTR(err)); + return err; + } + + /* valid values are 0 (disabled) and 1 (enabled) */ + if (value > 1) { + xe_gt_sriov_err(gt, "Invalid sched groups status %u\n", value); + return -EPROTO; + } + + xe_gt_sriov_dbg(gt, "sched groups %s\n", str_enabled_disabled(value)); + return value; +} + +static int vf_cache_sched_groups_status(struct xe_gt *gt) +{ + int ret; + + xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt))); + + ret = vf_query_sched_groups(gt); + if (ret < 0) + return ret; + + gt->sriov.vf.runtime.uses_sched_groups = ret; + + return 0; +} + /** * xe_gt_sriov_vf_query_config - Query SR-IOV config data over MMIO. * @gt: the &xe_gt @@ -593,6 +686,10 @@ int xe_gt_sriov_vf_query_config(struct xe_gt *gt) if (unlikely(err)) return err; + err = vf_cache_sched_groups_status(gt); + if (unlikely(err)) + return err; + if (has_gmdid(xe)) vf_cache_gmdid(gt); @@ -600,6 +697,23 @@ int xe_gt_sriov_vf_query_config(struct xe_gt *gt) } /** + * xe_gt_sriov_vf_sched_groups_enabled() - Check if PF has enabled multiple + * scheduler groups + * @gt: the &xe_gt + * + * This function is for VF use only. + * + * Return: true if shed groups were enabled, false otherwise. + */ +bool xe_gt_sriov_vf_sched_groups_enabled(struct xe_gt *gt) +{ + xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt))); + xe_gt_assert(gt, gt->sriov.vf.guc_version.major); + + return gt->sriov.vf.runtime.uses_sched_groups; +} + +/** * xe_gt_sriov_vf_guc_ids - VF GuC context IDs configuration. * @gt: the &xe_gt * @@ -733,7 +847,7 @@ static void vf_start_migration_recovery(struct xe_gt *gt) spin_lock(>->sriov.vf.migration.lock); - if (!gt->sriov.vf.migration.recovery_queued || + if (!gt->sriov.vf.migration.recovery_queued && !gt->sriov.vf.migration.recovery_teardown) { gt->sriov.vf.migration.recovery_queued = true; WRITE_ONCE(gt->sriov.vf.migration.recovery_inprogress, true); @@ -1123,12 +1237,8 @@ static bool vf_post_migration_shutdown(struct xe_gt *gt) return true; } - spin_lock_irq(>->sriov.vf.migration.lock); - gt->sriov.vf.migration.recovery_queued = false; - spin_unlock_irq(>->sriov.vf.migration.lock); - xe_guc_ct_flush_and_stop(>->uc.guc.ct); - xe_guc_submit_pause(>->uc.guc); + xe_guc_submit_pause_vf(>->uc.guc); xe_tlb_inval_reset(>->tlb_inval); return false; @@ -1144,6 +1254,8 @@ static int vf_post_migration_fixups(struct xe_gt *gt) void *buf = gt->sriov.vf.migration.scratch; int err; + VF_MIGRATION_INJECT_WAIT(gt, FIXUPS); + /* xe_gt_sriov_vf_query_config will fixup the GGTT addresses */ err = xe_gt_sriov_vf_query_config(gt); if (err) @@ -1162,13 +1274,22 @@ static int vf_post_migration_fixups(struct xe_gt *gt) static void vf_post_migration_rearm(struct xe_gt *gt) { + VF_MIGRATION_INJECT_WAIT(gt, RESTART_JOBS); + + /* + * Make sure interrupts on the new HW are properly set. The GuC IRQ + * must be working at this point, since the recovery did started, + * but the rest was not enabled using the procedure from spec. + */ + xe_irq_resume(gt_to_xe(gt)); + xe_guc_ct_restart(>->uc.guc.ct); - xe_guc_submit_unpause_prepare(>->uc.guc); + xe_guc_submit_unpause_prepare_vf(>->uc.guc); } static void vf_post_migration_kickstart(struct xe_gt *gt) { - xe_guc_submit_unpause(>->uc.guc); + xe_guc_submit_unpause_vf(>->uc.guc); } static void vf_post_migration_abort(struct xe_gt *gt) @@ -1183,37 +1304,49 @@ static void vf_post_migration_abort(struct xe_gt *gt) xe_guc_submit_pause_abort(>->uc.guc); } -static int vf_post_migration_notify_resfix_done(struct xe_gt *gt) +static int vf_post_migration_resfix_done(struct xe_gt *gt, u16 marker) { - bool skip_resfix = false; + VF_MIGRATION_INJECT_WAIT(gt, RESFIX_DONE); spin_lock_irq(>->sriov.vf.migration.lock); - if (gt->sriov.vf.migration.recovery_queued) { - skip_resfix = true; - xe_gt_sriov_dbg(gt, "another recovery imminent, resfix skipped\n"); - } else { + if (gt->sriov.vf.migration.recovery_queued) + xe_gt_sriov_dbg(gt, "another recovery imminent\n"); + else WRITE_ONCE(gt->sriov.vf.migration.recovery_inprogress, false); - } spin_unlock_irq(>->sriov.vf.migration.lock); - if (skip_resfix) - return -EAGAIN; + return vf_resfix_done(gt, marker); +} - /* - * Make sure interrupts on the new HW are properly set. The GuC IRQ - * must be working at this point, since the recovery did started, - * but the rest was not enabled using the procedure from spec. - */ - xe_irq_resume(gt_to_xe(gt)); +static int vf_post_migration_resfix_start(struct xe_gt *gt, u16 marker) +{ + int err; + + err = vf_resfix_start(gt, marker); + + guard(spinlock_irq) (>->sriov.vf.migration.lock); + gt->sriov.vf.migration.recovery_queued = false; - return vf_notify_resfix_done(gt); + return err; +} + +static u16 vf_post_migration_next_resfix_marker(struct xe_gt *gt) +{ + xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt))); + + BUILD_BUG_ON(1 + ((typeof(gt->sriov.vf.migration.resfix_marker))~0) > + FIELD_MAX(VF2GUC_RESFIX_START_REQUEST_MSG_0_MARKER)); + + /* add 1 to avoid zero-marker */ + return 1 + gt->sriov.vf.migration.resfix_marker++; } static void vf_post_migration_recovery(struct xe_gt *gt) { struct xe_device *xe = gt_to_xe(gt); - int err; + u16 marker; bool retry; + int err; xe_gt_sriov_dbg(gt, "migration recovery in progress\n"); @@ -1227,15 +1360,30 @@ static void vf_post_migration_recovery(struct xe_gt *gt) goto fail; } + marker = vf_post_migration_next_resfix_marker(gt); + + err = vf_post_migration_resfix_start(gt, marker); + if (unlikely(err)) { + xe_gt_sriov_err(gt, "Recovery failed at GuC RESFIX_START step (%pe)\n", + ERR_PTR(err)); + goto fail; + } + err = vf_post_migration_fixups(gt); if (err) goto fail; vf_post_migration_rearm(gt); - err = vf_post_migration_notify_resfix_done(gt); - if (err && err != -EAGAIN) + err = vf_post_migration_resfix_done(gt, marker); + if (err) { + if (err == -EREMCHG) + goto queue; + + xe_gt_sriov_err(gt, "Recovery failed at GuC RESFIX_DONE step (%pe)\n", + ERR_PTR(err)); goto fail; + } vf_post_migration_kickstart(gt); diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_vf.h b/drivers/gpu/drm/xe/xe_gt_sriov_vf.h index af40276790fa..7d97189c2d3d 100644 --- a/drivers/gpu/drm/xe/xe_gt_sriov_vf.h +++ b/drivers/gpu/drm/xe/xe_gt_sriov_vf.h @@ -30,6 +30,7 @@ bool xe_gt_sriov_vf_recovery_pending(struct xe_gt *gt); u32 xe_gt_sriov_vf_gmdid(struct xe_gt *gt); u16 xe_gt_sriov_vf_guc_ids(struct xe_gt *gt); u64 xe_gt_sriov_vf_lmem(struct xe_gt *gt); +bool xe_gt_sriov_vf_sched_groups_enabled(struct xe_gt *gt); u32 xe_gt_sriov_vf_read32(struct xe_gt *gt, struct xe_reg reg); void xe_gt_sriov_vf_write32(struct xe_gt *gt, struct xe_reg reg, u32 val); diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_vf_debugfs.c b/drivers/gpu/drm/xe/xe_gt_sriov_vf_debugfs.c index 2ed5b6780d30..507718326e1f 100644 --- a/drivers/gpu/drm/xe/xe_gt_sriov_vf_debugfs.c +++ b/drivers/gpu/drm/xe/xe_gt_sriov_vf_debugfs.c @@ -69,4 +69,16 @@ void xe_gt_sriov_vf_debugfs_register(struct xe_gt *gt, struct dentry *root) vfdentry->d_inode->i_private = gt; drm_debugfs_create_files(vf_info, ARRAY_SIZE(vf_info), vfdentry, minor); + + /* + * /sys/kernel/debug/dri/BDF/ + * ├── tile0 + * ├── gt0 + * ├── vf + * ├── resfix_stoppers + */ + if (IS_ENABLED(CONFIG_DRM_XE_DEBUG)) { + debugfs_create_x8("resfix_stoppers", 0600, vfdentry, + >->sriov.vf.migration.debug.resfix_stoppers); + } } diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_vf_types.h b/drivers/gpu/drm/xe/xe_gt_sriov_vf_types.h index 420b0e6089de..4ef881b9b662 100644 --- a/drivers/gpu/drm/xe/xe_gt_sriov_vf_types.h +++ b/drivers/gpu/drm/xe/xe_gt_sriov_vf_types.h @@ -27,6 +27,8 @@ struct xe_gt_sriov_vf_selfconfig { struct xe_gt_sriov_vf_runtime { /** @gmdid: cached value of the GDMID register. */ u32 gmdid; + /** @uses_sched_groups: whether PF enabled sched groups or not. */ + bool uses_sched_groups; /** @regs_size: size of runtime register array. */ u32 regs_size; /** @num_regs: number of runtime registers in the array. */ @@ -41,10 +43,10 @@ struct xe_gt_sriov_vf_runtime { }; /** - * xe_gt_sriov_vf_migration - VF migration data. + * struct xe_gt_sriov_vf_migration - VF migration data. */ struct xe_gt_sriov_vf_migration { - /** @migration: VF migration recovery worker */ + /** @worker: VF migration recovery worker */ struct work_struct worker; /** @lock: Protects recovery_queued, teardown */ spinlock_t lock; @@ -52,6 +54,19 @@ struct xe_gt_sriov_vf_migration { wait_queue_head_t wq; /** @scratch: Scratch memory for VF recovery */ void *scratch; + /** @debug: Debug hooks for delaying migration */ + struct { + /** + * @debug.resfix_stoppers: Stop and wait at different stages + * during post migration recovery + */ + u8 resfix_stoppers; + } debug; + /** + * @resfix_marker: Marker sent on start and on end of post-migration + * steps. + */ + u8 resfix_marker; /** @recovery_teardown: VF post migration recovery is being torn down */ bool recovery_teardown; /** @recovery_queued: VF post migration recovery in queued */ diff --git a/drivers/gpu/drm/xe/xe_gt_stats.c b/drivers/gpu/drm/xe/xe_gt_stats.c index 5f74706bab81..37506434d7a3 100644 --- a/drivers/gpu/drm/xe/xe_gt_stats.c +++ b/drivers/gpu/drm/xe/xe_gt_stats.c @@ -7,8 +7,8 @@ #include <drm/drm_print.h> -#include "xe_gt.h" #include "xe_gt_stats.h" +#include "xe_gt_types.h" /** * xe_gt_stats_incr - Increments the specified stats counter @@ -66,6 +66,21 @@ static const char *const stat_description[__XE_GT_STATS_NUM_IDS] = { DEF_STAT_STR(SVM_4K_BIND_US, "svm_4K_bind_us"), DEF_STAT_STR(SVM_64K_BIND_US, "svm_64K_bind_us"), DEF_STAT_STR(SVM_2M_BIND_US, "svm_2M_bind_us"), + DEF_STAT_STR(HW_ENGINE_GROUP_SUSPEND_LR_QUEUE_COUNT, + "hw_engine_group_suspend_lr_queue_count"), + DEF_STAT_STR(HW_ENGINE_GROUP_SKIP_LR_QUEUE_COUNT, + "hw_engine_group_skip_lr_queue_count"), + DEF_STAT_STR(HW_ENGINE_GROUP_WAIT_DMA_QUEUE_COUNT, + "hw_engine_group_wait_dma_queue_count"), + DEF_STAT_STR(HW_ENGINE_GROUP_SUSPEND_LR_QUEUE_US, + "hw_engine_group_suspend_lr_queue_us"), + DEF_STAT_STR(HW_ENGINE_GROUP_WAIT_DMA_QUEUE_US, + "hw_engine_group_wait_dma_queue_us"), + DEF_STAT_STR(PRL_4K_ENTRY_COUNT, "prl_4k_entry_count"), + DEF_STAT_STR(PRL_64K_ENTRY_COUNT, "prl_64k_entry_count"), + DEF_STAT_STR(PRL_2M_ENTRY_COUNT, "prl_2m_entry_count"), + DEF_STAT_STR(PRL_ISSUED_COUNT, "prl_issued_count"), + DEF_STAT_STR(PRL_ABORTED_COUNT, "prl_aborted_count"), }; /** diff --git a/drivers/gpu/drm/xe/xe_gt_stats.h b/drivers/gpu/drm/xe/xe_gt_stats.h index e8aea32bc971..59a7bf60e242 100644 --- a/drivers/gpu/drm/xe/xe_gt_stats.h +++ b/drivers/gpu/drm/xe/xe_gt_stats.h @@ -6,6 +6,8 @@ #ifndef _XE_GT_STATS_H_ #define _XE_GT_STATS_H_ +#include <linux/ktime.h> + #include "xe_gt_stats_types.h" struct xe_gt; @@ -23,4 +25,34 @@ xe_gt_stats_incr(struct xe_gt *gt, const enum xe_gt_stats_id id, } #endif + +/** + * xe_gt_stats_ktime_us_delta() - Get delta in microseconds between now and a + * start time + * @start: Start time + * + * Helper for GT stats to get delta in microseconds between now and a start + * time, compiles out if GT stats are disabled. + * + * Return: Delta in microseconds between now and a start time + */ +static inline s64 xe_gt_stats_ktime_us_delta(ktime_t start) +{ + return IS_ENABLED(CONFIG_DEBUG_FS) ? + ktime_us_delta(ktime_get(), start) : 0; +} + +/** + * xe_gt_stats_ktime_get() - Get current ktime + * + * Helper for GT stats to get current ktime, compiles out if GT stats are + * disabled. + * + * Return: Get current ktime + */ +static inline ktime_t xe_gt_stats_ktime_get(void) +{ + return IS_ENABLED(CONFIG_DEBUG_FS) ? ktime_get() : 0; +} + #endif diff --git a/drivers/gpu/drm/xe/xe_gt_stats_types.h b/drivers/gpu/drm/xe/xe_gt_stats_types.h index d8348a8de2e1..b8accdbc54eb 100644 --- a/drivers/gpu/drm/xe/xe_gt_stats_types.h +++ b/drivers/gpu/drm/xe/xe_gt_stats_types.h @@ -44,6 +44,16 @@ enum xe_gt_stats_id { XE_GT_STATS_ID_SVM_4K_BIND_US, XE_GT_STATS_ID_SVM_64K_BIND_US, XE_GT_STATS_ID_SVM_2M_BIND_US, + XE_GT_STATS_ID_HW_ENGINE_GROUP_SUSPEND_LR_QUEUE_COUNT, + XE_GT_STATS_ID_HW_ENGINE_GROUP_SKIP_LR_QUEUE_COUNT, + XE_GT_STATS_ID_HW_ENGINE_GROUP_WAIT_DMA_QUEUE_COUNT, + XE_GT_STATS_ID_HW_ENGINE_GROUP_SUSPEND_LR_QUEUE_US, + XE_GT_STATS_ID_HW_ENGINE_GROUP_WAIT_DMA_QUEUE_US, + XE_GT_STATS_ID_PRL_4K_ENTRY_COUNT, + XE_GT_STATS_ID_PRL_64K_ENTRY_COUNT, + XE_GT_STATS_ID_PRL_2M_ENTRY_COUNT, + XE_GT_STATS_ID_PRL_ISSUED_COUNT, + XE_GT_STATS_ID_PRL_ABORTED_COUNT, /* must be the last entry */ __XE_GT_STATS_NUM_IDS, }; diff --git a/drivers/gpu/drm/xe/xe_gt_sysfs.c b/drivers/gpu/drm/xe/xe_gt_sysfs.c index ec2b8246204b..1448be047b4a 100644 --- a/drivers/gpu/drm/xe/xe_gt_sysfs.c +++ b/drivers/gpu/drm/xe/xe_gt_sysfs.c @@ -10,7 +10,7 @@ #include <drm/drm_managed.h> -#include "xe_gt.h" +#include "xe_gt_types.h" static void xe_gt_sysfs_kobj_release(struct kobject *kobj) { diff --git a/drivers/gpu/drm/xe/xe_gt_throttle.c b/drivers/gpu/drm/xe/xe_gt_throttle.c index 82c5fbcdfbe3..1e7e3a31aa69 100644 --- a/drivers/gpu/drm/xe/xe_gt_throttle.c +++ b/drivers/gpu/drm/xe/xe_gt_throttle.c @@ -6,7 +6,7 @@ #include <drm/drm_managed.h> #include <regs/xe_gt_regs.h> -#include "xe_device.h" +#include "xe_device_types.h" #include "xe_gt.h" #include "xe_gt_sysfs.h" #include "xe_gt_throttle.h" @@ -85,7 +85,7 @@ u32 xe_gt_throttle_get_limit_reasons(struct xe_gt *gt) { struct xe_device *xe = gt_to_xe(gt); struct xe_reg reg; - u32 val, mask; + u32 mask; if (xe_gt_is_media_type(gt)) reg = MTL_MEDIA_PERF_LIMIT_REASONS; @@ -97,11 +97,8 @@ u32 xe_gt_throttle_get_limit_reasons(struct xe_gt *gt) else mask = GT0_PERF_LIMIT_REASONS_MASK; - xe_pm_runtime_get(xe); - val = xe_mmio_read32(>->mmio, reg) & mask; - xe_pm_runtime_put(xe); - - return val; + guard(xe_pm_runtime)(xe); + return xe_mmio_read32(>->mmio, reg) & mask; } static bool is_throttled_by(struct xe_gt *gt, u32 mask) @@ -140,7 +137,7 @@ static ssize_t reasons_show(struct kobject *kobj, struct throttle_attribute *other_ta = kobj_attribute_to_throttle(kattr); if (other_ta->mask != U32_MAX && reasons & other_ta->mask) - ret += sysfs_emit_at(buff, ret, "%s ", (*pother)->name); + ret += sysfs_emit_at(buff, ret, "%s ", (*pother)->name + strlen("reason_")); } if (drm_WARN_ONCE(&xe->drm, !ret, "Unknown reason: %#x\n", reasons)) diff --git a/drivers/gpu/drm/xe/xe_gt_types.h b/drivers/gpu/drm/xe/xe_gt_types.h index 0a728180b6fe..5318d92fd473 100644 --- a/drivers/gpu/drm/xe/xe_gt_types.h +++ b/drivers/gpu/drm/xe/xe_gt_types.h @@ -140,6 +140,11 @@ struct xe_gt { u64 engine_mask; /** @info.gmdid: raw GMD_ID value from hardware */ u32 gmdid; + /** + * @multi_queue_engine_class_mask: Bitmask of engine classes with + * multi queue support enabled. + */ + u16 multi_queue_engine_class_mask; /** @info.id: Unique ID of this GT within the PCI Device */ u8 id; /** @info.has_indirect_ring_state: GT has indirect ring state support */ diff --git a/drivers/gpu/drm/xe/xe_guc.c b/drivers/gpu/drm/xe/xe_guc.c index a686b04879d6..44360437beeb 100644 --- a/drivers/gpu/drm/xe/xe_guc.c +++ b/drivers/gpu/drm/xe/xe_guc.c @@ -104,7 +104,7 @@ static u32 guc_ctl_log_params_flags(struct xe_guc *guc) u32 offset = guc_bo_ggtt_addr(guc, guc->log.bo) >> PAGE_SHIFT; u32 flags; - #if (((CRASH_BUFFER_SIZE) % SZ_1M) == 0) + #if (((XE_GUC_LOG_CRASH_DUMP_BUFFER_SIZE) % SZ_1M) == 0) #define LOG_UNIT SZ_1M #define LOG_FLAG GUC_LOG_LOG_ALLOC_UNITS #else @@ -112,7 +112,7 @@ static u32 guc_ctl_log_params_flags(struct xe_guc *guc) #define LOG_FLAG 0 #endif - #if (((CAPTURE_BUFFER_SIZE) % SZ_1M) == 0) + #if (((XE_GUC_LOG_STATE_CAPTURE_BUFFER_SIZE) % SZ_1M) == 0) #define CAPTURE_UNIT SZ_1M #define CAPTURE_FLAG GUC_LOG_CAPTURE_ALLOC_UNITS #else @@ -120,20 +120,21 @@ static u32 guc_ctl_log_params_flags(struct xe_guc *guc) #define CAPTURE_FLAG 0 #endif - BUILD_BUG_ON(!CRASH_BUFFER_SIZE); - BUILD_BUG_ON(!IS_ALIGNED(CRASH_BUFFER_SIZE, LOG_UNIT)); - BUILD_BUG_ON(!DEBUG_BUFFER_SIZE); - BUILD_BUG_ON(!IS_ALIGNED(DEBUG_BUFFER_SIZE, LOG_UNIT)); - BUILD_BUG_ON(!CAPTURE_BUFFER_SIZE); - BUILD_BUG_ON(!IS_ALIGNED(CAPTURE_BUFFER_SIZE, CAPTURE_UNIT)); + BUILD_BUG_ON(!XE_GUC_LOG_CRASH_DUMP_BUFFER_SIZE); + BUILD_BUG_ON(!IS_ALIGNED(XE_GUC_LOG_CRASH_DUMP_BUFFER_SIZE, LOG_UNIT)); + BUILD_BUG_ON(!XE_GUC_LOG_EVENT_DATA_BUFFER_SIZE); + BUILD_BUG_ON(!IS_ALIGNED(XE_GUC_LOG_EVENT_DATA_BUFFER_SIZE, LOG_UNIT)); + BUILD_BUG_ON(!XE_GUC_LOG_STATE_CAPTURE_BUFFER_SIZE); + BUILD_BUG_ON(!IS_ALIGNED(XE_GUC_LOG_STATE_CAPTURE_BUFFER_SIZE, CAPTURE_UNIT)); flags = GUC_LOG_VALID | GUC_LOG_NOTIFY_ON_HALF_FULL | CAPTURE_FLAG | LOG_FLAG | - FIELD_PREP(GUC_LOG_CRASH, CRASH_BUFFER_SIZE / LOG_UNIT - 1) | - FIELD_PREP(GUC_LOG_DEBUG, DEBUG_BUFFER_SIZE / LOG_UNIT - 1) | - FIELD_PREP(GUC_LOG_CAPTURE, CAPTURE_BUFFER_SIZE / CAPTURE_UNIT - 1) | + FIELD_PREP(GUC_LOG_CRASH_DUMP, XE_GUC_LOG_CRASH_DUMP_BUFFER_SIZE / LOG_UNIT - 1) | + FIELD_PREP(GUC_LOG_EVENT_DATA, XE_GUC_LOG_EVENT_DATA_BUFFER_SIZE / LOG_UNIT - 1) | + FIELD_PREP(GUC_LOG_STATE_CAPTURE, XE_GUC_LOG_STATE_CAPTURE_BUFFER_SIZE / + CAPTURE_UNIT - 1) | FIELD_PREP(GUC_LOG_BUF_ADDR, offset); #undef LOG_UNIT @@ -174,7 +175,7 @@ static bool needs_wa_dual_queue(struct xe_gt *gt) * the DUAL_QUEUE_WA on all newer platforms on GTs that have CCS engines * to move management back to the GuC. */ - if (CCS_MASK(gt) && GRAPHICS_VERx100(gt_to_xe(gt)) >= 1270) + if (CCS_INSTANCES(gt) && GRAPHICS_VERx100(gt_to_xe(gt)) >= 1270) return true; return false; @@ -660,11 +661,9 @@ static void guc_fini_hw(void *arg) { struct xe_guc *guc = arg; struct xe_gt *gt = guc_to_gt(guc); - unsigned int fw_ref; - fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FORCEWAKE_ALL); - xe_uc_sanitize_reset(&guc_to_gt(guc)->uc); - xe_force_wake_put(gt_to_fw(gt), fw_ref); + xe_with_force_wake(fw_ref, gt_to_fw(gt), XE_FORCEWAKE_ALL) + xe_uc_sanitize_reset(&guc_to_gt(guc)->uc); guc_g2g_fini(guc); } @@ -768,6 +767,10 @@ int xe_guc_init(struct xe_guc *guc) if (!xe_uc_fw_is_enabled(&guc->fw)) return 0; + /* Disable page reclaim if GuC FW does not support */ + if (GUC_SUBMIT_VER(guc) < MAKE_GUC_VER(1, 14, 0)) + xe->info.has_page_reclaim_hw_assist = false; + if (IS_SRIOV_VF(xe)) { ret = xe_guc_ct_init(&guc->ct); if (ret) @@ -1485,6 +1488,12 @@ timeout: u32 hint = FIELD_GET(GUC_HXG_FAILURE_MSG_0_HINT, header); u32 error = FIELD_GET(GUC_HXG_FAILURE_MSG_0_ERROR, header); + if (unlikely(error == XE_GUC_RESPONSE_VF_MIGRATED)) { + xe_gt_dbg(gt, "GuC mmio request %#x rejected due to MIGRATION (hint %#x)\n", + request[0], hint); + return -EREMCHG; + } + xe_gt_err(gt, "GuC mmio request %#x: failure %#x hint %#x\n", request[0], error, hint); return -ENXIO; @@ -1618,18 +1627,51 @@ int xe_guc_start(struct xe_guc *guc) return xe_guc_submit_start(guc); } +/** + * xe_guc_runtime_suspend() - GuC runtime suspend + * @guc: The GuC object + * + * Stop further runs of submission tasks on given GuC and runtime suspend + * GuC CT. + */ +void xe_guc_runtime_suspend(struct xe_guc *guc) +{ + xe_guc_submit_pause(guc); + xe_guc_submit_disable(guc); + xe_guc_ct_runtime_suspend(&guc->ct); +} + +/** + * xe_guc_runtime_resume() - GuC runtime resume + * @guc: The GuC object + * + * Runtime resume GuC CT and allow further runs of submission tasks on + * given GuC. + */ +void xe_guc_runtime_resume(struct xe_guc *guc) +{ + /* + * Runtime PM flows are not applicable for VFs, so it's safe to + * directly enable IRQ. + */ + guc_enable_irq(guc); + + xe_guc_ct_runtime_resume(&guc->ct); + xe_guc_submit_enable(guc); + xe_guc_submit_unpause(guc); +} + void xe_guc_print_info(struct xe_guc *guc, struct drm_printer *p) { struct xe_gt *gt = guc_to_gt(guc); - unsigned int fw_ref; u32 status; int i; xe_uc_fw_print(&guc->fw, p); if (!IS_SRIOV_VF(gt_to_xe(gt))) { - fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT); - if (!fw_ref) + CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT); + if (!fw_ref.domains) return; status = xe_mmio_read32(>->mmio, GUC_STATUS); @@ -1649,8 +1691,6 @@ void xe_guc_print_info(struct xe_guc *guc, struct drm_printer *p) drm_printf(p, "\t%2d: \t0x%x\n", i, xe_mmio_read32(>->mmio, SOFT_SCRATCH(i))); } - - xe_force_wake_put(gt_to_fw(gt), fw_ref); } drm_puts(p, "\n"); diff --git a/drivers/gpu/drm/xe/xe_guc.h b/drivers/gpu/drm/xe/xe_guc.h index e2d4c5f44ae3..a169f231cbd8 100644 --- a/drivers/gpu/drm/xe/xe_guc.h +++ b/drivers/gpu/drm/xe/xe_guc.h @@ -18,10 +18,16 @@ */ #define MAKE_GUC_VER(maj, min, pat) (((maj) << 16) | ((min) << 8) | (pat)) #define MAKE_GUC_VER_STRUCT(ver) MAKE_GUC_VER((ver).major, (ver).minor, (ver).patch) +#define MAKE_GUC_VER_ARGS(ver...) \ + (BUILD_BUG_ON_ZERO(COUNT_ARGS(ver) < 2 || COUNT_ARGS(ver) > 3) + \ + MAKE_GUC_VER(PICK_ARG1(ver), PICK_ARG2(ver), IF_ARGS(PICK_ARG3(ver), 0, PICK_ARG3(ver)))) + #define GUC_SUBMIT_VER(guc) \ MAKE_GUC_VER_STRUCT((guc)->fw.versions.found[XE_UC_FW_VER_COMPATIBILITY]) #define GUC_FIRMWARE_VER(guc) \ MAKE_GUC_VER_STRUCT((guc)->fw.versions.found[XE_UC_FW_VER_RELEASE]) +#define GUC_FIRMWARE_VER_AT_LEAST(guc, ver...) \ + xe_guc_fw_version_at_least((guc), MAKE_GUC_VER_ARGS(ver)) struct drm_printer; @@ -35,6 +41,8 @@ int xe_guc_upload(struct xe_guc *guc); int xe_guc_min_load_for_hwconfig(struct xe_guc *guc); int xe_guc_enable_communication(struct xe_guc *guc); int xe_guc_opt_in_features_enable(struct xe_guc *guc); +void xe_guc_runtime_suspend(struct xe_guc *guc); +void xe_guc_runtime_resume(struct xe_guc *guc); int xe_guc_suspend(struct xe_guc *guc); void xe_guc_notify(struct xe_guc *guc); int xe_guc_auth_huc(struct xe_guc *guc, u32 rsa_addr); @@ -94,4 +102,19 @@ static inline struct drm_device *guc_to_drm(struct xe_guc *guc) return &guc_to_xe(guc)->drm; } +/** + * xe_guc_fw_version_at_least() - Check if GuC is at least of given version. + * @guc: the &xe_guc + * @ver: the version to check + * + * The @ver should be prepared using MAKE_GUC_VER(major, minor, patch). + * + * Return: true if loaded GuC firmware is at least of given version, + * false otherwise. + */ +static inline bool xe_guc_fw_version_at_least(const struct xe_guc *guc, u32 ver) +{ + return GUC_FIRMWARE_VER(guc) >= ver; +} + #endif diff --git a/drivers/gpu/drm/xe/xe_guc_ads.c b/drivers/gpu/drm/xe/xe_guc_ads.c index bcb85a1bf26d..f4cbc030f4c8 100644 --- a/drivers/gpu/drm/xe/xe_guc_ads.c +++ b/drivers/gpu/drm/xe/xe_guc_ads.c @@ -28,8 +28,6 @@ #include "xe_lrc.h" #include "xe_map.h" #include "xe_mmio.h" -#include "xe_platform_types.h" -#include "xe_uc_fw.h" #include "xe_wa.h" /* Slack of a few additional entries per engine */ @@ -317,7 +315,7 @@ static void guc_waklv_init(struct xe_guc_ads *ads) offset = guc_ads_waklv_offset(ads); remain = guc_ads_waklv_size(ads); - if (XE_GT_WA(gt, 14019882105) || XE_GT_WA(gt, 16021333562)) + if (XE_GT_WA(gt, 16021333562)) guc_waklv_enable(ads, NULL, 0, &offset, &remain, GUC_WORKAROUND_KLV_BLOCK_INTERRUPTS_WHEN_MGSR_BLOCKED); if (XE_GT_WA(gt, 18024947630)) @@ -347,10 +345,10 @@ static void guc_waklv_init(struct xe_guc_ads *ads) guc_waklv_enable(ads, NULL, 0, &offset, &remain, GUC_WORKAROUND_KLV_ID_BACK_TO_BACK_RCS_ENGINE_RESET); - if (GUC_FIRMWARE_VER(>->uc.guc) >= MAKE_GUC_VER(70, 44, 0) && XE_GT_WA(gt, 16026508708)) + if (GUC_FIRMWARE_VER_AT_LEAST(>->uc.guc, 70, 44) && XE_GT_WA(gt, 16026508708)) guc_waklv_enable(ads, NULL, 0, &offset, &remain, GUC_WA_KLV_RESET_BB_STACK_PTR_ON_VF_SWITCH); - if (GUC_FIRMWARE_VER(>->uc.guc) >= MAKE_GUC_VER(70, 47, 0) && XE_GT_WA(gt, 16026007364)) { + if (GUC_FIRMWARE_VER_AT_LEAST(>->uc.guc, 70, 47) && XE_GT_WA(gt, 16026007364)) { u32 data[] = { 0x0, 0xF, @@ -451,7 +449,7 @@ static void guc_policies_init(struct xe_guc_ads *ads) ads_blob_write(ads, policies.max_num_work_items, GLOBAL_POLICY_MAX_NUM_WI); - if (xe->wedged.mode == 2) + if (xe->wedged.mode == XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET) global_flags |= GLOBAL_POLICY_DISABLE_ENGINE_RESET; ads_blob_write(ads, policies.global_flags, global_flags); @@ -983,16 +981,17 @@ static int guc_ads_action_update_policies(struct xe_guc_ads *ads, u32 policy_off /** * xe_guc_ads_scheduler_policy_toggle_reset - Toggle reset policy * @ads: Additional data structures object + * @enable_engine_reset: true to enable engine resets, false otherwise * - * This function update the GuC's engine reset policy based on wedged.mode. + * This function update the GuC's engine reset policy. * * Return: 0 on success, and negative error code otherwise. */ -int xe_guc_ads_scheduler_policy_toggle_reset(struct xe_guc_ads *ads) +int xe_guc_ads_scheduler_policy_toggle_reset(struct xe_guc_ads *ads, + bool enable_engine_reset) { struct guc_policies *policies; struct xe_guc *guc = ads_to_guc(ads); - struct xe_device *xe = ads_to_xe(ads); CLASS(xe_guc_buf, buf)(&guc->buf, sizeof(*policies)); if (!xe_guc_buf_is_valid(buf)) @@ -1004,10 +1003,11 @@ int xe_guc_ads_scheduler_policy_toggle_reset(struct xe_guc_ads *ads) policies->dpc_promote_time = ads_blob_read(ads, policies.dpc_promote_time); policies->max_num_work_items = ads_blob_read(ads, policies.max_num_work_items); policies->is_valid = 1; - if (xe->wedged.mode == 2) - policies->global_flags |= GLOBAL_POLICY_DISABLE_ENGINE_RESET; - else + + if (enable_engine_reset) policies->global_flags &= ~GLOBAL_POLICY_DISABLE_ENGINE_RESET; + else + policies->global_flags |= GLOBAL_POLICY_DISABLE_ENGINE_RESET; return guc_ads_action_update_policies(ads, xe_guc_buf_flush(buf)); } diff --git a/drivers/gpu/drm/xe/xe_guc_ads.h b/drivers/gpu/drm/xe/xe_guc_ads.h index 2e6674c760ff..7a39f361cb17 100644 --- a/drivers/gpu/drm/xe/xe_guc_ads.h +++ b/drivers/gpu/drm/xe/xe_guc_ads.h @@ -6,6 +6,8 @@ #ifndef _XE_GUC_ADS_H_ #define _XE_GUC_ADS_H_ +#include <linux/types.h> + struct xe_guc_ads; int xe_guc_ads_init(struct xe_guc_ads *ads); @@ -13,6 +15,7 @@ int xe_guc_ads_init_post_hwconfig(struct xe_guc_ads *ads); void xe_guc_ads_populate(struct xe_guc_ads *ads); void xe_guc_ads_populate_minimal(struct xe_guc_ads *ads); void xe_guc_ads_populate_post_load(struct xe_guc_ads *ads); -int xe_guc_ads_scheduler_policy_toggle_reset(struct xe_guc_ads *ads); +int xe_guc_ads_scheduler_policy_toggle_reset(struct xe_guc_ads *ads, + bool enable_engine_reset); #endif diff --git a/drivers/gpu/drm/xe/xe_guc_buf.c b/drivers/gpu/drm/xe/xe_guc_buf.c index 3ce442500130..11f77decd8d1 100644 --- a/drivers/gpu/drm/xe/xe_guc_buf.c +++ b/drivers/gpu/drm/xe/xe_guc_buf.c @@ -6,7 +6,6 @@ #include <linux/cleanup.h> #include <drm/drm_managed.h> -#include "xe_assert.h" #include "xe_bo.h" #include "xe_gt_printk.h" #include "xe_guc.h" @@ -30,7 +29,7 @@ static int guc_buf_cache_init(struct xe_guc_buf_cache *cache, u32 size) struct xe_gt *gt = cache_to_gt(cache); struct xe_sa_manager *sam; - sam = __xe_sa_bo_manager_init(gt_to_tile(gt), size, 0, sizeof(u32)); + sam = __xe_sa_bo_manager_init(gt_to_tile(gt), size, 0, sizeof(u32), 0); if (IS_ERR(sam)) return PTR_ERR(sam); cache->sam = sam; diff --git a/drivers/gpu/drm/xe/xe_guc_capture.c b/drivers/gpu/drm/xe/xe_guc_capture.c index 0c1fbe97b8bf..2f5816c78fba 100644 --- a/drivers/gpu/drm/xe/xe_guc_capture.c +++ b/drivers/gpu/drm/xe/xe_guc_capture.c @@ -13,17 +13,14 @@ #include "abi/guc_log_abi.h" #include "regs/xe_engine_regs.h" #include "regs/xe_gt_regs.h" -#include "regs/xe_guc_regs.h" -#include "regs/xe_regs.h" -#include "xe_bo.h" +#include "xe_bo_types.h" #include "xe_device.h" #include "xe_exec_queue_types.h" #include "xe_gt.h" #include "xe_gt_mcr.h" #include "xe_gt_printk.h" #include "xe_guc.h" -#include "xe_guc_ads.h" #include "xe_guc_capture.h" #include "xe_guc_capture_types.h" #include "xe_guc_ct.h" @@ -843,7 +840,7 @@ static void check_guc_capture_size(struct xe_guc *guc) { int capture_size = guc_capture_output_size_est(guc); int spare_size = capture_size * GUC_CAPTURE_OVERBUFFER_MULTIPLIER; - u32 buffer_size = xe_guc_log_section_size_capture(&guc->log); + u32 buffer_size = XE_GUC_LOG_STATE_CAPTURE_BUFFER_SIZE; /* * NOTE: capture_size is much smaller than the capture region @@ -949,7 +946,7 @@ guc_capture_init_node(struct xe_guc *guc, struct __guc_capture_parsed_output *no * ADS module also calls separately for PF vs VF. * * --> alloc B: GuC output capture buf (registered via guc_init_params(log_param)) - * Size = #define CAPTURE_BUFFER_SIZE (warns if on too-small) + * Size = XE_GUC_LOG_STATE_CAPTURE_BUFFER_SIZE (warns if on too-small) * Note2: 'x 3' to hold multiple capture groups * * GUC Runtime notify capture: @@ -1367,7 +1364,7 @@ static int __guc_capture_flushlog_complete(struct xe_guc *guc) { u32 action[] = { XE_GUC_ACTION_LOG_BUFFER_FILE_FLUSH_COMPLETE, - GUC_LOG_BUFFER_CAPTURE + GUC_LOG_TYPE_STATE_CAPTURE }; return xe_guc_ct_send_g2h_handler(&guc->ct, action, ARRAY_SIZE(action)); @@ -1384,8 +1381,8 @@ static void __guc_capture_process_output(struct xe_guc *guc) u32 log_buf_state_offset; u32 src_data_offset; - log_buf_state_offset = sizeof(struct guc_log_buffer_state) * GUC_LOG_BUFFER_CAPTURE; - src_data_offset = xe_guc_get_log_buffer_offset(&guc->log, GUC_LOG_BUFFER_CAPTURE); + log_buf_state_offset = sizeof(struct guc_log_buffer_state) * GUC_LOG_TYPE_STATE_CAPTURE; + src_data_offset = XE_GUC_LOG_STATE_CAPTURE_OFFSET; /* * Make a copy of the state structure, inside GuC log buffer @@ -1395,15 +1392,15 @@ static void __guc_capture_process_output(struct xe_guc *guc) xe_map_memcpy_from(guc_to_xe(guc), &log_buf_state_local, &guc->log.bo->vmap, log_buf_state_offset, sizeof(struct guc_log_buffer_state)); - buffer_size = xe_guc_get_log_buffer_size(&guc->log, GUC_LOG_BUFFER_CAPTURE); + buffer_size = XE_GUC_LOG_STATE_CAPTURE_BUFFER_SIZE; read_offset = log_buf_state_local.read_ptr; write_offset = log_buf_state_local.sampled_write_ptr; full_count = FIELD_GET(GUC_LOG_BUFFER_STATE_BUFFER_FULL_CNT, log_buf_state_local.flags); /* Bookkeeping stuff */ tmp = FIELD_GET(GUC_LOG_BUFFER_STATE_FLUSH_TO_FILE, log_buf_state_local.flags); - guc->log.stats[GUC_LOG_BUFFER_CAPTURE].flush += tmp; - new_overflow = xe_guc_check_log_buf_overflow(&guc->log, GUC_LOG_BUFFER_CAPTURE, + guc->log.stats[GUC_LOG_TYPE_STATE_CAPTURE].flush += tmp; + new_overflow = xe_guc_check_log_buf_overflow(&guc->log, GUC_LOG_TYPE_STATE_CAPTURE, full_count); /* Now copy the actual logs. */ @@ -1889,7 +1886,14 @@ xe_guc_capture_get_matching_and_lock(struct xe_exec_queue *q) return NULL; xe = gt_to_xe(q->gt); - if (xe->wedged.mode >= 2 || !xe_device_uc_enabled(xe) || IS_SRIOV_VF(xe)) + + if (xe->wedged.mode == XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET) + return NULL; + + if (!xe_device_uc_enabled(xe)) + return NULL; + + if (IS_SRIOV_VF(xe)) return NULL; ss = &xe->devcoredump.snapshot; diff --git a/drivers/gpu/drm/xe/xe_guc_capture.h b/drivers/gpu/drm/xe/xe_guc_capture.h index 20a078dc4b85..34d6fdc64f56 100644 --- a/drivers/gpu/drm/xe/xe_guc_capture.h +++ b/drivers/gpu/drm/xe/xe_guc_capture.h @@ -8,8 +8,8 @@ #include <linux/types.h> #include "abi/guc_capture_abi.h" +#include "abi/guc_scheduler_abi.h" #include "xe_guc.h" -#include "xe_guc_fwif.h" struct xe_exec_queue; struct xe_guc; diff --git a/drivers/gpu/drm/xe/xe_guc_ct.c b/drivers/gpu/drm/xe/xe_guc_ct.c index 4ac434ad216f..dfbf76037b04 100644 --- a/drivers/gpu/drm/xe/xe_guc_ct.c +++ b/drivers/gpu/drm/xe/xe_guc_ct.c @@ -42,6 +42,21 @@ static void ct_exit_safe_mode(struct xe_guc_ct *ct); static void guc_ct_change_state(struct xe_guc_ct *ct, enum xe_guc_ct_state state); +static struct xe_guc *ct_to_guc(struct xe_guc_ct *ct) +{ + return container_of(ct, struct xe_guc, ct); +} + +static struct xe_gt *ct_to_gt(struct xe_guc_ct *ct) +{ + return container_of(ct, struct xe_gt, uc.guc.ct); +} + +static struct xe_device *ct_to_xe(struct xe_guc_ct *ct) +{ + return gt_to_xe(ct_to_gt(ct)); +} + #if IS_ENABLED(CONFIG_DRM_XE_DEBUG) enum { /* Internal states, not error conditions */ @@ -68,14 +83,101 @@ enum { static void ct_dead_worker_func(struct work_struct *w); static void ct_dead_capture(struct xe_guc_ct *ct, struct guc_ctb *ctb, u32 reason_code); -#define CT_DEAD(ct, ctb, reason_code) ct_dead_capture((ct), (ctb), CT_DEAD_##reason_code) +static void ct_dead_fini(struct xe_guc_ct *ct) +{ + cancel_work_sync(&ct->dead.worker); +} + +static void ct_dead_init(struct xe_guc_ct *ct) +{ + spin_lock_init(&ct->dead.lock); + INIT_WORK(&ct->dead.worker, ct_dead_worker_func); + +#if IS_ENABLED(CONFIG_DRM_XE_DEBUG_GUC) + stack_depot_init(); +#endif +} + +static void fast_req_stack_save(struct xe_guc_ct *ct, unsigned int slot) +{ +#if IS_ENABLED(CONFIG_DRM_XE_DEBUG_GUC) + unsigned long entries[SZ_32]; + unsigned int n; + + n = stack_trace_save(entries, ARRAY_SIZE(entries), 1); + /* May be called under spinlock, so avoid sleeping */ + ct->fast_req[slot].stack = stack_depot_save(entries, n, GFP_NOWAIT); +#endif +} + +static void fast_req_dump(struct xe_guc_ct *ct, u16 fence, unsigned int slot) +{ + struct xe_gt *gt = ct_to_gt(ct); +#if IS_ENABLED(CONFIG_DRM_XE_DEBUG_GUC) + char *buf __cleanup(kfree) = kmalloc(SZ_4K, GFP_NOWAIT); + + if (buf && stack_depot_snprint(ct->fast_req[slot].stack, buf, SZ_4K, 0)) + xe_gt_err(gt, "Fence 0x%x was used by action %#04x sent at:\n%s\n", + fence, ct->fast_req[slot].action, buf); + else + xe_gt_err(gt, "Fence 0x%x was used by action %#04x [failed to retrieve stack]\n", + fence, ct->fast_req[slot].action); +#else + xe_gt_err(gt, "Fence 0x%x was used by action %#04x\n", + fence, ct->fast_req[slot].action); +#endif +} + +static void fast_req_report(struct xe_guc_ct *ct, u16 fence) +{ + u16 fence_min = U16_MAX, fence_max = 0; + struct xe_gt *gt = ct_to_gt(ct); + unsigned int n; + + lockdep_assert_held(&ct->lock); + + for (n = 0; n < ARRAY_SIZE(ct->fast_req); n++) { + if (ct->fast_req[n].fence < fence_min) + fence_min = ct->fast_req[n].fence; + if (ct->fast_req[n].fence > fence_max) + fence_max = ct->fast_req[n].fence; + + if (ct->fast_req[n].fence != fence) + continue; + + return fast_req_dump(ct, fence, n); + } + + xe_gt_warn(gt, "Fence 0x%x not found - tracking buffer wrapped? [range = 0x%x -> 0x%x, next = 0x%X]\n", + fence, fence_min, fence_max, ct->fence_seqno); +} + +static void fast_req_track(struct xe_guc_ct *ct, u16 fence, u16 action) +{ + unsigned int slot = fence % ARRAY_SIZE(ct->fast_req); + + fast_req_stack_save(ct, slot); + ct->fast_req[slot].fence = fence; + ct->fast_req[slot].action = action; +} + +#define CT_DEAD(ct, ctb, reason_code) ct_dead_capture((ct), (ctb), CT_DEAD_##reason_code) + #else + +static void ct_dead_fini(struct xe_guc_ct *ct) { } +static void ct_dead_init(struct xe_guc_ct *ct) { } + +static void fast_req_report(struct xe_guc_ct *ct, u16 fence) { } +static void fast_req_track(struct xe_guc_ct *ct, u16 fence, u16 action) { } + #define CT_DEAD(ct, ctb, reason) \ do { \ struct guc_ctb *_ctb = (ctb); \ if (_ctb) \ _ctb->info.broken = true; \ } while (0) + #endif /* Used when a CT send wants to block and / or receive data */ @@ -104,7 +206,9 @@ static void g2h_fence_cancel(struct g2h_fence *g2h_fence) { g2h_fence->cancel = true; g2h_fence->fail = true; - g2h_fence->done = true; + + /* WRITE_ONCE pairs with READ_ONCEs in guc_ct_send_recv. */ + WRITE_ONCE(g2h_fence->done, true); } static bool g2h_fence_needs_alloc(struct g2h_fence *g2h_fence) @@ -112,24 +216,6 @@ static bool g2h_fence_needs_alloc(struct g2h_fence *g2h_fence) return g2h_fence->seqno == ~0x0; } -static struct xe_guc * -ct_to_guc(struct xe_guc_ct *ct) -{ - return container_of(ct, struct xe_guc, ct); -} - -static struct xe_gt * -ct_to_gt(struct xe_guc_ct *ct) -{ - return container_of(ct, struct xe_gt, uc.guc.ct); -} - -static struct xe_device * -ct_to_xe(struct xe_guc_ct *ct) -{ - return gt_to_xe(ct_to_gt(ct)); -} - /** * DOC: GuC CTB Blob * @@ -169,8 +255,11 @@ ct_to_xe(struct xe_guc_ct *ct) #define CTB_DESC_SIZE ALIGN(sizeof(struct guc_ct_buffer_desc), SZ_2K) #define CTB_H2G_BUFFER_OFFSET (CTB_DESC_SIZE * 2) #define CTB_H2G_BUFFER_SIZE (SZ_4K) +#define CTB_H2G_BUFFER_DWORDS (CTB_H2G_BUFFER_SIZE / sizeof(u32)) #define CTB_G2H_BUFFER_SIZE (SZ_128K) +#define CTB_G2H_BUFFER_DWORDS (CTB_G2H_BUFFER_SIZE / sizeof(u32)) #define G2H_ROOM_BUFFER_SIZE (CTB_G2H_BUFFER_SIZE / 2) +#define G2H_ROOM_BUFFER_DWORDS (CTB_G2H_BUFFER_DWORDS / 2) /** * xe_guc_ct_queue_proc_time_jiffies - Return maximum time to process a full @@ -199,9 +288,7 @@ static void guc_ct_fini(struct drm_device *drm, void *arg) { struct xe_guc_ct *ct = arg; -#if IS_ENABLED(CONFIG_DRM_XE_DEBUG) - cancel_work_sync(&ct->dead.worker); -#endif + ct_dead_fini(ct); ct_exit_safe_mode(ct); destroy_workqueue(ct->g2h_wq); xa_destroy(&ct->fence_lookup); @@ -239,13 +326,8 @@ int xe_guc_ct_init_noalloc(struct xe_guc_ct *ct) xa_init(&ct->fence_lookup); INIT_WORK(&ct->g2h_worker, g2h_worker_func); INIT_DELAYED_WORK(&ct->safe_mode_worker, safe_mode_worker_func); -#if IS_ENABLED(CONFIG_DRM_XE_DEBUG) - spin_lock_init(&ct->dead.lock); - INIT_WORK(&ct->dead.worker, ct_dead_worker_func); -#if IS_ENABLED(CONFIG_DRM_XE_DEBUG_GUC) - stack_depot_init(); -#endif -#endif + + ct_dead_init(ct); init_waitqueue_head(&ct->wq); init_waitqueue_head(&ct->g2h_fence_wq); @@ -326,7 +408,7 @@ int xe_guc_ct_init_post_hwconfig(struct xe_guc_ct *ct) static void guc_ct_ctb_h2g_init(struct xe_device *xe, struct guc_ctb *h2g, struct iosys_map *map) { - h2g->info.size = CTB_H2G_BUFFER_SIZE / sizeof(u32); + h2g->info.size = CTB_H2G_BUFFER_DWORDS; h2g->info.resv_space = 0; h2g->info.tail = 0; h2g->info.head = 0; @@ -344,8 +426,8 @@ static void guc_ct_ctb_h2g_init(struct xe_device *xe, struct guc_ctb *h2g, static void guc_ct_ctb_g2h_init(struct xe_device *xe, struct guc_ctb *g2h, struct iosys_map *map) { - g2h->info.size = CTB_G2H_BUFFER_SIZE / sizeof(u32); - g2h->info.resv_space = G2H_ROOM_BUFFER_SIZE / sizeof(u32); + g2h->info.size = CTB_G2H_BUFFER_DWORDS; + g2h->info.resv_space = G2H_ROOM_BUFFER_DWORDS; g2h->info.head = 0; g2h->info.tail = 0; g2h->info.space = CIRC_SPACE(g2h->info.tail, g2h->info.head, @@ -447,7 +529,12 @@ static void guc_ct_change_state(struct xe_guc_ct *ct, if (ct->g2h_outstanding) xe_pm_runtime_put(ct_to_xe(ct)); ct->g2h_outstanding = 0; - ct->state = state; + + /* + * WRITE_ONCE pairs with READ_ONCEs in xe_guc_ct_initialized and + * xe_guc_ct_enabled. + */ + WRITE_ONCE(ct->state, state); xe_gt_dbg(gt, "GuC CT communication channel %s\n", state == XE_GUC_CT_STATE_STOPPED ? "stopped" : @@ -640,6 +727,39 @@ void xe_guc_ct_stop(struct xe_guc_ct *ct) stop_g2h_handler(ct); } +/** + * xe_guc_ct_runtime_suspend() - GuC CT runtime suspend + * @ct: the &xe_guc_ct + * + * Set GuC CT to disabled state. + */ +void xe_guc_ct_runtime_suspend(struct xe_guc_ct *ct) +{ + struct guc_ctb *g2h = &ct->ctbs.g2h; + u32 credits = CIRC_SPACE(0, 0, CTB_G2H_BUFFER_DWORDS) - G2H_ROOM_BUFFER_DWORDS; + + /* We should be back to guc_ct_ctb_g2h_init() values */ + xe_gt_assert(ct_to_gt(ct), g2h->info.space == credits); + + /* + * Since we're already in runtime suspend path, we shouldn't have pending + * messages. But if there happen to be any, we'd probably want them to be + * thrown as errors for further investigation. + */ + xe_guc_ct_disable(ct); +} + +/** + * xe_guc_ct_runtime_resume() - GuC CT runtime resume + * @ct: the &xe_guc_ct + * + * Restart GuC CT and set it to enabled state. + */ +void xe_guc_ct_runtime_resume(struct xe_guc_ct *ct) +{ + xe_guc_ct_restart(ct); +} + static bool h2g_has_room(struct xe_guc_ct *ct, u32 cmd_len) { struct guc_ctb *h2g = &ct->ctbs.h2g; @@ -747,28 +867,6 @@ static void g2h_release_space(struct xe_guc_ct *ct, u32 g2h_len) spin_unlock_irq(&ct->fast_lock); } -#if IS_ENABLED(CONFIG_DRM_XE_DEBUG) -static void fast_req_track(struct xe_guc_ct *ct, u16 fence, u16 action) -{ - unsigned int slot = fence % ARRAY_SIZE(ct->fast_req); -#if IS_ENABLED(CONFIG_DRM_XE_DEBUG_GUC) - unsigned long entries[SZ_32]; - unsigned int n; - - n = stack_trace_save(entries, ARRAY_SIZE(entries), 1); - - /* May be called under spinlock, so avoid sleeping */ - ct->fast_req[slot].stack = stack_depot_save(entries, n, GFP_NOWAIT); -#endif - ct->fast_req[slot].fence = fence; - ct->fast_req[slot].action = action; -} -#else -static void fast_req_track(struct xe_guc_ct *ct, u16 fence, u16 action) -{ -} -#endif - /* * The CT protocol accepts a 16 bits fence. This field is fully owned by the * driver, the GuC will just copy it to the reply message. Since we need to @@ -1203,10 +1301,13 @@ retry_same_fence: return ret; } - ret = wait_event_timeout(ct->g2h_fence_wq, g2h_fence.done, HZ); + /* READ_ONCEs pairs with WRITE_ONCEs in parse_g2h_response + * and g2h_fence_cancel. + */ + ret = wait_event_timeout(ct->g2h_fence_wq, READ_ONCE(g2h_fence.done), HZ); if (!ret) { LNL_FLUSH_WORK(&ct->g2h_worker); - if (g2h_fence.done) { + if (READ_ONCE(g2h_fence.done)) { xe_gt_warn(gt, "G2H fence %u, action %04x, done\n", g2h_fence.seqno, action[0]); ret = 1; @@ -1310,10 +1411,12 @@ static int parse_g2h_event(struct xe_guc_ct *ct, u32 *msg, u32 len) lockdep_assert_held(&ct->lock); switch (action) { + case XE_GUC_ACTION_NOTIFY_MULTI_QUEUE_CONTEXT_CGP_SYNC_DONE: case XE_GUC_ACTION_SCHED_CONTEXT_MODE_DONE: case XE_GUC_ACTION_DEREGISTER_CONTEXT_DONE: case XE_GUC_ACTION_SCHED_ENGINE_MODE_DONE: case XE_GUC_ACTION_TLB_INVALIDATION_DONE: + case XE_GUC_ACTION_PAGE_RECLAMATION_DONE: g2h_release_space(ct, len); } @@ -1338,55 +1441,6 @@ static int guc_crash_process_msg(struct xe_guc_ct *ct, u32 action) return 0; } -#if IS_ENABLED(CONFIG_DRM_XE_DEBUG) -static void fast_req_report(struct xe_guc_ct *ct, u16 fence) -{ - u16 fence_min = U16_MAX, fence_max = 0; - struct xe_gt *gt = ct_to_gt(ct); - bool found = false; - unsigned int n; -#if IS_ENABLED(CONFIG_DRM_XE_DEBUG_GUC) - char *buf; -#endif - - lockdep_assert_held(&ct->lock); - - for (n = 0; n < ARRAY_SIZE(ct->fast_req); n++) { - if (ct->fast_req[n].fence < fence_min) - fence_min = ct->fast_req[n].fence; - if (ct->fast_req[n].fence > fence_max) - fence_max = ct->fast_req[n].fence; - - if (ct->fast_req[n].fence != fence) - continue; - found = true; - -#if IS_ENABLED(CONFIG_DRM_XE_DEBUG_GUC) - buf = kmalloc(SZ_4K, GFP_NOWAIT); - if (buf && stack_depot_snprint(ct->fast_req[n].stack, buf, SZ_4K, 0)) - xe_gt_err(gt, "Fence 0x%x was used by action %#04x sent at:\n%s", - fence, ct->fast_req[n].action, buf); - else - xe_gt_err(gt, "Fence 0x%x was used by action %#04x [failed to retrieve stack]\n", - fence, ct->fast_req[n].action); - kfree(buf); -#else - xe_gt_err(gt, "Fence 0x%x was used by action %#04x\n", - fence, ct->fast_req[n].action); -#endif - break; - } - - if (!found) - xe_gt_warn(gt, "Fence 0x%x not found - tracking buffer wrapped? [range = 0x%x -> 0x%x, next = 0x%X]\n", - fence, fence_min, fence_max, ct->fence_seqno); -} -#else -static void fast_req_report(struct xe_guc_ct *ct, u16 fence) -{ -} -#endif - static int parse_g2h_response(struct xe_guc_ct *ct, u32 *msg, u32 len) { struct xe_gt *gt = ct_to_gt(ct); @@ -1454,7 +1508,8 @@ static int parse_g2h_response(struct xe_guc_ct *ct, u32 *msg, u32 len) g2h_release_space(ct, GUC_CTB_HXG_MSG_MAX_LEN); - g2h_fence->done = true; + /* WRITE_ONCE pairs with READ_ONCEs in guc_ct_send_recv. */ + WRITE_ONCE(g2h_fence->done, true); smp_mb(); wake_up_all(&ct->g2h_fence_wq); @@ -1549,6 +1604,15 @@ static int process_g2h_msg(struct xe_guc_ct *ct, u32 *msg, u32 len) ret = xe_guc_pagefault_handler(guc, payload, adj_len); break; case XE_GUC_ACTION_TLB_INVALIDATION_DONE: + case XE_GUC_ACTION_PAGE_RECLAMATION_DONE: + /* + * Page reclamation is an extension of TLB invalidation. Both + * operations share the same seqno and fence. When either + * action completes, we need to signal the corresponding + * fence. Since the handling logic (lookup fence by seqno, + * fence signalling) is identical, we use the same handler + * for both G2H events. + */ ret = xe_guc_tlb_inval_done_handler(guc, payload, adj_len); break; case XE_GUC_ACTION_GUC2PF_RELAY_FROM_VF: @@ -1572,6 +1636,13 @@ static int process_g2h_msg(struct xe_guc_ct *ct, u32 *msg, u32 len) ret = xe_guc_g2g_test_notification(guc, payload, adj_len); break; #endif + case XE_GUC_ACTION_NOTIFY_MULTI_QUEUE_CONTEXT_CGP_SYNC_DONE: + ret = xe_guc_exec_queue_cgp_sync_done_handler(guc, payload, adj_len); + break; + case XE_GUC_ACTION_NOTIFY_MULTI_QUEUE_CGP_CONTEXT_ERROR: + ret = xe_guc_exec_queue_cgp_context_error_handler(guc, payload, + adj_len); + break; default: xe_gt_err(gt, "unexpected G2H action 0x%04x\n", action); } @@ -1714,6 +1785,7 @@ static int g2h_read(struct xe_guc_ct *ct, u32 *msg, bool fast_path) switch (action) { case XE_GUC_ACTION_REPORT_PAGE_FAULT_REQ_DESC: case XE_GUC_ACTION_TLB_INVALIDATION_DONE: + case XE_GUC_ACTION_PAGE_RECLAMATION_DONE: break; /* Process these in fast-path */ default: return 0; @@ -1750,6 +1822,12 @@ static void g2h_fast_path(struct xe_guc_ct *ct, u32 *msg, u32 len) ret = xe_guc_pagefault_handler(guc, payload, adj_len); break; case XE_GUC_ACTION_TLB_INVALIDATION_DONE: + case XE_GUC_ACTION_PAGE_RECLAMATION_DONE: + /* + * Seqno and fence handling of page reclamation and TLB + * invalidation is identical, so we can use the same handler + * for both actions. + */ __g2h_release_space(ct, len); ret = xe_guc_tlb_inval_done_handler(guc, payload, adj_len); break; diff --git a/drivers/gpu/drm/xe/xe_guc_ct.h b/drivers/gpu/drm/xe/xe_guc_ct.h index ca1ce2b3c354..767365a33dee 100644 --- a/drivers/gpu/drm/xe/xe_guc_ct.h +++ b/drivers/gpu/drm/xe/xe_guc_ct.h @@ -17,6 +17,8 @@ int xe_guc_ct_init_post_hwconfig(struct xe_guc_ct *ct); int xe_guc_ct_enable(struct xe_guc_ct *ct); int xe_guc_ct_restart(struct xe_guc_ct *ct); void xe_guc_ct_disable(struct xe_guc_ct *ct); +void xe_guc_ct_runtime_resume(struct xe_guc_ct *ct); +void xe_guc_ct_runtime_suspend(struct xe_guc_ct *ct); void xe_guc_ct_stop(struct xe_guc_ct *ct); void xe_guc_ct_flush_and_stop(struct xe_guc_ct *ct); void xe_guc_ct_fast_path(struct xe_guc_ct *ct); @@ -28,12 +30,14 @@ void xe_guc_ct_print(struct xe_guc_ct *ct, struct drm_printer *p, bool want_ctb) static inline bool xe_guc_ct_initialized(struct xe_guc_ct *ct) { - return ct->state != XE_GUC_CT_STATE_NOT_INITIALIZED; + /* READ_ONCE pairs with WRITE_ONCE in guc_ct_change_state. */ + return READ_ONCE(ct->state) != XE_GUC_CT_STATE_NOT_INITIALIZED; } static inline bool xe_guc_ct_enabled(struct xe_guc_ct *ct) { - return ct->state == XE_GUC_CT_STATE_ENABLED; + /* READ_ONCE pairs with WRITE_ONCE in guc_ct_change_state. */ + return READ_ONCE(ct->state) == XE_GUC_CT_STATE_ENABLED; } static inline void xe_guc_ct_irq_handler(struct xe_guc_ct *ct) diff --git a/drivers/gpu/drm/xe/xe_guc_debugfs.c b/drivers/gpu/drm/xe/xe_guc_debugfs.c index 0b102ab46c4d..2f23119686d4 100644 --- a/drivers/gpu/drm/xe/xe_guc_debugfs.c +++ b/drivers/gpu/drm/xe/xe_guc_debugfs.c @@ -8,13 +8,12 @@ #include <drm/drm_debugfs.h> #include <drm/drm_managed.h> -#include "xe_device.h" -#include "xe_gt.h" +#include "xe_device_types.h" +#include "xe_gt_types.h" #include "xe_guc.h" #include "xe_guc_ct.h" #include "xe_guc_log.h" #include "xe_guc_pc.h" -#include "xe_macros.h" #include "xe_pm.h" /* @@ -70,13 +69,9 @@ static int guc_debugfs_show(struct seq_file *m, void *data) struct xe_gt *gt = grandparent->d_inode->i_private; struct xe_device *xe = gt_to_xe(gt); int (*print)(struct xe_guc *, struct drm_printer *) = node->info_ent->data; - int ret; - xe_pm_runtime_get(xe); - ret = print(>->uc.guc, &p); - xe_pm_runtime_put(xe); - - return ret; + guard(xe_pm_runtime)(xe); + return print(>->uc.guc, &p); } static int guc_log(struct xe_guc *guc, struct drm_printer *p) @@ -85,6 +80,12 @@ static int guc_log(struct xe_guc *guc, struct drm_printer *p) return 0; } +static int guc_log_lfd(struct xe_guc *guc, struct drm_printer *p) +{ + xe_guc_log_print_lfd(&guc->log, p); + return 0; +} + static int guc_log_dmesg(struct xe_guc *guc, struct drm_printer *p) { xe_guc_log_print_dmesg(&guc->log); @@ -121,6 +122,7 @@ static const struct drm_info_list slpc_debugfs_list[] = { /* everything else should be added here */ static const struct drm_info_list pf_only_debugfs_list[] = { { "guc_log", .show = guc_debugfs_show, .data = guc_log }, + { "guc_log_lfd", .show = guc_debugfs_show, .data = guc_log_lfd }, { "guc_log_dmesg", .show = guc_debugfs_show, .data = guc_log_dmesg }, }; diff --git a/drivers/gpu/drm/xe/xe_guc_exec_queue_types.h b/drivers/gpu/drm/xe/xe_guc_exec_queue_types.h index a3b034e4b205..fd0915ed8eb1 100644 --- a/drivers/gpu/drm/xe/xe_guc_exec_queue_types.h +++ b/drivers/gpu/drm/xe/xe_guc_exec_queue_types.h @@ -33,8 +33,6 @@ struct xe_guc_exec_queue { */ #define MAX_STATIC_MSG_TYPE 3 struct xe_sched_msg static_msgs[MAX_STATIC_MSG_TYPE]; - /** @lr_tdr: long running TDR worker */ - struct work_struct lr_tdr; /** @destroy_async: do final destroy async from this worker */ struct work_struct destroy_async; /** @resume_time: time of last resume */ diff --git a/drivers/gpu/drm/xe/xe_guc_fwif.h b/drivers/gpu/drm/xe/xe_guc_fwif.h index c90dd266e9cf..a33ea288b907 100644 --- a/drivers/gpu/drm/xe/xe_guc_fwif.h +++ b/drivers/gpu/drm/xe/xe_guc_fwif.h @@ -10,63 +10,15 @@ #include "abi/guc_capture_abi.h" #include "abi/guc_klvs_abi.h" +#include "abi/guc_scheduler_abi.h" #include "xe_hw_engine_types.h" #define G2H_LEN_DW_SCHED_CONTEXT_MODE_SET 4 #define G2H_LEN_DW_DEREGISTER_CONTEXT 3 #define G2H_LEN_DW_TLB_INVALIDATE 3 #define G2H_LEN_DW_G2G_NOTIFY_MIN 3 - -#define GUC_ID_MAX 65535 -#define GUC_ID_UNKNOWN 0xffffffff - -#define GUC_CONTEXT_DISABLE 0 -#define GUC_CONTEXT_ENABLE 1 - -#define GUC_CLIENT_PRIORITY_KMD_HIGH 0 -#define GUC_CLIENT_PRIORITY_HIGH 1 -#define GUC_CLIENT_PRIORITY_KMD_NORMAL 2 -#define GUC_CLIENT_PRIORITY_NORMAL 3 -#define GUC_CLIENT_PRIORITY_NUM 4 - -#define GUC_RENDER_ENGINE 0 -#define GUC_VIDEO_ENGINE 1 -#define GUC_BLITTER_ENGINE 2 -#define GUC_VIDEOENHANCE_ENGINE 3 -#define GUC_VIDEO_ENGINE2 4 -#define GUC_MAX_ENGINES_NUM (GUC_VIDEO_ENGINE2 + 1) - -#define GUC_RENDER_CLASS 0 -#define GUC_VIDEO_CLASS 1 -#define GUC_VIDEOENHANCE_CLASS 2 -#define GUC_BLITTER_CLASS 3 -#define GUC_COMPUTE_CLASS 4 -#define GUC_GSC_OTHER_CLASS 5 -#define GUC_LAST_ENGINE_CLASS GUC_GSC_OTHER_CLASS -#define GUC_MAX_ENGINE_CLASSES 16 -#define GUC_MAX_INSTANCES_PER_CLASS 32 - -#define GUC_CONTEXT_NORMAL 0 -#define GUC_CONTEXT_COMPRESSION_SAVE 1 -#define GUC_CONTEXT_COMPRESSION_RESTORE 2 -#define GUC_CONTEXT_COUNT (GUC_CONTEXT_COMPRESSION_RESTORE + 1) - -/* Helper for context registration H2G */ -struct guc_ctxt_registration_info { - u32 flags; - u32 context_idx; - u32 engine_class; - u32 engine_submit_mask; - u32 wq_desc_lo; - u32 wq_desc_hi; - u32 wq_base_lo; - u32 wq_base_hi; - u32 wq_size; - u32 hwlrca_lo; - u32 hwlrca_hi; -}; -#define CONTEXT_REGISTRATION_FLAG_KMD BIT(0) -#define CONTEXT_REGISTRATION_FLAG_TYPE GENMASK(2, 1) +#define G2H_LEN_DW_MULTI_QUEUE_CONTEXT 3 +#define G2H_LEN_DW_PAGE_RECLAMATION 3 /* 32-bit KLV structure as used by policy updates and others */ struct guc_klv_generic_dw_t { @@ -91,9 +43,9 @@ struct guc_update_exec_queue_policy { #define GUC_LOG_NOTIFY_ON_HALF_FULL BIT(1) #define GUC_LOG_CAPTURE_ALLOC_UNITS BIT(2) #define GUC_LOG_LOG_ALLOC_UNITS BIT(3) -#define GUC_LOG_CRASH REG_GENMASK(5, 4) -#define GUC_LOG_DEBUG REG_GENMASK(9, 6) -#define GUC_LOG_CAPTURE REG_GENMASK(11, 10) +#define GUC_LOG_CRASH_DUMP REG_GENMASK(5, 4) +#define GUC_LOG_EVENT_DATA REG_GENMASK(9, 6) +#define GUC_LOG_STATE_CAPTURE REG_GENMASK(11, 10) #define GUC_LOG_BUF_ADDR REG_GENMASK(31, 12) #define GUC_CTL_WA 1 diff --git a/drivers/gpu/drm/xe/xe_guc_hwconfig.c b/drivers/gpu/drm/xe/xe_guc_hwconfig.c index af2c817d552c..b300901dbb8e 100644 --- a/drivers/gpu/drm/xe/xe_guc_hwconfig.c +++ b/drivers/gpu/drm/xe/xe_guc_hwconfig.c @@ -10,8 +10,8 @@ #include "abi/guc_actions_abi.h" #include "xe_bo.h" -#include "xe_device.h" -#include "xe_gt.h" +#include "xe_device_types.h" +#include "xe_gt_types.h" #include "xe_guc.h" #include "xe_map.h" diff --git a/drivers/gpu/drm/xe/xe_guc_klv_helpers.c b/drivers/gpu/drm/xe/xe_guc_klv_helpers.c index 146a6eda9e06..97600edda837 100644 --- a/drivers/gpu/drm/xe/xe_guc_klv_helpers.c +++ b/drivers/gpu/drm/xe/xe_guc_klv_helpers.c @@ -21,11 +21,16 @@ const char *xe_guc_klv_key_to_string(u16 key) { switch (key) { + /* GuC Global Config KLVs */ + case GUC_KLV_GLOBAL_CFG_GROUP_SCHEDULING_AVAILABLE_KEY: + return "group_scheduling_available"; /* VGT POLICY keys */ case GUC_KLV_VGT_POLICY_SCHED_IF_IDLE_KEY: return "sched_if_idle"; case GUC_KLV_VGT_POLICY_ADVERSE_SAMPLE_PERIOD_KEY: return "sample_period"; + case GUC_KLV_VGT_POLICY_ENGINE_GROUP_CONFIG_KEY: + return "engine_group_config"; case GUC_KLV_VGT_POLICY_RESET_AFTER_VF_SWITCH_KEY: return "reset_engine"; /* VF CFG keys */ @@ -51,6 +56,10 @@ const char *xe_guc_klv_key_to_string(u16 key) return "begin_ctx_id"; case GUC_KLV_VF_CFG_SCHED_PRIORITY_KEY: return "sched_priority"; + case GUC_KLV_VF_CFG_ENGINE_GROUP_EXEC_QUANTUM_KEY: + return "sched_groups_exec_quantum"; + case GUC_KLV_VF_CFG_ENGINE_GROUP_PREEMPT_TIMEOUT_KEY: + return "sched_groups_preempt_timeout"; /* VF CFG threshold keys */ #define define_threshold_key_to_string_case(TAG, NAME, ...) \ diff --git a/drivers/gpu/drm/xe/xe_guc_klv_thresholds_set_types.h b/drivers/gpu/drm/xe/xe_guc_klv_thresholds_set_types.h index 0a028c94756d..45ab5a3b5218 100644 --- a/drivers/gpu/drm/xe/xe_guc_klv_thresholds_set_types.h +++ b/drivers/gpu/drm/xe/xe_guc_klv_thresholds_set_types.h @@ -24,6 +24,11 @@ * ABI and the associated &NAME, that may be used in code or debugfs/sysfs:: * * define(TAG, NAME) + * + * If required, KLVs can be labeled with GuC firmware version that added them:: + * + * define(TAG, NAME, MAJOR, MINOR) + * define(TAG, NAME, MAJOR, MINOR, PATCH) */ #define MAKE_XE_GUC_KLV_THRESHOLDS_SET(define) \ define(CAT_ERR, cat_error_count) \ @@ -32,6 +37,7 @@ define(H2G_STORM, guc_time_us) \ define(IRQ_STORM, irq_time_us) \ define(DOORBELL_STORM, doorbell_time_us) \ + define(MULTI_LRC_COUNT, multi_lrc_count, 70, 53)\ /* end */ /** diff --git a/drivers/gpu/drm/xe/xe_guc_log.c b/drivers/gpu/drm/xe/xe_guc_log.c index c01ccb35dc75..acac66a4eca7 100644 --- a/drivers/gpu/drm/xe/xe_guc_log.c +++ b/drivers/gpu/drm/xe/xe_guc_log.c @@ -7,18 +7,91 @@ #include <linux/fault-inject.h> +#include <linux/utsname.h> #include <drm/drm_managed.h> +#include "abi/guc_lfd_abi.h" #include "regs/xe_guc_regs.h" #include "xe_bo.h" #include "xe_devcoredump.h" #include "xe_force_wake.h" -#include "xe_gt.h" #include "xe_gt_printk.h" +#include "xe_gt_types.h" #include "xe_map.h" #include "xe_mmio.h" #include "xe_module.h" +#define GUC_LOG_CHUNK_SIZE SZ_2M + +/* Magic keys define */ +#define GUC_LFD_DRIVER_KEY_STREAMING 0x8086AAAA474C5346 +#define GUC_LFD_LOG_BUFFER_MARKER_2 0xDEADFEED +#define GUC_LFD_CRASH_DUMP_BUFFER_MARKER_2 0x8086DEAD +#define GUC_LFD_STATE_CAPTURE_BUFFER_MARKER_2 0xBEEFFEED +#define GUC_LFD_LOG_BUFFER_MARKER_1V2 0xCABBA9E6 +#define GUC_LFD_STATE_CAPTURE_BUFFER_MARKER_1V2 0xCABBA9F7 +#define GUC_LFD_DATA_HEADER_MAGIC 0x8086 + +/* LFD supported LIC type range */ +#define GUC_LIC_TYPE_FIRST GUC_LIC_TYPE_GUC_SW_VERSION +#define GUC_LIC_TYPE_LAST GUC_LIC_TYPE_BUILD_PLATFORM_ID +#define GUC_LFD_TYPE_FW_RANGE_FIRST GUC_LFD_TYPE_FW_VERSION +#define GUC_LFD_TYPE_FW_RANGE_LAST GUC_LFD_TYPE_BUILD_PLATFORM_ID + +#define GUC_LOG_BUFFER_STATE_HEADER_LENGTH 4096 +#define GUC_LOG_BUFFER_INIT_CONFIG 3 + +struct guc_log_buffer_entry_list { + u32 offset; + u32 rd_ptr; + u32 wr_ptr; + u32 wrap_offset; + u32 buf_size; +}; + +struct guc_lic_save { + u32 version; + /* + * Array of init config KLV values. + * Range from GUC_LOG_LIC_TYPE_FIRST to GUC_LOG_LIC_TYPE_LAST + */ + u32 values[GUC_LIC_TYPE_LAST - GUC_LIC_TYPE_FIRST + 1]; + struct guc_log_buffer_entry_list entry[GUC_LOG_BUFFER_INIT_CONFIG]; +}; + +static struct guc_log_buffer_entry_markers { + u32 key[2]; +} const entry_markers[GUC_LOG_BUFFER_INIT_CONFIG + 1] = { + {{ + GUC_LFD_LOG_BUFFER_MARKER_1V2, + GUC_LFD_LOG_BUFFER_MARKER_2 + }}, + {{ + GUC_LFD_LOG_BUFFER_MARKER_1V2, + GUC_LFD_CRASH_DUMP_BUFFER_MARKER_2 + }}, + {{ + GUC_LFD_STATE_CAPTURE_BUFFER_MARKER_1V2, + GUC_LFD_STATE_CAPTURE_BUFFER_MARKER_2 + }}, + {{ + GUC_LIC_MAGIC, + (FIELD_PREP_CONST(GUC_LIC_VERSION_MASK_MAJOR, GUC_LIC_VERSION_MAJOR) | + FIELD_PREP_CONST(GUC_LIC_VERSION_MASK_MINOR, GUC_LIC_VERSION_MINOR)) + }} +}; + +static struct guc_log_lic_lfd_map { + u32 lic; + u32 lfd; +} const lic_lfd_type_map[] = { + {GUC_LIC_TYPE_GUC_SW_VERSION, GUC_LFD_TYPE_FW_VERSION}, + {GUC_LIC_TYPE_GUC_DEVICE_ID, GUC_LFD_TYPE_GUC_DEVICE_ID}, + {GUC_LIC_TYPE_TSC_FREQUENCY, GUC_LFD_TYPE_TSC_FREQUENCY}, + {GUC_LIC_TYPE_GMD_ID, GUC_LFD_TYPE_GMD_ID}, + {GUC_LIC_TYPE_BUILD_PLATFORM_ID, GUC_LFD_TYPE_BUILD_PLATFORM_ID} +}; + static struct xe_guc * log_to_guc(struct xe_guc_log *log) { @@ -37,33 +110,6 @@ log_to_xe(struct xe_guc_log *log) return gt_to_xe(log_to_gt(log)); } -static size_t guc_log_size(void) -{ - /* - * GuC Log buffer Layout - * - * +===============================+ 00B - * | Crash dump state header | - * +-------------------------------+ 32B - * | Debug state header | - * +-------------------------------+ 64B - * | Capture state header | - * +-------------------------------+ 96B - * | | - * +===============================+ PAGE_SIZE (4KB) - * | Crash Dump logs | - * +===============================+ + CRASH_SIZE - * | Debug logs | - * +===============================+ + DEBUG_SIZE - * | Capture logs | - * +===============================+ + CAPTURE_SIZE - */ - return PAGE_SIZE + CRASH_BUFFER_SIZE + DEBUG_BUFFER_SIZE + - CAPTURE_BUFFER_SIZE; -} - -#define GUC_LOG_CHUNK_SIZE SZ_2M - static struct xe_guc_log_snapshot *xe_guc_log_snapshot_alloc(struct xe_guc_log *log, bool atomic) { struct xe_guc_log_snapshot *snapshot; @@ -145,7 +191,6 @@ struct xe_guc_log_snapshot *xe_guc_log_snapshot_capture(struct xe_guc_log *log, struct xe_device *xe = log_to_xe(log); struct xe_guc *guc = log_to_guc(log); struct xe_gt *gt = log_to_gt(log); - unsigned int fw_ref; size_t remain; int i; @@ -165,13 +210,12 @@ struct xe_guc_log_snapshot *xe_guc_log_snapshot_capture(struct xe_guc_log *log, remain -= size; } - fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT); - if (!fw_ref) { + CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT); + if (!fw_ref.domains) snapshot->stamp = ~0ULL; - } else { + else snapshot->stamp = xe_mmio_read64_2x32(>->mmio, GUC_PMTIMESTAMP_LO); - xe_force_wake_put(gt_to_fw(gt), fw_ref); - } + snapshot->ktime = ktime_get_boottime_ns(); snapshot->level = log->level; snapshot->ver_found = guc->fw.versions.found[XE_UC_FW_VER_RELEASE]; @@ -216,6 +260,318 @@ void xe_guc_log_snapshot_print(struct xe_guc_log_snapshot *snapshot, struct drm_ } } +static inline void lfd_output_binary(struct drm_printer *p, char *buf, int buf_size) +{ + seq_write(p->arg, buf, buf_size); +} + +static inline int xe_guc_log_add_lfd_header(struct guc_lfd_data *lfd) +{ + lfd->header = FIELD_PREP_CONST(GUC_LFD_DATA_HEADER_MASK_MAGIC, GUC_LFD_DATA_HEADER_MAGIC); + return offsetof(struct guc_lfd_data, data); +} + +static int xe_guc_log_add_typed_payload(struct drm_printer *p, u32 type, + u32 data_len, void *data) +{ + struct guc_lfd_data lfd; + int len; + + len = xe_guc_log_add_lfd_header(&lfd); + lfd.header |= FIELD_PREP(GUC_LFD_DATA_HEADER_MASK_TYPE, type); + /* make length DW aligned */ + lfd.data_count = DIV_ROUND_UP(data_len, sizeof(u32)); + lfd_output_binary(p, (char *)&lfd, len); + + lfd_output_binary(p, data, data_len); + len += lfd.data_count * sizeof(u32); + + return len; +} + +static inline int lic_type_to_index(u32 lic_type) +{ + XE_WARN_ON(lic_type < GUC_LIC_TYPE_FIRST || lic_type > GUC_LIC_TYPE_LAST); + + return lic_type - GUC_LIC_TYPE_FIRST; +} + +static inline int lfd_type_to_index(u32 lfd_type) +{ + int i, lic_type = 0; + + XE_WARN_ON(lfd_type < GUC_LFD_TYPE_FW_RANGE_FIRST || lfd_type > GUC_LFD_TYPE_FW_RANGE_LAST); + + for (i = 0; i < ARRAY_SIZE(lic_lfd_type_map); i++) + if (lic_lfd_type_map[i].lfd == lfd_type) + lic_type = lic_lfd_type_map[i].lic; + + /* If not found, lic_type_to_index will warning invalid type */ + return lic_type_to_index(lic_type); +} + +static int xe_guc_log_add_klv(struct drm_printer *p, u32 lfd_type, + struct guc_lic_save *config) +{ + int klv_index = lfd_type_to_index(lfd_type); + + return xe_guc_log_add_typed_payload(p, lfd_type, sizeof(u32), &config->values[klv_index]); +} + +static int xe_guc_log_add_os_id(struct drm_printer *p, u32 id) +{ + struct guc_lfd_data_os_info os_id; + struct guc_lfd_data lfd; + int len, info_len, section_len; + char *version; + u32 blank = 0; + + len = xe_guc_log_add_lfd_header(&lfd); + lfd.header |= FIELD_PREP(GUC_LFD_DATA_HEADER_MASK_TYPE, GUC_LFD_TYPE_OS_ID); + + os_id.os_id = id; + section_len = offsetof(struct guc_lfd_data_os_info, build_version); + + version = init_utsname()->release; + info_len = strlen(version); + + /* make length DW aligned */ + lfd.data_count = DIV_ROUND_UP(section_len + info_len, sizeof(u32)); + lfd_output_binary(p, (char *)&lfd, len); + lfd_output_binary(p, (char *)&os_id, section_len); + lfd_output_binary(p, version, info_len); + + /* Padding with 0 */ + section_len = lfd.data_count * sizeof(u32) - section_len - info_len; + if (section_len) + lfd_output_binary(p, (char *)&blank, section_len); + + len += lfd.data_count * sizeof(u32); + return len; +} + +static void xe_guc_log_loop_log_init(struct guc_lic *init, struct guc_lic_save *config) +{ + struct guc_klv_generic_dw_t *p = (void *)init->data; + int i; + + for (i = 0; i < init->data_count;) { + int klv_len = FIELD_GET(GUC_KLV_0_LEN, p->kl) + 1; + int key = FIELD_GET(GUC_KLV_0_KEY, p->kl); + + if (key < GUC_LIC_TYPE_FIRST || key > GUC_LIC_TYPE_LAST) { + XE_WARN_ON(key < GUC_LIC_TYPE_FIRST || key > GUC_LIC_TYPE_LAST); + break; + } + config->values[lic_type_to_index(key)] = p->value; + i += klv_len + 1; /* Whole KLV structure length in dwords */ + p = (void *)((u32 *)p + klv_len); + } +} + +static int find_marker(u32 mark0, u32 mark1) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(entry_markers); i++) + if (mark0 == entry_markers[i].key[0] && mark1 == entry_markers[i].key[1]) + return i; + + return ARRAY_SIZE(entry_markers); +} + +static void xe_guc_log_load_lic(void *guc_log, struct guc_lic_save *config) +{ + u32 offset = GUC_LOG_BUFFER_STATE_HEADER_LENGTH; + struct guc_log_buffer_state *p = guc_log; + + config->version = p->version; + while (p->marker[0]) { + int index; + + index = find_marker(p->marker[0], p->marker[1]); + + if (index < ARRAY_SIZE(entry_markers)) { + if (index == GUC_LOG_BUFFER_INIT_CONFIG) { + /* Load log init config */ + xe_guc_log_loop_log_init((void *)p, config); + + /* LIC structure is the last */ + return; + } + config->entry[index].offset = offset; + config->entry[index].rd_ptr = p->read_ptr; + config->entry[index].wr_ptr = p->write_ptr; + config->entry[index].wrap_offset = p->wrap_offset; + config->entry[index].buf_size = p->size; + } + offset += p->size; + p++; + } +} + +static int +xe_guc_log_output_lfd_init(struct drm_printer *p, struct xe_guc_log_snapshot *snapshot, + struct guc_lic_save *config) +{ + int type, len; + size_t size = 0; + + /* FW required types */ + for (type = GUC_LFD_TYPE_FW_RANGE_FIRST; type <= GUC_LFD_TYPE_FW_RANGE_LAST; type++) + size += xe_guc_log_add_klv(p, type, config); + + /* KMD required type(s) */ + len = xe_guc_log_add_os_id(p, GUC_LFD_OS_TYPE_OSID_LIN); + size += len; + + return size; +} + +static void +xe_guc_log_print_chunks(struct drm_printer *p, struct xe_guc_log_snapshot *snapshot, + u32 from, u32 to) +{ + int chunk_from = from % GUC_LOG_CHUNK_SIZE; + int chunk_id = from / GUC_LOG_CHUNK_SIZE; + int to_chunk_id = to / GUC_LOG_CHUNK_SIZE; + int chunk_to = to % GUC_LOG_CHUNK_SIZE; + int pos = from; + + do { + size_t size = (to_chunk_id == chunk_id ? chunk_to : GUC_LOG_CHUNK_SIZE) - + chunk_from; + + lfd_output_binary(p, snapshot->copy[chunk_id] + chunk_from, size); + pos += size; + chunk_id++; + chunk_from = 0; + } while (pos < to); +} + +static inline int +xe_guc_log_add_log_event(struct drm_printer *p, struct xe_guc_log_snapshot *snapshot, + struct guc_lic_save *config) +{ + size_t size; + u32 data_len, section_len; + struct guc_lfd_data lfd; + struct guc_log_buffer_entry_list *entry; + struct guc_lfd_data_log_events_buf events_buf; + + entry = &config->entry[GUC_LOG_TYPE_EVENT_DATA]; + + /* Skip empty log */ + if (entry->rd_ptr == entry->wr_ptr) + return 0; + + size = xe_guc_log_add_lfd_header(&lfd); + lfd.header |= FIELD_PREP(GUC_LFD_DATA_HEADER_MASK_TYPE, GUC_LFD_TYPE_LOG_EVENTS_BUFFER); + events_buf.log_events_format_version = config->version; + + /* Adjust to log_format_buf */ + section_len = offsetof(struct guc_lfd_data_log_events_buf, log_event); + data_len = section_len; + + /* Calculate data length */ + data_len += entry->rd_ptr < entry->wr_ptr ? (entry->wr_ptr - entry->rd_ptr) : + (entry->wr_ptr + entry->wrap_offset - entry->rd_ptr); + /* make length u32 aligned */ + lfd.data_count = DIV_ROUND_UP(data_len, sizeof(u32)); + + /* Output GUC_LFD_TYPE_LOG_EVENTS_BUFFER header */ + lfd_output_binary(p, (char *)&lfd, size); + lfd_output_binary(p, (char *)&events_buf, section_len); + + /* Output data from guc log chunks directly */ + if (entry->rd_ptr < entry->wr_ptr) { + xe_guc_log_print_chunks(p, snapshot, entry->offset + entry->rd_ptr, + entry->offset + entry->wr_ptr); + } else { + /* 1st, print from rd to wrap offset */ + xe_guc_log_print_chunks(p, snapshot, entry->offset + entry->rd_ptr, + entry->offset + entry->wrap_offset); + + /* 2nd, print from buf start to wr */ + xe_guc_log_print_chunks(p, snapshot, entry->offset, entry->offset + entry->wr_ptr); + } + return size; +} + +static int +xe_guc_log_add_crash_dump(struct drm_printer *p, struct xe_guc_log_snapshot *snapshot, + struct guc_lic_save *config) +{ + struct guc_log_buffer_entry_list *entry; + int chunk_from, chunk_id; + int from, to, i; + size_t size = 0; + u32 *buf32; + + entry = &config->entry[GUC_LOG_TYPE_CRASH_DUMP]; + + /* Skip zero sized crash dump */ + if (!entry->buf_size) + return 0; + + /* Check if crash dump section are all zero */ + from = entry->offset; + to = entry->offset + entry->buf_size; + chunk_from = from % GUC_LOG_CHUNK_SIZE; + chunk_id = from / GUC_LOG_CHUNK_SIZE; + buf32 = snapshot->copy[chunk_id] + chunk_from; + + for (i = 0; i < entry->buf_size / sizeof(u32); i++) + if (buf32[i]) + break; + + /* Buffer has non-zero data? */ + if (i < entry->buf_size / sizeof(u32)) { + struct guc_lfd_data lfd; + + size = xe_guc_log_add_lfd_header(&lfd); + lfd.header |= FIELD_PREP(GUC_LFD_DATA_HEADER_MASK_TYPE, GUC_LFD_TYPE_FW_CRASH_DUMP); + /* Calculate data length */ + lfd.data_count = DIV_ROUND_UP(entry->buf_size, sizeof(u32)); + /* Output GUC_LFD_TYPE_FW_CRASH_DUMP header */ + lfd_output_binary(p, (char *)&lfd, size); + + /* rd/wr ptr is not used for crash dump */ + xe_guc_log_print_chunks(p, snapshot, from, to); + } + return size; +} + +static void +xe_guc_log_snapshot_print_lfd(struct xe_guc_log_snapshot *snapshot, struct drm_printer *p) +{ + struct guc_lfd_file_header header; + struct guc_lic_save config; + size_t size; + + if (!snapshot || !snapshot->size) + return; + + header.magic = GUC_LFD_DRIVER_KEY_STREAMING; + header.version = FIELD_PREP_CONST(GUC_LFD_FILE_HEADER_VERSION_MASK_MINOR, + GUC_LFD_FORMAT_VERSION_MINOR) | + FIELD_PREP_CONST(GUC_LFD_FILE_HEADER_VERSION_MASK_MAJOR, + GUC_LFD_FORMAT_VERSION_MAJOR); + + /* Output LFD file header */ + lfd_output_binary(p, (char *)&header, + offsetof(struct guc_lfd_file_header, stream)); + + /* Output LFD stream */ + xe_guc_log_load_lic(snapshot->copy[0], &config); + size = xe_guc_log_output_lfd_init(p, snapshot, &config); + if (!size) + return; + + xe_guc_log_add_log_event(p, snapshot, &config); + xe_guc_log_add_crash_dump(p, snapshot, &config); +} + /** * xe_guc_log_print_dmesg - dump a copy of the GuC log to dmesg * @log: GuC log structure @@ -251,13 +607,27 @@ void xe_guc_log_print(struct xe_guc_log *log, struct drm_printer *p) xe_guc_log_snapshot_free(snapshot); } +/** + * xe_guc_log_print_lfd - dump a copy of the GuC log in LFD format + * @log: GuC log structure + * @p: the printer object to output to + */ +void xe_guc_log_print_lfd(struct xe_guc_log *log, struct drm_printer *p) +{ + struct xe_guc_log_snapshot *snapshot; + + snapshot = xe_guc_log_snapshot_capture(log, false); + xe_guc_log_snapshot_print_lfd(snapshot, p); + xe_guc_log_snapshot_free(snapshot); +} + int xe_guc_log_init(struct xe_guc_log *log) { struct xe_device *xe = log_to_xe(log); struct xe_tile *tile = gt_to_tile(log_to_gt(log)); struct xe_bo *bo; - bo = xe_managed_bo_create_pin_map(xe, tile, guc_log_size(), + bo = xe_managed_bo_create_pin_map(xe, tile, GUC_LOG_SIZE, XE_BO_FLAG_SYSTEM | XE_BO_FLAG_GGTT | XE_BO_FLAG_GGTT_INVALIDATE | @@ -265,7 +635,7 @@ int xe_guc_log_init(struct xe_guc_log *log) if (IS_ERR(bo)) return PTR_ERR(bo); - xe_map_memset(xe, &bo->vmap, 0, 0, guc_log_size()); + xe_map_memset(xe, &bo->vmap, 0, 0, xe_bo_size(bo)); log->bo = bo; log->level = xe_modparam.guc_log_level; @@ -274,71 +644,6 @@ int xe_guc_log_init(struct xe_guc_log *log) ALLOW_ERROR_INJECTION(xe_guc_log_init, ERRNO); /* See xe_pci_probe() */ -static u32 xe_guc_log_section_size_crash(struct xe_guc_log *log) -{ - return CRASH_BUFFER_SIZE; -} - -static u32 xe_guc_log_section_size_debug(struct xe_guc_log *log) -{ - return DEBUG_BUFFER_SIZE; -} - -/** - * xe_guc_log_section_size_capture - Get capture buffer size within log sections. - * @log: The log object. - * - * This function will return the capture buffer size within log sections. - * - * Return: capture buffer size. - */ -u32 xe_guc_log_section_size_capture(struct xe_guc_log *log) -{ - return CAPTURE_BUFFER_SIZE; -} - -/** - * xe_guc_get_log_buffer_size - Get log buffer size for a type. - * @log: The log object. - * @type: The log buffer type - * - * Return: buffer size. - */ -u32 xe_guc_get_log_buffer_size(struct xe_guc_log *log, enum guc_log_buffer_type type) -{ - switch (type) { - case GUC_LOG_BUFFER_CRASH_DUMP: - return xe_guc_log_section_size_crash(log); - case GUC_LOG_BUFFER_DEBUG: - return xe_guc_log_section_size_debug(log); - case GUC_LOG_BUFFER_CAPTURE: - return xe_guc_log_section_size_capture(log); - } - return 0; -} - -/** - * xe_guc_get_log_buffer_offset - Get offset in log buffer for a type. - * @log: The log object. - * @type: The log buffer type - * - * This function will return the offset in the log buffer for a type. - * Return: buffer offset. - */ -u32 xe_guc_get_log_buffer_offset(struct xe_guc_log *log, enum guc_log_buffer_type type) -{ - enum guc_log_buffer_type i; - u32 offset = PAGE_SIZE;/* for the log_buffer_states */ - - for (i = GUC_LOG_BUFFER_CRASH_DUMP; i < GUC_LOG_BUFFER_TYPE_MAX; ++i) { - if (i == type) - break; - offset += xe_guc_get_log_buffer_size(log, i); - } - - return offset; -} - /** * xe_guc_check_log_buf_overflow - Check if log buffer overflowed * @log: The log object. @@ -352,7 +657,7 @@ u32 xe_guc_get_log_buffer_offset(struct xe_guc_log *log, enum guc_log_buffer_typ * * Return: True if overflowed. */ -bool xe_guc_check_log_buf_overflow(struct xe_guc_log *log, enum guc_log_buffer_type type, +bool xe_guc_check_log_buf_overflow(struct xe_guc_log *log, enum guc_log_type type, unsigned int full_cnt) { unsigned int prev_full_cnt = log->stats[type].sampled_overflow; diff --git a/drivers/gpu/drm/xe/xe_guc_log.h b/drivers/gpu/drm/xe/xe_guc_log.h index 98a47ac42b08..1b05bb60c1c7 100644 --- a/drivers/gpu/drm/xe/xe_guc_log.h +++ b/drivers/gpu/drm/xe/xe_guc_log.h @@ -13,14 +13,26 @@ struct drm_printer; struct xe_device; #if IS_ENABLED(CONFIG_DRM_XE_DEBUG_GUC) -#define CRASH_BUFFER_SIZE SZ_1M -#define DEBUG_BUFFER_SIZE SZ_8M -#define CAPTURE_BUFFER_SIZE SZ_2M +#define XE_GUC_LOG_EVENT_DATA_BUFFER_SIZE SZ_8M +#define XE_GUC_LOG_CRASH_DUMP_BUFFER_SIZE SZ_1M +#define XE_GUC_LOG_STATE_CAPTURE_BUFFER_SIZE SZ_2M #else -#define CRASH_BUFFER_SIZE SZ_16K -#define DEBUG_BUFFER_SIZE SZ_64K -#define CAPTURE_BUFFER_SIZE SZ_1M +#define XE_GUC_LOG_EVENT_DATA_BUFFER_SIZE SZ_64K +#define XE_GUC_LOG_CRASH_DUMP_BUFFER_SIZE SZ_16K +#define XE_GUC_LOG_STATE_CAPTURE_BUFFER_SIZE SZ_1M #endif + +#define GUC_LOG_SIZE (SZ_4K + \ + XE_GUC_LOG_EVENT_DATA_BUFFER_SIZE + \ + XE_GUC_LOG_CRASH_DUMP_BUFFER_SIZE + \ + XE_GUC_LOG_STATE_CAPTURE_BUFFER_SIZE) + +#define XE_GUC_LOG_EVENT_DATA_OFFSET SZ_4K +#define XE_GUC_LOG_CRASH_DUMP_OFFSET (XE_GUC_LOG_EVENT_DATA_OFFSET + \ + XE_GUC_LOG_EVENT_DATA_BUFFER_SIZE) +#define XE_GUC_LOG_STATE_CAPTURE_OFFSET (XE_GUC_LOG_CRASH_DUMP_OFFSET + \ + XE_GUC_LOG_CRASH_DUMP_BUFFER_SIZE) + /* * While we're using plain log level in i915, GuC controls are much more... * "elaborate"? We have a couple of bits for verbosity, separate bit for actual @@ -40,6 +52,7 @@ struct xe_device; int xe_guc_log_init(struct xe_guc_log *log); void xe_guc_log_print(struct xe_guc_log *log, struct drm_printer *p); +void xe_guc_log_print_lfd(struct xe_guc_log *log, struct drm_printer *p); void xe_guc_log_print_dmesg(struct xe_guc_log *log); struct xe_guc_log_snapshot *xe_guc_log_snapshot_capture(struct xe_guc_log *log, bool atomic); void xe_guc_log_snapshot_print(struct xe_guc_log_snapshot *snapshot, struct drm_printer *p); @@ -51,11 +64,8 @@ xe_guc_log_get_level(struct xe_guc_log *log) return log->level; } -u32 xe_guc_log_section_size_capture(struct xe_guc_log *log); -u32 xe_guc_get_log_buffer_size(struct xe_guc_log *log, enum guc_log_buffer_type type); -u32 xe_guc_get_log_buffer_offset(struct xe_guc_log *log, enum guc_log_buffer_type type); bool xe_guc_check_log_buf_overflow(struct xe_guc_log *log, - enum guc_log_buffer_type type, + enum guc_log_type type, unsigned int full_cnt); #endif diff --git a/drivers/gpu/drm/xe/xe_guc_pc.c b/drivers/gpu/drm/xe/xe_guc_pc.c index 951a49fb1d3e..5e5495a39a3c 100644 --- a/drivers/gpu/drm/xe/xe_guc_pc.c +++ b/drivers/gpu/drm/xe/xe_guc_pc.c @@ -76,7 +76,7 @@ * exposes a programming interface to the host for the control of SLPC. * * Frequency management: - * ===================== + * --------------------- * * Xe driver enables SLPC with all of its defaults features and frequency * selection, which varies per platform. @@ -87,7 +87,7 @@ * for any workload. * * Render-C States: - * ================ + * ---------------- * * Render-C states is also a GuC PC feature that is now enabled in Xe for * all platforms. @@ -499,21 +499,17 @@ u32 xe_guc_pc_get_cur_freq_fw(struct xe_guc_pc *pc) int xe_guc_pc_get_cur_freq(struct xe_guc_pc *pc, u32 *freq) { struct xe_gt *gt = pc_to_gt(pc); - unsigned int fw_ref; /* * GuC SLPC plays with cur freq request when GuCRC is enabled * Block RC6 for a more reliable read. */ - fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT); - if (!xe_force_wake_ref_has_domain(fw_ref, XE_FW_GT)) { - xe_force_wake_put(gt_to_fw(gt), fw_ref); + CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT); + if (!xe_force_wake_ref_has_domain(fw_ref.domains, XE_FW_GT)) return -ETIMEDOUT; - } *freq = get_cur_freq(gt); - xe_force_wake_put(gt_to_fw(gt), fw_ref); return 0; } @@ -1087,13 +1083,8 @@ int xe_guc_pc_gucrc_disable(struct xe_guc_pc *pc) */ int xe_guc_pc_override_gucrc_mode(struct xe_guc_pc *pc, enum slpc_gucrc_mode mode) { - int ret; - - xe_pm_runtime_get(pc_to_xe(pc)); - ret = pc_action_set_param(pc, SLPC_PARAM_PWRGATE_RC_MODE, mode); - xe_pm_runtime_put(pc_to_xe(pc)); - - return ret; + guard(xe_pm_runtime)(pc_to_xe(pc)); + return pc_action_set_param(pc, SLPC_PARAM_PWRGATE_RC_MODE, mode); } /** @@ -1104,13 +1095,8 @@ int xe_guc_pc_override_gucrc_mode(struct xe_guc_pc *pc, enum slpc_gucrc_mode mod */ int xe_guc_pc_unset_gucrc_mode(struct xe_guc_pc *pc) { - int ret; - - xe_pm_runtime_get(pc_to_xe(pc)); - ret = pc_action_unset_param(pc, SLPC_PARAM_PWRGATE_RC_MODE); - xe_pm_runtime_put(pc_to_xe(pc)); - - return ret; + guard(xe_pm_runtime)(pc_to_xe(pc)); + return pc_action_unset_param(pc, SLPC_PARAM_PWRGATE_RC_MODE); } static void pc_init_pcode_freq(struct xe_guc_pc *pc) @@ -1198,7 +1184,7 @@ int xe_guc_pc_set_power_profile(struct xe_guc_pc *pc, const char *buf) return -EINVAL; guard(mutex)(&pc->freq_lock); - xe_pm_runtime_get_noresume(pc_to_xe(pc)); + guard(xe_pm_runtime_noresume)(pc_to_xe(pc)); ret = pc_action_set_param(pc, SLPC_PARAM_POWER_PROFILE, @@ -1209,7 +1195,35 @@ int xe_guc_pc_set_power_profile(struct xe_guc_pc *pc, const char *buf) else pc->power_profile = val; - xe_pm_runtime_put(pc_to_xe(pc)); + return ret; +} + +static int pc_action_set_dcc(struct xe_guc_pc *pc, bool enable) +{ + int ret; + + ret = pc_action_set_param(pc, + SLPC_PARAM_TASK_ENABLE_DCC, + enable); + if (!ret) + return pc_action_set_param(pc, + SLPC_PARAM_TASK_DISABLE_DCC, + !enable); + else + return ret; +} + +static int pc_modify_defaults(struct xe_guc_pc *pc) +{ + struct xe_device *xe = pc_to_xe(pc); + struct xe_gt *gt = pc_to_gt(pc); + int ret = 0; + + if (xe->info.platform == XE_PANTHERLAKE) { + ret = pc_action_set_dcc(pc, false); + if (unlikely(ret)) + xe_gt_err(gt, "Failed to modify DCC default: %pe\n", ERR_PTR(ret)); + } return ret; } @@ -1223,17 +1237,14 @@ int xe_guc_pc_start(struct xe_guc_pc *pc) struct xe_device *xe = pc_to_xe(pc); struct xe_gt *gt = pc_to_gt(pc); u32 size = PAGE_ALIGN(sizeof(struct slpc_shared_data)); - unsigned int fw_ref; ktime_t earlier; int ret; xe_gt_assert(gt, xe_device_uc_enabled(xe)); - fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT); - if (!xe_force_wake_ref_has_domain(fw_ref, XE_FW_GT)) { - xe_force_wake_put(gt_to_fw(gt), fw_ref); + CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT); + if (!xe_force_wake_ref_has_domain(fw_ref.domains, XE_FW_GT)) return -ETIMEDOUT; - } if (xe->info.skip_guc_pc) { if (xe->info.platform != XE_PVC) @@ -1241,9 +1252,7 @@ int xe_guc_pc_start(struct xe_guc_pc *pc) /* Request max possible since dynamic freq mgmt is not enabled */ pc_set_cur_freq(pc, UINT_MAX); - - ret = 0; - goto out; + return 0; } xe_map_memset(xe, &pc->bo->vmap, 0, 0, size); @@ -1252,7 +1261,7 @@ int xe_guc_pc_start(struct xe_guc_pc *pc) earlier = ktime_get(); ret = pc_action_reset(pc); if (ret) - goto out; + return ret; if (wait_for_pc_state(pc, SLPC_GLOBAL_STATE_RUNNING, SLPC_RESET_TIMEOUT_MS)) { @@ -1263,31 +1272,33 @@ int xe_guc_pc_start(struct xe_guc_pc *pc) if (wait_for_pc_state(pc, SLPC_GLOBAL_STATE_RUNNING, SLPC_RESET_EXTENDED_TIMEOUT_MS)) { xe_gt_err(gt, "GuC PC Start failed: Dynamic GT frequency control and GT sleep states are now disabled.\n"); - ret = -EIO; - goto out; + return -EIO; } xe_gt_warn(gt, "GuC PC excessive start time: %lldms", ktime_ms_delta(ktime_get(), earlier)); } + ret = pc_modify_defaults(pc); + if (ret) + return ret; + ret = pc_init_freqs(pc); if (ret) - goto out; + return ret; ret = pc_set_mert_freq_cap(pc); if (ret) - goto out; + return ret; if (xe->info.platform == XE_PVC) { xe_guc_pc_gucrc_disable(pc); - ret = 0; - goto out; + return 0; } ret = pc_action_setup_gucrc(pc, GUCRC_FIRMWARE_CONTROL); if (ret) - goto out; + return ret; /* Enable SLPC Optimized Strategy for compute */ ret = pc_action_set_strategy(pc, SLPC_OPTIMIZED_STRATEGY_COMPUTE); @@ -1297,8 +1308,6 @@ int xe_guc_pc_start(struct xe_guc_pc *pc) if (unlikely(ret)) xe_gt_err(gt, "Failed to set SLPC power profile: %pe\n", ERR_PTR(ret)); -out: - xe_force_wake_put(gt_to_fw(gt), fw_ref); return ret; } @@ -1330,19 +1339,16 @@ static void xe_guc_pc_fini_hw(void *arg) { struct xe_guc_pc *pc = arg; struct xe_device *xe = pc_to_xe(pc); - unsigned int fw_ref; if (xe_device_wedged(xe)) return; - fw_ref = xe_force_wake_get(gt_to_fw(pc_to_gt(pc)), XE_FORCEWAKE_ALL); + CLASS(xe_force_wake, fw_ref)(gt_to_fw(pc_to_gt(pc)), XE_FORCEWAKE_ALL); xe_guc_pc_gucrc_disable(pc); XE_WARN_ON(xe_guc_pc_stop(pc)); /* Bind requested freq to mert_freq_cap before unload */ pc_set_cur_freq(pc, min(pc_max_freq_cap(pc), xe_guc_pc_get_rpe_freq(pc))); - - xe_force_wake_put(gt_to_fw(pc_to_gt(pc)), fw_ref); } /** diff --git a/drivers/gpu/drm/xe/xe_guc_relay.c b/drivers/gpu/drm/xe/xe_guc_relay.c index 0c0ff24ba62a..577a315854af 100644 --- a/drivers/gpu/drm/xe/xe_guc_relay.c +++ b/drivers/gpu/drm/xe/xe_guc_relay.c @@ -17,8 +17,7 @@ #include "abi/guc_relay_communication_abi.h" #include "xe_assert.h" -#include "xe_device.h" -#include "xe_gt.h" +#include "xe_device_types.h" #include "xe_gt_sriov_printk.h" #include "xe_gt_sriov_pf_service.h" #include "xe_guc.h" diff --git a/drivers/gpu/drm/xe/xe_guc_submit.c b/drivers/gpu/drm/xe/xe_guc_submit.c index ed7be50b2f72..1b2f66f4425b 100644 --- a/drivers/gpu/drm/xe/xe_guc_submit.c +++ b/drivers/gpu/drm/xe/xe_guc_submit.c @@ -17,8 +17,8 @@ #include "abi/guc_actions_abi.h" #include "abi/guc_actions_slpc_abi.h" #include "abi/guc_klvs_abi.h" -#include "regs/xe_lrc_layout.h" #include "xe_assert.h" +#include "xe_bo.h" #include "xe_devcoredump.h" #include "xe_device.h" #include "xe_exec_queue.h" @@ -35,7 +35,6 @@ #include "xe_guc_klv_helpers.h" #include "xe_guc_submit_types.h" #include "xe_hw_engine.h" -#include "xe_hw_fence.h" #include "xe_lrc.h" #include "xe_macros.h" #include "xe_map.h" @@ -47,6 +46,8 @@ #include "xe_uc_fw.h" #include "xe_vm.h" +#define XE_GUC_EXEC_QUEUE_CGP_CONTEXT_ERROR_LEN 6 + static struct xe_guc * exec_queue_to_guc(struct xe_exec_queue *q) { @@ -68,10 +69,8 @@ exec_queue_to_guc(struct xe_exec_queue *q) #define EXEC_QUEUE_STATE_KILLED (1 << 7) #define EXEC_QUEUE_STATE_WEDGED (1 << 8) #define EXEC_QUEUE_STATE_BANNED (1 << 9) -#define EXEC_QUEUE_STATE_CHECK_TIMEOUT (1 << 10) -#define EXEC_QUEUE_STATE_EXTRA_REF (1 << 11) -#define EXEC_QUEUE_STATE_PENDING_RESUME (1 << 12) -#define EXEC_QUEUE_STATE_PENDING_TDR_EXIT (1 << 13) +#define EXEC_QUEUE_STATE_PENDING_RESUME (1 << 10) +#define EXEC_QUEUE_STATE_IDLE_SKIP_SUSPEND (1 << 11) static bool exec_queue_registered(struct xe_exec_queue *q) { @@ -203,36 +202,6 @@ static void set_exec_queue_wedged(struct xe_exec_queue *q) atomic_or(EXEC_QUEUE_STATE_WEDGED, &q->guc->state); } -static bool exec_queue_check_timeout(struct xe_exec_queue *q) -{ - return atomic_read(&q->guc->state) & EXEC_QUEUE_STATE_CHECK_TIMEOUT; -} - -static void set_exec_queue_check_timeout(struct xe_exec_queue *q) -{ - atomic_or(EXEC_QUEUE_STATE_CHECK_TIMEOUT, &q->guc->state); -} - -static void clear_exec_queue_check_timeout(struct xe_exec_queue *q) -{ - atomic_and(~EXEC_QUEUE_STATE_CHECK_TIMEOUT, &q->guc->state); -} - -static bool exec_queue_extra_ref(struct xe_exec_queue *q) -{ - return atomic_read(&q->guc->state) & EXEC_QUEUE_STATE_EXTRA_REF; -} - -static void set_exec_queue_extra_ref(struct xe_exec_queue *q) -{ - atomic_or(EXEC_QUEUE_STATE_EXTRA_REF, &q->guc->state); -} - -static void clear_exec_queue_extra_ref(struct xe_exec_queue *q) -{ - atomic_and(~EXEC_QUEUE_STATE_EXTRA_REF, &q->guc->state); -} - static bool exec_queue_pending_resume(struct xe_exec_queue *q) { return atomic_read(&q->guc->state) & EXEC_QUEUE_STATE_PENDING_RESUME; @@ -248,19 +217,19 @@ static void clear_exec_queue_pending_resume(struct xe_exec_queue *q) atomic_and(~EXEC_QUEUE_STATE_PENDING_RESUME, &q->guc->state); } -static bool exec_queue_pending_tdr_exit(struct xe_exec_queue *q) +static bool exec_queue_idle_skip_suspend(struct xe_exec_queue *q) { - return atomic_read(&q->guc->state) & EXEC_QUEUE_STATE_PENDING_TDR_EXIT; + return atomic_read(&q->guc->state) & EXEC_QUEUE_STATE_IDLE_SKIP_SUSPEND; } -static void set_exec_queue_pending_tdr_exit(struct xe_exec_queue *q) +static void set_exec_queue_idle_skip_suspend(struct xe_exec_queue *q) { - atomic_or(EXEC_QUEUE_STATE_PENDING_TDR_EXIT, &q->guc->state); + atomic_or(EXEC_QUEUE_STATE_IDLE_SKIP_SUSPEND, &q->guc->state); } -static void clear_exec_queue_pending_tdr_exit(struct xe_exec_queue *q) +static void clear_exec_queue_idle_skip_suspend(struct xe_exec_queue *q) { - atomic_and(~EXEC_QUEUE_STATE_PENDING_TDR_EXIT, &q->guc->state); + atomic_and(~EXEC_QUEUE_STATE_IDLE_SKIP_SUSPEND, &q->guc->state); } static bool exec_queue_killed_or_banned_or_wedged(struct xe_exec_queue *q) @@ -388,7 +357,7 @@ static int guc_init_global_schedule_policy(struct xe_guc *guc) *emit++ = XE_GUC_ACTION_UPDATE_SCHEDULING_POLICIES_KLV; - if (CCS_MASK(guc_to_gt(guc))) + if (CCS_INSTANCES(guc_to_gt(guc))) emit = emit_render_compute_yield_klv(emit); count = emit - data; @@ -541,7 +510,8 @@ static void init_policies(struct xe_guc *guc, struct xe_exec_queue *q) u32 slpc_exec_queue_freq_req = 0; u32 preempt_timeout_us = q->sched_props.preempt_timeout_us; - xe_gt_assert(guc_to_gt(guc), exec_queue_registered(q)); + xe_gt_assert(guc_to_gt(guc), exec_queue_registered(q) && + !xe_exec_queue_is_multi_queue_secondary(q)); if (q->flags & EXEC_QUEUE_FLAG_LOW_LATENCY) slpc_exec_queue_freq_req |= SLPC_CTX_FREQ_REQ_IS_COMPUTE; @@ -561,6 +531,8 @@ static void set_min_preemption_timeout(struct xe_guc *guc, struct xe_exec_queue { struct exec_queue_policy policy; + xe_assert(guc_to_xe(guc), !xe_exec_queue_is_multi_queue_secondary(q)); + __guc_exec_queue_policy_start_klv(&policy, q->guc->id); __guc_exec_queue_policy_add_preemption_timeout(&policy, 1); @@ -568,6 +540,103 @@ static void set_min_preemption_timeout(struct xe_guc *guc, struct xe_exec_queue __guc_exec_queue_policy_action_size(&policy), 0, 0); } +static bool vf_recovery(struct xe_guc *guc) +{ + return xe_gt_recovery_pending(guc_to_gt(guc)); +} + +static void xe_guc_exec_queue_trigger_cleanup(struct xe_exec_queue *q) +{ + struct xe_guc *guc = exec_queue_to_guc(q); + struct xe_device *xe = guc_to_xe(guc); + + /** to wakeup xe_wait_user_fence ioctl if exec queue is reset */ + wake_up_all(&xe->ufence_wq); + + xe_sched_tdr_queue_imm(&q->guc->sched); +} + +static void xe_guc_exec_queue_group_trigger_cleanup(struct xe_exec_queue *q) +{ + struct xe_exec_queue *primary = xe_exec_queue_multi_queue_primary(q); + struct xe_exec_queue_group *group = q->multi_queue.group; + struct xe_exec_queue *eq; + + xe_gt_assert(guc_to_gt(exec_queue_to_guc(q)), + xe_exec_queue_is_multi_queue(q)); + + /* Group banned, skip timeout check in TDR */ + WRITE_ONCE(group->banned, true); + xe_guc_exec_queue_trigger_cleanup(primary); + + mutex_lock(&group->list_lock); + list_for_each_entry(eq, &group->list, multi_queue.link) + xe_guc_exec_queue_trigger_cleanup(eq); + mutex_unlock(&group->list_lock); +} + +static void xe_guc_exec_queue_reset_trigger_cleanup(struct xe_exec_queue *q) +{ + if (xe_exec_queue_is_multi_queue(q)) { + struct xe_exec_queue *primary = xe_exec_queue_multi_queue_primary(q); + struct xe_exec_queue_group *group = q->multi_queue.group; + struct xe_exec_queue *eq; + + /* Group banned, skip timeout check in TDR */ + WRITE_ONCE(group->banned, true); + + set_exec_queue_reset(primary); + if (!exec_queue_banned(primary)) + xe_guc_exec_queue_trigger_cleanup(primary); + + mutex_lock(&group->list_lock); + list_for_each_entry(eq, &group->list, multi_queue.link) { + set_exec_queue_reset(eq); + if (!exec_queue_banned(eq)) + xe_guc_exec_queue_trigger_cleanup(eq); + } + mutex_unlock(&group->list_lock); + } else { + set_exec_queue_reset(q); + if (!exec_queue_banned(q)) + xe_guc_exec_queue_trigger_cleanup(q); + } +} + +static void set_exec_queue_group_banned(struct xe_exec_queue *q) +{ + struct xe_exec_queue *primary = xe_exec_queue_multi_queue_primary(q); + struct xe_exec_queue_group *group = q->multi_queue.group; + struct xe_exec_queue *eq; + + /* Ban all queues of the multi-queue group */ + xe_gt_assert(guc_to_gt(exec_queue_to_guc(q)), + xe_exec_queue_is_multi_queue(q)); + set_exec_queue_banned(primary); + + mutex_lock(&group->list_lock); + list_for_each_entry(eq, &group->list, multi_queue.link) + set_exec_queue_banned(eq); + mutex_unlock(&group->list_lock); +} + +/* Helper for context registration H2G */ +struct guc_ctxt_registration_info { + u32 flags; + u32 context_idx; + u32 engine_class; + u32 engine_submit_mask; + u32 wq_desc_lo; + u32 wq_desc_hi; + u32 wq_base_lo; + u32 wq_base_hi; + u32 wq_size; + u32 cgp_lo; + u32 cgp_hi; + u32 hwlrca_lo; + u32 hwlrca_hi; +}; + #define parallel_read(xe_, map_, field_) \ xe_map_rd_field(xe_, &map_, 0, struct guc_submit_parallel_scratch, \ field_) @@ -575,6 +644,181 @@ static void set_min_preemption_timeout(struct xe_guc *guc, struct xe_exec_queue xe_map_wr_field(xe_, &map_, 0, struct guc_submit_parallel_scratch, \ field_, val_) +/** + * DOC: Multi Queue Group GuC interface + * + * The multi queue group coordination between KMD and GuC is through a software + * construct called Context Group Page (CGP). The CGP is a KMD managed 4KB page + * allocated in the global GTT. + * + * CGP format: + * + * +-----------+---------------------------+---------------------------------------------+ + * | DWORD | Name | Description | + * +-----------+---------------------------+---------------------------------------------+ + * | 0 | Version | Bits [15:8]=Major ver, [7:0]=Minor ver | + * +-----------+---------------------------+---------------------------------------------+ + * | 1..15 | RESERVED | MBZ | + * +-----------+---------------------------+---------------------------------------------+ + * | 16 | KMD_QUEUE_UPDATE_MASK_DW0 | KMD queue mask for queues 31..0 | + * +-----------+---------------------------+---------------------------------------------+ + * | 17 | KMD_QUEUE_UPDATE_MASK_DW1 | KMD queue mask for queues 63..32 | + * +-----------+---------------------------+---------------------------------------------+ + * | 18..31 | RESERVED | MBZ | + * +-----------+---------------------------+---------------------------------------------+ + * | 32 | Q0CD_DW0 | Queue 0 context LRC descriptor lower DWORD | + * +-----------+---------------------------+---------------------------------------------+ + * | 33 | Q0ContextIndex | Context ID for Queue 0 | + * +-----------+---------------------------+---------------------------------------------+ + * | 34 | Q1CD_DW0 | Queue 1 context LRC descriptor lower DWORD | + * +-----------+---------------------------+---------------------------------------------+ + * | 35 | Q1ContextIndex | Context ID for Queue 1 | + * +-----------+---------------------------+---------------------------------------------+ + * | ... |... | ... | + * +-----------+---------------------------+---------------------------------------------+ + * | 158 | Q63CD_DW0 | Queue 63 context LRC descriptor lower DWORD | + * +-----------+---------------------------+---------------------------------------------+ + * | 159 | Q63ContextIndex | Context ID for Queue 63 | + * +-----------+---------------------------+---------------------------------------------+ + * | 160..1024 | RESERVED | MBZ | + * +-----------+---------------------------+---------------------------------------------+ + * + * While registering Q0 with GuC, CGP is updated with Q0 entry and GuC is notified + * through XE_GUC_ACTION_REGISTER_CONTEXT_MULTI_QUEUE H2G message which specifies + * the CGP address. When the secondary queues are added to the group, the CGP is + * updated with entry for that queue and GuC is notified through the H2G interface + * XE_GUC_ACTION_MULTI_QUEUE_CONTEXT_CGP_SYNC. GuC responds to these H2G messages + * with a XE_GUC_ACTION_NOTIFY_MULTIQ_CONTEXT_CGP_SYNC_DONE G2H message. GuC also + * sends a XE_GUC_ACTION_NOTIFY_MULTI_QUEUE_CGP_CONTEXT_ERROR notification for any + * error in the CGP. Only one of these CGP update messages can be outstanding + * (waiting for GuC response) at any time. The bits in KMD_QUEUE_UPDATE_MASK_DW* + * fields indicate which queue entry is being updated in the CGP. + * + * The primary queue (Q0) represents the multi queue group context in GuC and + * submission on any queue of the group must be through Q0 GuC interface only. + * + * As it is not required to register secondary queues with GuC, the secondary queue + * context ids in the CGP are populated with Q0 context id. + */ + +#define CGP_VERSION_MAJOR_SHIFT 8 + +static void xe_guc_exec_queue_group_cgp_update(struct xe_device *xe, + struct xe_exec_queue *q) +{ + struct xe_exec_queue_group *group = q->multi_queue.group; + u32 guc_id = group->primary->guc->id; + + /* Currently implementing CGP version 1.0 */ + xe_map_wr(xe, &group->cgp_bo->vmap, 0, u32, + 1 << CGP_VERSION_MAJOR_SHIFT); + + xe_map_wr(xe, &group->cgp_bo->vmap, + (32 + q->multi_queue.pos * 2) * sizeof(u32), + u32, lower_32_bits(xe_lrc_descriptor(q->lrc[0]))); + + xe_map_wr(xe, &group->cgp_bo->vmap, + (33 + q->multi_queue.pos * 2) * sizeof(u32), + u32, guc_id); + + if (q->multi_queue.pos / 32) { + xe_map_wr(xe, &group->cgp_bo->vmap, 17 * sizeof(u32), + u32, BIT(q->multi_queue.pos % 32)); + xe_map_wr(xe, &group->cgp_bo->vmap, 16 * sizeof(u32), u32, 0); + } else { + xe_map_wr(xe, &group->cgp_bo->vmap, 16 * sizeof(u32), + u32, BIT(q->multi_queue.pos)); + xe_map_wr(xe, &group->cgp_bo->vmap, 17 * sizeof(u32), u32, 0); + } +} + +static void xe_guc_exec_queue_group_cgp_sync(struct xe_guc *guc, + struct xe_exec_queue *q, + const u32 *action, u32 len) +{ + struct xe_exec_queue_group *group = q->multi_queue.group; + struct xe_device *xe = guc_to_xe(guc); + long ret; + + /* + * As all queues of a multi queue group use single drm scheduler + * submit workqueue, CGP synchronization with GuC are serialized. + * Hence, no locking is required here. + * Wait for any pending CGP_SYNC_DONE response before updating the + * CGP page and sending CGP_SYNC message. + * + * FIXME: Support VF migration + */ + ret = wait_event_timeout(guc->ct.wq, + !READ_ONCE(group->sync_pending) || + xe_guc_read_stopped(guc), HZ); + if (!ret || xe_guc_read_stopped(guc)) { + /* CGP_SYNC failed. Reset gt, cleanup the group */ + xe_gt_warn(guc_to_gt(guc), "Wait for CGP_SYNC_DONE response failed!\n"); + set_exec_queue_group_banned(q); + xe_gt_reset_async(q->gt); + xe_guc_exec_queue_group_trigger_cleanup(q); + return; + } + + xe_lrc_set_multi_queue_priority(q->lrc[0], q->multi_queue.priority); + xe_guc_exec_queue_group_cgp_update(xe, q); + + WRITE_ONCE(group->sync_pending, true); + xe_guc_ct_send(&guc->ct, action, len, G2H_LEN_DW_MULTI_QUEUE_CONTEXT, 1); +} + +static void __register_exec_queue_group(struct xe_guc *guc, + struct xe_exec_queue *q, + struct guc_ctxt_registration_info *info) +{ +#define MAX_MULTI_QUEUE_REG_SIZE (8) + u32 action[MAX_MULTI_QUEUE_REG_SIZE]; + int len = 0; + + action[len++] = XE_GUC_ACTION_REGISTER_CONTEXT_MULTI_QUEUE; + action[len++] = info->flags; + action[len++] = info->context_idx; + action[len++] = info->engine_class; + action[len++] = info->engine_submit_mask; + action[len++] = 0; /* Reserved */ + action[len++] = info->cgp_lo; + action[len++] = info->cgp_hi; + + xe_gt_assert(guc_to_gt(guc), len <= MAX_MULTI_QUEUE_REG_SIZE); +#undef MAX_MULTI_QUEUE_REG_SIZE + + /* + * The above XE_GUC_ACTION_REGISTER_CONTEXT_MULTI_QUEUE do expect a + * XE_GUC_ACTION_NOTIFY_MULTI_QUEUE_CONTEXT_CGP_SYNC_DONE response + * from guc. + */ + xe_guc_exec_queue_group_cgp_sync(guc, q, action, len); +} + +static void xe_guc_exec_queue_group_add(struct xe_guc *guc, + struct xe_exec_queue *q) +{ +#define MAX_MULTI_QUEUE_CGP_SYNC_SIZE (2) + u32 action[MAX_MULTI_QUEUE_CGP_SYNC_SIZE]; + int len = 0; + + xe_gt_assert(guc_to_gt(guc), xe_exec_queue_is_multi_queue_secondary(q)); + + action[len++] = XE_GUC_ACTION_MULTI_QUEUE_CONTEXT_CGP_SYNC; + action[len++] = q->multi_queue.group->primary->guc->id; + + xe_gt_assert(guc_to_gt(guc), len <= MAX_MULTI_QUEUE_CGP_SYNC_SIZE); +#undef MAX_MULTI_QUEUE_CGP_SYNC_SIZE + + /* + * The above XE_GUC_ACTION_MULTI_QUEUE_CONTEXT_CGP_SYNC do expect a + * XE_GUC_ACTION_NOTIFY_MULTI_QUEUE_CONTEXT_CGP_SYNC_DONE response + * from guc. + */ + xe_guc_exec_queue_group_cgp_sync(guc, q, action, len); +} + static void __register_mlrc_exec_queue(struct xe_guc *guc, struct xe_exec_queue *q, struct guc_ctxt_registration_info *info) @@ -670,6 +914,13 @@ static void register_exec_queue(struct xe_exec_queue *q, int ctx_type) info.flags = CONTEXT_REGISTRATION_FLAG_KMD | FIELD_PREP(CONTEXT_REGISTRATION_FLAG_TYPE, ctx_type); + if (xe_exec_queue_is_multi_queue(q)) { + struct xe_exec_queue_group *group = q->multi_queue.group; + + info.cgp_lo = xe_bo_ggtt_addr(group->cgp_bo); + info.cgp_hi = 0; + } + if (xe_exec_queue_is_parallel(q)) { u64 ggtt_addr = xe_lrc_parallel_ggtt_addr(lrc); struct iosys_map map = xe_lrc_parallel_map(lrc); @@ -690,21 +941,20 @@ static void register_exec_queue(struct xe_exec_queue *q, int ctx_type) parallel_write(xe, map, wq_desc.wq_status, WQ_STATUS_ACTIVE); } - /* - * We must keep a reference for LR engines if engine is registered with - * the GuC as jobs signal immediately and can't destroy an engine if the - * GuC has a reference to it. - */ - if (xe_exec_queue_is_lr(q)) - xe_exec_queue_get(q); - set_exec_queue_registered(q); trace_xe_exec_queue_register(q); - if (xe_exec_queue_is_parallel(q)) + if (xe_exec_queue_is_multi_queue_primary(q)) + __register_exec_queue_group(guc, q, &info); + else if (xe_exec_queue_is_parallel(q)) __register_mlrc_exec_queue(guc, q, &info); - else + else if (!xe_exec_queue_is_multi_queue_secondary(q)) __register_exec_queue(guc, &info); - init_policies(guc, q); + + if (!xe_exec_queue_is_multi_queue_secondary(q)) + init_policies(guc, q); + + if (xe_exec_queue_is_multi_queue_secondary(q)) + xe_guc_exec_queue_group_add(guc, q); } static u32 wq_space_until_wrap(struct xe_exec_queue *q) @@ -712,9 +962,22 @@ static u32 wq_space_until_wrap(struct xe_exec_queue *q) return (WQ_SIZE - q->guc->wqi_tail); } -static bool vf_recovery(struct xe_guc *guc) +static inline void relaxed_ms_sleep(unsigned int delay_ms) { - return xe_gt_recovery_pending(guc_to_gt(guc)); + unsigned long min_us, max_us; + + if (!delay_ms) + return; + + if (delay_ms > 20) { + msleep(delay_ms); + return; + } + + min_us = mul_u32_u32(delay_ms, 1000); + max_us = min_us + 500; + + usleep_range(min_us, max_us); } static int wq_wait_for_space(struct xe_exec_queue *q, u32 wqi_size) @@ -722,21 +985,23 @@ static int wq_wait_for_space(struct xe_exec_queue *q, u32 wqi_size) struct xe_guc *guc = exec_queue_to_guc(q); struct xe_device *xe = guc_to_xe(guc); struct iosys_map map = xe_lrc_parallel_map(q->lrc[0]); - unsigned int sleep_period_ms = 1; + unsigned int sleep_period_ms = 1, sleep_total_ms = 0; #define AVAILABLE_SPACE \ CIRC_SPACE(q->guc->wqi_tail, q->guc->wqi_head, WQ_SIZE) if (wqi_size > AVAILABLE_SPACE && !vf_recovery(guc)) { try_again: q->guc->wqi_head = parallel_read(xe, map, wq_desc.head); - if (wqi_size > AVAILABLE_SPACE) { - if (sleep_period_ms == 1024) { + if (wqi_size > AVAILABLE_SPACE && !vf_recovery(guc)) { + if (sleep_total_ms > 2000) { xe_gt_reset_async(q->gt); return -ENODEV; } msleep(sleep_period_ms); - sleep_period_ms <<= 1; + sleep_total_ms += sleep_period_ms; + if (sleep_period_ms < 64) + sleep_period_ms <<= 1; goto try_again; } } @@ -825,7 +1090,7 @@ static void submit_exec_queue(struct xe_exec_queue *q, struct xe_sched_job *job) if (!job->restore_replay || job->last_replay) { if (xe_exec_queue_is_parallel(q)) wq_item_append(q); - else + else if (!exec_queue_idle_skip_suspend(q)) xe_lrc_set_ring_tail(lrc, lrc->ring.tail); job->last_replay = false; } @@ -833,6 +1098,12 @@ static void submit_exec_queue(struct xe_exec_queue *q, struct xe_sched_job *job) if (exec_queue_suspended(q) && !xe_exec_queue_is_parallel(q)) return; + /* + * All queues in a multi-queue group will use the primary queue + * of the group to interface with GuC. + */ + q = xe_exec_queue_multi_queue_primary(q); + if (!exec_queue_enabled(q) && !exec_queue_suspended(q)) { action[len++] = XE_GUC_ACTION_SCHED_CONTEXT_MODE_SET; action[len++] = q->guc->id; @@ -870,7 +1141,7 @@ guc_exec_queue_run_job(struct drm_sched_job *drm_job) struct xe_sched_job *job = to_xe_sched_job(drm_job); struct xe_exec_queue *q = job->q; struct xe_guc *guc = exec_queue_to_guc(q); - bool lr = xe_exec_queue_is_lr(q), killed_or_banned_or_wedged = + bool killed_or_banned_or_wedged = exec_queue_killed_or_banned_or_wedged(q); xe_gt_assert(guc_to_gt(guc), !(exec_queue_destroyed(q) || exec_queue_pending_disable(q)) || @@ -879,6 +1150,18 @@ guc_exec_queue_run_job(struct drm_sched_job *drm_job) trace_xe_sched_job_run(job); if (!killed_or_banned_or_wedged && !xe_sched_job_is_error(job)) { + if (xe_exec_queue_is_multi_queue_secondary(q)) { + struct xe_exec_queue *primary = xe_exec_queue_multi_queue_primary(q); + + if (exec_queue_killed_or_banned_or_wedged(primary)) { + killed_or_banned_or_wedged = true; + goto run_job_out; + } + + if (!exec_queue_registered(primary)) + register_exec_queue(primary, GUC_CONTEXT_NORMAL); + } + if (!exec_queue_registered(q)) register_exec_queue(q, GUC_CONTEXT_NORMAL); if (!job->restore_replay) @@ -887,14 +1170,7 @@ guc_exec_queue_run_job(struct drm_sched_job *drm_job) job->restore_replay = false; } - /* - * We don't care about job-fence ordering in LR VMs because these fences - * are never exported; they are used solely to keep jobs on the pending - * list. Once a queue enters an error state, there's no need to track - * them. - */ - if (killed_or_banned_or_wedged && lr) - xe_sched_job_set_error(job, -ECANCELED); +run_job_out: return job->fence; } @@ -912,6 +1188,11 @@ int xe_guc_read_stopped(struct xe_guc *guc) return atomic_read(&guc->submission_state.stopped); } +static void handle_multi_queue_secondary_sched_done(struct xe_guc *guc, + struct xe_exec_queue *q, + u32 runnable_state); +static void handle_deregister_done(struct xe_guc *guc, struct xe_exec_queue *q); + #define MAKE_SCHED_CONTEXT_ACTION(q, enable_disable) \ u32 action[] = { \ XE_GUC_ACTION_SCHED_CONTEXT_MODE_SET, \ @@ -925,7 +1206,9 @@ static void disable_scheduling_deregister(struct xe_guc *guc, MAKE_SCHED_CONTEXT_ACTION(q, DISABLE); int ret; - set_min_preemption_timeout(guc, q); + if (!xe_exec_queue_is_multi_queue_secondary(q)) + set_min_preemption_timeout(guc, q); + smp_rmb(); ret = wait_event_timeout(guc->ct.wq, (!exec_queue_pending_enable(q) && @@ -939,8 +1222,7 @@ static void disable_scheduling_deregister(struct xe_guc *guc, xe_gt_warn(q->gt, "Pending enable/disable failed to respond\n"); xe_sched_submission_start(sched); xe_gt_reset_async(q->gt); - if (!xe_exec_queue_is_lr(q)) - xe_sched_tdr_queue_imm(sched); + xe_sched_tdr_queue_imm(sched); return; } @@ -953,23 +1235,12 @@ static void disable_scheduling_deregister(struct xe_guc *guc, * Reserve space for both G2H here as the 2nd G2H is sent from a G2H * handler and we are not allowed to reserved G2H space in handlers. */ - xe_guc_ct_send(&guc->ct, action, ARRAY_SIZE(action), - G2H_LEN_DW_SCHED_CONTEXT_MODE_SET + - G2H_LEN_DW_DEREGISTER_CONTEXT, 2); -} - -static void xe_guc_exec_queue_trigger_cleanup(struct xe_exec_queue *q) -{ - struct xe_guc *guc = exec_queue_to_guc(q); - struct xe_device *xe = guc_to_xe(guc); - - /** to wakeup xe_wait_user_fence ioctl if exec queue is reset */ - wake_up_all(&xe->ufence_wq); - - if (xe_exec_queue_is_lr(q)) - queue_work(guc_to_gt(guc)->ordered_wq, &q->guc->lr_tdr); + if (xe_exec_queue_is_multi_queue_secondary(q)) + handle_multi_queue_secondary_sched_done(guc, q, 0); else - xe_sched_tdr_queue_imm(&q->guc->sched); + xe_guc_ct_send(&guc->ct, action, ARRAY_SIZE(action), + G2H_LEN_DW_SCHED_CONTEXT_MODE_SET + + G2H_LEN_DW_DEREGISTER_CONTEXT, 2); } /** @@ -998,8 +1269,9 @@ void xe_guc_submit_wedge(struct xe_guc *guc) err = devm_add_action_or_reset(guc_to_xe(guc)->drm.dev, guc_submit_wedged_fini, guc); if (err) { - xe_gt_err(gt, "Failed to register clean-up on wedged.mode=2; " - "Although device is wedged.\n"); + xe_gt_err(gt, "Failed to register clean-up in wedged.mode=%s; " + "Although device is wedged.\n", + xe_wedged_mode_to_string(XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET)); return; } @@ -1014,7 +1286,7 @@ static bool guc_submit_hint_wedged(struct xe_guc *guc) { struct xe_device *xe = guc_to_xe(guc); - if (xe->wedged.mode != 2) + if (xe->wedged.mode != XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET) return false; if (xe_device_wedged(xe)) @@ -1025,84 +1297,6 @@ static bool guc_submit_hint_wedged(struct xe_guc *guc) return true; } -static void xe_guc_exec_queue_lr_cleanup(struct work_struct *w) -{ - struct xe_guc_exec_queue *ge = - container_of(w, struct xe_guc_exec_queue, lr_tdr); - struct xe_exec_queue *q = ge->q; - struct xe_guc *guc = exec_queue_to_guc(q); - struct xe_gpu_scheduler *sched = &ge->sched; - struct xe_sched_job *job; - bool wedged = false; - - xe_gt_assert(guc_to_gt(guc), xe_exec_queue_is_lr(q)); - - if (vf_recovery(guc)) - return; - - trace_xe_exec_queue_lr_cleanup(q); - - if (!exec_queue_killed(q)) - wedged = guc_submit_hint_wedged(exec_queue_to_guc(q)); - - /* Kill the run_job / process_msg entry points */ - xe_sched_submission_stop(sched); - - /* - * Engine state now mostly stable, disable scheduling / deregister if - * needed. This cleanup routine might be called multiple times, where - * the actual async engine deregister drops the final engine ref. - * Calling disable_scheduling_deregister will mark the engine as - * destroyed and fire off the CT requests to disable scheduling / - * deregister, which we only want to do once. We also don't want to mark - * the engine as pending_disable again as this may race with the - * xe_guc_deregister_done_handler() which treats it as an unexpected - * state. - */ - if (!wedged && exec_queue_registered(q) && !exec_queue_destroyed(q)) { - struct xe_guc *guc = exec_queue_to_guc(q); - int ret; - - set_exec_queue_banned(q); - disable_scheduling_deregister(guc, q); - - /* - * Must wait for scheduling to be disabled before signalling - * any fences, if GT broken the GT reset code should signal us. - */ - ret = wait_event_timeout(guc->ct.wq, - !exec_queue_pending_disable(q) || - xe_guc_read_stopped(guc) || - vf_recovery(guc), HZ * 5); - if (vf_recovery(guc)) - return; - - if (!ret) { - xe_gt_warn(q->gt, "Schedule disable failed to respond, guc_id=%d\n", - q->guc->id); - xe_devcoredump(q, NULL, "Schedule disable failed to respond, guc_id=%d\n", - q->guc->id); - xe_sched_submission_start(sched); - xe_gt_reset_async(q->gt); - return; - } - } - - if (!exec_queue_killed(q) && !xe_lrc_ring_is_idle(q->lrc[0])) - xe_devcoredump(q, NULL, "LR job cleanup, guc_id=%d", q->guc->id); - - xe_hw_fence_irq_stop(q->fence_irq); - - xe_sched_submission_start(sched); - - spin_lock(&sched->base.job_list_lock); - list_for_each_entry(job, &sched->base.pending_list, drm.list) - xe_sched_job_set_error(job, -ECANCELED); - spin_unlock(&sched->base.job_list_lock); - - xe_hw_fence_irq_start(q->fence_irq); -} - #define ADJUST_FIVE_PERCENT(__t) mul_u64_u32_div(__t, 105, 100) static bool check_timeout(struct xe_exec_queue *q, struct xe_sched_job *job) @@ -1121,7 +1315,21 @@ static bool check_timeout(struct xe_exec_queue *q, struct xe_sched_job *job) return xe_sched_invalidate_job(job, 2); } - ctx_timestamp = lower_32_bits(xe_lrc_ctx_timestamp(q->lrc[0])); + ctx_timestamp = lower_32_bits(xe_lrc_timestamp(q->lrc[0])); + if (ctx_timestamp == job->sample_timestamp) { + if (IS_SRIOV_VF(gt_to_xe(gt))) + xe_gt_notice(gt, "Check job timeout: seqno=%u, lrc_seqno=%u, guc_id=%d, timestamp stuck", + xe_sched_job_seqno(job), + xe_sched_job_lrc_seqno(job), q->guc->id); + else + xe_gt_warn(gt, "Check job timeout: seqno=%u, lrc_seqno=%u, guc_id=%d, timestamp stuck", + xe_sched_job_seqno(job), + xe_sched_job_lrc_seqno(job), q->guc->id); + + return xe_sched_invalidate_job(job, 0); + } + + job->sample_timestamp = ctx_timestamp; ctx_job_timestamp = xe_lrc_ctx_job_timestamp(q->lrc[0]); /* @@ -1161,8 +1369,11 @@ static void enable_scheduling(struct xe_exec_queue *q) set_exec_queue_enabled(q); trace_xe_exec_queue_scheduling_enable(q); - xe_guc_ct_send(&guc->ct, action, ARRAY_SIZE(action), - G2H_LEN_DW_SCHED_CONTEXT_MODE_SET, 1); + if (xe_exec_queue_is_multi_queue_secondary(q)) + handle_multi_queue_secondary_sched_done(guc, q, 1); + else + xe_guc_ct_send(&guc->ct, action, ARRAY_SIZE(action), + G2H_LEN_DW_SCHED_CONTEXT_MODE_SET, 1); ret = wait_event_timeout(guc->ct.wq, !exec_queue_pending_enable(q) || @@ -1172,8 +1383,7 @@ static void enable_scheduling(struct xe_exec_queue *q) xe_gt_warn(guc_to_gt(guc), "Schedule enable failed to respond"); set_exec_queue_banned(q); xe_gt_reset_async(q->gt); - if (!xe_exec_queue_is_lr(q)) - xe_sched_tdr_queue_imm(&q->guc->sched); + xe_sched_tdr_queue_imm(&q->guc->sched); } } @@ -1186,52 +1396,34 @@ static void disable_scheduling(struct xe_exec_queue *q, bool immediate) xe_gt_assert(guc_to_gt(guc), exec_queue_registered(q)); xe_gt_assert(guc_to_gt(guc), !exec_queue_pending_disable(q)); - if (immediate) + if (immediate && !xe_exec_queue_is_multi_queue_secondary(q)) set_min_preemption_timeout(guc, q); clear_exec_queue_enabled(q); set_exec_queue_pending_disable(q); trace_xe_exec_queue_scheduling_disable(q); - xe_guc_ct_send(&guc->ct, action, ARRAY_SIZE(action), - G2H_LEN_DW_SCHED_CONTEXT_MODE_SET, 1); -} - -static void __deregister_exec_queue(struct xe_guc *guc, struct xe_exec_queue *q) -{ - u32 action[] = { - XE_GUC_ACTION_DEREGISTER_CONTEXT, - q->guc->id, - }; - - xe_gt_assert(guc_to_gt(guc), !exec_queue_destroyed(q)); - xe_gt_assert(guc_to_gt(guc), exec_queue_registered(q)); - xe_gt_assert(guc_to_gt(guc), !exec_queue_pending_enable(q)); - xe_gt_assert(guc_to_gt(guc), !exec_queue_pending_disable(q)); - - set_exec_queue_destroyed(q); - trace_xe_exec_queue_deregister(q); - - xe_guc_ct_send(&guc->ct, action, ARRAY_SIZE(action), - G2H_LEN_DW_DEREGISTER_CONTEXT, 1); + if (xe_exec_queue_is_multi_queue_secondary(q)) + handle_multi_queue_secondary_sched_done(guc, q, 0); + else + xe_guc_ct_send(&guc->ct, action, ARRAY_SIZE(action), + G2H_LEN_DW_SCHED_CONTEXT_MODE_SET, 1); } static enum drm_gpu_sched_stat guc_exec_queue_timedout_job(struct drm_sched_job *drm_job) { struct xe_sched_job *job = to_xe_sched_job(drm_job); - struct xe_sched_job *tmp_job; + struct drm_sched_job *tmp_job; struct xe_exec_queue *q = job->q; struct xe_gpu_scheduler *sched = &q->guc->sched; struct xe_guc *guc = exec_queue_to_guc(q); const char *process_name = "no process"; struct xe_device *xe = guc_to_xe(guc); - unsigned int fw_ref; int err = -ETIME; pid_t pid = -1; - int i = 0; bool wedged = false, skip_timeout_check; - xe_gt_assert(guc_to_gt(guc), !xe_exec_queue_is_lr(q)); + xe_gt_assert(guc_to_gt(guc), !exec_queue_destroyed(q)); /* * TDR has fired before free job worker. Common if exec queue @@ -1248,8 +1440,24 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job) /* Must check all state after stopping scheduler */ skip_timeout_check = exec_queue_reset(q) || - exec_queue_killed_or_banned_or_wedged(q) || - exec_queue_destroyed(q); + exec_queue_killed_or_banned_or_wedged(q); + + /* Skip timeout check if multi-queue group is banned */ + if (xe_exec_queue_is_multi_queue(q) && + READ_ONCE(q->multi_queue.group->banned)) + skip_timeout_check = true; + + /* LR jobs can only get here if queue has been killed or hit an error */ + if (xe_exec_queue_is_lr(q)) + xe_gt_assert(guc_to_gt(guc), skip_timeout_check); + + /* + * FIXME: In multi-queue scenario, the TDR must ensure that the whole + * multi-queue group is off the HW before signaling the fences to avoid + * possible memory corruptions. This means disabling scheduling on the + * primary queue before or during the secondary queue's TDR. Need to + * implement this in least obtrusive way. + */ /* * If devcoredump not captured and GuC capture for the job is not ready @@ -1258,33 +1466,32 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job) if (!exec_queue_killed(q) && !xe->devcoredump.captured && !xe_guc_capture_get_matching_and_lock(q)) { /* take force wake before engine register manual capture */ - fw_ref = xe_force_wake_get(gt_to_fw(q->gt), XE_FORCEWAKE_ALL); - if (!xe_force_wake_ref_has_domain(fw_ref, XE_FORCEWAKE_ALL)) + CLASS(xe_force_wake, fw_ref)(gt_to_fw(q->gt), XE_FORCEWAKE_ALL); + if (!xe_force_wake_ref_has_domain(fw_ref.domains, XE_FORCEWAKE_ALL)) xe_gt_info(q->gt, "failed to get forcewake for coredump capture\n"); xe_engine_snapshot_capture_for_queue(q); - - xe_force_wake_put(gt_to_fw(q->gt), fw_ref); } /* - * XXX: Sampling timeout doesn't work in wedged mode as we have to - * modify scheduling state to read timestamp. We could read the - * timestamp from a register to accumulate current running time but this - * doesn't work for SRIOV. For now assuming timeouts in wedged mode are - * genuine timeouts. + * Check if job is actually timed out, if so restart job execution and TDR */ + if (!skip_timeout_check && !check_timeout(q, job)) + goto rearm; + if (!exec_queue_killed(q)) wedged = guc_submit_hint_wedged(exec_queue_to_guc(q)); - /* Engine state now stable, disable scheduling to check timestamp */ - if (!wedged && exec_queue_registered(q)) { + set_exec_queue_banned(q); + + /* Kick job / queue off hardware */ + if (!wedged && (exec_queue_enabled(q) || exec_queue_pending_disable(q))) { int ret; if (exec_queue_reset(q)) err = -EIO; - if (!exec_queue_destroyed(q)) { + if (xe_uc_fw_is_running(&guc->fw)) { /* * Wait for any pending G2H to flush out before * modifying state @@ -1299,13 +1506,6 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job) if (!ret || xe_guc_read_stopped(guc)) goto trigger_reset; - /* - * Flag communicates to G2H handler that schedule - * disable originated from a timeout check. The G2H then - * avoid triggering cleanup or deregistering the exec - * queue. - */ - set_exec_queue_check_timeout(q); disable_scheduling(q, skip_timeout_check); } @@ -1319,6 +1519,7 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job) */ smp_rmb(); ret = wait_event_timeout(guc->ct.wq, + !xe_uc_fw_is_running(&guc->fw) || !exec_queue_pending_disable(q) || xe_guc_read_stopped(guc) || vf_recovery(guc), HZ * 5); @@ -1333,24 +1534,12 @@ trigger_reset: xe_devcoredump(q, job, "Schedule disable failed to respond, guc_id=%d, ret=%d, guc_read=%d", q->guc->id, ret, xe_guc_read_stopped(guc)); - set_exec_queue_extra_ref(q); - xe_exec_queue_get(q); /* GT reset owns this */ - set_exec_queue_banned(q); xe_gt_reset_async(q->gt); xe_sched_tdr_queue_imm(sched); goto rearm; } } - /* - * Check if job is actually timed out, if so restart job execution and TDR - */ - if (!wedged && !skip_timeout_check && !check_timeout(q, job) && - !exec_queue_reset(q) && exec_queue_registered(q)) { - clear_exec_queue_check_timeout(q); - goto sched_enable; - } - if (q->vm && q->vm->xef) { process_name = q->vm->xef->process_name; pid = q->vm->xef->pid; @@ -1381,46 +1570,29 @@ trigger_reset: if (!wedged && (q->flags & EXEC_QUEUE_FLAG_KERNEL || (q->flags & EXEC_QUEUE_FLAG_VM && !exec_queue_killed(q)))) { if (!xe_sched_invalidate_job(job, 2)) { - clear_exec_queue_check_timeout(q); xe_gt_reset_async(q->gt); goto rearm; } } - /* Finish cleaning up exec queue via deregister */ - set_exec_queue_banned(q); - if (!wedged && exec_queue_registered(q) && !exec_queue_destroyed(q)) { - set_exec_queue_extra_ref(q); - xe_exec_queue_get(q); - __deregister_exec_queue(guc, q); - } - - /* Stop fence signaling */ - xe_hw_fence_irq_stop(q->fence_irq); + /* Mark all outstanding jobs as bad, thus completing them */ + xe_sched_job_set_error(job, err); + drm_sched_for_each_pending_job(tmp_job, &sched->base, NULL) + xe_sched_job_set_error(to_xe_sched_job(tmp_job), -ECANCELED); - /* - * Fence state now stable, stop / start scheduler which cleans up any - * fences that are complete - */ - xe_sched_add_pending_job(sched, job); xe_sched_submission_start(sched); - xe_guc_exec_queue_trigger_cleanup(q); - - /* Mark all outstanding jobs as bad, thus completing them */ - spin_lock(&sched->base.job_list_lock); - list_for_each_entry(tmp_job, &sched->base.pending_list, drm.list) - xe_sched_job_set_error(tmp_job, !i++ ? err : -ECANCELED); - spin_unlock(&sched->base.job_list_lock); - - /* Start fence signaling */ - xe_hw_fence_irq_start(q->fence_irq); + if (xe_exec_queue_is_multi_queue(q)) + xe_guc_exec_queue_group_trigger_cleanup(q); + else + xe_guc_exec_queue_trigger_cleanup(q); - return DRM_GPU_SCHED_STAT_RESET; + /* + * We want the job added back to the pending list so it gets freed; this + * is what DRM_GPU_SCHED_STAT_NO_HANG does. + */ + return DRM_GPU_SCHED_STAT_NO_HANG; -sched_enable: - set_exec_queue_pending_tdr_exit(q); - enable_scheduling(q); rearm: /* * XXX: Ideally want to adjust timeout based on current execution time @@ -1455,17 +1627,21 @@ static void __guc_exec_queue_destroy_async(struct work_struct *w) struct xe_exec_queue *q = ge->q; struct xe_guc *guc = exec_queue_to_guc(q); - xe_pm_runtime_get(guc_to_xe(guc)); + guard(xe_pm_runtime)(guc_to_xe(guc)); trace_xe_exec_queue_destroy(q); - if (xe_exec_queue_is_lr(q)) - cancel_work_sync(&ge->lr_tdr); + if (xe_exec_queue_is_multi_queue_secondary(q)) { + struct xe_exec_queue_group *group = q->multi_queue.group; + + mutex_lock(&group->list_lock); + list_del(&q->multi_queue.link); + mutex_unlock(&group->list_lock); + } + /* Confirm no work left behind accessing device structures */ cancel_delayed_work_sync(&ge->sched.base.work_tdr); xe_exec_queue_fini(q); - - xe_pm_runtime_put(guc_to_xe(guc)); } static void guc_exec_queue_destroy_async(struct xe_exec_queue *q) @@ -1570,9 +1746,10 @@ static void __guc_exec_queue_process_msg_suspend(struct xe_sched_msg *msg) { struct xe_exec_queue *q = msg->private_data; struct xe_guc *guc = exec_queue_to_guc(q); + bool idle_skip_suspend = xe_exec_queue_idle_skip_suspend(q); - if (guc_exec_queue_allowed_to_change_state(q) && !exec_queue_suspended(q) && - exec_queue_enabled(q)) { + if (!idle_skip_suspend && guc_exec_queue_allowed_to_change_state(q) && + !exec_queue_suspended(q) && exec_queue_enabled(q)) { wait_event(guc->ct.wq, vf_recovery(guc) || ((q->guc->resume_time != RESUME_PENDING || xe_guc_read_stopped(guc)) && !exec_queue_pending_disable(q))); @@ -1585,17 +1762,39 @@ static void __guc_exec_queue_process_msg_suspend(struct xe_sched_msg *msg) since_resume_ms; if (wait_ms > 0 && q->guc->resume_time) - msleep(wait_ms); + relaxed_ms_sleep(wait_ms); set_exec_queue_suspended(q); disable_scheduling(q, false); } } else if (q->guc->suspend_pending) { + if (idle_skip_suspend) + set_exec_queue_idle_skip_suspend(q); set_exec_queue_suspended(q); suspend_fence_signal(q); } } +static void sched_context(struct xe_exec_queue *q) +{ + struct xe_guc *guc = exec_queue_to_guc(q); + struct xe_lrc *lrc = q->lrc[0]; + u32 action[] = { + XE_GUC_ACTION_SCHED_CONTEXT, + q->guc->id, + }; + + xe_gt_assert(guc_to_gt(guc), !xe_exec_queue_is_parallel(q)); + xe_gt_assert(guc_to_gt(guc), !exec_queue_destroyed(q)); + xe_gt_assert(guc_to_gt(guc), exec_queue_registered(q)); + xe_gt_assert(guc_to_gt(guc), !exec_queue_pending_disable(q)); + + trace_xe_exec_queue_submit(q); + + xe_lrc_set_ring_tail(lrc, lrc->ring.tail); + xe_guc_ct_send(&guc->ct, action, ARRAY_SIZE(action), 0, 0); +} + static void __guc_exec_queue_process_msg_resume(struct xe_sched_msg *msg) { struct xe_exec_queue *q = msg->private_data; @@ -1603,19 +1802,53 @@ static void __guc_exec_queue_process_msg_resume(struct xe_sched_msg *msg) if (guc_exec_queue_allowed_to_change_state(q)) { clear_exec_queue_suspended(q); if (!exec_queue_enabled(q)) { + if (exec_queue_idle_skip_suspend(q)) { + struct xe_lrc *lrc = q->lrc[0]; + + clear_exec_queue_idle_skip_suspend(q); + xe_lrc_set_ring_tail(lrc, lrc->ring.tail); + } q->guc->resume_time = RESUME_PENDING; set_exec_queue_pending_resume(q); enable_scheduling(q); + } else if (exec_queue_idle_skip_suspend(q)) { + clear_exec_queue_idle_skip_suspend(q); + sched_context(q); } } else { clear_exec_queue_suspended(q); + clear_exec_queue_idle_skip_suspend(q); + } +} + +static void __guc_exec_queue_process_msg_set_multi_queue_priority(struct xe_sched_msg *msg) +{ + struct xe_exec_queue *q = msg->private_data; + + if (guc_exec_queue_allowed_to_change_state(q)) { +#define MAX_MULTI_QUEUE_CGP_SYNC_SIZE (2) + struct xe_guc *guc = exec_queue_to_guc(q); + struct xe_exec_queue_group *group = q->multi_queue.group; + u32 action[MAX_MULTI_QUEUE_CGP_SYNC_SIZE]; + int len = 0; + + action[len++] = XE_GUC_ACTION_MULTI_QUEUE_CONTEXT_CGP_SYNC; + action[len++] = group->primary->guc->id; + + xe_gt_assert(guc_to_gt(guc), len <= MAX_MULTI_QUEUE_CGP_SYNC_SIZE); +#undef MAX_MULTI_QUEUE_CGP_SYNC_SIZE + + xe_guc_exec_queue_group_cgp_sync(guc, q, action, len); } + + kfree(msg); } -#define CLEANUP 1 /* Non-zero values to catch uninitialized msg */ -#define SET_SCHED_PROPS 2 -#define SUSPEND 3 -#define RESUME 4 +#define CLEANUP 1 /* Non-zero values to catch uninitialized msg */ +#define SET_SCHED_PROPS 2 +#define SUSPEND 3 +#define RESUME 4 +#define SET_MULTI_QUEUE_PRIORITY 5 #define OPCODE_MASK 0xf #define MSG_LOCKED BIT(8) #define MSG_HEAD BIT(9) @@ -1639,6 +1872,9 @@ static void guc_exec_queue_process_msg(struct xe_sched_msg *msg) case RESUME: __guc_exec_queue_process_msg_resume(msg); break; + case SET_MULTI_QUEUE_PRIORITY: + __guc_exec_queue_process_msg_set_multi_queue_priority(msg); + break; default: XE_WARN_ON("Unknown message type"); } @@ -1660,6 +1896,7 @@ static int guc_exec_queue_init(struct xe_exec_queue *q) { struct xe_gpu_scheduler *sched; struct xe_guc *guc = exec_queue_to_guc(q); + struct workqueue_struct *submit_wq = NULL; struct xe_guc_exec_queue *ge; long timeout; int err, i; @@ -1680,8 +1917,20 @@ static int guc_exec_queue_init(struct xe_exec_queue *q) timeout = (q->vm && xe_vm_in_lr_mode(q->vm)) ? MAX_SCHEDULE_TIMEOUT : msecs_to_jiffies(q->sched_props.job_timeout_ms); + + /* + * Use primary queue's submit_wq for all secondary queues of a + * multi queue group. This serialization avoids any locking around + * CGP synchronization with GuC. + */ + if (xe_exec_queue_is_multi_queue_secondary(q)) { + struct xe_exec_queue *primary = xe_exec_queue_multi_queue_primary(q); + + submit_wq = primary->guc->sched.base.submit_wq; + } + err = xe_sched_init(&ge->sched, &drm_sched_ops, &xe_sched_ops, - NULL, xe_lrc_ring_size() / MAX_JOB_SIZE_BYTES, 64, + submit_wq, xe_lrc_ring_size() / MAX_JOB_SIZE_BYTES, 64, timeout, guc_to_gt(guc)->ordered_wq, NULL, q->name, gt_to_xe(q->gt)->drm.dev); if (err) @@ -1692,9 +1941,6 @@ static int guc_exec_queue_init(struct xe_exec_queue *q) if (err) goto err_sched; - if (xe_exec_queue_is_lr(q)) - INIT_WORK(&q->guc->lr_tdr, xe_guc_exec_queue_lr_cleanup); - mutex_lock(&guc->submission_state.lock); err = alloc_guc_id(guc, q); @@ -1710,7 +1956,23 @@ static int guc_exec_queue_init(struct xe_exec_queue *q) xe_exec_queue_assign_name(q, q->guc->id); - trace_xe_exec_queue_create(q); + /* + * Maintain secondary queues of the multi queue group in a list + * for handling dependencies across the queues in the group. + */ + if (xe_exec_queue_is_multi_queue_secondary(q)) { + struct xe_exec_queue_group *group = q->multi_queue.group; + + INIT_LIST_HEAD(&q->multi_queue.link); + mutex_lock(&group->list_lock); + list_add_tail(&q->multi_queue.link, &group->list); + mutex_unlock(&group->list_lock); + } + + if (xe_exec_queue_is_multi_queue(q)) + trace_xe_exec_queue_create_multi_queue(q); + else + trace_xe_exec_queue_create(q); return 0; @@ -1842,6 +2104,27 @@ static int guc_exec_queue_set_preempt_timeout(struct xe_exec_queue *q, return 0; } +static int guc_exec_queue_set_multi_queue_priority(struct xe_exec_queue *q, + enum xe_multi_queue_priority priority) +{ + struct xe_sched_msg *msg; + + xe_gt_assert(guc_to_gt(exec_queue_to_guc(q)), xe_exec_queue_is_multi_queue(q)); + + if (q->multi_queue.priority == priority || + exec_queue_killed_or_banned_or_wedged(q)) + return 0; + + msg = kmalloc(sizeof(*msg), GFP_KERNEL); + if (!msg) + return -ENOMEM; + + q->multi_queue.priority = priority; + guc_exec_queue_add_msg(q, msg, SET_MULTI_QUEUE_PRIORITY); + + return 0; +} + static int guc_exec_queue_suspend(struct xe_exec_queue *q) { struct xe_gpu_scheduler *sched = &q->guc->sched; @@ -1916,6 +2199,10 @@ static void guc_exec_queue_resume(struct xe_exec_queue *q) static bool guc_exec_queue_reset_status(struct xe_exec_queue *q) { + if (xe_exec_queue_is_multi_queue_secondary(q) && + guc_exec_queue_reset_status(xe_exec_queue_multi_queue_primary(q))) + return true; + return exec_queue_reset(q) || exec_queue_killed_or_banned_or_wedged(q); } @@ -1933,6 +2220,7 @@ static const struct xe_exec_queue_ops guc_exec_queue_ops = { .set_priority = guc_exec_queue_set_priority, .set_timeslice = guc_exec_queue_set_timeslice, .set_preempt_timeout = guc_exec_queue_set_preempt_timeout, + .set_multi_queue_priority = guc_exec_queue_set_multi_queue_priority, .suspend = guc_exec_queue_suspend, .suspend_wait = guc_exec_queue_suspend_wait, .resume = guc_exec_queue_resume, @@ -1948,9 +2236,7 @@ static void guc_exec_queue_stop(struct xe_guc *guc, struct xe_exec_queue *q) /* Clean up lost G2H + reset engine state */ if (exec_queue_registered(q)) { - if (exec_queue_extra_ref(q) || xe_exec_queue_is_lr(q)) - xe_exec_queue_put(q); - else if (exec_queue_destroyed(q)) + if (exec_queue_destroyed(q)) __guc_exec_queue_destroy(guc, q); } if (q->guc->suspend_pending) { @@ -1980,9 +2266,6 @@ static void guc_exec_queue_stop(struct xe_guc *guc, struct xe_exec_queue *q) trace_xe_sched_job_ban(job); ban = true; } - } else if (xe_exec_queue_is_lr(q) && - !xe_lrc_ring_is_idle(q->lrc[0])) { - ban = true; } if (ban) { @@ -2062,11 +2345,8 @@ static void guc_exec_queue_revert_pending_state_change(struct xe_guc *guc, q->guc->id); } - if (pending_enable && !pending_resume && - !exec_queue_pending_tdr_exit(q)) { + if (pending_enable && !pending_resume) { clear_exec_queue_registered(q); - if (xe_exec_queue_is_lr(q)) - xe_exec_queue_put(q); xe_gt_dbg(guc_to_gt(guc), "Replay REGISTER - guc_id=%d", q->guc->id); } @@ -2074,7 +2354,6 @@ static void guc_exec_queue_revert_pending_state_change(struct xe_guc *guc, if (pending_enable) { clear_exec_queue_enabled(q); clear_exec_queue_pending_resume(q); - clear_exec_queue_pending_tdr_exit(q); clear_exec_queue_pending_enable(q); xe_gt_dbg(guc_to_gt(guc), "Replay ENABLE - guc_id=%d", q->guc->id); @@ -2082,11 +2361,7 @@ static void guc_exec_queue_revert_pending_state_change(struct xe_guc *guc, if (exec_queue_destroyed(q) && exec_queue_registered(q)) { clear_exec_queue_destroyed(q); - if (exec_queue_extra_ref(q)) - xe_exec_queue_put(q); - else - q->guc->needs_cleanup = true; - clear_exec_queue_extra_ref(q); + q->guc->needs_cleanup = true; xe_gt_dbg(guc_to_gt(guc), "Replay CLEANUP - guc_id=%d", q->guc->id); } @@ -2104,7 +2379,6 @@ static void guc_exec_queue_revert_pending_state_change(struct xe_guc *guc, if (!pending_enable) set_exec_queue_enabled(q); clear_exec_queue_pending_disable(q); - clear_exec_queue_check_timeout(q); xe_gt_dbg(guc_to_gt(guc), "Replay DISABLE - guc_id=%d", q->guc->id); } @@ -2139,10 +2413,7 @@ static void guc_exec_queue_pause(struct xe_guc *guc, struct xe_exec_queue *q) /* Stop scheduling + flush any DRM scheduler operations */ xe_sched_submission_stop(sched); - if (xe_exec_queue_is_lr(q)) - cancel_work_sync(&q->guc->lr_tdr); - else - cancel_delayed_work_sync(&sched->base.work_tdr); + cancel_delayed_work_sync(&sched->base.work_tdr); guc_exec_queue_revert_pending_state_change(guc, q); @@ -2182,6 +2453,22 @@ void xe_guc_submit_pause(struct xe_guc *guc) struct xe_exec_queue *q; unsigned long index; + mutex_lock(&guc->submission_state.lock); + xa_for_each(&guc->submission_state.exec_queue_lookup, index, q) + xe_sched_submission_stop(&q->guc->sched); + mutex_unlock(&guc->submission_state.lock); +} + +/** + * xe_guc_submit_pause_vf - Stop further runs of submission tasks for VF. + * @guc: the &xe_guc struct instance whose scheduler is to be disabled + */ +void xe_guc_submit_pause_vf(struct xe_guc *guc) +{ + struct xe_exec_queue *q; + unsigned long index; + + xe_gt_assert(guc_to_gt(guc), IS_SRIOV_VF(guc_to_xe(guc))); xe_gt_assert(guc_to_gt(guc), vf_recovery(guc)); mutex_lock(&guc->submission_state.lock); @@ -2254,9 +2541,11 @@ static void guc_exec_queue_unpause_prepare(struct xe_guc *guc, { struct xe_gpu_scheduler *sched = &q->guc->sched; struct xe_sched_job *job = NULL; + struct drm_sched_job *s_job; bool restore_replay = false; - list_for_each_entry(job, &sched->base.pending_list, drm.list) { + drm_sched_for_each_pending_job(s_job, &sched->base, NULL) { + job = to_xe_sched_job(s_job); restore_replay |= job->restore_replay; if (restore_replay) { xe_gt_dbg(guc_to_gt(guc), "Replay JOB - guc_id=%d, seqno=%d", @@ -2272,14 +2561,15 @@ static void guc_exec_queue_unpause_prepare(struct xe_guc *guc, } /** - * xe_guc_submit_unpause_prepare - Prepare unpause submission tasks on given GuC. + * xe_guc_submit_unpause_prepare_vf - Prepare unpause submission tasks for VF. * @guc: the &xe_guc struct instance whose scheduler is to be prepared for unpause */ -void xe_guc_submit_unpause_prepare(struct xe_guc *guc) +void xe_guc_submit_unpause_prepare_vf(struct xe_guc *guc) { struct xe_exec_queue *q; unsigned long index; + xe_gt_assert(guc_to_gt(guc), IS_SRIOV_VF(guc_to_xe(guc))); xe_gt_assert(guc_to_gt(guc), vf_recovery(guc)); mutex_lock(&guc->submission_state.lock); @@ -2356,13 +2646,30 @@ void xe_guc_submit_unpause(struct xe_guc *guc) unsigned long index; mutex_lock(&guc->submission_state.lock); + xa_for_each(&guc->submission_state.exec_queue_lookup, index, q) + xe_sched_submission_start(&q->guc->sched); + mutex_unlock(&guc->submission_state.lock); +} + +/** + * xe_guc_submit_unpause_vf - Allow further runs of submission tasks for VF. + * @guc: the &xe_guc struct instance whose scheduler is to be enabled + */ +void xe_guc_submit_unpause_vf(struct xe_guc *guc) +{ + struct xe_exec_queue *q; + unsigned long index; + + xe_gt_assert(guc_to_gt(guc), IS_SRIOV_VF(guc_to_xe(guc))); + + mutex_lock(&guc->submission_state.lock); xa_for_each(&guc->submission_state.exec_queue_lookup, index, q) { /* * Prevent redundant attempts to stop parallel queues, or queues * created after resfix done. */ if (q->guc->id != index || - !READ_ONCE(q->guc->sched.base.pause_submit)) + !drm_sched_is_stopped(&q->guc->sched.base)) continue; guc_exec_queue_unpause(guc, q); @@ -2431,7 +2738,11 @@ static void deregister_exec_queue(struct xe_guc *guc, struct xe_exec_queue *q) trace_xe_exec_queue_deregister(q); - xe_guc_ct_send_g2h_handler(&guc->ct, action, ARRAY_SIZE(action)); + if (xe_exec_queue_is_multi_queue_secondary(q)) + handle_deregister_done(guc, q); + else + xe_guc_ct_send_g2h_handler(&guc->ct, action, + ARRAY_SIZE(action)); } static void handle_sched_done(struct xe_guc *guc, struct xe_exec_queue *q, @@ -2444,13 +2755,10 @@ static void handle_sched_done(struct xe_guc *guc, struct xe_exec_queue *q, q->guc->resume_time = ktime_get(); clear_exec_queue_pending_resume(q); - clear_exec_queue_pending_tdr_exit(q); clear_exec_queue_pending_enable(q); smp_wmb(); wake_up_all(&guc->ct.wq); } else { - bool check_timeout = exec_queue_check_timeout(q); - xe_gt_assert(guc_to_gt(guc), runnable_state == 0); xe_gt_assert(guc_to_gt(guc), exec_queue_pending_disable(q)); @@ -2458,11 +2766,11 @@ static void handle_sched_done(struct xe_guc *guc, struct xe_exec_queue *q, suspend_fence_signal(q); clear_exec_queue_pending_disable(q); } else { - if (exec_queue_banned(q) || check_timeout) { + if (exec_queue_banned(q)) { smp_wmb(); wake_up_all(&guc->ct.wq); } - if (!check_timeout && exec_queue_destroyed(q)) { + if (exec_queue_destroyed(q)) { /* * Make sure to clear the pending_disable only * after sampling the destroyed state. We want @@ -2481,6 +2789,16 @@ static void handle_sched_done(struct xe_guc *guc, struct xe_exec_queue *q, } } +static void handle_multi_queue_secondary_sched_done(struct xe_guc *guc, + struct xe_exec_queue *q, + u32 runnable_state) +{ + /* Take CT lock here as handle_sched_done() do send a h2g message */ + mutex_lock(&guc->ct.lock); + handle_sched_done(guc, q, runnable_state); + mutex_unlock(&guc->ct.lock); +} + int xe_guc_sched_done_handler(struct xe_guc *guc, u32 *msg, u32 len) { struct xe_exec_queue *q; @@ -2515,11 +2833,7 @@ static void handle_deregister_done(struct xe_guc *guc, struct xe_exec_queue *q) trace_xe_exec_queue_deregister_done(q); clear_exec_queue_registered(q); - - if (exec_queue_extra_ref(q) || xe_exec_queue_is_lr(q)) - xe_exec_queue_put(q); - else - __guc_exec_queue_destroy(guc, q); + __guc_exec_queue_destroy(guc, q); } int xe_guc_deregister_done_handler(struct xe_guc *guc, u32 *msg, u32 len) @@ -2564,8 +2878,9 @@ int xe_guc_exec_queue_reset_handler(struct xe_guc *guc, u32 *msg, u32 len) if (unlikely(!q)) return -EPROTO; - xe_gt_info(gt, "Engine reset: engine_class=%s, logical_mask: 0x%x, guc_id=%d", - xe_hw_engine_class_to_str(q->class), q->logical_mask, guc_id); + xe_gt_info(gt, "Engine reset: engine_class=%s, logical_mask: 0x%x, guc_id=%d, state=0x%0x", + xe_hw_engine_class_to_str(q->class), q->logical_mask, guc_id, + atomic_read(&q->guc->state)); trace_xe_exec_queue_reset(q); @@ -2575,9 +2890,7 @@ int xe_guc_exec_queue_reset_handler(struct xe_guc *guc, u32 *msg, u32 len) * jobs by setting timeout of the job to the minimum value kicking * guc_exec_queue_timedout_job. */ - set_exec_queue_reset(q); - if (!exec_queue_banned(q) && !exec_queue_check_timeout(q)) - xe_guc_exec_queue_trigger_cleanup(q); + xe_guc_exec_queue_reset_trigger_cleanup(q); return 0; } @@ -2645,20 +2958,18 @@ int xe_guc_exec_queue_memory_cat_error_handler(struct xe_guc *guc, u32 *msg, * See bspec 54047 and 72187 for details. */ if (type != XE_GUC_CAT_ERR_TYPE_INVALID) - xe_gt_dbg(gt, - "Engine memory CAT error [%u]: class=%s, logical_mask: 0x%x, guc_id=%d", - type, xe_hw_engine_class_to_str(q->class), q->logical_mask, guc_id); + xe_gt_info(gt, + "Engine memory CAT error [%u]: class=%s, logical_mask: 0x%x, guc_id=%d", + type, xe_hw_engine_class_to_str(q->class), q->logical_mask, guc_id); else - xe_gt_dbg(gt, - "Engine memory CAT error: class=%s, logical_mask: 0x%x, guc_id=%d", - xe_hw_engine_class_to_str(q->class), q->logical_mask, guc_id); + xe_gt_info(gt, + "Engine memory CAT error: class=%s, logical_mask: 0x%x, guc_id=%d", + xe_hw_engine_class_to_str(q->class), q->logical_mask, guc_id); trace_xe_exec_queue_memory_cat_error(q); /* Treat the same as engine reset */ - set_exec_queue_reset(q); - if (!exec_queue_banned(q) && !exec_queue_check_timeout(q)) - xe_guc_exec_queue_trigger_cleanup(q); + xe_guc_exec_queue_reset_trigger_cleanup(q); return 0; } @@ -2685,6 +2996,73 @@ int xe_guc_exec_queue_reset_failure_handler(struct xe_guc *guc, u32 *msg, u32 le return 0; } +int xe_guc_exec_queue_cgp_context_error_handler(struct xe_guc *guc, u32 *msg, + u32 len) +{ + struct xe_gt *gt = guc_to_gt(guc); + struct xe_device *xe = guc_to_xe(guc); + struct xe_exec_queue *q; + u32 guc_id = msg[2]; + + if (unlikely(len != XE_GUC_EXEC_QUEUE_CGP_CONTEXT_ERROR_LEN)) { + drm_err(&xe->drm, "Invalid length %u", len); + return -EPROTO; + } + + q = g2h_exec_queue_lookup(guc, guc_id); + if (unlikely(!q)) + return -EPROTO; + + xe_gt_dbg(gt, + "CGP context error: [%s] err=0x%x, q0_id=0x%x LRCA=0x%x guc_id=0x%x", + msg[0] & 1 ? "uc" : "kmd", msg[1], msg[2], msg[3], msg[4]); + + trace_xe_exec_queue_cgp_context_error(q); + + /* Treat the same as engine reset */ + xe_guc_exec_queue_reset_trigger_cleanup(q); + + return 0; +} + +/** + * xe_guc_exec_queue_cgp_sync_done_handler - CGP synchronization done handler + * @guc: guc + * @msg: message indicating CGP sync done + * @len: length of message + * + * Set multi queue group's sync_pending flag to false and wakeup anyone waiting + * for CGP synchronization to complete. + * + * Return: 0 on success, -EPROTO for malformed messages. + */ +int xe_guc_exec_queue_cgp_sync_done_handler(struct xe_guc *guc, u32 *msg, u32 len) +{ + struct xe_device *xe = guc_to_xe(guc); + struct xe_exec_queue *q; + u32 guc_id = msg[0]; + + if (unlikely(len < 1)) { + drm_err(&xe->drm, "Invalid CGP_SYNC_DONE length %u", len); + return -EPROTO; + } + + q = g2h_exec_queue_lookup(guc, guc_id); + if (unlikely(!q)) + return -EPROTO; + + if (!xe_exec_queue_is_multi_queue_primary(q)) { + drm_err(&xe->drm, "Unexpected CGP_SYNC_DONE response"); + return -EPROTO; + } + + /* Wakeup the serialized cgp update wait */ + WRITE_ONCE(q->multi_queue.group->sync_pending, false); + xe_guc_ct_wake_waiters(&guc->ct); + + return 0; +} + static void guc_exec_queue_wq_snapshot_capture(struct xe_exec_queue *q, struct xe_guc_submit_exec_queue_snapshot *snapshot) @@ -2784,30 +3162,12 @@ xe_guc_exec_queue_snapshot_capture(struct xe_exec_queue *q) if (snapshot->parallel_execution) guc_exec_queue_wq_snapshot_capture(q, snapshot); - spin_lock(&sched->base.job_list_lock); - snapshot->pending_list_size = list_count_nodes(&sched->base.pending_list); - snapshot->pending_list = kmalloc_array(snapshot->pending_list_size, - sizeof(struct pending_list_snapshot), - GFP_ATOMIC); - - if (snapshot->pending_list) { - struct xe_sched_job *job_iter; - - i = 0; - list_for_each_entry(job_iter, &sched->base.pending_list, drm.list) { - snapshot->pending_list[i].seqno = - xe_sched_job_seqno(job_iter); - snapshot->pending_list[i].fence = - dma_fence_is_signaled(job_iter->fence) ? 1 : 0; - snapshot->pending_list[i].finished = - dma_fence_is_signaled(&job_iter->drm.s_fence->finished) - ? 1 : 0; - i++; - } + if (xe_exec_queue_is_multi_queue(q)) { + snapshot->multi_queue.valid = true; + snapshot->multi_queue.primary = xe_exec_queue_multi_queue_primary(q)->guc->id; + snapshot->multi_queue.pos = q->multi_queue.pos; } - spin_unlock(&sched->base.job_list_lock); - return snapshot; } @@ -2866,12 +3226,10 @@ xe_guc_exec_queue_snapshot_print(struct xe_guc_submit_exec_queue_snapshot *snaps if (snapshot->parallel_execution) guc_exec_queue_wq_snapshot_print(snapshot, p); - for (i = 0; snapshot->pending_list && i < snapshot->pending_list_size; - i++) - drm_printf(p, "\tJob: seqno=%d, fence=%d, finished=%d\n", - snapshot->pending_list[i].seqno, - snapshot->pending_list[i].fence, - snapshot->pending_list[i].finished); + if (snapshot->multi_queue.valid) { + drm_printf(p, "\tMulti queue primary GuC ID: %d\n", snapshot->multi_queue.primary); + drm_printf(p, "\tMulti queue position: %d\n", snapshot->multi_queue.pos); + } } /** @@ -2894,7 +3252,6 @@ void xe_guc_exec_queue_snapshot_free(struct xe_guc_submit_exec_queue_snapshot *s xe_lrc_snapshot_free(snapshot->lrc[i]); kfree(snapshot->lrc); } - kfree(snapshot->pending_list); kfree(snapshot); } @@ -2957,6 +3314,27 @@ void xe_guc_submit_print(struct xe_guc *guc, struct drm_printer *p) } /** + * xe_guc_has_registered_mlrc_queues - check whether there are any MLRC queues + * registered with the GuC + * @guc: GuC. + * + * Return: true if any MLRC queue is registered with the GuC, false otherwise. + */ +bool xe_guc_has_registered_mlrc_queues(struct xe_guc *guc) +{ + struct xe_exec_queue *q; + unsigned long index; + + guard(mutex)(&guc->submission_state.lock); + + xa_for_each(&guc->submission_state.exec_queue_lookup, index, q) + if (q->width > 1) + return true; + + return false; +} + +/** * xe_guc_contexts_hwsp_rebase - Re-compute GGTT references within all * exec queues registered to given GuC. * @guc: the &xe_guc struct instance diff --git a/drivers/gpu/drm/xe/xe_guc_submit.h b/drivers/gpu/drm/xe/xe_guc_submit.h index b49a2748ec46..b3839a90c142 100644 --- a/drivers/gpu/drm/xe/xe_guc_submit.h +++ b/drivers/gpu/drm/xe/xe_guc_submit.h @@ -21,9 +21,11 @@ void xe_guc_submit_reset_wait(struct xe_guc *guc); void xe_guc_submit_stop(struct xe_guc *guc); int xe_guc_submit_start(struct xe_guc *guc); void xe_guc_submit_pause(struct xe_guc *guc); -void xe_guc_submit_unpause(struct xe_guc *guc); -void xe_guc_submit_unpause_prepare(struct xe_guc *guc); void xe_guc_submit_pause_abort(struct xe_guc *guc); +void xe_guc_submit_pause_vf(struct xe_guc *guc); +void xe_guc_submit_unpause(struct xe_guc *guc); +void xe_guc_submit_unpause_vf(struct xe_guc *guc); +void xe_guc_submit_unpause_prepare_vf(struct xe_guc *guc); void xe_guc_submit_wedge(struct xe_guc *guc); int xe_guc_read_stopped(struct xe_guc *guc); @@ -34,6 +36,9 @@ int xe_guc_exec_queue_memory_cat_error_handler(struct xe_guc *guc, u32 *msg, u32 len); int xe_guc_exec_queue_reset_failure_handler(struct xe_guc *guc, u32 *msg, u32 len); int xe_guc_error_capture_handler(struct xe_guc *guc, u32 *msg, u32 len); +int xe_guc_exec_queue_cgp_sync_done_handler(struct xe_guc *guc, u32 *msg, u32 len); +int xe_guc_exec_queue_cgp_context_error_handler(struct xe_guc *guc, u32 *msg, + u32 len); struct xe_guc_submit_exec_queue_snapshot * xe_guc_exec_queue_snapshot_capture(struct xe_exec_queue *q); @@ -47,6 +52,8 @@ xe_guc_exec_queue_snapshot_free(struct xe_guc_submit_exec_queue_snapshot *snapsh void xe_guc_submit_print(struct xe_guc *guc, struct drm_printer *p); void xe_guc_register_vf_exec_queue(struct xe_exec_queue *q, int ctx_type); +bool xe_guc_has_registered_mlrc_queues(struct xe_guc *guc); + int xe_guc_contexts_hwsp_rebase(struct xe_guc *guc, void *scratch); #endif diff --git a/drivers/gpu/drm/xe/xe_guc_submit_types.h b/drivers/gpu/drm/xe/xe_guc_submit_types.h index dc7456c34583..5ccc5f959bb3 100644 --- a/drivers/gpu/drm/xe/xe_guc_submit_types.h +++ b/drivers/gpu/drm/xe/xe_guc_submit_types.h @@ -61,12 +61,6 @@ struct guc_submit_parallel_scratch { u32 wq[WQ_SIZE / sizeof(u32)]; }; -struct pending_list_snapshot { - u32 seqno; - bool fence; - bool finished; -}; - /** * struct xe_guc_submit_exec_queue_snapshot - Snapshot for devcoredump */ @@ -135,10 +129,18 @@ struct xe_guc_submit_exec_queue_snapshot { u32 wq[WQ_SIZE / sizeof(u32)]; } parallel; - /** @pending_list_size: Size of the pending list snapshot array */ - int pending_list_size; - /** @pending_list: snapshot of the pending list info */ - struct pending_list_snapshot *pending_list; + /** @multi_queue: snapshot of the multi queue information */ + struct { + /** + * @multi_queue.primary: GuC id of the primary exec queue + * of the multi queue group. + */ + u32 primary; + /** @multi_queue.pos: Position of the exec queue within the multi queue group */ + u8 pos; + /** @valid: The exec queue is part of a multi queue group */ + bool valid; + } multi_queue; }; #endif diff --git a/drivers/gpu/drm/xe/xe_guc_tlb_inval.c b/drivers/gpu/drm/xe/xe_guc_tlb_inval.c index a80175c7c478..774467befbb9 100644 --- a/drivers/gpu/drm/xe/xe_guc_tlb_inval.c +++ b/drivers/gpu/drm/xe/xe_guc_tlb_inval.c @@ -13,6 +13,7 @@ #include "xe_guc_tlb_inval.h" #include "xe_force_wake.h" #include "xe_mmio.h" +#include "xe_sa.h" #include "xe_tlb_inval.h" #include "regs/xe_guc_regs.h" @@ -34,9 +35,12 @@ static int send_tlb_inval(struct xe_guc *guc, const u32 *action, int len) G2H_LEN_DW_TLB_INVALIDATE, 1); } -#define MAKE_INVAL_OP(type) ((type << XE_GUC_TLB_INVAL_TYPE_SHIFT) | \ +#define MAKE_INVAL_OP_FLUSH(type, flush_cache) ((type << XE_GUC_TLB_INVAL_TYPE_SHIFT) | \ XE_GUC_TLB_INVAL_MODE_HEAVY << XE_GUC_TLB_INVAL_MODE_SHIFT | \ - XE_GUC_TLB_INVAL_FLUSH_CACHE) + (flush_cache ? \ + XE_GUC_TLB_INVAL_FLUSH_CACHE : 0)) + +#define MAKE_INVAL_OP(type) MAKE_INVAL_OP_FLUSH(type, true) static int send_tlb_inval_all(struct xe_tlb_inval *tlb_inval, u32 seqno) { @@ -71,12 +75,11 @@ static int send_tlb_inval_ggtt(struct xe_tlb_inval *tlb_inval, u32 seqno) return send_tlb_inval(guc, action, ARRAY_SIZE(action)); } else if (xe_device_uc_enabled(xe) && !xe_device_wedged(xe)) { struct xe_mmio *mmio = >->mmio; - unsigned int fw_ref; if (IS_SRIOV_VF(xe)) return -ECANCELED; - fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT); + CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT); if (xe->info.platform == XE_PVC || GRAPHICS_VER(xe) >= 20) { xe_mmio_write32(mmio, PVC_GUC_TLB_INV_DESC1, PVC_GUC_TLB_INV_DESC1_INVALIDATE); @@ -86,12 +89,28 @@ static int send_tlb_inval_ggtt(struct xe_tlb_inval *tlb_inval, u32 seqno) xe_mmio_write32(mmio, GUC_TLB_INV_CR, GUC_TLB_INV_CR_INVALIDATE); } - xe_force_wake_put(gt_to_fw(gt), fw_ref); } return -ECANCELED; } +static int send_page_reclaim(struct xe_guc *guc, u32 seqno, + u64 gpu_addr) +{ + struct xe_gt *gt = guc_to_gt(guc); + u32 action[] = { + XE_GUC_ACTION_PAGE_RECLAMATION, + seqno, + lower_32_bits(gpu_addr), + upper_32_bits(gpu_addr), + }; + + xe_gt_stats_incr(gt, XE_GT_STATS_ID_PRL_ISSUED_COUNT, 1); + + return xe_guc_ct_send(&guc->ct, action, ARRAY_SIZE(action), + G2H_LEN_DW_PAGE_RECLAMATION, 1); +} + /* * Ensure that roundup_pow_of_two(length) doesn't overflow. * Note that roundup_pow_of_two() operates on unsigned long, @@ -100,20 +119,21 @@ static int send_tlb_inval_ggtt(struct xe_tlb_inval *tlb_inval, u32 seqno) #define MAX_RANGE_TLB_INVALIDATION_LENGTH (rounddown_pow_of_two(ULONG_MAX)) static int send_tlb_inval_ppgtt(struct xe_tlb_inval *tlb_inval, u32 seqno, - u64 start, u64 end, u32 asid) + u64 start, u64 end, u32 asid, + struct drm_suballoc *prl_sa) { #define MAX_TLB_INVALIDATION_LEN 7 struct xe_guc *guc = tlb_inval->private; struct xe_gt *gt = guc_to_gt(guc); u32 action[MAX_TLB_INVALIDATION_LEN]; u64 length = end - start; - int len = 0; + int len = 0, err; if (guc_to_xe(guc)->info.force_execlist) return -ECANCELED; action[len++] = XE_GUC_ACTION_TLB_INVALIDATION; - action[len++] = seqno; + action[len++] = !prl_sa ? seqno : TLB_INVALIDATION_SEQNO_INVALID; if (!gt_to_xe(gt)->info.has_range_tlb_inval || length > MAX_RANGE_TLB_INVALIDATION_LENGTH) { action[len++] = MAKE_INVAL_OP(XE_GUC_TLB_INVAL_FULL); @@ -154,7 +174,8 @@ static int send_tlb_inval_ppgtt(struct xe_tlb_inval *tlb_inval, u32 seqno, ilog2(SZ_2M) + 1))); xe_gt_assert(gt, IS_ALIGNED(start, length)); - action[len++] = MAKE_INVAL_OP(XE_GUC_TLB_INVAL_PAGE_SELECTIVE); + /* Flush on NULL case, Media is not required to modify flush due to no PPC so NOP */ + action[len++] = MAKE_INVAL_OP_FLUSH(XE_GUC_TLB_INVAL_PAGE_SELECTIVE, !prl_sa); action[len++] = asid; action[len++] = lower_32_bits(start); action[len++] = upper_32_bits(start); @@ -163,7 +184,10 @@ static int send_tlb_inval_ppgtt(struct xe_tlb_inval *tlb_inval, u32 seqno, xe_gt_assert(gt, len <= MAX_TLB_INVALIDATION_LEN); - return send_tlb_inval(guc, action, len); + err = send_tlb_inval(guc, action, len); + if (!err && prl_sa) + err = send_page_reclaim(guc, seqno, xe_sa_bo_gpu_addr(prl_sa)); + return err; } static bool tlb_inval_initialized(struct xe_tlb_inval *tlb_inval) diff --git a/drivers/gpu/drm/xe/xe_heci_gsc.c b/drivers/gpu/drm/xe/xe_heci_gsc.c index 2b3d49dd394c..c1f15313f92e 100644 --- a/drivers/gpu/drm/xe/xe_heci_gsc.c +++ b/drivers/gpu/drm/xe/xe_heci_gsc.c @@ -11,7 +11,6 @@ #include <drm/drm_print.h> #include "xe_device_types.h" -#include "xe_drv.h" #include "xe_heci_gsc.h" #include "regs/xe_gsc_regs.h" #include "xe_platform_types.h" @@ -223,7 +222,7 @@ void xe_heci_gsc_irq_handler(struct xe_device *xe, u32 iir) if (xe->heci_gsc.irq < 0) return; - ret = generic_handle_irq(xe->heci_gsc.irq); + ret = generic_handle_irq_safe(xe->heci_gsc.irq); if (ret) drm_err_ratelimited(&xe->drm, "error handling GSC irq: %d\n", ret); } @@ -243,7 +242,7 @@ void xe_heci_csc_irq_handler(struct xe_device *xe, u32 iir) if (xe->heci_gsc.irq < 0) return; - ret = generic_handle_irq(xe->heci_gsc.irq); + ret = generic_handle_irq_safe(xe->heci_gsc.irq); if (ret) drm_err_ratelimited(&xe->drm, "error handling GSC irq: %d\n", ret); } diff --git a/drivers/gpu/drm/xe/xe_huc.c b/drivers/gpu/drm/xe/xe_huc.c index 0a70c8924582..57afe21444b1 100644 --- a/drivers/gpu/drm/xe/xe_huc.c +++ b/drivers/gpu/drm/xe/xe_huc.c @@ -12,7 +12,6 @@ #include "abi/gsc_pxp_commands_abi.h" #include "regs/xe_gsc_regs.h" #include "regs/xe_guc_regs.h" -#include "xe_assert.h" #include "xe_bo.h" #include "xe_device.h" #include "xe_force_wake.h" @@ -300,19 +299,16 @@ void xe_huc_sanitize(struct xe_huc *huc) void xe_huc_print_info(struct xe_huc *huc, struct drm_printer *p) { struct xe_gt *gt = huc_to_gt(huc); - unsigned int fw_ref; xe_uc_fw_print(&huc->fw, p); if (!xe_uc_fw_is_enabled(&huc->fw)) return; - fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT); - if (!fw_ref) + CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT); + if (!fw_ref.domains) return; drm_printf(p, "\nHuC status: 0x%08x\n", xe_mmio_read32(>->mmio, HUC_KERNEL_LOAD_INFO)); - - xe_force_wake_put(gt_to_fw(gt), fw_ref); } diff --git a/drivers/gpu/drm/xe/xe_huc_debugfs.c b/drivers/gpu/drm/xe/xe_huc_debugfs.c index 3a888a40188b..80829967b3d7 100644 --- a/drivers/gpu/drm/xe/xe_huc_debugfs.c +++ b/drivers/gpu/drm/xe/xe_huc_debugfs.c @@ -7,11 +7,10 @@ #include <drm/drm_debugfs.h> #include <drm/drm_managed.h> +#include <drm/drm_print.h> -#include "xe_device.h" -#include "xe_gt.h" +#include "xe_gt_types.h" #include "xe_huc.h" -#include "xe_macros.h" #include "xe_pm.h" static struct xe_gt * @@ -37,9 +36,8 @@ static int huc_info(struct seq_file *m, void *data) struct xe_device *xe = huc_to_xe(huc); struct drm_printer p = drm_seq_file_printer(m); - xe_pm_runtime_get(xe); + guard(xe_pm_runtime)(xe); xe_huc_print_info(huc, &p); - xe_pm_runtime_put(xe); return 0; } diff --git a/drivers/gpu/drm/xe/xe_hw_engine.c b/drivers/gpu/drm/xe/xe_hw_engine.c index 6a9e2a4272dd..4d3ee5226e3a 100644 --- a/drivers/gpu/drm/xe/xe_hw_engine.c +++ b/drivers/gpu/drm/xe/xe_hw_engine.c @@ -33,7 +33,6 @@ #include "xe_hw_fence.h" #include "xe_irq.h" #include "xe_lrc.h" -#include "xe_macros.h" #include "xe_mmio.h" #include "xe_reg_sr.h" #include "xe_reg_whitelist.h" diff --git a/drivers/gpu/drm/xe/xe_hw_engine_class_sysfs.c b/drivers/gpu/drm/xe/xe_hw_engine_class_sysfs.c index 640950172088..3c65becb39ad 100644 --- a/drivers/gpu/drm/xe/xe_hw_engine_class_sysfs.c +++ b/drivers/gpu/drm/xe/xe_hw_engine_class_sysfs.c @@ -7,7 +7,7 @@ #include <linux/kobject.h> #include <linux/sysfs.h> -#include "xe_device.h" +#include "xe_device_types.h" #include "xe_gt.h" #include "xe_hw_engine_class_sysfs.h" #include "xe_pm.h" @@ -43,16 +43,14 @@ static ssize_t xe_hw_engine_class_sysfs_attr_show(struct kobject *kobj, { struct xe_device *xe = kobj_to_xe(kobj); struct kobj_attribute *kattr; - ssize_t ret = -EIO; kattr = container_of(attr, struct kobj_attribute, attr); if (kattr->show) { - xe_pm_runtime_get(xe); - ret = kattr->show(kobj, kattr, buf); - xe_pm_runtime_put(xe); + guard(xe_pm_runtime)(xe); + return kattr->show(kobj, kattr, buf); } - return ret; + return -EIO; } static ssize_t xe_hw_engine_class_sysfs_attr_store(struct kobject *kobj, @@ -62,16 +60,14 @@ static ssize_t xe_hw_engine_class_sysfs_attr_store(struct kobject *kobj, { struct xe_device *xe = kobj_to_xe(kobj); struct kobj_attribute *kattr; - ssize_t ret = -EIO; kattr = container_of(attr, struct kobj_attribute, attr); if (kattr->store) { - xe_pm_runtime_get(xe); - ret = kattr->store(kobj, kattr, buf, count); - xe_pm_runtime_put(xe); + guard(xe_pm_runtime)(xe); + return kattr->store(kobj, kattr, buf, count); } - return ret; + return -EIO; } static const struct sysfs_ops xe_hw_engine_class_sysfs_ops = { diff --git a/drivers/gpu/drm/xe/xe_hw_engine_group.c b/drivers/gpu/drm/xe/xe_hw_engine_group.c index fa4db5f23342..2ef33dfbe3a2 100644 --- a/drivers/gpu/drm/xe/xe_hw_engine_group.c +++ b/drivers/gpu/drm/xe/xe_hw_engine_group.c @@ -6,10 +6,12 @@ #include <drm/drm_managed.h> #include "xe_assert.h" -#include "xe_device.h" +#include "xe_device_types.h" #include "xe_exec_queue.h" #include "xe_gt.h" +#include "xe_gt_stats.h" #include "xe_hw_engine_group.h" +#include "xe_sync.h" #include "xe_vm.h" static void @@ -20,7 +22,8 @@ hw_engine_group_resume_lr_jobs_func(struct work_struct *w) int err; enum xe_hw_engine_group_execution_mode previous_mode; - err = xe_hw_engine_group_get_mode(group, EXEC_MODE_LR, &previous_mode); + err = xe_hw_engine_group_get_mode(group, EXEC_MODE_LR, &previous_mode, + NULL, 0); if (err) return; @@ -188,23 +191,39 @@ void xe_hw_engine_group_resume_faulting_lr_jobs(struct xe_hw_engine_group *group /** * xe_hw_engine_group_suspend_faulting_lr_jobs() - Suspend the faulting LR jobs of this group * @group: The hw engine group + * @has_deps: dma-fence job triggering suspend has dependencies * * Return: 0 on success, negative error code on error. */ -static int xe_hw_engine_group_suspend_faulting_lr_jobs(struct xe_hw_engine_group *group) +static int xe_hw_engine_group_suspend_faulting_lr_jobs(struct xe_hw_engine_group *group, + bool has_deps) { int err; struct xe_exec_queue *q; + struct xe_gt *gt = NULL; bool need_resume = false; + ktime_t start = xe_gt_stats_ktime_get(); lockdep_assert_held_write(&group->mode_sem); list_for_each_entry(q, &group->exec_queue_list, hw_engine_group_link) { + bool idle_skip_suspend; + if (!xe_vm_in_fault_mode(q->vm)) continue; - need_resume = true; + idle_skip_suspend = xe_exec_queue_idle_skip_suspend(q); + if (!idle_skip_suspend && has_deps) + return -EAGAIN; + + xe_gt_stats_incr(q->gt, XE_GT_STATS_ID_HW_ENGINE_GROUP_SUSPEND_LR_QUEUE_COUNT, 1); + if (idle_skip_suspend) + xe_gt_stats_incr(q->gt, + XE_GT_STATS_ID_HW_ENGINE_GROUP_SKIP_LR_QUEUE_COUNT, 1); + + need_resume |= !idle_skip_suspend; q->ops->suspend(q); + gt = q->gt; } list_for_each_entry(q, &group->exec_queue_list, hw_engine_group_link) { @@ -216,6 +235,12 @@ static int xe_hw_engine_group_suspend_faulting_lr_jobs(struct xe_hw_engine_group return err; } + if (gt) { + xe_gt_stats_incr(gt, + XE_GT_STATS_ID_HW_ENGINE_GROUP_SUSPEND_LR_QUEUE_US, + xe_gt_stats_ktime_us_delta(start)); + } + if (need_resume) xe_hw_engine_group_resume_faulting_lr_jobs(group); @@ -236,7 +261,9 @@ static int xe_hw_engine_group_wait_for_dma_fence_jobs(struct xe_hw_engine_group { long timeout; struct xe_exec_queue *q; + struct xe_gt *gt = NULL; struct dma_fence *fence; + ktime_t start = xe_gt_stats_ktime_get(); lockdep_assert_held_write(&group->mode_sem); @@ -244,18 +271,26 @@ static int xe_hw_engine_group_wait_for_dma_fence_jobs(struct xe_hw_engine_group if (xe_vm_in_lr_mode(q->vm)) continue; + xe_gt_stats_incr(q->gt, XE_GT_STATS_ID_HW_ENGINE_GROUP_WAIT_DMA_QUEUE_COUNT, 1); fence = xe_exec_queue_last_fence_get_for_resume(q, q->vm); timeout = dma_fence_wait(fence, false); dma_fence_put(fence); + gt = q->gt; if (timeout < 0) return -ETIME; } + if (gt) { + xe_gt_stats_incr(gt, + XE_GT_STATS_ID_HW_ENGINE_GROUP_WAIT_DMA_QUEUE_US, + xe_gt_stats_ktime_us_delta(start)); + } + return 0; } -static int switch_mode(struct xe_hw_engine_group *group) +static int switch_mode(struct xe_hw_engine_group *group, bool has_deps) { int err = 0; enum xe_hw_engine_group_execution_mode new_mode; @@ -265,7 +300,8 @@ static int switch_mode(struct xe_hw_engine_group *group) switch (group->cur_mode) { case EXEC_MODE_LR: new_mode = EXEC_MODE_DMA_FENCE; - err = xe_hw_engine_group_suspend_faulting_lr_jobs(group); + err = xe_hw_engine_group_suspend_faulting_lr_jobs(group, + has_deps); break; case EXEC_MODE_DMA_FENCE: new_mode = EXEC_MODE_LR; @@ -281,19 +317,36 @@ static int switch_mode(struct xe_hw_engine_group *group) return 0; } +static int wait_syncs(struct xe_sync_entry *syncs, int num_syncs) +{ + int err, i; + + for (i = 0; i < num_syncs; ++i) { + err = xe_sync_entry_wait(syncs + i); + if (err) + return err; + } + + return 0; +} + /** * xe_hw_engine_group_get_mode() - Get the group to execute in the new mode * @group: The hw engine group * @new_mode: The new execution mode * @previous_mode: Pointer to the previous mode provided for use by caller + * @syncs: Syncs from exec IOCTL + * @num_syncs: Number of syncs from exec IOCTL * * Return: 0 if successful, -EINTR if locking failed. */ int xe_hw_engine_group_get_mode(struct xe_hw_engine_group *group, enum xe_hw_engine_group_execution_mode new_mode, - enum xe_hw_engine_group_execution_mode *previous_mode) + enum xe_hw_engine_group_execution_mode *previous_mode, + struct xe_sync_entry *syncs, int num_syncs) __acquires(&group->mode_sem) { + bool has_deps = !!num_syncs; int err = down_read_interruptible(&group->mode_sem); if (err) @@ -303,15 +356,25 @@ __acquires(&group->mode_sem) if (new_mode != group->cur_mode) { up_read(&group->mode_sem); +retry: err = down_write_killable(&group->mode_sem); if (err) return err; if (new_mode != group->cur_mode) { - err = switch_mode(group); + err = switch_mode(group, has_deps); if (err) { up_write(&group->mode_sem); - return err; + + if (err != -EAGAIN) + return err; + + err = wait_syncs(syncs, num_syncs); + if (err) + return err; + + has_deps = false; + goto retry; } } downgrade_write(&group->mode_sem); diff --git a/drivers/gpu/drm/xe/xe_hw_engine_group.h b/drivers/gpu/drm/xe/xe_hw_engine_group.h index 797ee81acbf2..8b17ccd30b70 100644 --- a/drivers/gpu/drm/xe/xe_hw_engine_group.h +++ b/drivers/gpu/drm/xe/xe_hw_engine_group.h @@ -11,6 +11,7 @@ struct drm_device; struct xe_exec_queue; struct xe_gt; +struct xe_sync_entry; int xe_hw_engine_setup_groups(struct xe_gt *gt); @@ -19,7 +20,8 @@ void xe_hw_engine_group_del_exec_queue(struct xe_hw_engine_group *group, struct int xe_hw_engine_group_get_mode(struct xe_hw_engine_group *group, enum xe_hw_engine_group_execution_mode new_mode, - enum xe_hw_engine_group_execution_mode *previous_mode); + enum xe_hw_engine_group_execution_mode *previous_mode, + struct xe_sync_entry *syncs, int num_syncs); void xe_hw_engine_group_put(struct xe_hw_engine_group *group); enum xe_hw_engine_group_execution_mode diff --git a/drivers/gpu/drm/xe/xe_hw_fence.c b/drivers/gpu/drm/xe/xe_hw_fence.c index f6057456e460..ae8ed15b64c5 100644 --- a/drivers/gpu/drm/xe/xe_hw_fence.c +++ b/drivers/gpu/drm/xe/xe_hw_fence.c @@ -8,9 +8,7 @@ #include <linux/device.h> #include <linux/slab.h> -#include "xe_bo.h" -#include "xe_device.h" -#include "xe_gt.h" +#include "xe_device_types.h" #include "xe_hw_engine.h" #include "xe_macros.h" #include "xe_map.h" @@ -108,22 +106,6 @@ void xe_hw_fence_irq_run(struct xe_hw_fence_irq *irq) irq_work_queue(&irq->work); } -void xe_hw_fence_irq_stop(struct xe_hw_fence_irq *irq) -{ - spin_lock_irq(&irq->lock); - irq->enabled = false; - spin_unlock_irq(&irq->lock); -} - -void xe_hw_fence_irq_start(struct xe_hw_fence_irq *irq) -{ - spin_lock_irq(&irq->lock); - irq->enabled = true; - spin_unlock_irq(&irq->lock); - - irq_work_queue(&irq->work); -} - void xe_hw_fence_ctx_init(struct xe_hw_fence_ctx *ctx, struct xe_gt *gt, struct xe_hw_fence_irq *irq, const char *name) { diff --git a/drivers/gpu/drm/xe/xe_hw_fence.h b/drivers/gpu/drm/xe/xe_hw_fence.h index f13a1c4982c7..599492c13f80 100644 --- a/drivers/gpu/drm/xe/xe_hw_fence.h +++ b/drivers/gpu/drm/xe/xe_hw_fence.h @@ -17,8 +17,6 @@ void xe_hw_fence_module_exit(void); void xe_hw_fence_irq_init(struct xe_hw_fence_irq *irq); void xe_hw_fence_irq_finish(struct xe_hw_fence_irq *irq); void xe_hw_fence_irq_run(struct xe_hw_fence_irq *irq); -void xe_hw_fence_irq_stop(struct xe_hw_fence_irq *irq); -void xe_hw_fence_irq_start(struct xe_hw_fence_irq *irq); void xe_hw_fence_ctx_init(struct xe_hw_fence_ctx *ctx, struct xe_gt *gt, struct xe_hw_fence_irq *irq, const char *name); diff --git a/drivers/gpu/drm/xe/xe_hwmon.c b/drivers/gpu/drm/xe/xe_hwmon.c index 97879daeefc1..baf277955b33 100644 --- a/drivers/gpu/drm/xe/xe_hwmon.c +++ b/drivers/gpu/drm/xe/xe_hwmon.c @@ -39,10 +39,16 @@ enum xe_hwmon_reg_operation { REG_READ64, }; +#define MAX_VRAM_CHANNELS (16) + enum xe_hwmon_channel { CHANNEL_CARD, CHANNEL_PKG, CHANNEL_VRAM, + CHANNEL_MCTRL, + CHANNEL_PCIE, + CHANNEL_VRAM_N, + CHANNEL_VRAM_N_MAX = CHANNEL_VRAM_N + MAX_VRAM_CHANNELS, CHANNEL_MAX, }; @@ -53,6 +59,15 @@ enum xe_fan_channel { FAN_MAX, }; +enum xe_temp_limit { + TEMP_LIMIT_PKG_SHUTDOWN, + TEMP_LIMIT_PKG_CRIT, + TEMP_LIMIT_MEM_SHUTDOWN, + TEMP_LIMIT_PKG_MAX, + TEMP_LIMIT_MEM_CRIT, + TEMP_LIMIT_MAX +}; + /* Attribute index for powerX_xxx_interval sysfs entries */ enum sensor_attr_power { SENSOR_INDEX_PSYS_PL1, @@ -91,6 +106,12 @@ enum sensor_attr_power { */ #define PL_WRITE_MBX_TIMEOUT_MS (1) +/* Index of memory controller in READ_THERMAL_DATA output */ +#define TEMP_INDEX_MCTRL 2 + +/* Maximum characters in hwmon label name */ +#define MAX_LABEL_SIZE 16 + /** * struct xe_hwmon_energy_info - to accumulate energy */ @@ -112,6 +133,24 @@ struct xe_hwmon_fan_info { }; /** + * struct xe_hwmon_thermal_info - to store temperature data + */ +struct xe_hwmon_thermal_info { + union { + /** @limit: temperatures limits */ + u8 limit[TEMP_LIMIT_MAX]; + /** @data: temperature limits in dwords */ + u32 data[DIV_ROUND_UP(TEMP_LIMIT_MAX, sizeof(u32))]; + }; + /** @count: no of temperature sensors available for the platform */ + u8 count; + /** @value: signed value from each sensor */ + s8 value[U8_MAX]; + /** @vram_label: vram label names */ + char vram_label[MAX_VRAM_CHANNELS][MAX_LABEL_SIZE]; +}; + +/** * struct xe_hwmon - xe hwmon data structure */ struct xe_hwmon { @@ -137,7 +176,8 @@ struct xe_hwmon { u32 pl1_on_boot[CHANNEL_MAX]; /** @pl2_on_boot: power limit PL2 on boot */ u32 pl2_on_boot[CHANNEL_MAX]; - + /** @temp: Temperature info */ + struct xe_hwmon_thermal_info temp; }; static int xe_hwmon_pcode_read_power_limit(const struct xe_hwmon *hwmon, u32 attr, int channel, @@ -224,6 +264,8 @@ static struct xe_reg xe_hwmon_get_reg(struct xe_hwmon *hwmon, enum xe_hwmon_reg return BMG_PACKAGE_TEMPERATURE; else if (channel == CHANNEL_VRAM) return BMG_VRAM_TEMPERATURE; + else if (in_range(channel, CHANNEL_VRAM_N, CHANNEL_VRAM_N_MAX)) + return BMG_VRAM_TEMPERATURE_N(channel - CHANNEL_VRAM_N); } else if (xe->info.platform == XE_DG2) { if (channel == CHANNEL_PKG) return PCU_CR_PACKAGE_TEMPERATURE; @@ -502,7 +544,7 @@ xe_hwmon_power_max_interval_show(struct device *dev, struct device_attribute *at int ret = 0; - xe_pm_runtime_get(hwmon->xe); + guard(xe_pm_runtime)(hwmon->xe); mutex_lock(&hwmon->hwmon_lock); @@ -521,8 +563,6 @@ xe_hwmon_power_max_interval_show(struct device *dev, struct device_attribute *at mutex_unlock(&hwmon->hwmon_lock); - xe_pm_runtime_put(hwmon->xe); - x = REG_FIELD_GET(PWR_LIM_TIME_X, reg_val); y = REG_FIELD_GET(PWR_LIM_TIME_Y, reg_val); @@ -604,7 +644,7 @@ xe_hwmon_power_max_interval_store(struct device *dev, struct device_attribute *a rxy = REG_FIELD_PREP(PWR_LIM_TIME_X, x) | REG_FIELD_PREP(PWR_LIM_TIME_Y, y); - xe_pm_runtime_get(hwmon->xe); + guard(xe_pm_runtime)(hwmon->xe); mutex_lock(&hwmon->hwmon_lock); @@ -616,8 +656,6 @@ xe_hwmon_power_max_interval_store(struct device *dev, struct device_attribute *a mutex_unlock(&hwmon->hwmon_lock); - xe_pm_runtime_put(hwmon->xe); - return count; } @@ -681,8 +719,29 @@ static const struct attribute_group *hwmon_groups[] = { }; static const struct hwmon_channel_info * const hwmon_info[] = { - HWMON_CHANNEL_INFO(temp, HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL, - HWMON_T_INPUT | HWMON_T_LABEL), + HWMON_CHANNEL_INFO(temp, + HWMON_T_LABEL, + HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL | + HWMON_T_MAX, + HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL), HWMON_CHANNEL_INFO(power, HWMON_P_MAX | HWMON_P_RATED_MAX | HWMON_P_LABEL | HWMON_P_CRIT | HWMON_P_CAP, HWMON_P_MAX | HWMON_P_RATED_MAX | HWMON_P_LABEL | HWMON_P_CAP), @@ -693,6 +752,75 @@ static const struct hwmon_channel_info * const hwmon_info[] = { NULL }; +static int xe_hwmon_pcode_read_thermal_info(struct xe_hwmon *hwmon) +{ + struct xe_tile *root_tile = xe_device_get_root_tile(hwmon->xe); + u32 config = 0; + int ret; + + ret = xe_pcode_read(root_tile, PCODE_MBOX(PCODE_THERMAL_INFO, READ_THERMAL_LIMITS, 0), + &hwmon->temp.data[0], &hwmon->temp.data[1]); + if (ret) + return ret; + + drm_dbg(&hwmon->xe->drm, "thermal info read val 0x%x val1 0x%x\n", + hwmon->temp.data[0], hwmon->temp.data[1]); + + ret = xe_pcode_read(root_tile, PCODE_MBOX(PCODE_THERMAL_INFO, READ_THERMAL_CONFIG, 0), + &config, NULL); + if (ret) + return ret; + + drm_dbg(&hwmon->xe->drm, "thermal config count 0x%x\n", config); + hwmon->temp.count = REG_FIELD_GET(TEMP_MASK, config); + + return ret; +} + +static int get_mc_temp(struct xe_hwmon *hwmon, long *val) +{ + struct xe_tile *root_tile = xe_device_get_root_tile(hwmon->xe); + u32 *dword = (u32 *)hwmon->temp.value; + s32 average = 0; + int ret, i; + + for (i = 0; i < DIV_ROUND_UP(TEMP_LIMIT_MAX, sizeof(u32)); i++) { + ret = xe_pcode_read(root_tile, PCODE_MBOX(PCODE_THERMAL_INFO, READ_THERMAL_DATA, i), + (dword + i), NULL); + if (ret) + return ret; + drm_dbg(&hwmon->xe->drm, "thermal data for group %d val 0x%x\n", i, dword[i]); + } + + for (i = TEMP_INDEX_MCTRL; i < hwmon->temp.count - 1; i++) + average += hwmon->temp.value[i]; + + average /= (hwmon->temp.count - TEMP_INDEX_MCTRL - 1); + *val = average * MILLIDEGREE_PER_DEGREE; + return 0; +} + +static int get_pcie_temp(struct xe_hwmon *hwmon, long *val) +{ + struct xe_tile *root_tile = xe_device_get_root_tile(hwmon->xe); + u32 data = 0; + int ret; + + ret = xe_pcode_read(root_tile, PCODE_MBOX(PCODE_THERMAL_INFO, READ_THERMAL_DATA, + PCIE_SENSOR_GROUP_ID), &data, NULL); + if (ret) + return ret; + + /* Sensor offset is different for G21 */ + if (hwmon->xe->info.subplatform != XE_SUBPLATFORM_BATTLEMAGE_G21) + data = REG_FIELD_GET(PCIE_SENSOR_MASK, data); + + data = REG_FIELD_GET(TEMP_MASK, data); + *val = (s8)data * MILLIDEGREE_PER_DEGREE; + + return 0; +} + /* I1 is exposed as power_crit or as curr_crit depending on bit 31 */ static int xe_hwmon_pcode_read_i1(const struct xe_hwmon *hwmon, u32 *uval) { @@ -787,13 +915,77 @@ static void xe_hwmon_get_voltage(struct xe_hwmon *hwmon, int channel, long *valu *value = DIV_ROUND_CLOSEST(REG_FIELD_GET(VOLTAGE_MASK, reg_val) * 2500, SF_VOLTAGE); } +static inline bool is_vram_ch_available(struct xe_hwmon *hwmon, int channel) +{ + struct xe_mmio *mmio = xe_root_tile_mmio(hwmon->xe); + int vram_id = channel - CHANNEL_VRAM_N; + struct xe_reg vram_reg; + + vram_reg = xe_hwmon_get_reg(hwmon, REG_TEMP, channel); + if (!xe_reg_is_valid(vram_reg) || !xe_mmio_read32(mmio, vram_reg)) + return false; + + /* Create label only for available vram channel */ + sprintf(hwmon->temp.vram_label[vram_id], "vram_ch_%d", vram_id); + return true; +} + static umode_t xe_hwmon_temp_is_visible(struct xe_hwmon *hwmon, u32 attr, int channel) { switch (attr) { + case hwmon_temp_emergency: + switch (channel) { + case CHANNEL_PKG: + return hwmon->temp.limit[TEMP_LIMIT_PKG_SHUTDOWN] ? 0444 : 0; + case CHANNEL_VRAM: + return hwmon->temp.limit[TEMP_LIMIT_MEM_SHUTDOWN] ? 0444 : 0; + case CHANNEL_MCTRL: + case CHANNEL_PCIE: + return hwmon->temp.count ? 0444 : 0; + case CHANNEL_VRAM_N...CHANNEL_VRAM_N_MAX: + return (is_vram_ch_available(hwmon, channel) && + hwmon->temp.limit[TEMP_LIMIT_MEM_SHUTDOWN]) ? 0444 : 0; + default: + return 0; + } + case hwmon_temp_crit: + switch (channel) { + case CHANNEL_PKG: + return hwmon->temp.limit[TEMP_LIMIT_PKG_CRIT] ? 0444 : 0; + case CHANNEL_VRAM: + return hwmon->temp.limit[TEMP_LIMIT_MEM_CRIT] ? 0444 : 0; + case CHANNEL_MCTRL: + case CHANNEL_PCIE: + return hwmon->temp.count ? 0444 : 0; + case CHANNEL_VRAM_N...CHANNEL_VRAM_N_MAX: + return (is_vram_ch_available(hwmon, channel) && + hwmon->temp.limit[TEMP_LIMIT_MEM_CRIT]) ? 0444 : 0; + default: + return 0; + } + case hwmon_temp_max: + switch (channel) { + case CHANNEL_PKG: + return hwmon->temp.limit[TEMP_LIMIT_PKG_MAX] ? 0444 : 0; + default: + return 0; + } case hwmon_temp_input: case hwmon_temp_label: - return xe_reg_is_valid(xe_hwmon_get_reg(hwmon, REG_TEMP, channel)) ? 0444 : 0; + switch (channel) { + case CHANNEL_PKG: + case CHANNEL_VRAM: + return xe_reg_is_valid(xe_hwmon_get_reg(hwmon, REG_TEMP, + channel)) ? 0444 : 0; + case CHANNEL_MCTRL: + case CHANNEL_PCIE: + return hwmon->temp.count ? 0444 : 0; + case CHANNEL_VRAM_N...CHANNEL_VRAM_N_MAX: + return is_vram_ch_available(hwmon, channel) ? 0444 : 0; + default: + return 0; + } default: return 0; } @@ -807,11 +999,67 @@ xe_hwmon_temp_read(struct xe_hwmon *hwmon, u32 attr, int channel, long *val) switch (attr) { case hwmon_temp_input: - reg_val = xe_mmio_read32(mmio, xe_hwmon_get_reg(hwmon, REG_TEMP, channel)); + switch (channel) { + case CHANNEL_PKG: + case CHANNEL_VRAM: + reg_val = xe_mmio_read32(mmio, xe_hwmon_get_reg(hwmon, REG_TEMP, channel)); - /* HW register value is in degrees Celsius, convert to millidegrees. */ - *val = REG_FIELD_GET(TEMP_MASK, reg_val) * MILLIDEGREE_PER_DEGREE; - return 0; + /* HW register value is in degrees Celsius, convert to millidegrees. */ + *val = REG_FIELD_GET(TEMP_MASK, reg_val) * MILLIDEGREE_PER_DEGREE; + return 0; + case CHANNEL_MCTRL: + return get_mc_temp(hwmon, val); + case CHANNEL_PCIE: + return get_pcie_temp(hwmon, val); + case CHANNEL_VRAM_N...CHANNEL_VRAM_N_MAX: + reg_val = xe_mmio_read32(mmio, xe_hwmon_get_reg(hwmon, REG_TEMP, channel)); + /* + * This temperature format is 24 bit [31:8] signed integer and 8 bit + * [7:0] fraction. + */ + *val = (s32)(REG_FIELD_GET(TEMP_MASK_VRAM_N, reg_val)) * + (REG_FIELD_GET(TEMP_SIGN_MASK, reg_val) ? -1 : 1) * + MILLIDEGREE_PER_DEGREE; + return 0; + default: + return -EOPNOTSUPP; + } + case hwmon_temp_emergency: + switch (channel) { + case CHANNEL_PKG: + case CHANNEL_MCTRL: + case CHANNEL_PCIE: + *val = hwmon->temp.limit[TEMP_LIMIT_PKG_SHUTDOWN] * MILLIDEGREE_PER_DEGREE; + return 0; + case CHANNEL_VRAM: + case CHANNEL_VRAM_N...CHANNEL_VRAM_N_MAX: + *val = hwmon->temp.limit[TEMP_LIMIT_MEM_SHUTDOWN] * MILLIDEGREE_PER_DEGREE; + return 0; + default: + return -EOPNOTSUPP; + } + case hwmon_temp_crit: + switch (channel) { + case CHANNEL_PKG: + case CHANNEL_MCTRL: + case CHANNEL_PCIE: + *val = hwmon->temp.limit[TEMP_LIMIT_PKG_CRIT] * MILLIDEGREE_PER_DEGREE; + return 0; + case CHANNEL_VRAM: + case CHANNEL_VRAM_N...CHANNEL_VRAM_N_MAX: + *val = hwmon->temp.limit[TEMP_LIMIT_MEM_CRIT] * MILLIDEGREE_PER_DEGREE; + return 0; + default: + return -EOPNOTSUPP; + } + case hwmon_temp_max: + switch (channel) { + case CHANNEL_PKG: + *val = hwmon->temp.limit[TEMP_LIMIT_PKG_MAX] * MILLIDEGREE_PER_DEGREE; + return 0; + default: + return -EOPNOTSUPP; + } default: return -EOPNOTSUPP; } @@ -1124,37 +1372,25 @@ xe_hwmon_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long *val) { struct xe_hwmon *hwmon = dev_get_drvdata(dev); - int ret; - xe_pm_runtime_get(hwmon->xe); + guard(xe_pm_runtime)(hwmon->xe); switch (type) { case hwmon_temp: - ret = xe_hwmon_temp_read(hwmon, attr, channel, val); - break; + return xe_hwmon_temp_read(hwmon, attr, channel, val); case hwmon_power: - ret = xe_hwmon_power_read(hwmon, attr, channel, val); - break; + return xe_hwmon_power_read(hwmon, attr, channel, val); case hwmon_curr: - ret = xe_hwmon_curr_read(hwmon, attr, channel, val); - break; + return xe_hwmon_curr_read(hwmon, attr, channel, val); case hwmon_in: - ret = xe_hwmon_in_read(hwmon, attr, channel, val); - break; + return xe_hwmon_in_read(hwmon, attr, channel, val); case hwmon_energy: - ret = xe_hwmon_energy_read(hwmon, attr, channel, val); - break; + return xe_hwmon_energy_read(hwmon, attr, channel, val); case hwmon_fan: - ret = xe_hwmon_fan_read(hwmon, attr, channel, val); - break; + return xe_hwmon_fan_read(hwmon, attr, channel, val); default: - ret = -EOPNOTSUPP; - break; + return -EOPNOTSUPP; } - - xe_pm_runtime_put(hwmon->xe); - - return ret; } static int @@ -1162,37 +1398,37 @@ xe_hwmon_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long val) { struct xe_hwmon *hwmon = dev_get_drvdata(dev); - int ret; - xe_pm_runtime_get(hwmon->xe); + guard(xe_pm_runtime)(hwmon->xe); switch (type) { case hwmon_power: - ret = xe_hwmon_power_write(hwmon, attr, channel, val); - break; + return xe_hwmon_power_write(hwmon, attr, channel, val); case hwmon_curr: - ret = xe_hwmon_curr_write(hwmon, attr, channel, val); - break; + return xe_hwmon_curr_write(hwmon, attr, channel, val); default: - ret = -EOPNOTSUPP; - break; + return -EOPNOTSUPP; } - - xe_pm_runtime_put(hwmon->xe); - - return ret; } static int xe_hwmon_read_label(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, const char **str) { + struct xe_hwmon *hwmon = dev_get_drvdata(dev); + switch (type) { case hwmon_temp: if (channel == CHANNEL_PKG) *str = "pkg"; else if (channel == CHANNEL_VRAM) *str = "vram"; + else if (channel == CHANNEL_MCTRL) + *str = "mctrl"; + else if (channel == CHANNEL_PCIE) + *str = "pcie"; + else if (in_range(channel, CHANNEL_VRAM_N, CHANNEL_VRAM_N_MAX)) + *str = hwmon->temp.vram_label[channel - CHANNEL_VRAM_N]; return 0; case hwmon_power: case hwmon_energy: @@ -1287,6 +1523,9 @@ xe_hwmon_get_preregistration_info(struct xe_hwmon *hwmon) for (channel = 0; channel < FAN_MAX; channel++) if (xe_hwmon_is_visible(hwmon, hwmon_fan, hwmon_fan_input, channel)) xe_hwmon_fan_input_read(hwmon, channel, &fan_speed); + + if (hwmon->xe->info.has_mbx_thermal_info && xe_hwmon_pcode_read_thermal_info(hwmon)) + drm_warn(&hwmon->xe->drm, "Thermal mailbox not supported by card firmware\n"); } int xe_hwmon_register(struct xe_device *xe) diff --git a/drivers/gpu/drm/xe/xe_i2c.c b/drivers/gpu/drm/xe/xe_i2c.c index 0b5452be0c87..1e1fb72e49bf 100644 --- a/drivers/gpu/drm/xe/xe_i2c.c +++ b/drivers/gpu/drm/xe/xe_i2c.c @@ -5,6 +5,7 @@ * Copyright (C) 2025 Intel Corporation. */ +#include <drm/drm_print.h> #include <linux/array_size.h> #include <linux/container_of.h> #include <linux/device.h> @@ -26,11 +27,11 @@ #include "regs/xe_i2c_regs.h" #include "regs/xe_irq_regs.h" -#include "xe_device.h" #include "xe_device_types.h" #include "xe_i2c.h" #include "xe_mmio.h" -#include "xe_platform_types.h" +#include "xe_sriov.h" +#include "xe_survivability_mode.h" /** * DOC: Xe I2C devices @@ -213,11 +214,13 @@ static const struct irq_domain_ops xe_i2c_irq_ops = { .map = xe_i2c_irq_map, }; -static int xe_i2c_create_irq(struct xe_i2c *i2c) +static int xe_i2c_create_irq(struct xe_device *xe) { + struct xe_i2c *i2c = xe->i2c; struct irq_domain *domain; - if (!(i2c->ep.capabilities & XE_I2C_EP_CAP_IRQ)) + if (!(i2c->ep.capabilities & XE_I2C_EP_CAP_IRQ) || + xe_survivability_mode_is_boot_enabled(xe)) return 0; domain = irq_domain_create_linear(dev_fwnode(i2c->drm_dev), 1, &xe_i2c_irq_ops, NULL); @@ -319,7 +322,7 @@ int xe_i2c_probe(struct xe_device *xe) struct xe_i2c *i2c; int ret; - if (xe->info.platform != XE_BATTLEMAGE) + if (!xe->info.has_i2c) return 0; if (IS_SRIOV_VF(xe)) @@ -351,7 +354,7 @@ int xe_i2c_probe(struct xe_device *xe) if (ret) return ret; - ret = xe_i2c_create_irq(i2c); + ret = xe_i2c_create_irq(xe); if (ret) goto err_unregister_notifier; diff --git a/drivers/gpu/drm/xe/xe_irq.c b/drivers/gpu/drm/xe/xe_irq.c index 024e13e606ec..7560a45f7f64 100644 --- a/drivers/gpu/drm/xe/xe_irq.c +++ b/drivers/gpu/drm/xe/xe_irq.c @@ -10,7 +10,6 @@ #include <drm/drm_managed.h> #include "display/xe_display.h" -#include "regs/xe_guc_regs.h" #include "regs/xe_irq_regs.h" #include "xe_device.h" #include "xe_drv.h" @@ -21,6 +20,7 @@ #include "xe_hw_error.h" #include "xe_i2c.h" #include "xe_memirq.h" +#include "xe_mert.h" #include "xe_mmio.h" #include "xe_pxp.h" #include "xe_sriov.h" @@ -525,6 +525,7 @@ static irqreturn_t dg1_irq_handler(int irq, void *arg) xe_heci_csc_irq_handler(xe, master_ctl); xe_display_irq_handler(xe, master_ctl); xe_i2c_irq_handler(xe, master_ctl); + xe_mert_irq_handler(xe, master_ctl); gu_misc_iir = gu_misc_irq_ack(xe, master_ctl); } } diff --git a/drivers/gpu/drm/xe/xe_late_bind_fw_types.h b/drivers/gpu/drm/xe/xe_late_bind_fw_types.h index 0f5da89ce98b..2a8a985c37e7 100644 --- a/drivers/gpu/drm/xe/xe_late_bind_fw_types.h +++ b/drivers/gpu/drm/xe/xe_late_bind_fw_types.h @@ -15,10 +15,12 @@ #define XE_LB_MAX_PAYLOAD_SIZE SZ_4K /** - * xe_late_bind_fw_id - enum to determine late binding fw index + * enum xe_late_bind_fw_id - enum to determine late binding fw index */ enum xe_late_bind_fw_id { + /** @XE_LB_FW_FAN_CONTROL: Fan control */ XE_LB_FW_FAN_CONTROL = 0, + /** @XE_LB_FW_MAX_ID: Number of IDs */ XE_LB_FW_MAX_ID }; diff --git a/drivers/gpu/drm/xe/xe_lmtt.c b/drivers/gpu/drm/xe/xe_lmtt.c index 4dc1de482eee..2077e1ef8b43 100644 --- a/drivers/gpu/drm/xe/xe_lmtt.c +++ b/drivers/gpu/drm/xe/xe_lmtt.c @@ -8,15 +8,18 @@ #include <drm/drm_managed.h> #include "regs/xe_gt_regs.h" +#include "regs/xe_mert_regs.h" #include "xe_assert.h" #include "xe_bo.h" #include "xe_tlb_inval.h" #include "xe_lmtt.h" #include "xe_map.h" +#include "xe_mert.h" #include "xe_mmio.h" #include "xe_res_cursor.h" #include "xe_sriov.h" +#include "xe_tile.h" #include "xe_tile_sriov_printk.h" /** @@ -196,16 +199,22 @@ static void lmtt_setup_dir_ptr(struct xe_lmtt *lmtt) struct xe_device *xe = tile_to_xe(tile); dma_addr_t offset = xe_bo_main_addr(lmtt->pd->bo, XE_PAGE_SIZE); struct xe_gt *gt; + u32 config; u8 id; lmtt_debug(lmtt, "DIR offset %pad\n", &offset); lmtt_assert(lmtt, xe_bo_is_vram(lmtt->pd->bo)); lmtt_assert(lmtt, IS_ALIGNED(offset, SZ_64K)); + config = LMEM_EN | REG_FIELD_PREP(LMTT_DIR_PTR, offset / SZ_64K); + for_each_gt_on_tile(gt, tile, id) xe_mmio_write32(>->mmio, GRAPHICS_VER(xe) >= 20 ? XE2_LMEM_CFG : LMEM_CFG, - LMEM_EN | REG_FIELD_PREP(LMTT_DIR_PTR, offset / SZ_64K)); + config); + + if (xe_device_has_mert(xe) && xe_tile_is_root(tile)) + xe_mmio_write32(&tile->mmio, MERT_LMEM_CFG, config); } /** @@ -262,19 +271,29 @@ static int lmtt_invalidate_hw(struct xe_lmtt *lmtt) * @lmtt: the &xe_lmtt to invalidate * * Send requests to all GuCs on this tile to invalidate all TLBs. + * If the platform has a standalone MERT, also invalidate MERT's TLB. * * This function should be called only when running as a PF driver. */ void xe_lmtt_invalidate_hw(struct xe_lmtt *lmtt) { + struct xe_tile *tile = lmtt_to_tile(lmtt); + struct xe_device *xe = lmtt_to_xe(lmtt); int err; - lmtt_assert(lmtt, IS_SRIOV_PF(lmtt_to_xe(lmtt))); + lmtt_assert(lmtt, IS_SRIOV_PF(xe)); err = lmtt_invalidate_hw(lmtt); if (err) - xe_tile_sriov_err(lmtt_to_tile(lmtt), "LMTT invalidation failed (%pe)", + xe_tile_sriov_err(tile, "LMTT invalidation failed (%pe)", ERR_PTR(err)); + + if (xe_device_has_mert(xe) && xe_tile_is_root(tile)) { + err = xe_mert_invalidate_lmtt(xe); + if (err) + xe_tile_sriov_err(tile, "MERT LMTT invalidation failed (%pe)", + ERR_PTR(err)); + } } static void lmtt_write_pte(struct xe_lmtt *lmtt, struct xe_lmtt_pt *pt, diff --git a/drivers/gpu/drm/xe/xe_lrc.c b/drivers/gpu/drm/xe/xe_lrc.c index b5083c99dd50..f4f31bc240d9 100644 --- a/drivers/gpu/drm/xe/xe_lrc.c +++ b/drivers/gpu/drm/xe/xe_lrc.c @@ -44,6 +44,11 @@ #define LRC_INDIRECT_CTX_BO_SIZE SZ_4K #define LRC_INDIRECT_RING_STATE_SIZE SZ_4K +#define LRC_PRIORITY GENMASK_ULL(10, 9) +#define LRC_PRIORITY_LOW 0 +#define LRC_PRIORITY_NORMAL 1 +#define LRC_PRIORITY_HIGH 2 + /* * Layout of the LRC and associated data allocated as * lrc->bo: @@ -91,13 +96,19 @@ gt_engine_needs_indirect_ctx(struct xe_gt *gt, enum xe_engine_class class) return false; } -size_t xe_gt_lrc_size(struct xe_gt *gt, enum xe_engine_class class) +/** + * xe_gt_lrc_hang_replay_size() - Hang replay size + * @gt: The GT + * @class: Hardware engine class + * + * Determine size of GPU hang replay state for a GT and hardware engine class. + * + * Return: Size of GPU hang replay size + */ +size_t xe_gt_lrc_hang_replay_size(struct xe_gt *gt, enum xe_engine_class class) { struct xe_device *xe = gt_to_xe(gt); - size_t size; - - /* Per-process HW status page (PPHWSP) */ - size = LRC_PPHWSP_SIZE; + size_t size = 0; /* Engine context image */ switch (class) { @@ -123,11 +134,18 @@ size_t xe_gt_lrc_size(struct xe_gt *gt, enum xe_engine_class class) size += 1 * SZ_4K; } + return size; +} + +size_t xe_gt_lrc_size(struct xe_gt *gt, enum xe_engine_class class) +{ + size_t size = xe_gt_lrc_hang_replay_size(gt, class); + /* Add indirect ring state page */ if (xe_gt_has_indirect_ring_state(gt)) size += LRC_INDIRECT_RING_STATE_SIZE; - return size; + return size + LRC_PPHWSP_SIZE; } /* @@ -839,7 +857,7 @@ u32 xe_lrc_ctx_timestamp_udw_ggtt_addr(struct xe_lrc *lrc) * * Returns: ctx timestamp value */ -u64 xe_lrc_ctx_timestamp(struct xe_lrc *lrc) +static u64 xe_lrc_ctx_timestamp(struct xe_lrc *lrc) { struct xe_device *xe = lrc_to_xe(lrc); struct iosys_map map; @@ -1050,6 +1068,9 @@ static ssize_t setup_utilization_wa(struct xe_lrc *lrc, { u32 *cmd = batch; + if (IS_SRIOV_VF(gt_to_xe(lrc->gt))) + return 0; + if (xe_gt_WARN_ON(lrc->gt, max_len < 12)) return -ENOSPC; @@ -1386,8 +1407,33 @@ setup_indirect_ctx(struct xe_lrc *lrc, struct xe_hw_engine *hwe) return 0; } +static u8 xe_multi_queue_prio_to_lrc(struct xe_lrc *lrc, enum xe_multi_queue_priority priority) +{ + struct xe_device *xe = gt_to_xe(lrc->gt); + + xe_assert(xe, (priority >= XE_MULTI_QUEUE_PRIORITY_LOW && + priority <= XE_MULTI_QUEUE_PRIORITY_HIGH)); + + /* xe_multi_queue_priority is directly mapped to LRC priority values */ + return priority; +} + +/** + * xe_lrc_set_multi_queue_priority() - Set multi queue priority in LRC + * @lrc: Logical Ring Context + * @priority: Multi queue priority of the exec queue + * + * Convert @priority to LRC multi queue priority and update the @lrc descriptor + */ +void xe_lrc_set_multi_queue_priority(struct xe_lrc *lrc, enum xe_multi_queue_priority priority) +{ + lrc->desc &= ~LRC_PRIORITY; + lrc->desc |= FIELD_PREP(LRC_PRIORITY, xe_multi_queue_prio_to_lrc(lrc, priority)); +} + static int xe_lrc_init(struct xe_lrc *lrc, struct xe_hw_engine *hwe, - struct xe_vm *vm, u32 ring_size, u16 msix_vec, + struct xe_vm *vm, void *replay_state, u32 ring_size, + u16 msix_vec, u32 init_flags) { struct xe_gt *gt = hwe->gt; @@ -1402,6 +1448,7 @@ static int xe_lrc_init(struct xe_lrc *lrc, struct xe_hw_engine *hwe, kref_init(&lrc->refcount); lrc->gt = gt; + lrc->replay_size = xe_gt_lrc_hang_replay_size(gt, hwe->class); lrc->size = lrc_size; lrc->flags = 0; lrc->ring.size = ring_size; @@ -1438,11 +1485,14 @@ static int xe_lrc_init(struct xe_lrc *lrc, struct xe_hw_engine *hwe, * scratch. */ map = __xe_lrc_pphwsp_map(lrc); - if (gt->default_lrc[hwe->class]) { + if (gt->default_lrc[hwe->class] || replay_state) { xe_map_memset(xe, &map, 0, 0, LRC_PPHWSP_SIZE); /* PPHWSP */ xe_map_memcpy_to(xe, &map, LRC_PPHWSP_SIZE, gt->default_lrc[hwe->class] + LRC_PPHWSP_SIZE, lrc_size - LRC_PPHWSP_SIZE); + if (replay_state) + xe_map_memcpy_to(xe, &map, LRC_PPHWSP_SIZE, + replay_state, lrc->replay_size); } else { void *init_data = empty_lrc_data(hwe); @@ -1550,6 +1600,7 @@ err_lrc_finish: * xe_lrc_create - Create a LRC * @hwe: Hardware Engine * @vm: The VM (address space) + * @replay_state: GPU hang replay state * @ring_size: LRC ring size * @msix_vec: MSI-X interrupt vector (for platforms that support it) * @flags: LRC initialization flags @@ -1560,7 +1611,7 @@ err_lrc_finish: * upon failure. */ struct xe_lrc *xe_lrc_create(struct xe_hw_engine *hwe, struct xe_vm *vm, - u32 ring_size, u16 msix_vec, u32 flags) + void *replay_state, u32 ring_size, u16 msix_vec, u32 flags) { struct xe_lrc *lrc; int err; @@ -1569,7 +1620,7 @@ struct xe_lrc *xe_lrc_create(struct xe_hw_engine *hwe, struct xe_vm *vm, if (!lrc) return ERR_PTR(-ENOMEM); - err = xe_lrc_init(lrc, hwe, vm, ring_size, msix_vec, flags); + err = xe_lrc_init(lrc, hwe, vm, replay_state, ring_size, msix_vec, flags); if (err) { kfree(lrc); return ERR_PTR(err); @@ -2235,6 +2286,8 @@ struct xe_lrc_snapshot *xe_lrc_snapshot_capture(struct xe_lrc *lrc) snapshot->lrc_bo = xe_bo_get(lrc->bo); snapshot->lrc_offset = xe_lrc_pphwsp_offset(lrc); snapshot->lrc_size = lrc->size; + snapshot->replay_offset = 0; + snapshot->replay_size = lrc->replay_size; snapshot->lrc_snapshot = NULL; snapshot->ctx_timestamp = lower_32_bits(xe_lrc_ctx_timestamp(lrc)); snapshot->ctx_job_timestamp = xe_lrc_ctx_job_timestamp(lrc); @@ -2305,6 +2358,9 @@ void xe_lrc_snapshot_print(struct xe_lrc_snapshot *snapshot, struct drm_printer } drm_printf(p, "\n\t[HWCTX].length: 0x%lx\n", snapshot->lrc_size - LRC_PPHWSP_SIZE); + drm_printf(p, "\n\t[HWCTX].replay_offset: 0x%lx\n", snapshot->replay_offset); + drm_printf(p, "\n\t[HWCTX].replay_length: 0x%lx\n", snapshot->replay_size); + drm_puts(p, "\t[HWCTX].data: "); for (; i < snapshot->lrc_size; i += sizeof(u32)) { u32 *val = snapshot->lrc_snapshot + i; @@ -2353,35 +2409,31 @@ static int get_ctx_timestamp(struct xe_lrc *lrc, u32 engine_id, u64 *reg_ctx_ts) } /** - * xe_lrc_update_timestamp() - Update ctx timestamp + * xe_lrc_timestamp() - Current ctx timestamp * @lrc: Pointer to the lrc. - * @old_ts: Old timestamp value * - * Populate @old_ts current saved ctx timestamp, read new ctx timestamp and - * update saved value. With support for active contexts, the calculation may be - * slightly racy, so follow a read-again logic to ensure that the context is - * still active before returning the right timestamp. + * Return latest ctx timestamp. With support for active contexts, the + * calculation may bb slightly racy, so follow a read-again logic to ensure that + * the context is still active before returning the right timestamp. * * Returns: New ctx timestamp value */ -u64 xe_lrc_update_timestamp(struct xe_lrc *lrc, u64 *old_ts) +u64 xe_lrc_timestamp(struct xe_lrc *lrc) { - u64 lrc_ts, reg_ts; + u64 lrc_ts, reg_ts, new_ts; u32 engine_id; - *old_ts = lrc->ctx_timestamp; - lrc_ts = xe_lrc_ctx_timestamp(lrc); /* CTX_TIMESTAMP mmio read is invalid on VF, so return the LRC value */ if (IS_SRIOV_VF(lrc_to_xe(lrc))) { - lrc->ctx_timestamp = lrc_ts; + new_ts = lrc_ts; goto done; } if (lrc_ts == CONTEXT_ACTIVE) { engine_id = xe_lrc_engine_id(lrc); if (!get_ctx_timestamp(lrc, engine_id, ®_ts)) - lrc->ctx_timestamp = reg_ts; + new_ts = reg_ts; /* read lrc again to ensure context is still active */ lrc_ts = xe_lrc_ctx_timestamp(lrc); @@ -2392,9 +2444,27 @@ u64 xe_lrc_update_timestamp(struct xe_lrc *lrc, u64 *old_ts) * be a separate if condition. */ if (lrc_ts != CONTEXT_ACTIVE) - lrc->ctx_timestamp = lrc_ts; + new_ts = lrc_ts; done: + return new_ts; +} + +/** + * xe_lrc_update_timestamp() - Update ctx timestamp + * @lrc: Pointer to the lrc. + * @old_ts: Old timestamp value + * + * Populate @old_ts current saved ctx timestamp, read new ctx timestamp and + * update saved value. + * + * Returns: New ctx timestamp value + */ +u64 xe_lrc_update_timestamp(struct xe_lrc *lrc, u64 *old_ts) +{ + *old_ts = lrc->ctx_timestamp; + lrc->ctx_timestamp = xe_lrc_timestamp(lrc); + trace_xe_lrc_update_timestamp(lrc, *old_ts); return lrc->ctx_timestamp; diff --git a/drivers/gpu/drm/xe/xe_lrc.h b/drivers/gpu/drm/xe/xe_lrc.h index 2fb628da5c43..c307a3fd9ea2 100644 --- a/drivers/gpu/drm/xe/xe_lrc.h +++ b/drivers/gpu/drm/xe/xe_lrc.h @@ -13,6 +13,7 @@ struct drm_printer; struct xe_bb; struct xe_device; struct xe_exec_queue; +enum xe_multi_queue_priority; enum xe_engine_class; struct xe_gt; struct xe_hw_engine; @@ -23,6 +24,7 @@ struct xe_lrc_snapshot { struct xe_bo *lrc_bo; void *lrc_snapshot; unsigned long lrc_size, lrc_offset; + unsigned long replay_size, replay_offset; u32 context_desc; u32 ring_addr; @@ -49,7 +51,7 @@ struct xe_lrc_snapshot { #define XE_LRC_CREATE_USER_CTX BIT(2) struct xe_lrc *xe_lrc_create(struct xe_hw_engine *hwe, struct xe_vm *vm, - u32 ring_size, u16 msix_vec, u32 flags); + void *replay_state, u32 ring_size, u16 msix_vec, u32 flags); void xe_lrc_destroy(struct kref *ref); /** @@ -86,6 +88,7 @@ static inline size_t xe_lrc_ring_size(void) return SZ_16K; } +size_t xe_gt_lrc_hang_replay_size(struct xe_gt *gt, enum xe_engine_class class); size_t xe_gt_lrc_size(struct xe_gt *gt, enum xe_engine_class class); u32 xe_lrc_pphwsp_offset(struct xe_lrc *lrc); u32 xe_lrc_regs_offset(struct xe_lrc *lrc); @@ -133,6 +136,8 @@ void xe_lrc_dump_default(struct drm_printer *p, u32 *xe_lrc_emit_hwe_state_instructions(struct xe_exec_queue *q, u32 *cs); +void xe_lrc_set_multi_queue_priority(struct xe_lrc *lrc, enum xe_multi_queue_priority priority); + struct xe_lrc_snapshot *xe_lrc_snapshot_capture(struct xe_lrc *lrc); void xe_lrc_snapshot_capture_delayed(struct xe_lrc_snapshot *snapshot); void xe_lrc_snapshot_print(struct xe_lrc_snapshot *snapshot, struct drm_printer *p); @@ -140,7 +145,6 @@ void xe_lrc_snapshot_free(struct xe_lrc_snapshot *snapshot); u32 xe_lrc_ctx_timestamp_ggtt_addr(struct xe_lrc *lrc); u32 xe_lrc_ctx_timestamp_udw_ggtt_addr(struct xe_lrc *lrc); -u64 xe_lrc_ctx_timestamp(struct xe_lrc *lrc); u32 xe_lrc_ctx_job_timestamp_ggtt_addr(struct xe_lrc *lrc); u32 xe_lrc_ctx_job_timestamp(struct xe_lrc *lrc); int xe_lrc_setup_wa_bb_with_scratch(struct xe_lrc *lrc, struct xe_hw_engine *hwe, @@ -160,4 +164,6 @@ int xe_lrc_setup_wa_bb_with_scratch(struct xe_lrc *lrc, struct xe_hw_engine *hwe */ u64 xe_lrc_update_timestamp(struct xe_lrc *lrc, u64 *old_ts); +u64 xe_lrc_timestamp(struct xe_lrc *lrc); + #endif diff --git a/drivers/gpu/drm/xe/xe_lrc_types.h b/drivers/gpu/drm/xe/xe_lrc_types.h index e9883706e004..a4373d280c39 100644 --- a/drivers/gpu/drm/xe/xe_lrc_types.h +++ b/drivers/gpu/drm/xe/xe_lrc_types.h @@ -25,6 +25,9 @@ struct xe_lrc { /** @size: size of the lrc and optional indirect ring state */ u32 size; + /** @replay_size: Size LRC needed for replaying a hang */ + u32 replay_size; + /** @gt: gt which this LRC belongs to */ struct xe_gt *gt; diff --git a/drivers/gpu/drm/xe/xe_memirq.c b/drivers/gpu/drm/xe/xe_memirq.c index b0c7ce0a5d1e..811e07136efb 100644 --- a/drivers/gpu/drm/xe/xe_memirq.c +++ b/drivers/gpu/drm/xe/xe_memirq.c @@ -7,7 +7,6 @@ #include "regs/xe_guc_regs.h" #include "regs/xe_irq_regs.h" -#include "regs/xe_regs.h" #include "xe_assert.h" #include "xe_bo.h" @@ -16,7 +15,6 @@ #include "xe_gt.h" #include "xe_guc.h" #include "xe_hw_engine.h" -#include "xe_map.h" #include "xe_memirq.h" #include "xe_tile_printk.h" diff --git a/drivers/gpu/drm/xe/xe_mert.c b/drivers/gpu/drm/xe/xe_mert.c new file mode 100644 index 000000000000..f637df95418b --- /dev/null +++ b/drivers/gpu/drm/xe/xe_mert.c @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright(c) 2025, Intel Corporation. All rights reserved. + */ + +#include "regs/xe_irq_regs.h" +#include "regs/xe_mert_regs.h" + +#include "xe_device.h" +#include "xe_mert.h" +#include "xe_mmio.h" +#include "xe_sriov_printk.h" +#include "xe_tile.h" + +/** + * xe_mert_init_early() - Initialize MERT data + * @xe: the &xe_device with MERT to init + */ +void xe_mert_init_early(struct xe_device *xe) +{ + struct xe_tile *tile = xe_device_get_root_tile(xe); + struct xe_mert *mert = &tile->mert; + + spin_lock_init(&mert->lock); + init_completion(&mert->tlb_inv_done); +} + +/** + * xe_mert_invalidate_lmtt() - Invalidate MERT LMTT + * @xe: the &xe_device with MERT + * + * Trigger invalidation of the MERT LMTT and wait for completion. + * + * Return: 0 on success or -ETIMEDOUT in case of a timeout. + */ +int xe_mert_invalidate_lmtt(struct xe_device *xe) +{ + struct xe_tile *tile = xe_device_get_root_tile(xe); + struct xe_mert *mert = &tile->mert; + const long timeout = HZ / 4; + unsigned long flags; + + xe_assert(xe, xe_device_has_mert(xe)); + + spin_lock_irqsave(&mert->lock, flags); + if (!mert->tlb_inv_triggered) { + mert->tlb_inv_triggered = true; + reinit_completion(&mert->tlb_inv_done); + xe_mmio_write32(&tile->mmio, MERT_TLB_INV_DESC_A, MERT_TLB_INV_DESC_A_VALID); + } + spin_unlock_irqrestore(&mert->lock, flags); + + if (!wait_for_completion_timeout(&mert->tlb_inv_done, timeout)) + return -ETIMEDOUT; + + return 0; +} + +static void mert_handle_cat_error(struct xe_device *xe) +{ + struct xe_tile *tile = xe_device_get_root_tile(xe); + u32 reg_val, vfid, code; + + reg_val = xe_mmio_read32(&tile->mmio, MERT_TLB_CT_INTR_ERR_ID_PORT); + if (!reg_val) + return; + xe_mmio_write32(&tile->mmio, MERT_TLB_CT_INTR_ERR_ID_PORT, 0); + + vfid = FIELD_GET(CATERR_VFID, reg_val); + code = FIELD_GET(CATERR_CODES, reg_val); + + switch (code) { + case CATERR_NO_ERROR: + break; + case CATERR_UNMAPPED_GGTT: + xe_sriov_err(xe, "MERT: CAT_ERR: Access to an unmapped GGTT!\n"); + xe_device_declare_wedged(xe); + break; + case CATERR_LMTT_FAULT: + xe_sriov_dbg(xe, "MERT: CAT_ERR: VF%u LMTT fault!\n", vfid); + /* XXX: track/report malicious VF activity */ + break; + default: + xe_sriov_err(xe, "MERT: Unexpected CAT_ERR code=%#x!\n", code); + xe_device_declare_wedged(xe); + break; + } +} + +/** + * xe_mert_irq_handler - Handler for MERT interrupts + * @xe: the &xe_device + * @master_ctl: interrupt register + * + * Handle interrupts generated by MERT. + */ +void xe_mert_irq_handler(struct xe_device *xe, u32 master_ctl) +{ + struct xe_tile *tile = xe_device_get_root_tile(xe); + struct xe_mert *mert = &tile->mert; + unsigned long flags; + u32 reg_val; + + if (!(master_ctl & SOC_H2DMEMINT_IRQ)) + return; + + mert_handle_cat_error(xe); + + spin_lock_irqsave(&mert->lock, flags); + if (mert->tlb_inv_triggered) { + reg_val = xe_mmio_read32(&tile->mmio, MERT_TLB_INV_DESC_A); + if (!(reg_val & MERT_TLB_INV_DESC_A_VALID)) { + mert->tlb_inv_triggered = false; + complete_all(&mert->tlb_inv_done); + } + } + spin_unlock_irqrestore(&mert->lock, flags); +} diff --git a/drivers/gpu/drm/xe/xe_mert.h b/drivers/gpu/drm/xe/xe_mert.h new file mode 100644 index 000000000000..fc977203692d --- /dev/null +++ b/drivers/gpu/drm/xe/xe_mert.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright(c) 2025, Intel Corporation. All rights reserved. + */ + +#ifndef _XE_MERT_H_ +#define _XE_MERT_H_ + +#include <linux/completion.h> +#include <linux/spinlock.h> +#include <linux/types.h> + +struct xe_device; + +/** + * struct xe_mert - MERT related data + */ +struct xe_mert { + /** @lock: protects the TLB invalidation status */ + spinlock_t lock; + /** @tlb_inv_triggered: indicates if TLB invalidation was triggered */ + bool tlb_inv_triggered; + /** @tlb_inv_done: completion of TLB invalidation */ + struct completion tlb_inv_done; +}; + +#ifdef CONFIG_PCI_IOV +void xe_mert_init_early(struct xe_device *xe); +int xe_mert_invalidate_lmtt(struct xe_device *xe); +void xe_mert_irq_handler(struct xe_device *xe, u32 master_ctl); +#else +static inline void xe_mert_irq_handler(struct xe_device *xe, u32 master_ctl) { } +#endif + +#endif diff --git a/drivers/gpu/drm/xe/xe_migrate.c b/drivers/gpu/drm/xe/xe_migrate.c index 2184af413b91..6e202428aac2 100644 --- a/drivers/gpu/drm/xe/xe_migrate.c +++ b/drivers/gpu/drm/xe/xe_migrate.c @@ -34,6 +34,8 @@ #include "xe_res_cursor.h" #include "xe_sa.h" #include "xe_sched_job.h" +#include "xe_sriov_vf_ccs.h" +#include "xe_svm.h" #include "xe_sync.h" #include "xe_trace_bo.h" #include "xe_validation.h" @@ -470,7 +472,8 @@ int xe_migrate_init(struct xe_migrate *m) EXEC_QUEUE_FLAG_KERNEL | EXEC_QUEUE_FLAG_PERMANENT | EXEC_QUEUE_FLAG_HIGH_PRIORITY | - EXEC_QUEUE_FLAG_MIGRATE, 0); + EXEC_QUEUE_FLAG_MIGRATE | + EXEC_QUEUE_FLAG_LOW_LATENCY, 0); } else { m->q = xe_exec_queue_create_class(xe, primary_gt, vm, XE_ENGINE_CLASS_COPY, @@ -1103,12 +1106,16 @@ int xe_migrate_ccs_rw_copy(struct xe_tile *tile, struct xe_exec_queue *q, u32 batch_size, batch_size_allocated; struct xe_device *xe = gt_to_xe(gt); struct xe_res_cursor src_it, ccs_it; + struct xe_sriov_vf_ccs_ctx *ctx; + struct xe_sa_manager *bb_pool; u64 size = xe_bo_size(src_bo); struct xe_bb *bb = NULL; u64 src_L0, src_L0_ofs; u32 src_L0_pt; int err; + ctx = &xe->sriov.vf.ccs.contexts[read_write]; + xe_res_first_sg(xe_bo_sg(src_bo), 0, size, &src_it); xe_res_first_sg(xe_bo_sg(src_bo), xe_bo_ccs_pages_start(src_bo), @@ -1141,11 +1148,15 @@ int xe_migrate_ccs_rw_copy(struct xe_tile *tile, struct xe_exec_queue *q, size -= src_L0; } + bb_pool = ctx->mem.ccs_bb_pool; + guard(mutex) (xe_sa_bo_swap_guard(bb_pool)); + xe_sa_bo_swap_shadow(bb_pool); + bb = xe_bb_ccs_new(gt, batch_size, read_write); if (IS_ERR(bb)) { drm_err(&xe->drm, "BB allocation failed.\n"); err = PTR_ERR(bb); - goto err_ret; + return err; } batch_size_allocated = batch_size; @@ -1194,10 +1205,52 @@ int xe_migrate_ccs_rw_copy(struct xe_tile *tile, struct xe_exec_queue *q, xe_assert(xe, (batch_size_allocated == bb->len)); src_bo->bb_ccs[read_write] = bb; + xe_sriov_vf_ccs_rw_update_bb_addr(ctx); + xe_sa_bo_sync_shadow(bb->bo); return 0; +} -err_ret: - return err; +/** + * xe_migrate_ccs_rw_copy_clear() - Clear the CCS read/write batch buffer + * content. + * @src_bo: The buffer object @src is currently bound to. + * @read_write : Creates BB commands for CCS read/write. + * + * Directly clearing the BB lacks atomicity and can lead to undefined + * behavior if the vCPU is halted mid-operation during the clearing + * process. To avoid this issue, we use a shadow buffer object approach. + * + * First swap the SA BO address with the shadow BO, perform the clearing + * operation on the BB, update the shadow BO in the ring buffer, then + * sync the shadow and the actual buffer to maintain consistency. + * + * Returns: None. + */ +void xe_migrate_ccs_rw_copy_clear(struct xe_bo *src_bo, + enum xe_sriov_vf_ccs_rw_ctxs read_write) +{ + struct xe_bb *bb = src_bo->bb_ccs[read_write]; + struct xe_device *xe = xe_bo_device(src_bo); + struct xe_sriov_vf_ccs_ctx *ctx; + struct xe_sa_manager *bb_pool; + u32 *cs; + + xe_assert(xe, IS_SRIOV_VF(xe)); + + ctx = &xe->sriov.vf.ccs.contexts[read_write]; + bb_pool = ctx->mem.ccs_bb_pool; + + guard(mutex) (xe_sa_bo_swap_guard(bb_pool)); + xe_sa_bo_swap_shadow(bb_pool); + + cs = xe_sa_bo_cpu_addr(bb->bo); + memset(cs, MI_NOOP, bb->len * sizeof(u32)); + xe_sriov_vf_ccs_rw_update_bb_addr(ctx); + + xe_sa_bo_sync_shadow(bb->bo); + + xe_bb_free(bb, NULL); + src_bo->bb_ccs[read_write] = NULL; } /** @@ -1997,7 +2050,8 @@ static void build_pt_update_batch_sram(struct xe_migrate *m, u64 pte; xe_tile_assert(m->tile, sram_addr[i].proto == - DRM_INTERCONNECT_SYSTEM); + DRM_INTERCONNECT_SYSTEM || + sram_addr[i].proto == XE_INTERCONNECT_P2P); xe_tile_assert(m->tile, addr); xe_tile_assert(m->tile, PAGE_ALIGNED(addr)); @@ -2062,6 +2116,7 @@ static struct dma_fence *xe_migrate_vram(struct xe_migrate *m, unsigned long sram_offset, struct drm_pagemap_addr *sram_addr, u64 vram_addr, + struct dma_fence *deps, const enum xe_migrate_copy_dir dir) { struct xe_gt *gt = m->tile->primary_gt; @@ -2150,6 +2205,14 @@ static struct dma_fence *xe_migrate_vram(struct xe_migrate *m, xe_sched_job_add_migrate_flush(job, MI_INVALIDATE_TLB); + if (deps && !dma_fence_is_signaled(deps)) { + dma_fence_get(deps); + err = drm_sched_job_add_dependency(&job->drm, deps); + if (err) + dma_fence_wait(deps, false); + err = 0; + } + mutex_lock(&m->job_mutex); xe_sched_job_arm(job); fence = dma_fence_get(&job->drm.s_fence->finished); @@ -2175,6 +2238,8 @@ err: * @npages: Number of pages to migrate. * @src_addr: Array of DMA information (source of migrate) * @dst_addr: Device physical address of VRAM (destination of migrate) + * @deps: struct dma_fence representing the dependencies that need + * to be signaled before migration. * * Copy from an array dma addresses to a VRAM device physical address * @@ -2184,10 +2249,11 @@ err: struct dma_fence *xe_migrate_to_vram(struct xe_migrate *m, unsigned long npages, struct drm_pagemap_addr *src_addr, - u64 dst_addr) + u64 dst_addr, + struct dma_fence *deps) { return xe_migrate_vram(m, npages * PAGE_SIZE, 0, src_addr, dst_addr, - XE_MIGRATE_COPY_TO_VRAM); + deps, XE_MIGRATE_COPY_TO_VRAM); } /** @@ -2196,6 +2262,8 @@ struct dma_fence *xe_migrate_to_vram(struct xe_migrate *m, * @npages: Number of pages to migrate. * @src_addr: Device physical address of VRAM (source of migrate) * @dst_addr: Array of DMA information (destination of migrate) + * @deps: struct dma_fence representing the dependencies that need + * to be signaled before migration. * * Copy from a VRAM device physical address to an array dma addresses * @@ -2205,10 +2273,11 @@ struct dma_fence *xe_migrate_to_vram(struct xe_migrate *m, struct dma_fence *xe_migrate_from_vram(struct xe_migrate *m, unsigned long npages, u64 src_addr, - struct drm_pagemap_addr *dst_addr) + struct drm_pagemap_addr *dst_addr, + struct dma_fence *deps) { return xe_migrate_vram(m, npages * PAGE_SIZE, 0, dst_addr, src_addr, - XE_MIGRATE_COPY_TO_SRAM); + deps, XE_MIGRATE_COPY_TO_SRAM); } static void xe_migrate_dma_unmap(struct xe_device *xe, @@ -2384,7 +2453,7 @@ int xe_migrate_access_memory(struct xe_migrate *m, struct xe_bo *bo, __fence = xe_migrate_vram(m, current_bytes, (unsigned long)buf & ~PAGE_MASK, &pagemap_addr[current_page], - vram_addr, write ? + vram_addr, NULL, write ? XE_MIGRATE_COPY_TO_VRAM : XE_MIGRATE_COPY_TO_SRAM); if (IS_ERR(__fence)) { @@ -2430,7 +2499,7 @@ void xe_migrate_job_lock(struct xe_migrate *m, struct xe_exec_queue *q) if (is_migrate) mutex_lock(&m->job_mutex); else - xe_vm_assert_held(q->vm); /* User queues VM's should be locked */ + xe_vm_assert_held(q->user_vm); /* User queues VM's should be locked */ } /** @@ -2448,7 +2517,7 @@ void xe_migrate_job_unlock(struct xe_migrate *m, struct xe_exec_queue *q) if (is_migrate) mutex_unlock(&m->job_mutex); else - xe_vm_assert_held(q->vm); /* User queues VM's should be locked */ + xe_vm_assert_held(q->user_vm); /* User queues VM's should be locked */ } #if IS_ENABLED(CONFIG_PROVE_LOCKING) diff --git a/drivers/gpu/drm/xe/xe_migrate.h b/drivers/gpu/drm/xe/xe_migrate.h index 260e298e5dd7..1522afb37dcf 100644 --- a/drivers/gpu/drm/xe/xe_migrate.h +++ b/drivers/gpu/drm/xe/xe_migrate.h @@ -116,12 +116,14 @@ int xe_migrate_init(struct xe_migrate *m); struct dma_fence *xe_migrate_to_vram(struct xe_migrate *m, unsigned long npages, struct drm_pagemap_addr *src_addr, - u64 dst_addr); + u64 dst_addr, + struct dma_fence *deps); struct dma_fence *xe_migrate_from_vram(struct xe_migrate *m, unsigned long npages, u64 src_addr, - struct drm_pagemap_addr *dst_addr); + struct drm_pagemap_addr *dst_addr, + struct dma_fence *deps); struct dma_fence *xe_migrate_copy(struct xe_migrate *m, struct xe_bo *src_bo, @@ -134,6 +136,9 @@ int xe_migrate_ccs_rw_copy(struct xe_tile *tile, struct xe_exec_queue *q, struct xe_bo *src_bo, enum xe_sriov_vf_ccs_rw_ctxs read_write); +void xe_migrate_ccs_rw_copy_clear(struct xe_bo *src_bo, + enum xe_sriov_vf_ccs_rw_ctxs read_write); + struct xe_lrc *xe_migrate_lrc(struct xe_migrate *migrate); struct xe_exec_queue *xe_migrate_exec_queue(struct xe_migrate *migrate); struct dma_fence *xe_migrate_vram_copy_chunk(struct xe_bo *vram_bo, u64 vram_offset, diff --git a/drivers/gpu/drm/xe/xe_mmio.c b/drivers/gpu/drm/xe/xe_mmio.c index 350dca1f0925..bcb6674b7dac 100644 --- a/drivers/gpu/drm/xe/xe_mmio.c +++ b/drivers/gpu/drm/xe/xe_mmio.c @@ -14,12 +14,8 @@ #include <drm/drm_print.h> #include "regs/xe_bars.h" -#include "regs/xe_regs.h" #include "xe_device.h" -#include "xe_gt.h" -#include "xe_gt_printk.h" #include "xe_gt_sriov_vf.h" -#include "xe_macros.h" #include "xe_sriov.h" #include "xe_trace.h" #include "xe_wa.h" diff --git a/drivers/gpu/drm/xe/xe_mocs.c b/drivers/gpu/drm/xe/xe_mocs.c index 6613d3b48a84..54822497c21e 100644 --- a/drivers/gpu/drm/xe/xe_mocs.c +++ b/drivers/gpu/drm/xe/xe_mocs.c @@ -6,7 +6,6 @@ #include "xe_mocs.h" #include "regs/xe_gt_regs.h" -#include "xe_bo.h" #include "xe_device.h" #include "xe_exec_queue.h" #include "xe_force_wake.h" @@ -17,7 +16,6 @@ #include "xe_platform_types.h" #include "xe_pm.h" #include "xe_sriov.h" -#include "xe_step_types.h" #if IS_ENABLED(CONFIG_DRM_XE_DEBUG) #define mocs_dbg xe_gt_dbg @@ -811,26 +809,20 @@ int xe_mocs_dump(struct xe_gt *gt, struct drm_printer *p) struct xe_device *xe = gt_to_xe(gt); enum xe_force_wake_domains domain; struct xe_mocs_info table; - unsigned int fw_ref, flags; - int err = 0; + unsigned int flags; flags = get_mocs_settings(xe, &table); domain = flags & HAS_LNCF_MOCS ? XE_FORCEWAKE_ALL : XE_FW_GT; - xe_pm_runtime_get_noresume(xe); - fw_ref = xe_force_wake_get(gt_to_fw(gt), domain); - if (!xe_force_wake_ref_has_domain(fw_ref, domain)) { - err = -ETIMEDOUT; - goto err_fw; - } + guard(xe_pm_runtime_noresume)(xe); + CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), domain); + if (!xe_force_wake_ref_has_domain(fw_ref.domains, domain)) + return -ETIMEDOUT; table.ops->dump(&table, flags, gt, p); -err_fw: - xe_force_wake_put(gt_to_fw(gt), fw_ref); - xe_pm_runtime_put(xe); - return err; + return 0; } #if IS_ENABLED(CONFIG_DRM_XE_KUNIT_TEST) diff --git a/drivers/gpu/drm/xe/xe_module.c b/drivers/gpu/drm/xe/xe_module.c index d08338fc3bc1..a0048f64ed12 100644 --- a/drivers/gpu/drm/xe/xe_module.c +++ b/drivers/gpu/drm/xe/xe_module.c @@ -10,6 +10,7 @@ #include <drm/drm_module.h> +#include "xe_device_types.h" #include "xe_drv.h" #include "xe_configfs.h" #include "xe_hw_fence.h" @@ -29,7 +30,8 @@ #define DEFAULT_FORCE_PROBE CONFIG_DRM_XE_FORCE_PROBE #define DEFAULT_MAX_VFS ~0 #define DEFAULT_MAX_VFS_STR "unlimited" -#define DEFAULT_WEDGED_MODE 1 +#define DEFAULT_WEDGED_MODE XE_WEDGED_MODE_DEFAULT +#define DEFAULT_WEDGED_MODE_STR XE_WEDGED_MODE_DEFAULT_STR #define DEFAULT_SVM_NOTIFIER_SIZE 512 struct xe_modparam xe_modparam = { @@ -88,10 +90,10 @@ MODULE_PARM_DESC(max_vfs, "[default=" DEFAULT_MAX_VFS_STR "])"); #endif -module_param_named_unsafe(wedged_mode, xe_modparam.wedged_mode, int, 0600); +module_param_named_unsafe(wedged_mode, xe_modparam.wedged_mode, uint, 0600); MODULE_PARM_DESC(wedged_mode, - "Module's default policy for the wedged mode (0=never, 1=upon-critical-errors, 2=upon-any-hang " - "[default=" __stringify(DEFAULT_WEDGED_MODE) "])"); + "Module's default policy for the wedged mode (0=never, 1=upon-critical-error, 2=upon-any-hang-no-reset " + "[default=" DEFAULT_WEDGED_MODE_STR "])"); static int xe_check_nomodeset(void) { diff --git a/drivers/gpu/drm/xe/xe_module.h b/drivers/gpu/drm/xe/xe_module.h index 5a3bfea8b7b4..1c75f38ca393 100644 --- a/drivers/gpu/drm/xe/xe_module.h +++ b/drivers/gpu/drm/xe/xe_module.h @@ -21,7 +21,7 @@ struct xe_modparam { #ifdef CONFIG_PCI_IOV unsigned int max_vfs; #endif - int wedged_mode; + unsigned int wedged_mode; u32 svm_notifier_size; }; diff --git a/drivers/gpu/drm/xe/xe_nvm.c b/drivers/gpu/drm/xe/xe_nvm.c index 33f4ac82fc80..437375046517 100644 --- a/drivers/gpu/drm/xe/xe_nvm.c +++ b/drivers/gpu/drm/xe/xe_nvm.c @@ -6,10 +6,10 @@ #include <linux/intel_dg_nvm_aux.h> #include <linux/pci.h> -#include "xe_device.h" #include "xe_device_types.h" #include "xe_mmio.h" #include "xe_nvm.h" +#include "xe_pcode_api.h" #include "regs/xe_gsc_regs.h" #include "xe_sriov.h" @@ -45,39 +45,50 @@ static bool xe_nvm_non_posted_erase(struct xe_device *xe) { struct xe_mmio *mmio = xe_root_tile_mmio(xe); - if (xe->info.platform != XE_BATTLEMAGE) + switch (xe->info.platform) { + case XE_CRESCENTISLAND: + case XE_BATTLEMAGE: + return !(xe_mmio_read32(mmio, XE_REG(GEN12_CNTL_PROTECTED_NVM_REG)) & + NVM_NON_POSTED_ERASE_CHICKEN_BIT); + default: return false; - return !(xe_mmio_read32(mmio, XE_REG(GEN12_CNTL_PROTECTED_NVM_REG)) & - NVM_NON_POSTED_ERASE_CHICKEN_BIT); + } } static bool xe_nvm_writable_override(struct xe_device *xe) { struct xe_mmio *mmio = xe_root_tile_mmio(xe); bool writable_override; - resource_size_t base; + struct xe_reg reg; + u32 test_bit; switch (xe->info.platform) { + case XE_CRESCENTISLAND: + reg = PCODE_SCRATCH(0); + test_bit = FDO_MODE; + break; case XE_BATTLEMAGE: - base = DG2_GSC_HECI2_BASE; + reg = HECI_FWSTS2(DG2_GSC_HECI2_BASE); + test_bit = HECI_FW_STATUS_2_NVM_ACCESS_MODE; break; case XE_PVC: - base = PVC_GSC_HECI2_BASE; + reg = HECI_FWSTS2(PVC_GSC_HECI2_BASE); + test_bit = HECI_FW_STATUS_2_NVM_ACCESS_MODE; break; case XE_DG2: - base = DG2_GSC_HECI2_BASE; + reg = HECI_FWSTS2(DG2_GSC_HECI2_BASE); + test_bit = HECI_FW_STATUS_2_NVM_ACCESS_MODE; break; case XE_DG1: - base = DG1_GSC_HECI2_BASE; + reg = HECI_FWSTS2(DG1_GSC_HECI2_BASE); + test_bit = HECI_FW_STATUS_2_NVM_ACCESS_MODE; break; default: drm_err(&xe->drm, "Unknown platform\n"); return true; } - writable_override = - !(xe_mmio_read32(mmio, HECI_FWSTS2(base)) & - HECI_FW_STATUS_2_NVM_ACCESS_MODE); + writable_override = !(xe_mmio_read32(mmio, reg) & test_bit); if (writable_override) drm_info(&xe->drm, "NVM access overridden by jumper\n"); return writable_override; diff --git a/drivers/gpu/drm/xe/xe_oa.c b/drivers/gpu/drm/xe/xe_oa.c index 890c363282ae..abf87fe0b345 100644 --- a/drivers/gpu/drm/xe/xe_oa.c +++ b/drivers/gpu/drm/xe/xe_oa.c @@ -1105,11 +1105,12 @@ static int xe_oa_enable_metric_set(struct xe_oa_stream *stream) oag_buf_size_select(stream) | oag_configure_mmio_trigger(stream, true)); - xe_mmio_write32(mmio, __oa_regs(stream)->oa_ctx_ctrl, stream->periodic ? - (OAG_OAGLBCTXCTRL_COUNTER_RESUME | + xe_mmio_write32(mmio, __oa_regs(stream)->oa_ctx_ctrl, + OAG_OAGLBCTXCTRL_COUNTER_RESUME | + (stream->periodic ? OAG_OAGLBCTXCTRL_TIMER_ENABLE | REG_FIELD_PREP(OAG_OAGLBCTXCTRL_TIMER_PERIOD_MASK, - stream->period_exponent)) : 0); + stream->period_exponent) : 0)); /* * Initialize Super Queue Internal Cnt Register @@ -1254,6 +1255,9 @@ static int xe_oa_set_no_preempt(struct xe_oa *oa, u64 value, static int xe_oa_set_prop_num_syncs(struct xe_oa *oa, u64 value, struct xe_oa_open_param *param) { + if (XE_IOCTL_DBG(oa->xe, value > DRM_XE_MAX_SYNCS)) + return -EINVAL; + param->num_syncs = value; return 0; } @@ -1343,7 +1347,7 @@ static int xe_oa_user_ext_set_property(struct xe_oa *oa, enum xe_oa_user_extn_fr ARRAY_SIZE(xe_oa_set_property_funcs_config)); if (XE_IOCTL_DBG(oa->xe, ext.property >= ARRAY_SIZE(xe_oa_set_property_funcs_open)) || - XE_IOCTL_DBG(oa->xe, ext.pad)) + XE_IOCTL_DBG(oa->xe, !ext.property) || XE_IOCTL_DBG(oa->xe, ext.pad)) return -EINVAL; idx = array_index_nospec(ext.property, ARRAY_SIZE(xe_oa_set_property_funcs_open)); @@ -1937,6 +1941,7 @@ static bool oa_unit_supports_oa_format(struct xe_oa_open_param *param, int type) type == DRM_XE_OA_FMT_TYPE_OAC || type == DRM_XE_OA_FMT_TYPE_PEC; case DRM_XE_OA_UNIT_TYPE_OAM: case DRM_XE_OA_UNIT_TYPE_OAM_SAG: + case DRM_XE_OA_UNIT_TYPE_MERT: return type == DRM_XE_OA_FMT_TYPE_OAM || type == DRM_XE_OA_FMT_TYPE_OAM_MPEC; default: return false; @@ -1962,10 +1967,6 @@ static int xe_oa_assign_hwe(struct xe_oa *oa, struct xe_oa_open_param *param) enum xe_hw_engine_id id; int ret = 0; - /* If not provided, OA unit defaults to OA unit 0 as per uapi */ - if (!param->oa_unit) - param->oa_unit = &xe_root_mmio_gt(oa->xe)->oa.oa_unit[0]; - /* When we have an exec_q, get hwe from the exec_q */ if (param->exec_q) { param->hwe = xe_gt_hw_engine(param->exec_q->gt, param->exec_q->class, @@ -2031,7 +2032,15 @@ int xe_oa_stream_open_ioctl(struct drm_device *dev, u64 data, struct drm_file *f if (ret) return ret; + /* If not provided, OA unit defaults to OA unit 0 as per uapi */ + if (!param.oa_unit) + param.oa_unit = &xe_root_mmio_gt(oa->xe)->oa.oa_unit[0]; + if (param.exec_queue_id > 0) { + /* An exec_queue is only needed for OAR/OAC functionality on OAG */ + if (XE_IOCTL_DBG(oa->xe, param.oa_unit->type != DRM_XE_OA_UNIT_TYPE_OAG)) + return -EINVAL; + param.exec_q = xe_exec_queue_lookup(xef, param.exec_queue_id); if (XE_IOCTL_DBG(oa->xe, !param.exec_q)) return -ENOENT; @@ -2220,6 +2229,8 @@ static const struct xe_mmio_range xe2_oa_mux_regs[] = { { .start = 0xE18C, .end = 0xE18C }, /* SAMPLER_MODE */ { .start = 0xE590, .end = 0xE590 }, /* TDL_LSC_LAT_MEASURE_TDL_GFX */ { .start = 0x13000, .end = 0x137FC }, /* PES_0_PESL0 - PES_63_UPPER_PESL3 */ + { .start = 0x145194, .end = 0x145194 }, /* SYS_MEM_LAT_MEASURE */ + { .start = 0x145340, .end = 0x14537C }, /* MERTSS_PES_0 - MERTSS_PES_7 */ {}, }; @@ -2511,7 +2522,12 @@ int xe_oa_register(struct xe_device *xe) static u32 num_oa_units_per_gt(struct xe_gt *gt) { if (xe_gt_is_main_type(gt) || GRAPHICS_VER(gt_to_xe(gt)) < 20) - return 1; + /* + * Mert OA unit belongs to the SoC, not a gt, so should be accessed using + * xe_root_tile_mmio(). However, for all known platforms this is the same as + * accessing via xe_root_mmio_gt()->mmio. + */ + return xe_device_has_mert(gt_to_xe(gt)) ? 2 : 1; else if (!IS_DGFX(gt_to_xe(gt))) return XE_OAM_UNIT_SCMI_0 + 1; /* SAG + SCMI_0 */ else @@ -2566,40 +2582,57 @@ static u32 __hwe_oa_unit(struct xe_hw_engine *hwe) static struct xe_oa_regs __oam_regs(u32 base) { return (struct xe_oa_regs) { - base, - OAM_HEAD_POINTER(base), - OAM_TAIL_POINTER(base), - OAM_BUFFER(base), - OAM_CONTEXT_CONTROL(base), - OAM_CONTROL(base), - OAM_DEBUG(base), - OAM_STATUS(base), - OAM_CONTROL_COUNTER_SEL_MASK, + .base = base, + .oa_head_ptr = OAM_HEAD_POINTER(base), + .oa_tail_ptr = OAM_TAIL_POINTER(base), + .oa_buffer = OAM_BUFFER(base), + .oa_ctx_ctrl = OAM_CONTEXT_CONTROL(base), + .oa_ctrl = OAM_CONTROL(base), + .oa_debug = OAM_DEBUG(base), + .oa_status = OAM_STATUS(base), + .oa_mmio_trg = OAM_MMIO_TRG(base), + .oa_ctrl_counter_select_mask = OAM_CONTROL_COUNTER_SEL_MASK, }; } static struct xe_oa_regs __oag_regs(void) { return (struct xe_oa_regs) { - 0, - OAG_OAHEADPTR, - OAG_OATAILPTR, - OAG_OABUFFER, - OAG_OAGLBCTXCTRL, - OAG_OACONTROL, - OAG_OA_DEBUG, - OAG_OASTATUS, - OAG_OACONTROL_OA_COUNTER_SEL_MASK, + .base = 0, + .oa_head_ptr = OAG_OAHEADPTR, + .oa_tail_ptr = OAG_OATAILPTR, + .oa_buffer = OAG_OABUFFER, + .oa_ctx_ctrl = OAG_OAGLBCTXCTRL, + .oa_ctrl = OAG_OACONTROL, + .oa_debug = OAG_OA_DEBUG, + .oa_status = OAG_OASTATUS, + .oa_mmio_trg = OAG_MMIOTRIGGER, + .oa_ctrl_counter_select_mask = OAG_OACONTROL_OA_COUNTER_SEL_MASK, + }; +} + +static struct xe_oa_regs __oamert_regs(void) +{ + return (struct xe_oa_regs) { + .base = 0, + .oa_head_ptr = OAMERT_HEAD_POINTER, + .oa_tail_ptr = OAMERT_TAIL_POINTER, + .oa_buffer = OAMERT_BUFFER, + .oa_ctx_ctrl = OAMERT_CONTEXT_CONTROL, + .oa_ctrl = OAMERT_CONTROL, + .oa_debug = OAMERT_DEBUG, + .oa_status = OAMERT_STATUS, + .oa_mmio_trg = OAMERT_MMIO_TRG, + .oa_ctrl_counter_select_mask = OAM_CONTROL_COUNTER_SEL_MASK, }; } static void __xe_oa_init_oa_units(struct xe_gt *gt) { - /* Actual address is MEDIA_GT_GSI_OFFSET + oam_base_addr[i] */ const u32 oam_base_addr[] = { - [XE_OAM_UNIT_SAG] = 0x13000, - [XE_OAM_UNIT_SCMI_0] = 0x14000, - [XE_OAM_UNIT_SCMI_1] = 0x14800, + [XE_OAM_UNIT_SAG] = XE_OAM_SAG_BASE, + [XE_OAM_UNIT_SCMI_0] = XE_OAM_SCMI_0_BASE, + [XE_OAM_UNIT_SCMI_1] = XE_OAM_SCMI_1_BASE, }; int i, num_units = gt->oa.num_oa_units; @@ -2607,8 +2640,15 @@ static void __xe_oa_init_oa_units(struct xe_gt *gt) struct xe_oa_unit *u = >->oa.oa_unit[i]; if (xe_gt_is_main_type(gt)) { - u->regs = __oag_regs(); - u->type = DRM_XE_OA_UNIT_TYPE_OAG; + if (!i) { + u->regs = __oag_regs(); + u->type = DRM_XE_OA_UNIT_TYPE_OAG; + } else { + xe_gt_assert(gt, xe_device_has_mert(gt_to_xe(gt))); + xe_gt_assert(gt, gt == xe_root_mmio_gt(gt_to_xe(gt))); + u->regs = __oamert_regs(); + u->type = DRM_XE_OA_UNIT_TYPE_MERT; + } } else { xe_gt_assert(gt, GRAPHICS_VERx100(gt_to_xe(gt)) >= 1270); u->regs = __oam_regs(oam_base_addr[i]); diff --git a/drivers/gpu/drm/xe/xe_oa_types.h b/drivers/gpu/drm/xe/xe_oa_types.h index cf080f412189..08cc8d7c2215 100644 --- a/drivers/gpu/drm/xe/xe_oa_types.h +++ b/drivers/gpu/drm/xe/xe_oa_types.h @@ -87,6 +87,7 @@ struct xe_oa_regs { struct xe_reg oa_ctrl; struct xe_reg oa_debug; struct xe_reg oa_status; + struct xe_reg oa_mmio_trg; u32 oa_ctrl_counter_select_mask; }; diff --git a/drivers/gpu/drm/xe/xe_page_reclaim.c b/drivers/gpu/drm/xe/xe_page_reclaim.c new file mode 100644 index 000000000000..e13c71a89da2 --- /dev/null +++ b/drivers/gpu/drm/xe/xe_page_reclaim.c @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright © 2025 Intel Corporation + */ + +#include <linux/bitfield.h> +#include <linux/kref.h> +#include <linux/mm.h> +#include <linux/slab.h> + +#include "xe_page_reclaim.h" + +#include "xe_gt_stats.h" +#include "xe_macros.h" +#include "xe_pat.h" +#include "xe_sa.h" +#include "xe_tlb_inval_types.h" + +/** + * xe_page_reclaim_skip() - Decide whether PRL should be skipped for a VMA + * @tile: Tile owning the VMA + * @vma: VMA under consideration + * + * PPC flushing may be handled by HW for specific PAT encodings. + * Skip PPC flushing/Page Reclaim for scenarios below due to redundant + * flushes. + * - pat_index is transient display (1) + * + * Return: true when page reclamation is unnecessary, false otherwise. + */ +bool xe_page_reclaim_skip(struct xe_tile *tile, struct xe_vma *vma) +{ + u8 l3_policy; + + l3_policy = xe_pat_index_get_l3_policy(tile->xe, vma->attr.pat_index); + + /* + * - l3_policy: 0=WB, 1=XD ("WB - Transient Display"), 3=UC + * Transient display flushes is taken care by HW, l3_policy = 1. + * + * HW will sequence these transient flushes at various sync points so + * any event of page reclamation will hit these sync points before + * page reclamation could execute. + */ + return (l3_policy == XE_L3_POLICY_XD); +} + +/** + * xe_page_reclaim_create_prl_bo() - Back a PRL with a suballocated GGTT BO + * @tlb_inval: TLB invalidation frontend associated with the request + * @prl: page reclaim list data that bo will copy from + * @fence: tlb invalidation fence that page reclaim action is paired to + * + * Suballocates a 4K BO out of the tile reclaim pool, copies the PRL CPU + * copy into the BO and queues the buffer for release when @fence signals. + * + * Return: struct drm_suballoc pointer on success or ERR_PTR on failure. + */ +struct drm_suballoc *xe_page_reclaim_create_prl_bo(struct xe_tlb_inval *tlb_inval, + struct xe_page_reclaim_list *prl, + struct xe_tlb_inval_fence *fence) +{ + struct xe_gt *gt = container_of(tlb_inval, struct xe_gt, tlb_inval); + struct xe_tile *tile = gt_to_tile(gt); + /* (+1) for NULL page_reclaim_entry to indicate end of list */ + int prl_size = min(prl->num_entries + 1, XE_PAGE_RECLAIM_MAX_ENTRIES) * + sizeof(struct xe_guc_page_reclaim_entry); + struct drm_suballoc *prl_sa; + + /* Maximum size of PRL is 1 4K-page */ + prl_sa = __xe_sa_bo_new(tile->mem.reclaim_pool, + prl_size, GFP_ATOMIC); + if (IS_ERR(prl_sa)) + return prl_sa; + + memcpy(xe_sa_bo_cpu_addr(prl_sa), prl->entries, + prl_size); + xe_sa_bo_flush_write(prl_sa); + /* Queue up sa_bo_free on tlb invalidation fence signal */ + xe_sa_bo_free(prl_sa, &fence->base); + + return prl_sa; +} + +/** + * xe_page_reclaim_list_invalidate() - Mark a PRL as invalid + * @prl: Page reclaim list to reset + * + * Clears the entries pointer and marks the list as invalid so + * future use knows PRL is unusable. It is expected that the entries + * have already been released. + */ +void xe_page_reclaim_list_invalidate(struct xe_page_reclaim_list *prl) +{ + xe_page_reclaim_entries_put(prl->entries); + prl->entries = NULL; + prl->num_entries = XE_PAGE_RECLAIM_INVALID_LIST; +} + +/** + * xe_page_reclaim_list_init() - Initialize a page reclaim list + * @prl: Page reclaim list to initialize + * + * NULLs both values in list to prepare on initalization. + */ +void xe_page_reclaim_list_init(struct xe_page_reclaim_list *prl) +{ + prl->entries = NULL; + prl->num_entries = 0; +} + +/** + * xe_page_reclaim_list_alloc_entries() - Allocate page reclaim list entries + * @prl: Page reclaim list to allocate entries for + * + * Allocate one 4K page for the PRL entries, otherwise assign prl->entries to NULL. + */ +int xe_page_reclaim_list_alloc_entries(struct xe_page_reclaim_list *prl) +{ + struct page *page; + + if (XE_WARN_ON(prl->entries)) + return 0; + + page = alloc_page(GFP_KERNEL | __GFP_ZERO); + if (page) { + prl->entries = page_address(page); + prl->num_entries = 0; + } + + return page ? 0 : -ENOMEM; +} diff --git a/drivers/gpu/drm/xe/xe_page_reclaim.h b/drivers/gpu/drm/xe/xe_page_reclaim.h new file mode 100644 index 000000000000..3dd103e37beb --- /dev/null +++ b/drivers/gpu/drm/xe/xe_page_reclaim.h @@ -0,0 +1,125 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright © 2025 Intel Corporation + */ + +#ifndef _XE_PAGE_RECLAIM_H_ +#define _XE_PAGE_RECLAIM_H_ + +#include <linux/kref.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/workqueue.h> +#include <linux/bits.h> + +#define XE_PAGE_RECLAIM_MAX_ENTRIES 512 +#define XE_PAGE_RECLAIM_LIST_MAX_SIZE SZ_4K + +struct xe_tlb_inval; +struct xe_tlb_inval_fence; +struct xe_tile; +struct xe_gt; +struct xe_vma; + +struct xe_guc_page_reclaim_entry { + u64 qw; +/* valid reclaim entry bit */ +#define XE_PAGE_RECLAIM_VALID BIT_ULL(0) +/* + * offset order of page size to be reclaimed + * page_size = 1 << (XE_PTE_SHIFT + reclamation_size) + */ +#define XE_PAGE_RECLAIM_SIZE GENMASK_ULL(6, 1) +#define XE_PAGE_RECLAIM_RSVD_0 GENMASK_ULL(11, 7) +/* lower 20 bits of the physical address */ +#define XE_PAGE_RECLAIM_ADDR_LO GENMASK_ULL(31, 12) +/* upper 20 bits of the physical address */ +#define XE_PAGE_RECLAIM_ADDR_HI GENMASK_ULL(51, 32) +#define XE_PAGE_RECLAIM_RSVD_1 GENMASK_ULL(63, 52) +} __packed; + +struct xe_page_reclaim_list { + /** @entries: array of page reclaim entries, page allocated */ + struct xe_guc_page_reclaim_entry *entries; + /** @num_entries: number of entries */ + int num_entries; +#define XE_PAGE_RECLAIM_INVALID_LIST -1 +}; + +/** + * xe_page_reclaim_list_is_new() - Check if PRL is new allocation + * @prl: Pointer to page reclaim list + * + * PRL indicates it hasn't been allocated through both values being NULL + */ +static inline bool xe_page_reclaim_list_is_new(struct xe_page_reclaim_list *prl) +{ + return !prl->entries && prl->num_entries == 0; +} + +/** + * xe_page_reclaim_list_valid() - Check if the page reclaim list is valid + * @prl: Pointer to page reclaim list + * + * PRL uses the XE_PAGE_RECLAIM_INVALID_LIST to indicate that a PRL + * is unusable. + */ +static inline bool xe_page_reclaim_list_valid(struct xe_page_reclaim_list *prl) +{ + return !xe_page_reclaim_list_is_new(prl) && + prl->num_entries != XE_PAGE_RECLAIM_INVALID_LIST; +} + +bool xe_page_reclaim_skip(struct xe_tile *tile, struct xe_vma *vma); +struct drm_suballoc *xe_page_reclaim_create_prl_bo(struct xe_tlb_inval *tlb_inval, + struct xe_page_reclaim_list *prl, + struct xe_tlb_inval_fence *fence); +void xe_page_reclaim_list_invalidate(struct xe_page_reclaim_list *prl); + +/** + * xe_page_reclaim_list_abort() - Invalidate a PRL and log an abort reason + * @gt: GT owning the page reclaim request + * @prl: Page reclaim list to invalidate + * @fmt: format string for the log message with args + * + * Abort page reclaim process by invalidating PRL and doing any relevant logging. + */ +#define xe_page_reclaim_list_abort(gt, prl, fmt, ...) \ + do { \ + struct xe_gt *__gt = (gt); \ + struct xe_page_reclaim_list *__prl = (prl); \ + \ + xe_page_reclaim_list_invalidate(__prl); \ + xe_gt_stats_incr(__gt, XE_GT_STATS_ID_PRL_ABORTED_COUNT, 1); \ + vm_dbg(>_to_xe(__gt)->drm, "PRL aborted: " fmt, ##__VA_ARGS__); \ + } while (0) + +void xe_page_reclaim_list_init(struct xe_page_reclaim_list *prl); +int xe_page_reclaim_list_alloc_entries(struct xe_page_reclaim_list *prl); +/** + * xe_page_reclaim_entries_get() - Increment the reference count of page reclaim entries. + * @entries: Pointer to the array of page reclaim entries. + * + * This function increments the reference count of the backing page. + */ +static inline void xe_page_reclaim_entries_get(struct xe_guc_page_reclaim_entry *entries) +{ + if (entries) + get_page(virt_to_page(entries)); +} + +/** + * xe_page_reclaim_entries_put() - Decrement the reference count of page reclaim entries. + * @entries: Pointer to the array of page reclaim entries. + * + * This function decrements the reference count of the backing page + * and frees it if the count reaches zero. + */ +static inline void xe_page_reclaim_entries_put(struct xe_guc_page_reclaim_entry *entries) +{ + if (entries) + put_page(virt_to_page(entries)); +} + +#endif /* _XE_PAGE_RECLAIM_H_ */ diff --git a/drivers/gpu/drm/xe/xe_pagefault.c b/drivers/gpu/drm/xe/xe_pagefault.c index afb06598b6e1..6bee53d6ffc3 100644 --- a/drivers/gpu/drm/xe/xe_pagefault.c +++ b/drivers/gpu/drm/xe/xe_pagefault.c @@ -223,22 +223,22 @@ static bool xe_pagefault_queue_pop(struct xe_pagefault_queue *pf_queue, static void xe_pagefault_print(struct xe_pagefault *pf) { - xe_gt_dbg(pf->gt, "\n\tASID: %d\n" - "\tFaulted Address: 0x%08x%08x\n" - "\tFaultType: %d\n" - "\tAccessType: %d\n" - "\tFaultLevel: %d\n" - "\tEngineClass: %d %s\n" - "\tEngineInstance: %d\n", - pf->consumer.asid, - upper_32_bits(pf->consumer.page_addr), - lower_32_bits(pf->consumer.page_addr), - pf->consumer.fault_type, - pf->consumer.access_type, - pf->consumer.fault_level, - pf->consumer.engine_class, - xe_hw_engine_class_to_str(pf->consumer.engine_class), - pf->consumer.engine_instance); + xe_gt_info(pf->gt, "\n\tASID: %d\n" + "\tFaulted Address: 0x%08x%08x\n" + "\tFaultType: %d\n" + "\tAccessType: %d\n" + "\tFaultLevel: %d\n" + "\tEngineClass: %d %s\n" + "\tEngineInstance: %d\n", + pf->consumer.asid, + upper_32_bits(pf->consumer.page_addr), + lower_32_bits(pf->consumer.page_addr), + pf->consumer.fault_type, + pf->consumer.access_type, + pf->consumer.fault_level, + pf->consumer.engine_class, + xe_hw_engine_class_to_str(pf->consumer.engine_class), + pf->consumer.engine_instance); } static void xe_pagefault_queue_work(struct work_struct *w) @@ -260,8 +260,8 @@ static void xe_pagefault_queue_work(struct work_struct *w) err = xe_pagefault_service(&pf); if (err) { xe_pagefault_print(&pf); - xe_gt_dbg(pf.gt, "Fault response: Unsuccessful %pe\n", - ERR_PTR(err)); + xe_gt_info(pf.gt, "Fault response: Unsuccessful %pe\n", + ERR_PTR(err)); } pf.producer.ops->ack_fault(&pf, err); diff --git a/drivers/gpu/drm/xe/xe_pat.c b/drivers/gpu/drm/xe/xe_pat.c index 68171cceea18..14d0dce5190a 100644 --- a/drivers/gpu/drm/xe/xe_pat.c +++ b/drivers/gpu/drm/xe/xe_pat.c @@ -9,6 +9,7 @@ #include <generated/xe_wa_oob.h> +#include "regs/xe_gt_regs.h" #include "regs/xe_reg_defs.h" #include "xe_assert.h" #include "xe_device.h" @@ -50,8 +51,37 @@ #define XELP_PAT_WC REG_FIELD_PREP(XELP_MEM_TYPE_MASK, 1) #define XELP_PAT_UC REG_FIELD_PREP(XELP_MEM_TYPE_MASK, 0) +#define PAT_LABEL_LEN 20 + static const char *XELP_MEM_TYPE_STR_MAP[] = { "UC", "WC", "WT", "WB" }; +static void xe_pat_index_label(char *label, size_t len, int index) +{ + snprintf(label, len, "PAT[%2d] ", index); +} + +static void xelp_pat_entry_dump(struct drm_printer *p, int index, u32 pat) +{ + u8 mem_type = REG_FIELD_GET(XELP_MEM_TYPE_MASK, pat); + + drm_printf(p, "PAT[%2d] = %s (%#8x)\n", index, + XELP_MEM_TYPE_STR_MAP[mem_type], pat); +} + +static void xehpc_pat_entry_dump(struct drm_printer *p, int index, u32 pat) +{ + drm_printf(p, "PAT[%2d] = [ %u, %u ] (%#8x)\n", index, + REG_FIELD_GET(XELP_MEM_TYPE_MASK, pat), + REG_FIELD_GET(XEHPC_CLOS_LEVEL_MASK, pat), pat); +} + +static void xelpg_pat_entry_dump(struct drm_printer *p, int index, u32 pat) +{ + drm_printf(p, "PAT[%2d] = [ %u, %u ] (%#8x)\n", index, + REG_FIELD_GET(XELPG_L4_POLICY_MASK, pat), + REG_FIELD_GET(XELPG_INDEX_COH_MODE_MASK, pat), pat); +} + struct xe_pat_ops { void (*program_graphics)(struct xe_gt *gt, const struct xe_pat_table_entry table[], int n_entries); @@ -102,9 +132,10 @@ static const struct xe_pat_table_entry xelpg_pat_table[] = { * in the table. * * Note: There is an implicit assumption in the driver that compression and - * coh_1way+ are mutually exclusive. If this is ever not true then userptr - * and imported dma-buf from external device will have uncleared ccs state. See - * also xe_bo_needs_ccs_pages(). + * coh_1way+ are mutually exclusive for platforms prior to Xe3. Starting + * with Xe3, compression can be combined with coherency. If using compression + * with coherency, userptr and imported dma-buf from external device will + * have uncleared ccs state. See also xe_bo_needs_ccs_pages(). */ #define XE2_PAT(no_promote, comp_en, l3clos, l3_policy, l4_policy, __coh_mode) \ { \ @@ -114,8 +145,7 @@ static const struct xe_pat_table_entry xelpg_pat_table[] = { REG_FIELD_PREP(XE2_L3_POLICY, l3_policy) | \ REG_FIELD_PREP(XE2_L4_POLICY, l4_policy) | \ REG_FIELD_PREP(XE2_COH_MODE, __coh_mode), \ - .coh_mode = (BUILD_BUG_ON_ZERO(__coh_mode && comp_en) || __coh_mode) ? \ - XE_COH_AT_LEAST_1WAY : XE_COH_NONE, \ + .coh_mode = __coh_mode ? XE_COH_AT_LEAST_1WAY : XE_COH_NONE, \ .valid = 1 \ } @@ -151,6 +181,38 @@ static const struct xe_pat_table_entry xe2_pat_table[] = { [31] = XE2_PAT( 0, 0, 3, 0, 3, 3 ), }; +static const struct xe_pat_table_entry xe3_lpg_pat_table[] = { + [ 0] = XE2_PAT( 0, 0, 0, 0, 3, 0 ), + [ 1] = XE2_PAT( 0, 0, 0, 0, 3, 2 ), + [ 2] = XE2_PAT( 0, 0, 0, 0, 3, 3 ), + [ 3] = XE2_PAT( 0, 0, 0, 3, 3, 0 ), + [ 4] = XE2_PAT( 0, 0, 0, 3, 0, 2 ), + [ 5] = XE2_PAT( 0, 0, 0, 3, 3, 2 ), + [ 6] = XE2_PAT( 1, 0, 0, 1, 3, 0 ), + [ 7] = XE2_PAT( 0, 0, 0, 3, 0, 3 ), + [ 8] = XE2_PAT( 0, 0, 0, 3, 0, 0 ), + [ 9] = XE2_PAT( 0, 1, 0, 0, 3, 0 ), + [10] = XE2_PAT( 0, 1, 0, 3, 0, 0 ), + [11] = XE2_PAT( 1, 1, 0, 1, 3, 0 ), + [12] = XE2_PAT( 0, 1, 0, 3, 3, 0 ), + [13] = XE2_PAT( 0, 0, 0, 0, 0, 0 ), + [14] = XE2_PAT( 0, 1, 0, 0, 0, 0 ), + [15] = XE2_PAT( 1, 1, 0, 1, 1, 0 ), + [16] = XE2_PAT( 0, 1, 0, 0, 3, 2 ), + /* 17..19 are reserved; leave set to all 0's */ + [20] = XE2_PAT( 0, 0, 1, 0, 3, 0 ), + [21] = XE2_PAT( 0, 1, 1, 0, 3, 0 ), + [22] = XE2_PAT( 0, 0, 1, 0, 3, 2 ), + [23] = XE2_PAT( 0, 0, 1, 0, 3, 3 ), + [24] = XE2_PAT( 0, 0, 2, 0, 3, 0 ), + [25] = XE2_PAT( 0, 1, 2, 0, 3, 0 ), + [26] = XE2_PAT( 0, 0, 2, 0, 3, 2 ), + [27] = XE2_PAT( 0, 0, 2, 0, 3, 3 ), + [28] = XE2_PAT( 0, 0, 3, 0, 3, 0 ), + [29] = XE2_PAT( 0, 1, 3, 0, 3, 0 ), + [30] = XE2_PAT( 0, 0, 3, 0, 3, 2 ), + [31] = XE2_PAT( 0, 0, 3, 0, 3, 3 ), +}; /* Special PAT values programmed outside the main table */ static const struct xe_pat_table_entry xe2_pat_ats = XE2_PAT( 0, 0, 0, 0, 3, 3 ); static const struct xe_pat_table_entry xe2_pat_pta = XE2_PAT( 0, 0, 0, 0, 3, 0 ); @@ -196,6 +258,19 @@ u16 xe_pat_index_get_coh_mode(struct xe_device *xe, u16 pat_index) return xe->pat.table[pat_index].coh_mode; } +bool xe_pat_index_get_comp_en(struct xe_device *xe, u16 pat_index) +{ + WARN_ON(pat_index >= xe->pat.n_entries); + return !!(xe->pat.table[pat_index].value & XE2_COMP_EN); +} + +u16 xe_pat_index_get_l3_policy(struct xe_device *xe, u16 pat_index) +{ + WARN_ON(pat_index >= xe->pat.n_entries); + + return REG_FIELD_GET(XE2_L3_POLICY, xe->pat.table[pat_index].value); +} + static void program_pat(struct xe_gt *gt, const struct xe_pat_table_entry table[], int n_entries) { @@ -233,24 +308,20 @@ static void program_pat_mcr(struct xe_gt *gt, const struct xe_pat_table_entry ta static int xelp_dump(struct xe_gt *gt, struct drm_printer *p) { struct xe_device *xe = gt_to_xe(gt); - unsigned int fw_ref; int i; - fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT); - if (!fw_ref) + CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT); + if (!fw_ref.domains) return -ETIMEDOUT; drm_printf(p, "PAT table:\n"); for (i = 0; i < xe->pat.n_entries; i++) { u32 pat = xe_mmio_read32(>->mmio, XE_REG(_PAT_INDEX(i))); - u8 mem_type = REG_FIELD_GET(XELP_MEM_TYPE_MASK, pat); - drm_printf(p, "PAT[%2d] = %s (%#8x)\n", i, - XELP_MEM_TYPE_STR_MAP[mem_type], pat); + xelp_pat_entry_dump(p, i, pat); } - xe_force_wake_put(gt_to_fw(gt), fw_ref); return 0; } @@ -262,26 +333,20 @@ static const struct xe_pat_ops xelp_pat_ops = { static int xehp_dump(struct xe_gt *gt, struct drm_printer *p) { struct xe_device *xe = gt_to_xe(gt); - unsigned int fw_ref; int i; - fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT); - if (!fw_ref) + CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT); + if (!fw_ref.domains) return -ETIMEDOUT; drm_printf(p, "PAT table:\n"); for (i = 0; i < xe->pat.n_entries; i++) { u32 pat = xe_gt_mcr_unicast_read_any(gt, XE_REG_MCR(_PAT_INDEX(i))); - u8 mem_type; - - mem_type = REG_FIELD_GET(XELP_MEM_TYPE_MASK, pat); - drm_printf(p, "PAT[%2d] = %s (%#8x)\n", i, - XELP_MEM_TYPE_STR_MAP[mem_type], pat); + xelp_pat_entry_dump(p, i, pat); } - xe_force_wake_put(gt_to_fw(gt), fw_ref); return 0; } @@ -293,11 +358,10 @@ static const struct xe_pat_ops xehp_pat_ops = { static int xehpc_dump(struct xe_gt *gt, struct drm_printer *p) { struct xe_device *xe = gt_to_xe(gt); - unsigned int fw_ref; int i; - fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT); - if (!fw_ref) + CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT); + if (!fw_ref.domains) return -ETIMEDOUT; drm_printf(p, "PAT table:\n"); @@ -305,12 +369,9 @@ static int xehpc_dump(struct xe_gt *gt, struct drm_printer *p) for (i = 0; i < xe->pat.n_entries; i++) { u32 pat = xe_gt_mcr_unicast_read_any(gt, XE_REG_MCR(_PAT_INDEX(i))); - drm_printf(p, "PAT[%2d] = [ %u, %u ] (%#8x)\n", i, - REG_FIELD_GET(XELP_MEM_TYPE_MASK, pat), - REG_FIELD_GET(XEHPC_CLOS_LEVEL_MASK, pat), pat); + xehpc_pat_entry_dump(p, i, pat); } - xe_force_wake_put(gt_to_fw(gt), fw_ref); return 0; } @@ -322,11 +383,10 @@ static const struct xe_pat_ops xehpc_pat_ops = { static int xelpg_dump(struct xe_gt *gt, struct drm_printer *p) { struct xe_device *xe = gt_to_xe(gt); - unsigned int fw_ref; int i; - fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT); - if (!fw_ref) + CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT); + if (!fw_ref.domains) return -ETIMEDOUT; drm_printf(p, "PAT table:\n"); @@ -339,12 +399,9 @@ static int xelpg_dump(struct xe_gt *gt, struct drm_printer *p) else pat = xe_gt_mcr_unicast_read_any(gt, XE_REG_MCR(_PAT_INDEX(i))); - drm_printf(p, "PAT[%2d] = [ %u, %u ] (%#8x)\n", i, - REG_FIELD_GET(XELPG_L4_POLICY_MASK, pat), - REG_FIELD_GET(XELPG_INDEX_COH_MODE_MASK, pat), pat); + xelpg_pat_entry_dump(p, i, pat); } - xe_force_wake_put(gt_to_fw(gt), fw_ref); return 0; } @@ -358,15 +415,38 @@ static const struct xe_pat_ops xelpg_pat_ops = { .dump = xelpg_dump, }; +static void xe2_pat_entry_dump(struct drm_printer *p, const char *label, u32 pat, bool rsvd) +{ + drm_printf(p, "%s= [ %u, %u, %u, %u, %u, %u ] (%#8x)%s\n", label, + !!(pat & XE2_NO_PROMOTE), + !!(pat & XE2_COMP_EN), + REG_FIELD_GET(XE2_L3_CLOS, pat), + REG_FIELD_GET(XE2_L3_POLICY, pat), + REG_FIELD_GET(XE2_L4_POLICY, pat), + REG_FIELD_GET(XE2_COH_MODE, pat), + pat, rsvd ? " *" : ""); +} + +static void xe3p_xpc_pat_entry_dump(struct drm_printer *p, const char *label, u32 pat, bool rsvd) +{ + drm_printf(p, "%s= [ %u, %u, %u, %u, %u ] (%#8x)%s\n", label, + !!(pat & XE2_NO_PROMOTE), + REG_FIELD_GET(XE2_L3_CLOS, pat), + REG_FIELD_GET(XE2_L3_POLICY, pat), + REG_FIELD_GET(XE2_L4_POLICY, pat), + REG_FIELD_GET(XE2_COH_MODE, pat), + pat, rsvd ? " *" : ""); +} + static int xe2_dump(struct xe_gt *gt, struct drm_printer *p) { struct xe_device *xe = gt_to_xe(gt); - unsigned int fw_ref; u32 pat; int i; + char label[PAT_LABEL_LEN]; - fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT); - if (!fw_ref) + CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT); + if (!fw_ref.domains) return -ETIMEDOUT; drm_printf(p, "PAT table: (* = reserved entry)\n"); @@ -377,14 +457,8 @@ static int xe2_dump(struct xe_gt *gt, struct drm_printer *p) else pat = xe_gt_mcr_unicast_read_any(gt, XE_REG_MCR(_PAT_INDEX(i))); - drm_printf(p, "PAT[%2d] = [ %u, %u, %u, %u, %u, %u ] (%#8x)%s\n", i, - !!(pat & XE2_NO_PROMOTE), - !!(pat & XE2_COMP_EN), - REG_FIELD_GET(XE2_L3_CLOS, pat), - REG_FIELD_GET(XE2_L3_POLICY, pat), - REG_FIELD_GET(XE2_L4_POLICY, pat), - REG_FIELD_GET(XE2_COH_MODE, pat), - pat, xe->pat.table[i].valid ? "" : " *"); + xe_pat_index_label(label, sizeof(label), i); + xe2_pat_entry_dump(p, label, pat, !xe->pat.table[i].valid); } /* @@ -397,16 +471,8 @@ static int xe2_dump(struct xe_gt *gt, struct drm_printer *p) pat = xe_gt_mcr_unicast_read_any(gt, XE_REG_MCR(_PAT_PTA)); drm_printf(p, "Page Table Access:\n"); - drm_printf(p, "PTA_MODE= [ %u, %u, %u, %u, %u, %u ] (%#8x)\n", - !!(pat & XE2_NO_PROMOTE), - !!(pat & XE2_COMP_EN), - REG_FIELD_GET(XE2_L3_CLOS, pat), - REG_FIELD_GET(XE2_L3_POLICY, pat), - REG_FIELD_GET(XE2_L4_POLICY, pat), - REG_FIELD_GET(XE2_COH_MODE, pat), - pat); + xe2_pat_entry_dump(p, "PTA_MODE", pat, false); - xe_force_wake_put(gt_to_fw(gt), fw_ref); return 0; } @@ -419,12 +485,12 @@ static const struct xe_pat_ops xe2_pat_ops = { static int xe3p_xpc_dump(struct xe_gt *gt, struct drm_printer *p) { struct xe_device *xe = gt_to_xe(gt); - unsigned int fw_ref; u32 pat; int i; + char label[PAT_LABEL_LEN]; - fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT); - if (!fw_ref) + CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT); + if (!fw_ref.domains) return -ETIMEDOUT; drm_printf(p, "PAT table: (* = reserved entry)\n"); @@ -432,13 +498,8 @@ static int xe3p_xpc_dump(struct xe_gt *gt, struct drm_printer *p) for (i = 0; i < xe->pat.n_entries; i++) { pat = xe_gt_mcr_unicast_read_any(gt, XE_REG_MCR(_PAT_INDEX(i))); - drm_printf(p, "PAT[%2d] = [ %u, %u, %u, %u, %u ] (%#8x)%s\n", i, - !!(pat & XE2_NO_PROMOTE), - REG_FIELD_GET(XE2_L3_CLOS, pat), - REG_FIELD_GET(XE2_L3_POLICY, pat), - REG_FIELD_GET(XE2_L4_POLICY, pat), - REG_FIELD_GET(XE2_COH_MODE, pat), - pat, xe->pat.table[i].valid ? "" : " *"); + xe_pat_index_label(label, sizeof(label), i); + xe3p_xpc_pat_entry_dump(p, label, pat, !xe->pat.table[i].valid); } /* @@ -448,15 +509,8 @@ static int xe3p_xpc_dump(struct xe_gt *gt, struct drm_printer *p) pat = xe_gt_mcr_unicast_read_any(gt, XE_REG_MCR(_PAT_PTA)); drm_printf(p, "Page Table Access:\n"); - drm_printf(p, "PTA_MODE= [ %u, %u, %u, %u, %u ] (%#8x)\n", - !!(pat & XE2_NO_PROMOTE), - REG_FIELD_GET(XE2_L3_CLOS, pat), - REG_FIELD_GET(XE2_L3_POLICY, pat), - REG_FIELD_GET(XE2_L4_POLICY, pat), - REG_FIELD_GET(XE2_COH_MODE, pat), - pat); + xe3p_xpc_pat_entry_dump(p, "PTA_MODE", pat, false); - xe_force_wake_put(gt_to_fw(gt), fw_ref); return 0; } @@ -468,6 +522,7 @@ static const struct xe_pat_ops xe3p_xpc_pat_ops = { void xe_pat_init_early(struct xe_device *xe) { + xe->pat.idx[XE_CACHE_WB_COMPRESSION] = XE_PAT_INVALID_IDX; if (GRAPHICS_VERx100(xe) == 3511) { xe->pat.ops = &xe3p_xpc_pat_ops; xe->pat.table = xe3p_xpc_pat_table; @@ -479,7 +534,12 @@ void xe_pat_init_early(struct xe_device *xe) xe->pat.idx[XE_CACHE_WB] = 2; } else if (GRAPHICS_VER(xe) == 30 || GRAPHICS_VER(xe) == 20) { xe->pat.ops = &xe2_pat_ops; - xe->pat.table = xe2_pat_table; + if (GRAPHICS_VER(xe) == 30) { + xe->pat.table = xe3_lpg_pat_table; + xe->pat.idx[XE_CACHE_WB_COMPRESSION] = 16; + } else { + xe->pat.table = xe2_pat_table; + } xe->pat.pat_ats = &xe2_pat_ats; if (IS_DGFX(xe)) xe->pat.pat_pta = &xe2_pat_pta; @@ -578,3 +638,67 @@ int xe_pat_dump(struct xe_gt *gt, struct drm_printer *p) return xe->pat.ops->dump(gt, p); } + +/** + * xe_pat_dump_sw_config() - Dump the software-configured GT PAT table into a drm printer. + * @gt: the &xe_gt + * @p: the &drm_printer + * + * Return: 0 on success or a negative error code on failure. + */ +int xe_pat_dump_sw_config(struct xe_gt *gt, struct drm_printer *p) +{ + struct xe_device *xe = gt_to_xe(gt); + char label[PAT_LABEL_LEN]; + + if (!xe->pat.table || !xe->pat.n_entries) + return -EOPNOTSUPP; + + drm_printf(p, "PAT table:%s\n", GRAPHICS_VER(xe) >= 20 ? " (* = reserved entry)" : ""); + for (u32 i = 0; i < xe->pat.n_entries; i++) { + u32 pat = xe->pat.table[i].value; + + if (GRAPHICS_VERx100(xe) == 3511) { + xe_pat_index_label(label, sizeof(label), i); + xe3p_xpc_pat_entry_dump(p, label, pat, !xe->pat.table[i].valid); + } else if (GRAPHICS_VER(xe) == 30 || GRAPHICS_VER(xe) == 20) { + xe_pat_index_label(label, sizeof(label), i); + xe2_pat_entry_dump(p, label, pat, !xe->pat.table[i].valid); + } else if (xe->info.platform == XE_METEORLAKE) { + xelpg_pat_entry_dump(p, i, pat); + } else if (xe->info.platform == XE_PVC) { + xehpc_pat_entry_dump(p, i, pat); + } else if (xe->info.platform == XE_DG2 || GRAPHICS_VERx100(xe) <= 1210) { + xelp_pat_entry_dump(p, i, pat); + } else { + return -EOPNOTSUPP; + } + } + + if (xe->pat.pat_pta) { + u32 pat = xe->pat.pat_pta->value; + + drm_printf(p, "Page Table Access:\n"); + xe2_pat_entry_dump(p, "PTA_MODE", pat, false); + } + + if (xe->pat.pat_ats) { + u32 pat = xe->pat.pat_ats->value; + + drm_printf(p, "PCIe ATS/PASID:\n"); + xe2_pat_entry_dump(p, "PAT_ATS ", pat, false); + } + + drm_printf(p, "Cache Level:\n"); + drm_printf(p, "IDX[XE_CACHE_NONE] = %d\n", xe->pat.idx[XE_CACHE_NONE]); + drm_printf(p, "IDX[XE_CACHE_WT] = %d\n", xe->pat.idx[XE_CACHE_WT]); + drm_printf(p, "IDX[XE_CACHE_WB] = %d\n", xe->pat.idx[XE_CACHE_WB]); + if (GRAPHICS_VER(xe) >= 20) { + drm_printf(p, "IDX[XE_CACHE_NONE_COMPRESSION] = %d\n", + xe->pat.idx[XE_CACHE_NONE_COMPRESSION]); + drm_printf(p, "IDX[XE_CACHE_WB_COMPRESSION] = %d\n", + xe->pat.idx[XE_CACHE_WB_COMPRESSION]); + } + + return 0; +} diff --git a/drivers/gpu/drm/xe/xe_pat.h b/drivers/gpu/drm/xe/xe_pat.h index 05dae03a5f54..c7e2a53d8cee 100644 --- a/drivers/gpu/drm/xe/xe_pat.h +++ b/drivers/gpu/drm/xe/xe_pat.h @@ -12,6 +12,8 @@ struct drm_printer; struct xe_device; struct xe_gt; +#define XE_PAT_INVALID_IDX U16_MAX + /** * struct xe_pat_table_entry - The pat_index encoding and other meta information. */ @@ -49,6 +51,7 @@ void xe_pat_init_early(struct xe_device *xe); void xe_pat_init(struct xe_gt *gt); int xe_pat_dump(struct xe_gt *gt, struct drm_printer *p); +int xe_pat_dump_sw_config(struct xe_gt *gt, struct drm_printer *p); /** * xe_pat_index_get_coh_mode - Extract the coherency mode for the given @@ -58,4 +61,24 @@ int xe_pat_dump(struct xe_gt *gt, struct drm_printer *p); */ u16 xe_pat_index_get_coh_mode(struct xe_device *xe, u16 pat_index); +/** + * xe_pat_index_get_comp_en - Extract the compression enable flag for + * the given pat_index. + * @xe: xe device + * @pat_index: The pat_index to query + * + * Return: true if compression is enabled for this pat_index, false otherwise. + */ +bool xe_pat_index_get_comp_en(struct xe_device *xe, u16 pat_index); + +#define XE_L3_POLICY_WB 0 /* Write-back */ +#define XE_L3_POLICY_XD 1 /* WB - Transient Display */ +#define XE_L3_POLICY_UC 3 /* Uncached */ +/** + * xe_pat_index_get_l3_policy - Extract the L3 policy for the given pat_index. + * @xe: xe device + * @pat_index: The pat_index to query + */ +u16 xe_pat_index_get_l3_policy(struct xe_device *xe, u16 pat_index); + #endif diff --git a/drivers/gpu/drm/xe/xe_pci.c b/drivers/gpu/drm/xe/xe_pci.c index 9c9ea10d994c..09189ff3da44 100644 --- a/drivers/gpu/drm/xe/xe_pci.c +++ b/drivers/gpu/drm/xe/xe_pci.c @@ -24,9 +24,9 @@ #include "xe_gt.h" #include "xe_gt_sriov_vf.h" #include "xe_guc.h" -#include "xe_macros.h" #include "xe_mmio.h" #include "xe_module.h" +#include "xe_pci_rebar.h" #include "xe_pci_sriov.h" #include "xe_pci_types.h" #include "xe_pm.h" @@ -108,6 +108,7 @@ static const struct xe_graphics_desc graphics_xe2 = { static const struct xe_graphics_desc graphics_xe3p_xpc = { XE2_GFX_FEATURES, + .has_indirect_ring_state = 1, .hw_engine_mask = GENMASK(XE_HW_ENGINE_BCS8, XE_HW_ENGINE_BCS1) | GENMASK(XE_HW_ENGINE_CCS3, XE_HW_ENGINE_CCS0), @@ -168,6 +169,7 @@ static const struct xe_device_desc tgl_desc = { .pre_gmdid_media_ip = &media_ip_xem, PLATFORM(TIGERLAKE), .dma_mask_size = 39, + .has_cached_pt = true, .has_display = true, .has_llc = true, .has_sriov = true, @@ -182,6 +184,7 @@ static const struct xe_device_desc rkl_desc = { .pre_gmdid_media_ip = &media_ip_xem, PLATFORM(ROCKETLAKE), .dma_mask_size = 39, + .has_cached_pt = true, .has_display = true, .has_llc = true, .max_gt_per_tile = 1, @@ -197,6 +200,7 @@ static const struct xe_device_desc adl_s_desc = { .pre_gmdid_media_ip = &media_ip_xem, PLATFORM(ALDERLAKE_S), .dma_mask_size = 39, + .has_cached_pt = true, .has_display = true, .has_llc = true, .has_sriov = true, @@ -217,6 +221,7 @@ static const struct xe_device_desc adl_p_desc = { .pre_gmdid_media_ip = &media_ip_xem, PLATFORM(ALDERLAKE_P), .dma_mask_size = 39, + .has_cached_pt = true, .has_display = true, .has_llc = true, .has_sriov = true, @@ -235,6 +240,7 @@ static const struct xe_device_desc adl_n_desc = { .pre_gmdid_media_ip = &media_ip_xem, PLATFORM(ALDERLAKE_N), .dma_mask_size = 39, + .has_cached_pt = true, .has_display = true, .has_llc = true, .has_sriov = true, @@ -359,9 +365,13 @@ static const struct xe_device_desc bmg_desc = { .has_fan_control = true, .has_flat_ccs = 1, .has_mbx_power_limits = true, + .has_mbx_thermal_info = true, .has_gsc_nvm = 1, .has_heci_cscfi = 1, + .has_i2c = true, .has_late_bind = true, + .has_pre_prod_wa = 1, + .has_soc_remapper_telem = true, .has_sriov = true, .has_mem_copy_instr = true, .max_gt_per_tile = 2, @@ -381,6 +391,8 @@ static const struct xe_device_desc ptl_desc = { .has_flat_ccs = 1, .has_sriov = true, .has_mem_copy_instr = true, + .has_pre_prod_wa = 1, + .has_pxp = true, .max_gt_per_tile = 2, .needs_scratch = true, .needs_shared_vf_gt_wq = true, @@ -394,6 +406,7 @@ static const struct xe_device_desc nvls_desc = { .has_display = true, .has_flat_ccs = 1, .has_mem_copy_instr = true, + .has_pre_prod_wa = 1, .max_gt_per_tile = 2, .require_force_probe = true, .va_bits = 48, @@ -406,7 +419,14 @@ static const struct xe_device_desc cri_desc = { .dma_mask_size = 52, .has_display = false, .has_flat_ccs = false, + .has_gsc_nvm = 1, + .has_i2c = true, .has_mbx_power_limits = true, + .has_mbx_thermal_info = true, + .has_mert = true, + .has_pre_prod_wa = 1, + .has_soc_remapper_sysctrl = true, + .has_soc_remapper_telem = true, .has_sriov = true, .max_gt_per_tile = 2, .require_force_probe = true, @@ -663,16 +683,24 @@ static int xe_info_init_early(struct xe_device *xe, xe->info.vram_flags = desc->vram_flags; xe->info.is_dgfx = desc->is_dgfx; + xe->info.has_cached_pt = desc->has_cached_pt; xe->info.has_fan_control = desc->has_fan_control; /* runtime fusing may force flat_ccs to disabled later */ xe->info.has_flat_ccs = desc->has_flat_ccs; xe->info.has_mbx_power_limits = desc->has_mbx_power_limits; + xe->info.has_mbx_thermal_info = desc->has_mbx_thermal_info; xe->info.has_gsc_nvm = desc->has_gsc_nvm; xe->info.has_heci_gscfi = desc->has_heci_gscfi; xe->info.has_heci_cscfi = desc->has_heci_cscfi; + xe->info.has_i2c = desc->has_i2c; xe->info.has_late_bind = desc->has_late_bind; xe->info.has_llc = desc->has_llc; + xe->info.has_mert = desc->has_mert; + xe->info.has_page_reclaim_hw_assist = desc->has_page_reclaim_hw_assist; + xe->info.has_pre_prod_wa = desc->has_pre_prod_wa; xe->info.has_pxp = desc->has_pxp; + xe->info.has_soc_remapper_sysctrl = desc->has_soc_remapper_sysctrl; + xe->info.has_soc_remapper_telem = desc->has_soc_remapper_telem; xe->info.has_sriov = xe_configfs_primary_gt_allowed(to_pci_dev(xe->drm.dev)) && desc->has_sriov; xe->info.has_mem_copy_instr = desc->has_mem_copy_instr; @@ -755,6 +783,7 @@ static struct xe_gt *alloc_primary_gt(struct xe_tile *tile, gt->info.type = XE_GT_TYPE_MAIN; gt->info.id = tile->id * xe->info.max_gt_per_tile; gt->info.has_indirect_ring_state = graphics_desc->has_indirect_ring_state; + gt->info.multi_queue_engine_class_mask = graphics_desc->multi_queue_engine_class_mask; gt->info.engine_mask = graphics_desc->hw_engine_mask; /* @@ -996,7 +1025,7 @@ static int xe_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (err) return err; - xe_vram_resize_bar(xe); + xe_pci_rebar_resize(xe); err = xe_device_probe_early(xe); /* @@ -1153,6 +1182,15 @@ static int xe_pci_runtime_suspend(struct device *dev) struct xe_device *xe = pdev_to_xe_device(pdev); int err; + /* + * We hold an additional reference to the runtime PM to keep PF in D0 + * during VFs lifetime, as our VFs do not implement the PM capability. + * This means we should never be runtime suspending as long as VFs are + * enabled. + */ + xe_assert(xe, !IS_SRIOV_VF(xe)); + xe_assert(xe, !pci_num_vf(pdev)); + err = xe_pm_runtime_suspend(xe); if (err) return err; diff --git a/drivers/gpu/drm/xe/xe_pci_rebar.c b/drivers/gpu/drm/xe/xe_pci_rebar.c new file mode 100644 index 000000000000..7e2c7079b6ff --- /dev/null +++ b/drivers/gpu/drm/xe/xe_pci_rebar.c @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright © 2025 Intel Corporation + */ + +#include <linux/pci.h> +#include <linux/types.h> + +#include "regs/xe_bars.h" +#include "xe_device_types.h" +#include "xe_module.h" +#include "xe_pci_rebar.h" +#include "xe_printk.h" + +static void resize_bar(struct xe_device *xe, int resno, resource_size_t size) +{ + struct pci_dev *pdev = to_pci_dev(xe->drm.dev); + int bar_size = pci_rebar_bytes_to_size(size); + int ret; + + ret = pci_resize_resource(pdev, resno, bar_size, 0); + if (ret) { + xe_info(xe, "Failed to resize BAR%d to %dMiB (%pe). Consider enabling 'Resizable BAR' support in your BIOS\n", + resno, 1 << bar_size, ERR_PTR(ret)); + return; + } + + xe_info(xe, "BAR%d resized to %dMiB\n", resno, 1 << bar_size); +} + +/* + * xe_pci_rebar_resize - Resize the LMEMBAR + * @xe: xe device instance + * + * If vram_bar_size module param is set, attempt to set to the requested size + * else set to maximum possible size. + */ +void xe_pci_rebar_resize(struct xe_device *xe) +{ + int force_vram_bar_size = xe_modparam.force_vram_bar_size; + struct pci_dev *pdev = to_pci_dev(xe->drm.dev); + struct pci_bus *root = pdev->bus; + resource_size_t current_size; + resource_size_t rebar_size; + struct resource *root_res; + int max_size, i; + u32 pci_cmd; + + /* gather some relevant info */ + current_size = pci_resource_len(pdev, LMEM_BAR); + + if (force_vram_bar_size < 0) + return; + + /* set to a specific size? */ + if (force_vram_bar_size) { + rebar_size = pci_rebar_bytes_to_size(force_vram_bar_size * + (resource_size_t)SZ_1M); + + if (!pci_rebar_size_supported(pdev, LMEM_BAR, rebar_size)) { + xe_info(xe, "Requested size %lluMiB is not supported by rebar sizes: 0x%llx. Leaving default: %lluMiB\n", + (u64)pci_rebar_size_to_bytes(rebar_size) >> ilog2(SZ_1M), + pci_rebar_get_possible_sizes(pdev, LMEM_BAR), + (u64)current_size >> ilog2(SZ_1M)); + return; + } + + rebar_size = pci_rebar_size_to_bytes(rebar_size); + if (rebar_size == current_size) + return; + } else { + max_size = pci_rebar_get_max_size(pdev, LMEM_BAR); + if (max_size < 0) + return; + rebar_size = pci_rebar_size_to_bytes(max_size); + + /* only resize if larger than current */ + if (rebar_size <= current_size) + return; + } + + xe_info(xe, "Attempting to resize bar from %lluMiB -> %lluMiB\n", + (u64)current_size >> ilog2(SZ_1M), (u64)rebar_size >> ilog2(SZ_1M)); + + while (root->parent) + root = root->parent; + + pci_bus_for_each_resource(root, root_res, i) { + if (root_res && root_res->flags & (IORESOURCE_MEM | IORESOURCE_MEM_64) && + (u64)root_res->start > 0x100000000ul) + break; + } + + if (!root_res) { + xe_info(xe, "Can't resize VRAM BAR - platform support is missing. Consider enabling 'Resizable BAR' support in your BIOS\n"); + return; + } + + pci_read_config_dword(pdev, PCI_COMMAND, &pci_cmd); + pci_write_config_dword(pdev, PCI_COMMAND, pci_cmd & ~PCI_COMMAND_MEMORY); + + resize_bar(xe, LMEM_BAR, rebar_size); + + pci_assign_unassigned_bus_resources(pdev->bus); + pci_write_config_dword(pdev, PCI_COMMAND, pci_cmd); +} diff --git a/drivers/gpu/drm/xe/xe_pci_rebar.h b/drivers/gpu/drm/xe/xe_pci_rebar.h new file mode 100644 index 000000000000..8677921ac363 --- /dev/null +++ b/drivers/gpu/drm/xe/xe_pci_rebar.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright © 2025 Intel Corporation + */ + +#ifndef _XE_PCI_REBAR_H_ +#define _XE_PCI_REBAR_H_ + +struct xe_device; + +void xe_pci_rebar_resize(struct xe_device *xe); + +#endif diff --git a/drivers/gpu/drm/xe/xe_pci_sriov.c b/drivers/gpu/drm/xe/xe_pci_sriov.c index 9ff69c4843b0..3fd22034f03e 100644 --- a/drivers/gpu/drm/xe/xe_pci_sriov.c +++ b/drivers/gpu/drm/xe/xe_pci_sriov.c @@ -219,7 +219,6 @@ static int pf_disable_vfs(struct xe_device *xe) int xe_pci_sriov_configure(struct pci_dev *pdev, int num_vfs) { struct xe_device *xe = pdev_to_xe_device(pdev); - int ret; if (!IS_SRIOV_PF(xe)) return -ENODEV; @@ -233,14 +232,11 @@ int xe_pci_sriov_configure(struct pci_dev *pdev, int num_vfs) if (num_vfs && pci_num_vf(pdev)) return -EBUSY; - xe_pm_runtime_get(xe); + guard(xe_pm_runtime)(xe); if (num_vfs > 0) - ret = pf_enable_vfs(xe, num_vfs); + return pf_enable_vfs(xe, num_vfs); else - ret = pf_disable_vfs(xe); - xe_pm_runtime_put(xe); - - return ret; + return pf_disable_vfs(xe); } /** diff --git a/drivers/gpu/drm/xe/xe_pci_types.h b/drivers/gpu/drm/xe/xe_pci_types.h index 9892c063a9c5..20acc5349ee6 100644 --- a/drivers/gpu/drm/xe/xe_pci_types.h +++ b/drivers/gpu/drm/xe/xe_pci_types.h @@ -37,17 +37,25 @@ struct xe_device_desc { u8 require_force_probe:1; u8 is_dgfx:1; + u8 has_cached_pt:1; u8 has_display:1; u8 has_fan_control:1; u8 has_flat_ccs:1; u8 has_gsc_nvm:1; u8 has_heci_gscfi:1; u8 has_heci_cscfi:1; + u8 has_i2c:1; u8 has_late_bind:1; u8 has_llc:1; u8 has_mbx_power_limits:1; + u8 has_mbx_thermal_info:1; u8 has_mem_copy_instr:1; + u8 has_mert:1; + u8 has_pre_prod_wa:1; + u8 has_page_reclaim_hw_assist:1; u8 has_pxp:1; + u8 has_soc_remapper_sysctrl:1; + u8 has_soc_remapper_telem:1; u8 has_sriov:1; u8 needs_scratch:1; u8 skip_guc_pc:1; @@ -58,6 +66,7 @@ struct xe_device_desc { struct xe_graphics_desc { u64 hw_engine_mask; /* hardware engines provided by graphics IP */ + u16 multi_queue_engine_class_mask; /* bitmask of engine classes which support multi queue */ u8 has_asid:1; u8 has_atomic_enable_pte_bit:1; diff --git a/drivers/gpu/drm/xe/xe_pcode_api.h b/drivers/gpu/drm/xe/xe_pcode_api.h index 70dcd6625680..85cc7478b787 100644 --- a/drivers/gpu/drm/xe/xe_pcode_api.h +++ b/drivers/gpu/drm/xe/xe_pcode_api.h @@ -50,6 +50,13 @@ #define READ_PL_FROM_FW 0x1 #define READ_PL_FROM_PCODE 0x0 +#define PCODE_THERMAL_INFO 0x25 +#define READ_THERMAL_LIMITS 0x0 +#define READ_THERMAL_CONFIG 0x1 +#define READ_THERMAL_DATA 0x2 +#define PCIE_SENSOR_GROUP_ID 0x2 +#define PCIE_SENSOR_MASK REG_GENMASK(31, 16) + #define PCODE_LATE_BINDING 0x5C #define GET_CAPABILITY_STATUS 0x0 #define V1_FAN_SUPPORTED REG_BIT(0) @@ -77,11 +84,13 @@ #define PCODE_SCRATCH(x) XE_REG(0x138320 + ((x) * 4)) /* PCODE_SCRATCH0 */ +#define BREADCRUMB_VERSION REG_GENMASK(31, 29) #define AUXINFO_REG_OFFSET REG_GENMASK(17, 15) #define OVERFLOW_REG_OFFSET REG_GENMASK(14, 12) #define HISTORY_TRACKING REG_BIT(11) #define OVERFLOW_SUPPORT REG_BIT(10) #define AUXINFO_SUPPORT REG_BIT(9) +#define FDO_MODE REG_BIT(4) #define BOOT_STATUS REG_GENMASK(3, 1) #define CRITICAL_FAILURE 4 #define NON_CRITICAL_FAILURE 7 diff --git a/drivers/gpu/drm/xe/xe_pm.c b/drivers/gpu/drm/xe/xe_pm.c index 766922530265..559cf5490ac0 100644 --- a/drivers/gpu/drm/xe/xe_pm.c +++ b/drivers/gpu/drm/xe/xe_pm.c @@ -260,10 +260,19 @@ int xe_pm_resume(struct xe_device *xe) xe_irq_resume(xe); - for_each_gt(gt, xe, id) - xe_gt_resume(gt); + for_each_gt(gt, xe, id) { + err = xe_gt_resume(gt); + if (err) + break; + } + /* + * Try to bring up display before bailing from GT resume failure, + * so we don't leave the user clueless with a blank screen. + */ xe_display_pm_resume(xe); + if (err) + goto err; err = xe_bo_restore_late(xe); if (err) @@ -591,7 +600,7 @@ int xe_pm_runtime_suspend(struct xe_device *xe) } for_each_gt(gt, xe, id) { - err = xe_gt_suspend(gt); + err = xe->d3cold.allowed ? xe_gt_suspend(gt) : xe_gt_runtime_suspend(gt); if (err) goto out_resume; } @@ -633,10 +642,10 @@ int xe_pm_runtime_resume(struct xe_device *xe) xe_rpm_lockmap_acquire(xe); - for_each_gt(gt, xe, id) - xe_gt_idle_disable_c6(gt); - if (xe->d3cold.allowed) { + for_each_gt(gt, xe, id) + xe_gt_idle_disable_c6(gt); + err = xe_pcode_ready(xe, true); if (err) goto out; @@ -656,10 +665,19 @@ int xe_pm_runtime_resume(struct xe_device *xe) xe_irq_resume(xe); - for_each_gt(gt, xe, id) - xe_gt_resume(gt); + for_each_gt(gt, xe, id) { + err = xe->d3cold.allowed ? xe_gt_resume(gt) : xe_gt_runtime_resume(gt); + if (err) + break; + } + /* + * Try to bring up display before bailing from GT resume failure, + * so we don't leave the user clueless with a blank screen. + */ xe_display_pm_runtime_resume(xe); + if (err) + goto out; if (xe->d3cold.allowed) { err = xe_bo_restore_late(xe); diff --git a/drivers/gpu/drm/xe/xe_pmu.c b/drivers/gpu/drm/xe/xe_pmu.c index c63335eb69e5..0b20059dd7b3 100644 --- a/drivers/gpu/drm/xe/xe_pmu.c +++ b/drivers/gpu/drm/xe/xe_pmu.c @@ -425,7 +425,7 @@ static ssize_t event_attr_show(struct device *dev, struct perf_pmu_events_attr *pmu_attr = container_of(attr, struct perf_pmu_events_attr, attr); - return sprintf(buf, "event=%#04llx\n", pmu_attr->id); + return sysfs_emit(buf, "event=%#04llx\n", pmu_attr->id); } #define XE_EVENT_ATTR(name_, v_, id_) \ diff --git a/drivers/gpu/drm/xe/xe_psmi.c b/drivers/gpu/drm/xe/xe_psmi.c index 6a54e38b81ba..899b01f72ba3 100644 --- a/drivers/gpu/drm/xe/xe_psmi.c +++ b/drivers/gpu/drm/xe/xe_psmi.c @@ -6,7 +6,7 @@ #include <linux/debugfs.h> #include "xe_bo.h" -#include "xe_device.h" +#include "xe_device_types.h" #include "xe_configfs.h" #include "xe_psmi.h" diff --git a/drivers/gpu/drm/xe/xe_pt.c b/drivers/gpu/drm/xe/xe_pt.c index 884127b4d97d..6703a7049227 100644 --- a/drivers/gpu/drm/xe/xe_pt.c +++ b/drivers/gpu/drm/xe/xe_pt.c @@ -11,7 +11,9 @@ #include "xe_drm_client.h" #include "xe_exec_queue.h" #include "xe_gt.h" +#include "xe_gt_stats.h" #include "xe_migrate.h" +#include "xe_page_reclaim.h" #include "xe_pt_types.h" #include "xe_pt_walk.h" #include "xe_res_cursor.h" @@ -1535,6 +1537,9 @@ struct xe_pt_stage_unbind_walk { /** @modified_end: Walk range start, modified like @modified_start. */ u64 modified_end; + /** @prl: Backing pointer to page reclaim list in pt_update_ops */ + struct xe_page_reclaim_list *prl; + /* Output */ /* @wupd: Structure to track the page-table updates we're building */ struct xe_walk_update wupd; @@ -1572,6 +1577,69 @@ static bool xe_pt_check_kill(u64 addr, u64 next, unsigned int level, return false; } +/* page_size = 2^(reclamation_size + XE_PTE_SHIFT) */ +#define COMPUTE_RECLAIM_ADDRESS_MASK(page_size) \ +({ \ + BUILD_BUG_ON(!__builtin_constant_p(page_size)); \ + ilog2(page_size) - XE_PTE_SHIFT; \ +}) + +static int generate_reclaim_entry(struct xe_tile *tile, + struct xe_page_reclaim_list *prl, + u64 pte, struct xe_pt *xe_child) +{ + struct xe_gt *gt = tile->primary_gt; + struct xe_guc_page_reclaim_entry *reclaim_entries = prl->entries; + u64 phys_addr = pte & XE_PTE_ADDR_MASK; + u64 phys_page = phys_addr >> XE_PTE_SHIFT; + int num_entries = prl->num_entries; + u32 reclamation_size; + + xe_tile_assert(tile, xe_child->level <= MAX_HUGEPTE_LEVEL); + xe_tile_assert(tile, reclaim_entries); + xe_tile_assert(tile, num_entries < XE_PAGE_RECLAIM_MAX_ENTRIES - 1); + + if (!xe_page_reclaim_list_valid(prl)) + return -EINVAL; + + /** + * reclamation_size indicates the size of the page to be + * invalidated and flushed from non-coherent cache. + * Page size is computed as 2^(reclamation_size + XE_PTE_SHIFT) bytes. + * Only 4K, 64K (level 0), and 2M pages are supported by hardware for page reclaim + */ + if (xe_child->level == 0 && !(pte & XE_PTE_PS64)) { + xe_gt_stats_incr(gt, XE_GT_STATS_ID_PRL_4K_ENTRY_COUNT, 1); + reclamation_size = COMPUTE_RECLAIM_ADDRESS_MASK(SZ_4K); /* reclamation_size = 0 */ + xe_tile_assert(tile, phys_addr % SZ_4K == 0); + } else if (xe_child->level == 0) { + xe_gt_stats_incr(gt, XE_GT_STATS_ID_PRL_64K_ENTRY_COUNT, 1); + reclamation_size = COMPUTE_RECLAIM_ADDRESS_MASK(SZ_64K); /* reclamation_size = 4 */ + xe_tile_assert(tile, phys_addr % SZ_64K == 0); + } else if (xe_child->level == 1 && pte & XE_PDE_PS_2M) { + xe_gt_stats_incr(gt, XE_GT_STATS_ID_PRL_2M_ENTRY_COUNT, 1); + reclamation_size = COMPUTE_RECLAIM_ADDRESS_MASK(SZ_2M); /* reclamation_size = 9 */ + xe_tile_assert(tile, phys_addr % SZ_2M == 0); + } else { + xe_page_reclaim_list_abort(tile->primary_gt, prl, + "unsupported PTE level=%u pte=%#llx", + xe_child->level, pte); + return -EINVAL; + } + + reclaim_entries[num_entries].qw = + FIELD_PREP(XE_PAGE_RECLAIM_VALID, 1) | + FIELD_PREP(XE_PAGE_RECLAIM_SIZE, reclamation_size) | + FIELD_PREP(XE_PAGE_RECLAIM_ADDR_LO, phys_page) | + FIELD_PREP(XE_PAGE_RECLAIM_ADDR_HI, phys_page >> 20); + prl->num_entries++; + vm_dbg(&tile_to_xe(tile)->drm, + "PRL add entry: level=%u pte=%#llx reclamation_size=%u prl_idx=%d\n", + xe_child->level, pte, reclamation_size, num_entries); + + return 0; +} + static int xe_pt_stage_unbind_entry(struct xe_ptw *parent, pgoff_t offset, unsigned int level, u64 addr, u64 next, struct xe_ptw **child, @@ -1579,11 +1647,78 @@ static int xe_pt_stage_unbind_entry(struct xe_ptw *parent, pgoff_t offset, struct xe_pt_walk *walk) { struct xe_pt *xe_child = container_of(*child, typeof(*xe_child), base); + struct xe_pt_stage_unbind_walk *xe_walk = + container_of(walk, typeof(*xe_walk), base); + struct xe_device *xe = tile_to_xe(xe_walk->tile); + pgoff_t first = xe_pt_offset(addr, xe_child->level, walk); + bool killed; XE_WARN_ON(!*child); XE_WARN_ON(!level); + /* Check for leaf node */ + if (xe_walk->prl && xe_page_reclaim_list_valid(xe_walk->prl) && + (!xe_child->base.children || !xe_child->base.children[first])) { + struct iosys_map *leaf_map = &xe_child->bo->vmap; + pgoff_t count = xe_pt_num_entries(addr, next, xe_child->level, walk); - xe_pt_check_kill(addr, next, level - 1, xe_child, action, walk); + for (pgoff_t i = 0; i < count; i++) { + u64 pte = xe_map_rd(xe, leaf_map, (first + i) * sizeof(u64), u64); + int ret; + + /* + * In rare scenarios, pte may not be written yet due to racy conditions. + * In such cases, invalidate the PRL and fallback to full PPC invalidation. + */ + if (!pte) { + xe_page_reclaim_list_abort(xe_walk->tile->primary_gt, xe_walk->prl, + "found zero pte at addr=%#llx", addr); + break; + } + + /* Ensure it is a defined page */ + xe_tile_assert(xe_walk->tile, + xe_child->level == 0 || + (pte & (XE_PTE_PS64 | XE_PDE_PS_2M | XE_PDPE_PS_1G))); + + /* An entry should be added for 64KB but contigious 4K have XE_PTE_PS64 */ + if (pte & XE_PTE_PS64) + i += 15; /* Skip other 15 consecutive 4K pages in the 64K page */ + + /* Account for NULL terminated entry on end (-1) */ + if (xe_walk->prl->num_entries < XE_PAGE_RECLAIM_MAX_ENTRIES - 1) { + ret = generate_reclaim_entry(xe_walk->tile, xe_walk->prl, + pte, xe_child); + if (ret) + break; + } else { + /* overflow, mark as invalid */ + xe_page_reclaim_list_abort(xe_walk->tile->primary_gt, xe_walk->prl, + "overflow while adding pte=%#llx", + pte); + break; + } + } + } + + killed = xe_pt_check_kill(addr, next, level - 1, xe_child, action, walk); + + /* + * Verify PRL is active and if entry is not a leaf pte (base.children conditions), + * there is a potential need to invalidate the PRL if any PTE (num_live) are dropped. + */ + if (xe_walk->prl && level > 1 && xe_child->num_live && + xe_child->base.children && xe_child->base.children[first]) { + bool covered = xe_pt_covers(addr, next, xe_child->level, &xe_walk->base); + + /* + * If aborting page walk early (kill) or page walk completes the full range + * we need to invalidate the PRL. + */ + if (killed || covered) + xe_page_reclaim_list_abort(xe_walk->tile->primary_gt, xe_walk->prl, + "kill at level=%u addr=%#llx next=%#llx num_live=%u", + level, addr, next, xe_child->num_live); + } return 0; } @@ -1654,6 +1789,8 @@ static unsigned int xe_pt_stage_unbind(struct xe_tile *tile, { u64 start = range ? xe_svm_range_start(range) : xe_vma_start(vma); u64 end = range ? xe_svm_range_end(range) : xe_vma_end(vma); + struct xe_vm_pgtable_update_op *pt_update_op = + container_of(entries, struct xe_vm_pgtable_update_op, entries[0]); struct xe_pt_stage_unbind_walk xe_walk = { .base = { .ops = &xe_pt_stage_unbind_ops, @@ -1665,6 +1802,7 @@ static unsigned int xe_pt_stage_unbind(struct xe_tile *tile, .modified_start = start, .modified_end = end, .wupd.entries = entries, + .prl = pt_update_op->prl, }; struct xe_pt *pt = vm->pt_root[tile->id]; @@ -1897,6 +2035,7 @@ static int unbind_op_prepare(struct xe_tile *tile, struct xe_vm_pgtable_update_ops *pt_update_ops, struct xe_vma *vma) { + struct xe_device *xe = tile_to_xe(tile); u32 current_op = pt_update_ops->current_op; struct xe_vm_pgtable_update_op *pt_op = &pt_update_ops->ops[current_op]; int err; @@ -1914,6 +2053,17 @@ static int unbind_op_prepare(struct xe_tile *tile, pt_op->vma = vma; pt_op->bind = false; pt_op->rebind = false; + /* + * Maintain one PRL located in pt_update_ops that all others in unbind op reference. + * Ensure that PRL is allocated only once, and if invalidated, remains an invalidated PRL. + */ + if (xe->info.has_page_reclaim_hw_assist && + xe_page_reclaim_list_is_new(&pt_update_ops->prl)) + xe_page_reclaim_list_alloc_entries(&pt_update_ops->prl); + + /* Page reclaim may not be needed due to other features, so skip the corresponding VMA */ + pt_op->prl = (xe_page_reclaim_list_valid(&pt_update_ops->prl) && + !xe_page_reclaim_skip(tile, vma)) ? &pt_update_ops->prl : NULL; err = vma_reserve_fences(tile_to_xe(tile), vma); if (err) @@ -1979,6 +2129,7 @@ static int unbind_range_prepare(struct xe_vm *vm, pt_op->vma = XE_INVALID_VMA; pt_op->bind = false; pt_op->rebind = false; + pt_op->prl = NULL; pt_op->num_entries = xe_pt_stage_unbind(tile, vm, NULL, range, pt_op->entries); @@ -2096,6 +2247,7 @@ xe_pt_update_ops_init(struct xe_vm_pgtable_update_ops *pt_update_ops) init_llist_head(&pt_update_ops->deferred); pt_update_ops->start = ~0x0ull; pt_update_ops->last = 0x0ull; + xe_page_reclaim_list_init(&pt_update_ops->prl); } /** @@ -2393,6 +2545,17 @@ xe_pt_update_ops_run(struct xe_tile *tile, struct xe_vma_ops *vops) goto kill_vm_tile1; } update.ijob = ijob; + /* + * Only add page reclaim for the primary GT. Media GT does not have + * any PPC to flush, so enabling the PPC flush bit for media is + * effectively a NOP and provides no performance benefit nor + * interfere with primary GT. + */ + if (xe_page_reclaim_list_valid(&pt_update_ops->prl)) { + xe_tlb_inval_job_add_page_reclaim(ijob, &pt_update_ops->prl); + /* Release ref from alloc, job will now handle it */ + xe_page_reclaim_list_invalidate(&pt_update_ops->prl); + } if (tile->media_gt) { dep_scheduler = to_dep_scheduler(q, tile->media_gt); @@ -2518,6 +2681,8 @@ void xe_pt_update_ops_fini(struct xe_tile *tile, struct xe_vma_ops *vops) &vops->pt_update_ops[tile->id]; int i; + xe_page_reclaim_entries_put(pt_update_ops->prl.entries); + lockdep_assert_held(&vops->vm->lock); xe_vm_assert_held(vops->vm); diff --git a/drivers/gpu/drm/xe/xe_pt_types.h b/drivers/gpu/drm/xe/xe_pt_types.h index 881f01e14db8..84b51d3762a4 100644 --- a/drivers/gpu/drm/xe/xe_pt_types.h +++ b/drivers/gpu/drm/xe/xe_pt_types.h @@ -8,6 +8,7 @@ #include <linux/types.h> +#include "xe_page_reclaim.h" #include "xe_pt_walk.h" struct xe_bo; @@ -19,6 +20,7 @@ enum xe_cache_level { XE_CACHE_WT, XE_CACHE_WB, XE_CACHE_NONE_COMPRESSION, /*UC + COH_NONE + COMPRESSION */ + XE_CACHE_WB_COMPRESSION, __XE_CACHE_LEVEL_COUNT, }; @@ -79,6 +81,8 @@ struct xe_vm_pgtable_update_op { struct xe_vm_pgtable_update entries[XE_VM_MAX_LEVEL * 2 + 1]; /** @vma: VMA for operation, operation not valid if NULL */ struct xe_vma *vma; + /** @prl: Backing pointer to page reclaim list of pt_update_ops */ + struct xe_page_reclaim_list *prl; /** @num_entries: number of entries for this update operation */ u32 num_entries; /** @bind: is a bind */ @@ -95,6 +99,8 @@ struct xe_vm_pgtable_update_ops { struct llist_head deferred; /** @q: exec queue for PT operations */ struct xe_exec_queue *q; + /** @prl: embedded page reclaim list */ + struct xe_page_reclaim_list prl; /** @start: start address of ops */ u64 start; /** @last: last address of ops */ diff --git a/drivers/gpu/drm/xe/xe_pxp.c b/drivers/gpu/drm/xe/xe_pxp.c index bdbdbbf6a678..d61446bf9c19 100644 --- a/drivers/gpu/drm/xe/xe_pxp.c +++ b/drivers/gpu/drm/xe/xe_pxp.c @@ -15,7 +15,6 @@ #include "xe_force_wake.h" #include "xe_guc_submit.h" #include "xe_gsc_proxy.h" -#include "xe_gt.h" #include "xe_gt_types.h" #include "xe_huc.h" #include "xe_mmio.h" @@ -58,10 +57,9 @@ bool xe_pxp_is_enabled(const struct xe_pxp *pxp) static bool pxp_prerequisites_done(const struct xe_pxp *pxp) { struct xe_gt *gt = pxp->gt; - unsigned int fw_ref; bool ready; - fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FORCEWAKE_ALL); + CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FORCEWAKE_ALL); /* * If force_wake fails we could falsely report the prerequisites as not @@ -71,14 +69,12 @@ static bool pxp_prerequisites_done(const struct xe_pxp *pxp) * PXP. Therefore, we can just log the force_wake error and not escalate * it. */ - XE_WARN_ON(!xe_force_wake_ref_has_domain(fw_ref, XE_FORCEWAKE_ALL)); + XE_WARN_ON(!xe_force_wake_ref_has_domain(fw_ref.domains, XE_FORCEWAKE_ALL)); /* PXP requires both HuC authentication via GSC and GSC proxy initialized */ ready = xe_huc_is_authenticated(>->uc.huc, XE_HUC_AUTH_VIA_GSC) && xe_gsc_proxy_init_done(>->uc.gsc); - xe_force_wake_put(gt_to_fw(gt), fw_ref); - return ready; } @@ -104,13 +100,12 @@ int xe_pxp_get_readiness_status(struct xe_pxp *pxp) xe_uc_fw_status_to_error(pxp->gt->uc.gsc.fw.status)) return -EIO; - xe_pm_runtime_get(pxp->xe); + guard(xe_pm_runtime)(pxp->xe); /* PXP requires both HuC loaded and GSC proxy initialized */ if (pxp_prerequisites_done(pxp)) ret = 1; - xe_pm_runtime_put(pxp->xe); return ret; } @@ -135,35 +130,28 @@ static void pxp_invalidate_queues(struct xe_pxp *pxp); static int pxp_terminate_hw(struct xe_pxp *pxp) { struct xe_gt *gt = pxp->gt; - unsigned int fw_ref; int ret = 0; drm_dbg(&pxp->xe->drm, "Terminating PXP\n"); - fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT); - if (!xe_force_wake_ref_has_domain(fw_ref, XE_FW_GT)) { - ret = -EIO; - goto out; - } + CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT); + if (!xe_force_wake_ref_has_domain(fw_ref.domains, XE_FW_GT)) + return -EIO; /* terminate the hw session */ ret = xe_pxp_submit_session_termination(pxp, ARB_SESSION); if (ret) - goto out; + return ret; ret = pxp_wait_for_session_state(pxp, ARB_SESSION, false); if (ret) - goto out; + return ret; /* Trigger full HW cleanup */ xe_mmio_write32(>->mmio, KCR_GLOBAL_TERMINATE, 1); /* now we can tell the GSC to clean up its own state */ - ret = xe_pxp_submit_session_invalidation(&pxp->gsc_res, ARB_SESSION); - -out: - xe_force_wake_put(gt_to_fw(gt), fw_ref); - return ret; + return xe_pxp_submit_session_invalidation(&pxp->gsc_res, ARB_SESSION); } static void mark_termination_in_progress(struct xe_pxp *pxp) @@ -326,14 +314,12 @@ static int kcr_pxp_set_status(const struct xe_pxp *pxp, bool enable) { u32 val = enable ? _MASKED_BIT_ENABLE(KCR_INIT_ALLOW_DISPLAY_ME_WRITES) : _MASKED_BIT_DISABLE(KCR_INIT_ALLOW_DISPLAY_ME_WRITES); - unsigned int fw_ref; - fw_ref = xe_force_wake_get(gt_to_fw(pxp->gt), XE_FW_GT); - if (!xe_force_wake_ref_has_domain(fw_ref, XE_FW_GT)) + CLASS(xe_force_wake, fw_ref)(gt_to_fw(pxp->gt), XE_FW_GT); + if (!xe_force_wake_ref_has_domain(fw_ref.domains, XE_FW_GT)) return -EIO; xe_mmio_write32(&pxp->gt->mmio, KCR_INIT, val); - xe_force_wake_put(gt_to_fw(pxp->gt), fw_ref); return 0; } @@ -453,34 +439,28 @@ out: static int __pxp_start_arb_session(struct xe_pxp *pxp) { int ret; - unsigned int fw_ref; - fw_ref = xe_force_wake_get(gt_to_fw(pxp->gt), XE_FW_GT); - if (!xe_force_wake_ref_has_domain(fw_ref, XE_FW_GT)) + CLASS(xe_force_wake, fw_ref)(gt_to_fw(pxp->gt), XE_FW_GT); + if (!xe_force_wake_ref_has_domain(fw_ref.domains, XE_FW_GT)) return -EIO; - if (pxp_session_is_in_play(pxp, ARB_SESSION)) { - ret = -EEXIST; - goto out_force_wake; - } + if (pxp_session_is_in_play(pxp, ARB_SESSION)) + return -EEXIST; ret = xe_pxp_submit_session_init(&pxp->gsc_res, ARB_SESSION); if (ret) { drm_err(&pxp->xe->drm, "Failed to init PXP arb session: %pe\n", ERR_PTR(ret)); - goto out_force_wake; + return ret; } ret = pxp_wait_for_session_state(pxp, ARB_SESSION, true); if (ret) { drm_err(&pxp->xe->drm, "PXP ARB session failed to go in play%pe\n", ERR_PTR(ret)); - goto out_force_wake; + return ret; } drm_dbg(&pxp->xe->drm, "PXP ARB session is active\n"); - -out_force_wake: - xe_force_wake_put(gt_to_fw(pxp->gt), fw_ref); - return ret; + return 0; } /** diff --git a/drivers/gpu/drm/xe/xe_pxp_debugfs.c b/drivers/gpu/drm/xe/xe_pxp_debugfs.c index 525a2f6bb076..d6e2e41bc88c 100644 --- a/drivers/gpu/drm/xe/xe_pxp_debugfs.c +++ b/drivers/gpu/drm/xe/xe_pxp_debugfs.c @@ -11,7 +11,7 @@ #include <drm/drm_managed.h> #include <drm/drm_print.h> -#include "xe_device.h" +#include "xe_device_types.h" #include "xe_pxp.h" #include "xe_pxp_types.h" #include "regs/xe_irq_regs.h" diff --git a/drivers/gpu/drm/xe/xe_query.c b/drivers/gpu/drm/xe/xe_query.c index 1c0915e2cc16..75490683bad2 100644 --- a/drivers/gpu/drm/xe/xe_query.c +++ b/drivers/gpu/drm/xe/xe_query.c @@ -122,7 +122,6 @@ query_engine_cycles(struct xe_device *xe, __ktime_func_t cpu_clock; struct xe_hw_engine *hwe; struct xe_gt *gt; - unsigned int fw_ref; if (IS_SRIOV_VF(xe)) return -EOPNOTSUPP; @@ -158,16 +157,13 @@ query_engine_cycles(struct xe_device *xe, if (!hwe) return -EINVAL; - fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FORCEWAKE_ALL); - if (!xe_force_wake_ref_has_domain(fw_ref, XE_FORCEWAKE_ALL)) { - xe_force_wake_put(gt_to_fw(gt), fw_ref); - return -EIO; - } - - hwe_read_timestamp(hwe, &resp.engine_cycles, &resp.cpu_timestamp, - &resp.cpu_delta, cpu_clock); + xe_with_force_wake(fw_ref, gt_to_fw(gt), XE_FORCEWAKE_ALL) { + if (!xe_force_wake_ref_has_domain(fw_ref.domains, XE_FORCEWAKE_ALL)) + return -EIO; - xe_force_wake_put(gt_to_fw(gt), fw_ref); + hwe_read_timestamp(hwe, &resp.engine_cycles, &resp.cpu_timestamp, + &resp.cpu_delta, cpu_clock); + } if (GRAPHICS_VER(xe) >= 20) resp.width = 64; @@ -342,6 +338,9 @@ static int query_config(struct xe_device *xe, struct drm_xe_device_query *query) if (xe->info.has_usm && IS_ENABLED(CONFIG_DRM_XE_GPUSVM)) config->info[DRM_XE_QUERY_CONFIG_FLAGS] |= DRM_XE_QUERY_CONFIG_FLAG_HAS_CPU_ADDR_MIRROR; + if (GRAPHICS_VER(xe) >= 20) + config->info[DRM_XE_QUERY_CONFIG_FLAGS] |= + DRM_XE_QUERY_CONFIG_FLAG_HAS_NO_COMPRESSION_HINT; config->info[DRM_XE_QUERY_CONFIG_FLAGS] |= DRM_XE_QUERY_CONFIG_FLAG_HAS_LOW_LATENCY; config->info[DRM_XE_QUERY_CONFIG_MIN_ALIGNMENT] = @@ -686,7 +685,9 @@ static int query_oa_units(struct xe_device *xe, du->capabilities = DRM_XE_OA_CAPS_BASE | DRM_XE_OA_CAPS_SYNCS | DRM_XE_OA_CAPS_OA_BUFFER_SIZE | DRM_XE_OA_CAPS_WAIT_NUM_REPORTS | - DRM_XE_OA_CAPS_OAM; + DRM_XE_OA_CAPS_OAM | + DRM_XE_OA_CAPS_OA_UNIT_GT_ID; + du->gt_id = u->gt->info.id; j = 0; for_each_hw_engine(hwe, gt, hwe_id) { if (!xe_hw_engine_is_reserved(hwe) && diff --git a/drivers/gpu/drm/xe/xe_reg_sr.c b/drivers/gpu/drm/xe/xe_reg_sr.c index fc8447a838c4..d3e13ea33123 100644 --- a/drivers/gpu/drm/xe/xe_reg_sr.c +++ b/drivers/gpu/drm/xe/xe_reg_sr.c @@ -13,16 +13,13 @@ #include <drm/drm_managed.h> #include <drm/drm_print.h> -#include "regs/xe_engine_regs.h" -#include "regs/xe_gt_regs.h" #include "xe_device.h" #include "xe_device_types.h" #include "xe_force_wake.h" -#include "xe_gt.h" #include "xe_gt_mcr.h" #include "xe_gt_printk.h" +#include "xe_gt_types.h" #include "xe_hw_engine_types.h" -#include "xe_macros.h" #include "xe_mmio.h" #include "xe_rtp_types.h" @@ -168,7 +165,6 @@ void xe_reg_sr_apply_mmio(struct xe_reg_sr *sr, struct xe_gt *gt) { struct xe_reg_sr_entry *entry; unsigned long reg; - unsigned int fw_ref; if (xa_empty(&sr->xa)) return; @@ -178,20 +174,14 @@ void xe_reg_sr_apply_mmio(struct xe_reg_sr *sr, struct xe_gt *gt) xe_gt_dbg(gt, "Applying %s save-restore MMIOs\n", sr->name); - fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FORCEWAKE_ALL); - if (!xe_force_wake_ref_has_domain(fw_ref, XE_FORCEWAKE_ALL)) - goto err_force_wake; + CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FORCEWAKE_ALL); + if (!xe_force_wake_ref_has_domain(fw_ref.domains, XE_FORCEWAKE_ALL)) { + xe_gt_err(gt, "Failed to apply, err=-ETIMEDOUT\n"); + return; + } xa_for_each(&sr->xa, reg, entry) apply_one_mmio(gt, entry); - - xe_force_wake_put(gt_to_fw(gt), fw_ref); - - return; - -err_force_wake: - xe_force_wake_put(gt_to_fw(gt), fw_ref); - xe_gt_err(gt, "Failed to apply, err=-ETIMEDOUT\n"); } /** diff --git a/drivers/gpu/drm/xe/xe_reg_whitelist.c b/drivers/gpu/drm/xe/xe_reg_whitelist.c index 7ca360b2c20d..1d36c09681aa 100644 --- a/drivers/gpu/drm/xe/xe_reg_whitelist.c +++ b/drivers/gpu/drm/xe/xe_reg_whitelist.c @@ -8,7 +8,7 @@ #include "regs/xe_engine_regs.h" #include "regs/xe_gt_regs.h" #include "regs/xe_oa_regs.h" -#include "regs/xe_regs.h" +#include "xe_device.h" #include "xe_gt_types.h" #include "xe_gt_printk.h" #include "xe_platform_types.h" @@ -26,6 +26,13 @@ static bool match_not_render(const struct xe_device *xe, return hwe->class != XE_ENGINE_CLASS_RENDER; } +static bool match_has_mert(const struct xe_device *xe, + const struct xe_gt *gt, + const struct xe_hw_engine *hwe) +{ + return xe_device_has_mert((struct xe_device *)xe); +} + static const struct xe_rtp_entry_sr register_whitelist[] = { { XE_RTP_NAME("WaAllowPMDepthAndInvocationCountAccessFromUMD, 1408556865"), XE_RTP_RULES(GRAPHICS_VERSION_RANGE(1200, 1210), ENGINE_CLASS(RENDER)), @@ -67,28 +74,6 @@ static const struct xe_rtp_entry_sr register_whitelist[] = { ENGINE_CLASS(RENDER)), XE_RTP_ACTIONS(WHITELIST(CSBE_DEBUG_STATUS(RENDER_RING_BASE), 0)) }, - { XE_RTP_NAME("oa_reg_render"), - XE_RTP_RULES(GRAPHICS_VERSION_RANGE(1200, XE_RTP_END_VERSION_UNDEFINED), - ENGINE_CLASS(RENDER)), - XE_RTP_ACTIONS(WHITELIST(OAG_MMIOTRIGGER, - RING_FORCE_TO_NONPRIV_ACCESS_RW), - WHITELIST(OAG_OASTATUS, - RING_FORCE_TO_NONPRIV_ACCESS_RD), - WHITELIST(OAG_OAHEADPTR, - RING_FORCE_TO_NONPRIV_ACCESS_RD | - RING_FORCE_TO_NONPRIV_RANGE_4)) - }, - { XE_RTP_NAME("oa_reg_compute"), - XE_RTP_RULES(GRAPHICS_VERSION_RANGE(1200, XE_RTP_END_VERSION_UNDEFINED), - ENGINE_CLASS(COMPUTE)), - XE_RTP_ACTIONS(WHITELIST(OAG_MMIOTRIGGER, - RING_FORCE_TO_NONPRIV_ACCESS_RW), - WHITELIST(OAG_OASTATUS, - RING_FORCE_TO_NONPRIV_ACCESS_RD), - WHITELIST(OAG_OAHEADPTR, - RING_FORCE_TO_NONPRIV_ACCESS_RD | - RING_FORCE_TO_NONPRIV_RANGE_4)) - }, { XE_RTP_NAME("14024997852"), XE_RTP_RULES(GRAPHICS_VERSION_RANGE(3000, 3005), ENGINE_CLASS(RENDER)), XE_RTP_ACTIONS(WHITELIST(FF_MODE, @@ -96,6 +81,57 @@ static const struct xe_rtp_entry_sr register_whitelist[] = { WHITELIST(VFLSKPD, RING_FORCE_TO_NONPRIV_ACCESS_RW)) }, + +#define WHITELIST_OA_MMIO_TRG(trg, status, head) \ + WHITELIST(trg, RING_FORCE_TO_NONPRIV_ACCESS_RW), \ + WHITELIST(status, RING_FORCE_TO_NONPRIV_ACCESS_RD), \ + WHITELIST(head, RING_FORCE_TO_NONPRIV_ACCESS_RD | RING_FORCE_TO_NONPRIV_RANGE_4) + +#define WHITELIST_OAG_MMIO_TRG \ + WHITELIST_OA_MMIO_TRG(OAG_MMIOTRIGGER, OAG_OASTATUS, OAG_OAHEADPTR) + +#define WHITELIST_OAM_MMIO_TRG \ + WHITELIST_OA_MMIO_TRG(OAM_MMIO_TRG(XE_OAM_SAG_BASE_ADJ), \ + OAM_STATUS(XE_OAM_SAG_BASE_ADJ), \ + OAM_HEAD_POINTER(XE_OAM_SAG_BASE_ADJ)), \ + WHITELIST_OA_MMIO_TRG(OAM_MMIO_TRG(XE_OAM_SCMI_0_BASE_ADJ), \ + OAM_STATUS(XE_OAM_SCMI_0_BASE_ADJ), \ + OAM_HEAD_POINTER(XE_OAM_SCMI_0_BASE_ADJ)), \ + WHITELIST_OA_MMIO_TRG(OAM_MMIO_TRG(XE_OAM_SCMI_1_BASE_ADJ), \ + OAM_STATUS(XE_OAM_SCMI_1_BASE_ADJ), \ + OAM_HEAD_POINTER(XE_OAM_SCMI_1_BASE_ADJ)) + +#define WHITELIST_OA_MERT_MMIO_TRG \ + WHITELIST_OA_MMIO_TRG(OAMERT_MMIO_TRG, OAMERT_STATUS, OAMERT_HEAD_POINTER) + + { XE_RTP_NAME("oag_mmio_trg_rcs"), + XE_RTP_RULES(GRAPHICS_VERSION_RANGE(1200, XE_RTP_END_VERSION_UNDEFINED), + ENGINE_CLASS(RENDER)), + XE_RTP_ACTIONS(WHITELIST_OAG_MMIO_TRG) + }, + { XE_RTP_NAME("oag_mmio_trg_ccs"), + XE_RTP_RULES(GRAPHICS_VERSION_RANGE(1200, XE_RTP_END_VERSION_UNDEFINED), + ENGINE_CLASS(COMPUTE)), + XE_RTP_ACTIONS(WHITELIST_OAG_MMIO_TRG) + }, + { XE_RTP_NAME("oam_mmio_trg_vcs"), + XE_RTP_RULES(MEDIA_VERSION_RANGE(1300, XE_RTP_END_VERSION_UNDEFINED), + ENGINE_CLASS(VIDEO_DECODE)), + XE_RTP_ACTIONS(WHITELIST_OAM_MMIO_TRG) + }, + { XE_RTP_NAME("oam_mmio_trg_vecs"), + XE_RTP_RULES(MEDIA_VERSION_RANGE(1300, XE_RTP_END_VERSION_UNDEFINED), + ENGINE_CLASS(VIDEO_ENHANCE)), + XE_RTP_ACTIONS(WHITELIST_OAM_MMIO_TRG) + }, + { XE_RTP_NAME("oa_mert_mmio_trg_ccs"), + XE_RTP_RULES(FUNC(match_has_mert), ENGINE_CLASS(COMPUTE)), + XE_RTP_ACTIONS(WHITELIST_OA_MERT_MMIO_TRG) + }, + { XE_RTP_NAME("oa_mert_mmio_trg_bcs"), + XE_RTP_RULES(FUNC(match_has_mert), ENGINE_CLASS(COPY)), + XE_RTP_ACTIONS(WHITELIST_OA_MERT_MMIO_TRG) + }, }; static void whitelist_apply_to_hwe(struct xe_hw_engine *hwe) diff --git a/drivers/gpu/drm/xe/xe_ring_ops.c b/drivers/gpu/drm/xe/xe_ring_ops.c index ac0c6dcffe15..248620b0901d 100644 --- a/drivers/gpu/drm/xe/xe_ring_ops.c +++ b/drivers/gpu/drm/xe/xe_ring_ops.c @@ -11,11 +11,9 @@ #include "instructions/xe_mi_commands.h" #include "regs/xe_engine_regs.h" #include "regs/xe_gt_regs.h" -#include "regs/xe_lrc_layout.h" -#include "xe_exec_queue_types.h" -#include "xe_gt.h" +#include "xe_exec_queue.h" +#include "xe_gt_types.h" #include "xe_lrc.h" -#include "xe_macros.h" #include "xe_sched_job.h" #include "xe_sriov.h" #include "xe_vm_types.h" @@ -135,12 +133,11 @@ emit_pipe_control(u32 *dw, int i, u32 bit_group_0, u32 bit_group_1, u32 offset, return i; } -static int emit_pipe_invalidate(u32 mask_flags, bool invalidate_tlb, u32 *dw, - int i) +static int emit_pipe_invalidate(struct xe_exec_queue *q, u32 mask_flags, + bool invalidate_tlb, u32 *dw, int i) { u32 flags0 = 0; - u32 flags1 = PIPE_CONTROL_CS_STALL | - PIPE_CONTROL_COMMAND_CACHE_INVALIDATE | + u32 flags1 = PIPE_CONTROL_COMMAND_CACHE_INVALIDATE | PIPE_CONTROL_INSTRUCTION_CACHE_INVALIDATE | PIPE_CONTROL_TEXTURE_CACHE_INVALIDATE | PIPE_CONTROL_VF_CACHE_INVALIDATE | @@ -152,6 +149,11 @@ static int emit_pipe_invalidate(u32 mask_flags, bool invalidate_tlb, u32 *dw, if (invalidate_tlb) flags1 |= PIPE_CONTROL_TLB_INVALIDATE; + if (xe_exec_queue_is_multi_queue(q)) + flags0 |= PIPE_CONTROL0_QUEUE_DRAIN_MODE; + else + flags1 |= PIPE_CONTROL_CS_STALL; + flags1 &= ~mask_flags; if (flags1 & PIPE_CONTROL_VF_CACHE_INVALIDATE) @@ -175,54 +177,52 @@ static int emit_store_imm_ppgtt_posted(u64 addr, u64 value, static int emit_render_cache_flush(struct xe_sched_job *job, u32 *dw, int i) { - struct xe_gt *gt = job->q->gt; + struct xe_exec_queue *q = job->q; + struct xe_gt *gt = q->gt; bool lacks_render = !(gt->info.engine_mask & XE_HW_ENGINE_RCS_MASK); - u32 flags; + u32 flags0, flags1; if (XE_GT_WA(gt, 14016712196)) i = emit_pipe_control(dw, i, 0, PIPE_CONTROL_DEPTH_CACHE_FLUSH, LRC_PPHWSP_FLUSH_INVAL_SCRATCH_ADDR, 0); - flags = (PIPE_CONTROL_CS_STALL | - PIPE_CONTROL_TILE_CACHE_FLUSH | + flags0 = PIPE_CONTROL0_HDC_PIPELINE_FLUSH; + flags1 = (PIPE_CONTROL_TILE_CACHE_FLUSH | PIPE_CONTROL_RENDER_TARGET_CACHE_FLUSH | PIPE_CONTROL_DEPTH_CACHE_FLUSH | PIPE_CONTROL_DC_FLUSH_ENABLE | PIPE_CONTROL_FLUSH_ENABLE); if (XE_GT_WA(gt, 1409600907)) - flags |= PIPE_CONTROL_DEPTH_STALL; + flags1 |= PIPE_CONTROL_DEPTH_STALL; if (lacks_render) - flags &= ~PIPE_CONTROL_3D_ARCH_FLAGS; + flags1 &= ~PIPE_CONTROL_3D_ARCH_FLAGS; else if (job->q->class == XE_ENGINE_CLASS_COMPUTE) - flags &= ~PIPE_CONTROL_3D_ENGINE_FLAGS; - - return emit_pipe_control(dw, i, PIPE_CONTROL0_HDC_PIPELINE_FLUSH, flags, 0, 0); -} + flags1 &= ~PIPE_CONTROL_3D_ENGINE_FLAGS; -static int emit_pipe_control_to_ring_end(struct xe_hw_engine *hwe, u32 *dw, int i) -{ - if (hwe->class != XE_ENGINE_CLASS_RENDER) - return i; - - if (XE_GT_WA(hwe->gt, 16020292621)) - i = emit_pipe_control(dw, i, 0, PIPE_CONTROL_LRI_POST_SYNC, - RING_NOPID(hwe->mmio_base).addr, 0); + if (xe_exec_queue_is_multi_queue(q)) + flags0 |= PIPE_CONTROL0_QUEUE_DRAIN_MODE; + else + flags1 |= PIPE_CONTROL_CS_STALL; - return i; + return emit_pipe_control(dw, i, flags0, flags1, 0, 0); } -static int emit_pipe_imm_ggtt(u32 addr, u32 value, bool stall_only, u32 *dw, - int i) +static int emit_pipe_imm_ggtt(struct xe_exec_queue *q, u32 addr, u32 value, + bool stall_only, u32 *dw, int i) { - u32 flags = PIPE_CONTROL_CS_STALL | PIPE_CONTROL_GLOBAL_GTT_IVB | - PIPE_CONTROL_QW_WRITE; + u32 flags0 = 0, flags1 = PIPE_CONTROL_GLOBAL_GTT_IVB | PIPE_CONTROL_QW_WRITE; if (!stall_only) - flags |= PIPE_CONTROL_FLUSH_ENABLE; + flags1 |= PIPE_CONTROL_FLUSH_ENABLE; + + if (xe_exec_queue_is_multi_queue(q)) + flags0 |= PIPE_CONTROL0_QUEUE_DRAIN_MODE; + else + flags1 |= PIPE_CONTROL_CS_STALL; - return emit_pipe_control(dw, i, 0, flags, addr, value); + return emit_pipe_control(dw, i, flags0, flags1, addr, value); } static u32 get_ppgtt_flag(struct xe_sched_job *job) @@ -233,13 +233,26 @@ static u32 get_ppgtt_flag(struct xe_sched_job *job) return 0; } -static int emit_copy_timestamp(struct xe_lrc *lrc, u32 *dw, int i) +static int emit_copy_timestamp(struct xe_device *xe, struct xe_lrc *lrc, + u32 *dw, int i) { dw[i++] = MI_STORE_REGISTER_MEM | MI_SRM_USE_GGTT | MI_SRM_ADD_CS_OFFSET; dw[i++] = RING_CTX_TIMESTAMP(0).addr; dw[i++] = xe_lrc_ctx_job_timestamp_ggtt_addr(lrc); dw[i++] = 0; + /* + * Ensure CTX timestamp >= Job timestamp during VF sampling to avoid + * arithmetic wraparound in TDR. + */ + if (IS_SRIOV_VF(xe)) { + dw[i++] = MI_STORE_REGISTER_MEM | MI_SRM_USE_GGTT | + MI_SRM_ADD_CS_OFFSET; + dw[i++] = RING_CTX_TIMESTAMP(0).addr; + dw[i++] = xe_lrc_ctx_timestamp_ggtt_addr(lrc); + dw[i++] = 0; + } + return i; } @@ -253,7 +266,7 @@ static void __emit_job_gen12_simple(struct xe_sched_job *job, struct xe_lrc *lrc *head = lrc->ring.tail; - i = emit_copy_timestamp(lrc, dw, i); + i = emit_copy_timestamp(gt_to_xe(gt), lrc, dw, i); if (job->ring_ops_flush_tlb) { dw[i++] = preparser_disable(true); @@ -308,7 +321,7 @@ static void __emit_job_gen12_video(struct xe_sched_job *job, struct xe_lrc *lrc, *head = lrc->ring.tail; - i = emit_copy_timestamp(lrc, dw, i); + i = emit_copy_timestamp(xe, lrc, dw, i); dw[i++] = preparser_disable(true); @@ -362,7 +375,7 @@ static void __emit_job_gen12_render_compute(struct xe_sched_job *job, *head = lrc->ring.tail; - i = emit_copy_timestamp(lrc, dw, i); + i = emit_copy_timestamp(xe, lrc, dw, i); dw[i++] = preparser_disable(true); if (lacks_render) @@ -371,7 +384,7 @@ static void __emit_job_gen12_render_compute(struct xe_sched_job *job, mask_flags = PIPE_CONTROL_3D_ENGINE_FLAGS; /* See __xe_pt_bind_vma() for a discussion on TLB invalidations. */ - i = emit_pipe_invalidate(mask_flags, job->ring_ops_flush_tlb, dw, i); + i = emit_pipe_invalidate(job->q, mask_flags, job->ring_ops_flush_tlb, dw, i); /* hsdes: 1809175790 */ if (has_aux_ccs(xe)) @@ -391,12 +404,10 @@ static void __emit_job_gen12_render_compute(struct xe_sched_job *job, job->user_fence.value, dw, i); - i = emit_pipe_imm_ggtt(xe_lrc_seqno_ggtt_addr(lrc), seqno, lacks_render, dw, i); + i = emit_pipe_imm_ggtt(job->q, xe_lrc_seqno_ggtt_addr(lrc), seqno, lacks_render, dw, i); i = emit_user_interrupt(dw, i); - i = emit_pipe_control_to_ring_end(job->q->hwe, dw, i); - xe_gt_assert(gt, i <= MAX_JOB_SIZE_DW); xe_lrc_write_ring(lrc, dw, i * sizeof(*dw)); @@ -406,12 +417,14 @@ static void emit_migration_job_gen12(struct xe_sched_job *job, struct xe_lrc *lrc, u32 *head, u32 seqno) { + struct xe_gt *gt = job->q->gt; + struct xe_device *xe = gt_to_xe(gt); u32 saddr = xe_lrc_start_seqno_ggtt_addr(lrc); u32 dw[MAX_JOB_SIZE_DW], i = 0; *head = lrc->ring.tail; - i = emit_copy_timestamp(lrc, dw, i); + i = emit_copy_timestamp(xe, lrc, dw, i); i = emit_store_imm_ggtt(saddr, seqno, dw, i); diff --git a/drivers/gpu/drm/xe/xe_rtp.c b/drivers/gpu/drm/xe/xe_rtp.c index ed509b1c8cfc..b7c26e2fb411 100644 --- a/drivers/gpu/drm/xe/xe_rtp.c +++ b/drivers/gpu/drm/xe/xe_rtp.c @@ -12,7 +12,6 @@ #include "xe_configfs.h" #include "xe_gt.h" #include "xe_gt_topology.h" -#include "xe_macros.h" #include "xe_reg_sr.h" #include "xe_sriov.h" diff --git a/drivers/gpu/drm/xe/xe_sa.c b/drivers/gpu/drm/xe/xe_sa.c index 63a5263dcf1b..b738102575d4 100644 --- a/drivers/gpu/drm/xe/xe_sa.c +++ b/drivers/gpu/drm/xe/xe_sa.c @@ -10,7 +10,7 @@ #include <drm/drm_managed.h> #include "xe_bo.h" -#include "xe_device.h" +#include "xe_device_types.h" #include "xe_map.h" static void xe_sa_bo_manager_fini(struct drm_device *drm, void *arg) @@ -29,6 +29,7 @@ static void xe_sa_bo_manager_fini(struct drm_device *drm, void *arg) kvfree(sa_manager->cpu_ptr); sa_manager->bo = NULL; + sa_manager->shadow = NULL; } /** @@ -37,12 +38,14 @@ static void xe_sa_bo_manager_fini(struct drm_device *drm, void *arg) * @size: number of bytes to allocate * @guard: number of bytes to exclude from suballocations * @align: alignment for each suballocated chunk + * @flags: flags for suballocator * * Prepares the suballocation manager for suballocations. * * Return: a pointer to the &xe_sa_manager or an ERR_PTR on failure. */ -struct xe_sa_manager *__xe_sa_bo_manager_init(struct xe_tile *tile, u32 size, u32 guard, u32 align) +struct xe_sa_manager *__xe_sa_bo_manager_init(struct xe_tile *tile, u32 size, + u32 guard, u32 align, u32 flags) { struct xe_device *xe = tile_to_xe(tile); struct xe_sa_manager *sa_manager; @@ -79,6 +82,26 @@ struct xe_sa_manager *__xe_sa_bo_manager_init(struct xe_tile *tile, u32 size, u3 memset(sa_manager->cpu_ptr, 0, bo->ttm.base.size); } + if (flags & XE_SA_BO_MANAGER_FLAG_SHADOW) { + struct xe_bo *shadow; + + ret = drmm_mutex_init(&xe->drm, &sa_manager->swap_guard); + if (ret) + return ERR_PTR(ret); + + shadow = xe_managed_bo_create_pin_map(xe, tile, size, + XE_BO_FLAG_VRAM_IF_DGFX(tile) | + XE_BO_FLAG_GGTT | + XE_BO_FLAG_GGTT_INVALIDATE | + XE_BO_FLAG_PINNED_NORESTORE); + if (IS_ERR(shadow)) { + drm_err(&xe->drm, "Failed to prepare %uKiB BO for SA manager (%pe)\n", + size / SZ_1K, shadow); + return ERR_CAST(shadow); + } + sa_manager->shadow = shadow; + } + drm_suballoc_manager_init(&sa_manager->base, managed_size, align); ret = drmm_add_action_or_reset(&xe->drm, xe_sa_bo_manager_fini, sa_manager); @@ -89,6 +112,48 @@ struct xe_sa_manager *__xe_sa_bo_manager_init(struct xe_tile *tile, u32 size, u3 } /** + * xe_sa_bo_swap_shadow() - Swap the SA BO with shadow BO. + * @sa_manager: the XE sub allocator manager + * + * Swaps the sub-allocator primary buffer object with shadow buffer object. + * + * Return: None. + */ +void xe_sa_bo_swap_shadow(struct xe_sa_manager *sa_manager) +{ + struct xe_device *xe = tile_to_xe(sa_manager->bo->tile); + + xe_assert(xe, sa_manager->shadow); + lockdep_assert_held(&sa_manager->swap_guard); + + swap(sa_manager->bo, sa_manager->shadow); + if (!sa_manager->bo->vmap.is_iomem) + sa_manager->cpu_ptr = sa_manager->bo->vmap.vaddr; +} + +/** + * xe_sa_bo_sync_shadow() - Sync the SA Shadow BO with primary BO. + * @sa_bo: the sub-allocator buffer object. + * + * Synchronize sub-allocator shadow buffer object with primary buffer object. + * + * Return: None. + */ +void xe_sa_bo_sync_shadow(struct drm_suballoc *sa_bo) +{ + struct xe_sa_manager *sa_manager = to_xe_sa_manager(sa_bo->manager); + struct xe_device *xe = tile_to_xe(sa_manager->bo->tile); + + xe_assert(xe, sa_manager->shadow); + lockdep_assert_held(&sa_manager->swap_guard); + + xe_map_memcpy_to(xe, &sa_manager->shadow->vmap, + drm_suballoc_soffset(sa_bo), + xe_sa_bo_cpu_addr(sa_bo), + drm_suballoc_size(sa_bo)); +} + +/** * __xe_sa_bo_new() - Make a suballocation but use custom gfp flags. * @sa_manager: the &xe_sa_manager * @size: number of bytes we want to suballocate diff --git a/drivers/gpu/drm/xe/xe_sa.h b/drivers/gpu/drm/xe/xe_sa.h index 1be744350836..05e9a4e00e78 100644 --- a/drivers/gpu/drm/xe/xe_sa.h +++ b/drivers/gpu/drm/xe/xe_sa.h @@ -14,12 +14,14 @@ struct dma_fence; struct xe_tile; -struct xe_sa_manager *__xe_sa_bo_manager_init(struct xe_tile *tile, u32 size, u32 guard, u32 align); +#define XE_SA_BO_MANAGER_FLAG_SHADOW BIT(0) +struct xe_sa_manager *__xe_sa_bo_manager_init(struct xe_tile *tile, u32 size, + u32 guard, u32 align, u32 flags); struct drm_suballoc *__xe_sa_bo_new(struct xe_sa_manager *sa_manager, u32 size, gfp_t gfp); static inline struct xe_sa_manager *xe_sa_bo_manager_init(struct xe_tile *tile, u32 size, u32 align) { - return __xe_sa_bo_manager_init(tile, size, SZ_4K, align); + return __xe_sa_bo_manager_init(tile, size, SZ_4K, align, 0); } /** @@ -69,4 +71,18 @@ static inline void *xe_sa_bo_cpu_addr(struct drm_suballoc *sa) drm_suballoc_soffset(sa); } +void xe_sa_bo_swap_shadow(struct xe_sa_manager *sa_manager); +void xe_sa_bo_sync_shadow(struct drm_suballoc *sa_bo); + +/** + * xe_sa_bo_swap_guard() - Retrieve the SA BO swap guard within sub-allocator. + * @sa_manager: the &xe_sa_manager + * + * Return: Sub alloctor swap guard mutex. + */ +static inline struct mutex *xe_sa_bo_swap_guard(struct xe_sa_manager *sa_manager) +{ + return &sa_manager->swap_guard; +} + #endif diff --git a/drivers/gpu/drm/xe/xe_sa_types.h b/drivers/gpu/drm/xe/xe_sa_types.h index cb7238799dcb..1085c9c37d6b 100644 --- a/drivers/gpu/drm/xe/xe_sa_types.h +++ b/drivers/gpu/drm/xe/xe_sa_types.h @@ -12,6 +12,9 @@ struct xe_bo; struct xe_sa_manager { struct drm_suballoc_manager base; struct xe_bo *bo; + struct xe_bo *shadow; + /** @swap_guard: Timeline guard updating @bo and @shadow */ + struct mutex swap_guard; void *cpu_ptr; bool is_iomem; }; diff --git a/drivers/gpu/drm/xe/xe_sched_job.c b/drivers/gpu/drm/xe/xe_sched_job.c index cb674a322113..3927666fe556 100644 --- a/drivers/gpu/drm/xe/xe_sched_job.c +++ b/drivers/gpu/drm/xe/xe_sched_job.c @@ -11,7 +11,7 @@ #include "xe_device.h" #include "xe_exec_queue.h" -#include "xe_gt.h" +#include "xe_gt_types.h" #include "xe_hw_engine_types.h" #include "xe_hw_fence.h" #include "xe_lrc.h" @@ -110,6 +110,7 @@ struct xe_sched_job *xe_sched_job_create(struct xe_exec_queue *q, return ERR_PTR(-ENOMEM); job->q = q; + job->sample_timestamp = U64_MAX; kref_init(&job->refcount); xe_exec_queue_get(job->q); diff --git a/drivers/gpu/drm/xe/xe_sched_job_types.h b/drivers/gpu/drm/xe/xe_sched_job_types.h index 7c4c54fe920a..13c2970e81a8 100644 --- a/drivers/gpu/drm/xe/xe_sched_job_types.h +++ b/drivers/gpu/drm/xe/xe_sched_job_types.h @@ -59,6 +59,8 @@ struct xe_sched_job { u32 lrc_seqno; /** @migrate_flush_flags: Additional flush flags for migration jobs */ u32 migrate_flush_flags; + /** @sample_timestamp: Sampling of job timestamp in TDR */ + u64 sample_timestamp; /** @ring_ops_flush_tlb: The ring ops need to flush TLB before payload. */ bool ring_ops_flush_tlb; /** @ggtt: mapped in ggtt. */ diff --git a/drivers/gpu/drm/xe/xe_soc_remapper.c b/drivers/gpu/drm/xe/xe_soc_remapper.c new file mode 100644 index 000000000000..1c391d719196 --- /dev/null +++ b/drivers/gpu/drm/xe/xe_soc_remapper.c @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright © 2025 Intel Corporation + */ + +#include "regs/xe_soc_remapper_regs.h" +#include "xe_mmio.h" +#include "xe_soc_remapper.h" + +static void xe_soc_remapper_set_region(struct xe_device *xe, struct xe_reg reg, + u32 mask, u32 val) +{ + guard(spinlock_irqsave)(&xe->soc_remapper.lock); + xe_mmio_rmw32(xe_root_tile_mmio(xe), reg, mask, val); +} + +static void xe_soc_remapper_set_telem_region(struct xe_device *xe, u32 index) +{ + xe_soc_remapper_set_region(xe, SG_REMAP_INDEX1, SG_REMAP_TELEM_MASK, + REG_FIELD_PREP(SG_REMAP_TELEM_MASK, index)); +} + +static void xe_soc_remapper_set_sysctrl_region(struct xe_device *xe, u32 index) +{ + xe_soc_remapper_set_region(xe, SG_REMAP_INDEX1, SG_REMAP_SYSCTRL_MASK, + REG_FIELD_PREP(SG_REMAP_SYSCTRL_MASK, index)); +} + +/** + * xe_soc_remapper_init() - Initialize SoC remapper + * @xe: Pointer to xe device. + * + * Initialize SoC remapper. + * + * Return: 0 on success, error code on failure + */ +int xe_soc_remapper_init(struct xe_device *xe) +{ + bool has_soc_remapper = xe->info.has_soc_remapper_telem || + xe->info.has_soc_remapper_sysctrl; + + if (has_soc_remapper) + spin_lock_init(&xe->soc_remapper.lock); + + if (xe->info.has_soc_remapper_telem) + xe->soc_remapper.set_telem_region = xe_soc_remapper_set_telem_region; + + if (xe->info.has_soc_remapper_sysctrl) + xe->soc_remapper.set_sysctrl_region = xe_soc_remapper_set_sysctrl_region; + + return 0; +} diff --git a/drivers/gpu/drm/xe/xe_soc_remapper.h b/drivers/gpu/drm/xe/xe_soc_remapper.h new file mode 100644 index 000000000000..1060ad0f5abc --- /dev/null +++ b/drivers/gpu/drm/xe/xe_soc_remapper.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright © 2025 Intel Corporation + */ + +#ifndef _XE_SOC_REMAPPER_H_ +#define _XE_SOC_REMAPPER_H_ + +#include "xe_device_types.h" + +int xe_soc_remapper_init(struct xe_device *xe); + +#endif diff --git a/drivers/gpu/drm/xe/xe_sriov_packet.c b/drivers/gpu/drm/xe/xe_sriov_packet.c index bab994696896..7a4c3de662e5 100644 --- a/drivers/gpu/drm/xe/xe_sriov_packet.c +++ b/drivers/gpu/drm/xe/xe_sriov_packet.c @@ -6,7 +6,6 @@ #include "xe_bo.h" #include "xe_device.h" #include "xe_guc_klv_helpers.h" -#include "xe_printk.h" #include "xe_sriov_packet.h" #include "xe_sriov_packet_types.h" #include "xe_sriov_pf_helpers.h" @@ -358,7 +357,7 @@ ssize_t xe_sriov_packet_write_single(struct xe_device *xe, unsigned int vfid, #define MIGRATION_DESCRIPTOR_DWORDS (GUC_KLV_LEN_MIN + MIGRATION_KLV_DEVICE_DEVID_LEN + \ GUC_KLV_LEN_MIN + MIGRATION_KLV_DEVICE_REVID_LEN) -static size_t pf_descriptor_init(struct xe_device *xe, unsigned int vfid) +static int pf_descriptor_init(struct xe_device *xe, unsigned int vfid) { struct xe_sriov_packet **desc = pf_pick_descriptor(xe, vfid); struct xe_sriov_packet *data; diff --git a/drivers/gpu/drm/xe/xe_sriov_pf.c b/drivers/gpu/drm/xe/xe_sriov_pf.c index 7c779d63179f..6ce3c58e003c 100644 --- a/drivers/gpu/drm/xe/xe_sriov_pf.c +++ b/drivers/gpu/drm/xe/xe_sriov_pf.c @@ -111,6 +111,8 @@ int xe_sriov_pf_init_early(struct xe_device *xe) xe_sriov_pf_service_init(xe); + xe_mert_init_early(xe); + return 0; } diff --git a/drivers/gpu/drm/xe/xe_sriov_pf_debugfs.c b/drivers/gpu/drm/xe/xe_sriov_pf_debugfs.c index bad751217e1e..81b377830d6d 100644 --- a/drivers/gpu/drm/xe/xe_sriov_pf_debugfs.c +++ b/drivers/gpu/drm/xe/xe_sriov_pf_debugfs.c @@ -16,7 +16,6 @@ #include "xe_sriov_pf_migration.h" #include "xe_sriov_pf_provision.h" #include "xe_sriov_pf_service.h" -#include "xe_sriov_printk.h" #include "xe_tile_sriov_pf_debugfs.h" /* @@ -70,9 +69,8 @@ static ssize_t from_file_write_to_xe_call(struct file *file, const char __user * if (ret < 0) return ret; if (yes) { - xe_pm_runtime_get(xe); + guard(xe_pm_runtime)(xe); ret = call(xe); - xe_pm_runtime_put(xe); } if (ret < 0) return ret; @@ -209,9 +207,8 @@ static ssize_t from_file_write_to_vf_call(struct file *file, const char __user * if (ret < 0) return ret; if (yes) { - xe_pm_runtime_get(xe); + guard(xe_pm_runtime)(xe); ret = call(xe, vfid); - xe_pm_runtime_put(xe); } if (ret < 0) return ret; diff --git a/drivers/gpu/drm/xe/xe_sriov_pf_sysfs.c b/drivers/gpu/drm/xe/xe_sriov_pf_sysfs.c index c0b767ac735c..3d140506ba36 100644 --- a/drivers/gpu/drm/xe/xe_sriov_pf_sysfs.c +++ b/drivers/gpu/drm/xe/xe_sriov_pf_sysfs.c @@ -389,16 +389,12 @@ static ssize_t xe_sriov_dev_attr_store(struct kobject *kobj, struct attribute *a struct xe_sriov_dev_attr *vattr = to_xe_sriov_dev_attr(attr); struct xe_sriov_kobj *vkobj = to_xe_sriov_kobj(kobj); struct xe_device *xe = vkobj->xe; - ssize_t ret; if (!vattr->store) return -EPERM; - xe_pm_runtime_get(xe); - ret = xe_sriov_pf_wait_ready(xe) ?: vattr->store(xe, buf, count); - xe_pm_runtime_put(xe); - - return ret; + guard(xe_pm_runtime)(xe); + return xe_sriov_pf_wait_ready(xe) ?: vattr->store(xe, buf, count); } static ssize_t xe_sriov_vf_attr_show(struct kobject *kobj, struct attribute *attr, char *buf) @@ -423,18 +419,14 @@ static ssize_t xe_sriov_vf_attr_store(struct kobject *kobj, struct attribute *at struct xe_sriov_kobj *vkobj = to_xe_sriov_kobj(kobj); struct xe_device *xe = vkobj->xe; unsigned int vfid = vkobj->vfid; - ssize_t ret; xe_sriov_pf_assert_vfid(xe, vfid); if (!vattr->store) return -EPERM; - xe_pm_runtime_get(xe); - ret = xe_sriov_pf_wait_ready(xe) ?: vattr->store(xe, vfid, buf, count); - xe_pm_runtime_get(xe); - - return ret; + guard(xe_pm_runtime)(xe); + return xe_sriov_pf_wait_ready(xe) ?: vattr->store(xe, vfid, buf, count); } static const struct sysfs_ops xe_sriov_dev_sysfs_ops = { diff --git a/drivers/gpu/drm/xe/xe_sriov_vf.c b/drivers/gpu/drm/xe/xe_sriov_vf.c index 284ce37ca92d..29894bd081c0 100644 --- a/drivers/gpu/drm/xe/xe_sriov_vf.c +++ b/drivers/gpu/drm/xe/xe_sriov_vf.c @@ -6,7 +6,6 @@ #include <drm/drm_debugfs.h> #include <drm/drm_managed.h> -#include "xe_gt.h" #include "xe_gt_sriov_vf.h" #include "xe_guc.h" #include "xe_sriov_printk.h" @@ -49,11 +48,13 @@ * * As soon as Virtual GPU of the VM starts, the VF driver within receives * the MIGRATED interrupt and schedules post-migration recovery worker. - * That worker queries GuC for new provisioning (using MMIO communication), + * That worker sends `VF2GUC_RESFIX_START` action along with non-zero + * marker, queries GuC for new provisioning (using MMIO communication), * and applies fixups to any non-virtualized resources used by the VF. * * When the VF driver is ready to continue operation on the newly connected - * hardware, it sends `VF2GUC_NOTIFY_RESFIX_DONE` which causes it to + * hardware, it sends `VF2GUC_RESFIX_DONE` action along with the same + * marker which was sent with `VF2GUC_RESFIX_START` which causes it to * enter the long awaited `VF_RUNNING` state, and therefore start handling * CTB messages and scheduling workloads from the VF:: * @@ -102,12 +103,17 @@ * | [ ] new VF provisioning [ ] * | [ ]---------------------------> [ ] * | | [ ] + * | | VF2GUC_RESFIX_START [ ] + * | [ ] <---------------------------[ ] + * | [ ] [ ] + * | [ ] success [ ] + * | [ ]---------------------------> [ ] * | | VF driver applies post [ ] * | | migration fixups -------[ ] * | | | [ ] * | | -----> [ ] * | | [ ] - * | | VF2GUC_NOTIFY_RESFIX_DONE [ ] + * | | VF2GUC_RESFIX_DONE [ ] * | [ ] <---------------------------[ ] * | [ ] [ ] * | [ ] GuC sets new VF state to [ ] @@ -118,6 +124,55 @@ * | [ ]---------------------------> [ ] * | | | * | | | + * + * Handling of VF double migration flow is shown below:: + * + * GuC1 VF + * | | + * | [ ]<--- start fixups + * | VF2GUC_RESFIX_START(marker) [ ] + * [ ] <-------------------------------------------[ ] + * [ ] [ ] + * [ ]---\ [ ] + * [ ] store marker [ ] + * [ ]<--/ [ ] + * [ ] [ ] + * [ ] success [ ] + * [ ] ------------------------------------------> [ ] + * | [ ] + * | [ ]---\ + * | [ ] do fixups + * | [ ]<--/ + * | [ ] + * -------------- VF paused / saved ---------------- + * : + * + * GuC2 + * | + * ----------------- VF restored ------------------ + * | + * [ ] + * [ ]---\ + * [ ] reset marker + * [ ]<--/ + * [ ] + * ----------------- VF resumed ------------------ + * | [ ] + * | [ ] + * | VF2GUC_RESFIX_DONE(marker) [ ] + * [ ] <-------------------------------------------[ ] + * [ ] [ ] + * [ ]---\ [ ] + * [ ] check marker [ ] + * [ ] (mismatch) [ ] + * [ ]<--/ [ ] + * [ ] [ ] + * [ ] RESPONSE_VF_MIGRATED [ ] + * [ ] ------------------------------------------> [ ] + * | [ ]---\ + * | [ ] reschedule fixups + * | [ ]<--/ + * | | */ /** @@ -170,6 +225,26 @@ void xe_sriov_vf_init_early(struct xe_device *xe) vf_migration_init_early(xe); } +static int vf_migration_init_late(struct xe_device *xe) +{ + struct xe_gt *gt = xe_root_mmio_gt(xe); + struct xe_uc_fw_version guc_version; + + if (!xe_sriov_vf_migration_supported(xe)) + return 0; + + xe_gt_sriov_vf_guc_versions(gt, NULL, &guc_version); + if (MAKE_GUC_VER_STRUCT(guc_version) < MAKE_GUC_VER(1, 27, 0)) { + xe_sriov_vf_migration_disable(xe, + "requires GuC ABI >= 1.27.0, but only %u.%u.%u found", + guc_version.major, guc_version.minor, + guc_version.patch); + return 0; + } + + return xe_sriov_vf_ccs_init(xe); +} + /** * xe_sriov_vf_init_late() - SR-IOV VF late initialization functions. * @xe: the &xe_device to initialize @@ -180,7 +255,7 @@ void xe_sriov_vf_init_early(struct xe_device *xe) */ int xe_sriov_vf_init_late(struct xe_device *xe) { - return xe_sriov_vf_ccs_init(xe); + return vf_migration_init_late(xe); } static int sa_info_vf_ccs(struct seq_file *m, void *data) diff --git a/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c b/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c index 797a4b866226..db023fb66a27 100644 --- a/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c +++ b/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c @@ -150,7 +150,8 @@ static int alloc_bb_pool(struct xe_tile *tile, struct xe_sriov_vf_ccs_ctx *ctx) xe_sriov_info(xe, "Allocating %s CCS BB pool size = %lldMB\n", ctx->ctx_id ? "Restore" : "Save", bb_pool_size / SZ_1M); - sa_manager = xe_sa_bo_manager_init(tile, bb_pool_size, SZ_16); + sa_manager = __xe_sa_bo_manager_init(tile, bb_pool_size, SZ_4K, SZ_16, + XE_SA_BO_MANAGER_FLAG_SHADOW); if (IS_ERR(sa_manager)) { xe_sriov_err(xe, "Suballocator init failed with error: %pe\n", @@ -162,9 +163,12 @@ static int alloc_bb_pool(struct xe_tile *tile, struct xe_sriov_vf_ccs_ctx *ctx) offset = 0; xe_map_memset(xe, &sa_manager->bo->vmap, offset, MI_NOOP, bb_pool_size); + xe_map_memset(xe, &sa_manager->shadow->vmap, offset, MI_NOOP, + bb_pool_size); offset = bb_pool_size - sizeof(u32); xe_map_wr(xe, &sa_manager->bo->vmap, offset, u32, MI_BATCH_BUFFER_END); + xe_map_wr(xe, &sa_manager->shadow->vmap, offset, u32, MI_BATCH_BUFFER_END); ctx->mem.ccs_bb_pool = sa_manager; @@ -346,7 +350,7 @@ int xe_sriov_vf_ccs_init(struct xe_device *xe) flags = EXEC_QUEUE_FLAG_KERNEL | EXEC_QUEUE_FLAG_PERMANENT | EXEC_QUEUE_FLAG_MIGRATE; - q = xe_exec_queue_create_bind(xe, tile, flags, 0); + q = xe_exec_queue_create_bind(xe, tile, NULL, flags, 0); if (IS_ERR(q)) { err = PTR_ERR(q); goto err_ret; @@ -381,6 +385,18 @@ err_ret: return err; } +#define XE_SRIOV_VF_CCS_RW_BB_ADDR_OFFSET (2 * sizeof(u32)) +void xe_sriov_vf_ccs_rw_update_bb_addr(struct xe_sriov_vf_ccs_ctx *ctx) +{ + u64 addr = xe_sa_manager_gpu_addr(ctx->mem.ccs_bb_pool); + struct xe_lrc *lrc = xe_exec_queue_lrc(ctx->mig_q); + struct xe_device *xe = gt_to_xe(ctx->mig_q->gt); + + xe_device_wmb(xe); + xe_map_wr(xe, &lrc->bo->vmap, XE_SRIOV_VF_CCS_RW_BB_ADDR_OFFSET, u32, addr); + xe_device_wmb(xe); +} + /** * xe_sriov_vf_ccs_attach_bo - Insert CCS read write commands in the BO. * @bo: the &buffer object to which batch buffer commands will be added. @@ -441,9 +457,7 @@ int xe_sriov_vf_ccs_detach_bo(struct xe_bo *bo) if (!bb) continue; - memset(bb->cs, MI_NOOP, bb->len * sizeof(u32)); - xe_bb_free(bb, NULL); - bo->bb_ccs[ctx_id] = NULL; + xe_migrate_ccs_rw_copy_clear(bo, ctx_id); } return 0; } @@ -463,8 +477,7 @@ void xe_sriov_vf_ccs_print(struct xe_device *xe, struct drm_printer *p) if (!IS_VF_CCS_READY(xe)) return; - xe_pm_runtime_get(xe); - + guard(xe_pm_runtime)(xe); for_each_ccs_rw_ctx(ctx_id) { bb_pool = xe->sriov.vf.ccs.contexts[ctx_id].mem.ccs_bb_pool; if (!bb_pool) @@ -475,6 +488,4 @@ void xe_sriov_vf_ccs_print(struct xe_device *xe, struct drm_printer *p) drm_suballoc_dump_debug_info(&bb_pool->base, p, xe_sa_manager_gpu_addr(bb_pool)); drm_puts(p, "\n"); } - - xe_pm_runtime_put(xe); } diff --git a/drivers/gpu/drm/xe/xe_sriov_vf_ccs.h b/drivers/gpu/drm/xe/xe_sriov_vf_ccs.h index f8ca6efce9ee..00e58b36c510 100644 --- a/drivers/gpu/drm/xe/xe_sriov_vf_ccs.h +++ b/drivers/gpu/drm/xe/xe_sriov_vf_ccs.h @@ -20,6 +20,7 @@ int xe_sriov_vf_ccs_detach_bo(struct xe_bo *bo); int xe_sriov_vf_ccs_register_context(struct xe_device *xe); void xe_sriov_vf_ccs_rebase(struct xe_device *xe); void xe_sriov_vf_ccs_print(struct xe_device *xe, struct drm_printer *p); +void xe_sriov_vf_ccs_rw_update_bb_addr(struct xe_sriov_vf_ccs_ctx *ctx); static inline bool xe_sriov_vf_ccs_ready(struct xe_device *xe) { diff --git a/drivers/gpu/drm/xe/xe_sriov_vfio.c b/drivers/gpu/drm/xe/xe_sriov_vfio.c index e9a7615bb5c5..3da81af97b8b 100644 --- a/drivers/gpu/drm/xe/xe_sriov_vfio.c +++ b/drivers/gpu/drm/xe/xe_sriov_vfio.c @@ -21,7 +21,7 @@ EXPORT_SYMBOL_FOR_MODULES(xe_sriov_vfio_get_pf, "xe-vfio-pci"); bool xe_sriov_vfio_migration_supported(struct xe_device *xe) { if (!IS_SRIOV_PF(xe)) - return -EPERM; + return false; return xe_sriov_pf_migration_supported(xe); } diff --git a/drivers/gpu/drm/xe/xe_step.c b/drivers/gpu/drm/xe/xe_step.c index 10e88f2c9615..2860986f82f7 100644 --- a/drivers/gpu/drm/xe/xe_step.c +++ b/drivers/gpu/drm/xe/xe_step.c @@ -5,10 +5,11 @@ #include "xe_step.h" +#include <drm/drm_print.h> #include <kunit/visibility.h> #include <linux/bitfield.h> -#include "xe_device.h" +#include "xe_device_types.h" #include "xe_platform_types.h" /* diff --git a/drivers/gpu/drm/xe/xe_survivability_mode.c b/drivers/gpu/drm/xe/xe_survivability_mode.c index 1662bfddd4bc..6578ffc77bd5 100644 --- a/drivers/gpu/drm/xe/xe_survivability_mode.c +++ b/drivers/gpu/drm/xe/xe_survivability_mode.c @@ -12,15 +12,13 @@ #include "xe_configfs.h" #include "xe_device.h" -#include "xe_gt.h" #include "xe_heci_gsc.h" #include "xe_i2c.h" #include "xe_mmio.h" +#include "xe_nvm.h" #include "xe_pcode_api.h" #include "xe_vsec.h" -#define MAX_SCRATCH_MMIO 8 - /** * DOC: Survivability Mode * @@ -48,19 +46,43 @@ * * Refer :ref:`xe_configfs` for more details on how to use configfs * - * Survivability mode is indicated by the below admin-only readable sysfs which provides additional - * debug information:: + * Survivability mode is indicated by the below admin-only readable sysfs entry. It + * provides information about the type of survivability mode (Boot/Runtime). + * + * .. code-block:: shell + * + * # cat /sys/bus/pci/devices/<device>/survivability_mode + * Boot + * + * + * Any additional debug information if present will be visible under the directory + * ``survivability_info``:: + * + * /sys/bus/pci/devices/<device>/survivability_info/ + * ├── aux_info0 + * ├── aux_info1 + * ├── aux_info2 + * ├── aux_info3 + * ├── aux_info4 + * ├── capability_info + * ├── fdo_mode + * ├── postcode_trace + * └── postcode_trace_overflow + * + * This directory has the following attributes + * + * - ``capability_info`` : Indicates Boot status and support for additional information * - * /sys/bus/pci/devices/<device>/survivability_mode + * - ``postcode_trace``, ``postcode_trace_overflow`` : Each postcode is a 8bit value and + * represents a boot failure event. When a new failure event is logged by PCODE the + * existing postcodes are shifted left. These entries provide a history of 8 postcodes. * - * Capability Information: - * Provides boot status - * Postcode Information: - * Provides information about the failure - * Overflow Information - * Provides history of previous failures - * Auxiliary Information - * Certain failures may have information in addition to postcode information + * - ``aux_info<n>`` : Some failures have additional debug information + * + * - ``fdo_mode`` : To allow recovery in scenarios where MEI itself fails, a new SPI Flash + * Descriptor Override (FDO) mode is added in v2 survivability breadcrumbs. This mode is enabled + * by PCODE and provides the ability to directly update the firmware via SPI Driver without + * any dependency on MEI. Xe KMD initializes the nvm aux driver if FDO mode is enabled. * * Runtime Survivability * ===================== @@ -68,61 +90,77 @@ * Certain runtime firmware errors can cause the device to enter a wedged state * (:ref:`xe-device-wedging`) requiring a firmware flash to restore normal operation. * Runtime Survivability Mode indicates that a firmware flash is necessary to recover the device and - * is indicated by the presence of survivability mode sysfs:: + * is indicated by the presence of survivability mode sysfs. + * Survivability mode sysfs provides information about the type of survivability mode. * - * /sys/bus/pci/devices/<device>/survivability_mode + * .. code-block:: shell * - * Survivability mode sysfs provides information about the type of survivability mode. + * # cat /sys/bus/pci/devices/<device>/survivability_mode + * Runtime * * When such errors occur, userspace is notified with the drm device wedged uevent and runtime * survivability mode. User can then initiate a firmware flash using userspace tools like fwupd * to restore device to normal operation. */ -static u32 aux_history_offset(u32 reg_value) +static const char * const reg_map[] = { + [CAPABILITY_INFO] = "Capability Info", + [POSTCODE_TRACE] = "Postcode trace", + [POSTCODE_TRACE_OVERFLOW] = "Postcode trace overflow", + [AUX_INFO0] = "Auxiliary Info 0", + [AUX_INFO1] = "Auxiliary Info 1", + [AUX_INFO2] = "Auxiliary Info 2", + [AUX_INFO3] = "Auxiliary Info 3", + [AUX_INFO4] = "Auxiliary Info 4", +}; + +#define FDO_INFO (MAX_SCRATCH_REG + 1) + +struct xe_survivability_attribute { + struct device_attribute attr; + u8 index; +}; + +static struct +xe_survivability_attribute *dev_attr_to_survivability_attr(struct device_attribute *attr) { - return REG_FIELD_GET(AUXINFO_HISTORY_OFFSET, reg_value); + return container_of(attr, struct xe_survivability_attribute, attr); } -static void set_survivability_info(struct xe_mmio *mmio, struct xe_survivability_info *info, - int id, char *name) +static void set_survivability_info(struct xe_mmio *mmio, u32 *info, int id) { - strscpy(info[id].name, name, sizeof(info[id].name)); - info[id].reg = PCODE_SCRATCH(id).raw; - info[id].value = xe_mmio_read32(mmio, PCODE_SCRATCH(id)); + info[id] = xe_mmio_read32(mmio, PCODE_SCRATCH(id)); } static void populate_survivability_info(struct xe_device *xe) { struct xe_survivability *survivability = &xe->survivability; - struct xe_survivability_info *info = survivability->info; + u32 *info = survivability->info; struct xe_mmio *mmio; u32 id = 0, reg_value; - char name[NAME_MAX]; - int index; mmio = xe_root_tile_mmio(xe); - set_survivability_info(mmio, info, id, "Capability Info"); - reg_value = info[id].value; + set_survivability_info(mmio, info, CAPABILITY_INFO); + reg_value = info[CAPABILITY_INFO]; + + survivability->version = REG_FIELD_GET(BREADCRUMB_VERSION, reg_value); + /* FDO mode is exposed only from version 2 */ + if (survivability->version >= 2) + survivability->fdo_mode = REG_FIELD_GET(FDO_MODE, reg_value); if (reg_value & HISTORY_TRACKING) { - id++; - set_survivability_info(mmio, info, id, "Postcode Info"); + set_survivability_info(mmio, info, POSTCODE_TRACE); - if (reg_value & OVERFLOW_SUPPORT) { - id = REG_FIELD_GET(OVERFLOW_REG_OFFSET, reg_value); - set_survivability_info(mmio, info, id, "Overflow Info"); - } + if (reg_value & OVERFLOW_SUPPORT) + set_survivability_info(mmio, info, POSTCODE_TRACE_OVERFLOW); } + /* Traverse the linked list of aux info registers */ if (reg_value & AUXINFO_SUPPORT) { - id = REG_FIELD_GET(AUXINFO_REG_OFFSET, reg_value); - - for (index = 0; id && reg_value; index++, reg_value = info[id].value, - id = aux_history_offset(reg_value)) { - snprintf(name, NAME_MAX, "Auxiliary Info %d", index); - set_survivability_info(mmio, info, id, name); - } + for (id = REG_FIELD_GET(AUXINFO_REG_OFFSET, reg_value); + id >= AUX_INFO0 && id < MAX_SCRATCH_REG; + id = REG_FIELD_GET(AUXINFO_HISTORY_OFFSET, info[id])) + set_survivability_info(mmio, info, id); } } @@ -130,15 +168,14 @@ static void log_survivability_info(struct pci_dev *pdev) { struct xe_device *xe = pdev_to_xe_device(pdev); struct xe_survivability *survivability = &xe->survivability; - struct xe_survivability_info *info = survivability->info; + u32 *info = survivability->info; int id; dev_info(&pdev->dev, "Survivability Boot Status : Critical Failure (%d)\n", survivability->boot_status); - for (id = 0; id < MAX_SCRATCH_MMIO; id++) { - if (info[id].reg) - dev_info(&pdev->dev, "%s: 0x%x - 0x%x\n", info[id].name, - info[id].reg, info[id].value); + for (id = 0; id < MAX_SCRATCH_REG; id++) { + if (info[id]) + dev_info(&pdev->dev, "%s: 0x%x\n", reg_map[id], info[id]); } } @@ -156,43 +193,103 @@ static ssize_t survivability_mode_show(struct device *dev, struct pci_dev *pdev = to_pci_dev(dev); struct xe_device *xe = pdev_to_xe_device(pdev); struct xe_survivability *survivability = &xe->survivability; - struct xe_survivability_info *info = survivability->info; - int index = 0, count = 0; - count += sysfs_emit_at(buff, count, "Survivability mode type: %s\n", - survivability->type ? "Runtime" : "Boot"); + return sysfs_emit(buff, "%s\n", survivability->type ? "Runtime" : "Boot"); +} + +static DEVICE_ATTR_ADMIN_RO(survivability_mode); - if (!check_boot_failure(xe)) - return count; +static ssize_t survivability_info_show(struct device *dev, + struct device_attribute *attr, char *buff) +{ + struct xe_survivability_attribute *sa = dev_attr_to_survivability_attr(attr); + struct pci_dev *pdev = to_pci_dev(dev); + struct xe_device *xe = pdev_to_xe_device(pdev); + struct xe_survivability *survivability = &xe->survivability; + u32 *info = survivability->info; - for (index = 0; index < MAX_SCRATCH_MMIO; index++) { - if (info[index].reg) - count += sysfs_emit_at(buff, count, "%s: 0x%x - 0x%x\n", info[index].name, - info[index].reg, info[index].value); - } + if (sa->index == FDO_INFO) + return sysfs_emit(buff, "%s\n", str_enabled_disabled(survivability->fdo_mode)); - return count; + return sysfs_emit(buff, "0x%x\n", info[sa->index]); } -static DEVICE_ATTR_ADMIN_RO(survivability_mode); +#define SURVIVABILITY_ATTR_RO(name, _index) \ + struct xe_survivability_attribute attr_##name = { \ + .attr = __ATTR(name, 0400, survivability_info_show, NULL), \ + .index = _index, \ + } + +static SURVIVABILITY_ATTR_RO(capability_info, CAPABILITY_INFO); +static SURVIVABILITY_ATTR_RO(postcode_trace, POSTCODE_TRACE); +static SURVIVABILITY_ATTR_RO(postcode_trace_overflow, POSTCODE_TRACE_OVERFLOW); +static SURVIVABILITY_ATTR_RO(aux_info0, AUX_INFO0); +static SURVIVABILITY_ATTR_RO(aux_info1, AUX_INFO1); +static SURVIVABILITY_ATTR_RO(aux_info2, AUX_INFO2); +static SURVIVABILITY_ATTR_RO(aux_info3, AUX_INFO3); +static SURVIVABILITY_ATTR_RO(aux_info4, AUX_INFO4); +static SURVIVABILITY_ATTR_RO(fdo_mode, FDO_INFO); static void xe_survivability_mode_fini(void *arg) { struct xe_device *xe = arg; + struct xe_survivability *survivability = &xe->survivability; struct pci_dev *pdev = to_pci_dev(xe->drm.dev); struct device *dev = &pdev->dev; - sysfs_remove_file(&dev->kobj, &dev_attr_survivability_mode.attr); + if (survivability->fdo_mode) + xe_nvm_fini(xe); + + device_remove_file(dev, &dev_attr_survivability_mode); +} + +static umode_t survivability_info_attrs_visible(struct kobject *kobj, struct attribute *attr, + int idx) +{ + struct xe_device *xe = kdev_to_xe_device(kobj_to_dev(kobj)); + struct xe_survivability *survivability = &xe->survivability; + u32 *info = survivability->info; + + /* + * Last index in survivability_info_attrs is fdo mode and is applicable only in + * version 2 of survivability mode + */ + if (idx == MAX_SCRATCH_REG && survivability->version >= 2) + return 0400; + + if (idx < MAX_SCRATCH_REG && info[idx]) + return 0400; + + return 0; } +/* Attributes are ordered according to enum scratch_reg */ +static struct attribute *survivability_info_attrs[] = { + &attr_capability_info.attr.attr, + &attr_postcode_trace.attr.attr, + &attr_postcode_trace_overflow.attr.attr, + &attr_aux_info0.attr.attr, + &attr_aux_info1.attr.attr, + &attr_aux_info2.attr.attr, + &attr_aux_info3.attr.attr, + &attr_aux_info4.attr.attr, + &attr_fdo_mode.attr.attr, + NULL, +}; + +static const struct attribute_group survivability_info_group = { + .name = "survivability_info", + .attrs = survivability_info_attrs, + .is_visible = survivability_info_attrs_visible, +}; + static int create_survivability_sysfs(struct pci_dev *pdev) { struct device *dev = &pdev->dev; struct xe_device *xe = pdev_to_xe_device(pdev); int ret; - /* create survivability mode sysfs */ - ret = sysfs_create_file(&dev->kobj, &dev_attr_survivability_mode.attr); + ret = device_create_file(dev, &dev_attr_survivability_mode); if (ret) { dev_warn(dev, "Failed to create survivability sysfs files\n"); return ret; @@ -203,6 +300,12 @@ static int create_survivability_sysfs(struct pci_dev *pdev) if (ret) return ret; + if (check_boot_failure(xe)) { + ret = devm_device_add_group(dev, &survivability_info_group); + if (ret) + return ret; + } + return 0; } @@ -217,15 +320,19 @@ static int enable_boot_survivability_mode(struct pci_dev *pdev) if (ret) return ret; - /* Make sure xe_heci_gsc_init() knows about survivability mode */ + /* Make sure xe_heci_gsc_init() and xe_i2c_probe() are aware of survivability */ survivability->mode = true; - ret = xe_heci_gsc_init(xe); - if (ret) - goto err; + xe_heci_gsc_init(xe); xe_vsec_init(xe); + if (survivability->fdo_mode) { + ret = xe_nvm_init(xe); + if (ret) + goto err; + } + ret = xe_i2c_probe(xe); if (ret) goto err; @@ -235,29 +342,11 @@ static int enable_boot_survivability_mode(struct pci_dev *pdev) return 0; err: + dev_err(dev, "Failed to enable Survivability Mode\n"); survivability->mode = false; return ret; } -static int init_survivability_mode(struct xe_device *xe) -{ - struct xe_survivability *survivability = &xe->survivability; - struct xe_survivability_info *info; - - survivability->size = MAX_SCRATCH_MMIO; - - info = devm_kcalloc(xe->drm.dev, survivability->size, sizeof(*info), - GFP_KERNEL); - if (!info) - return -ENOMEM; - - survivability->info = info; - - populate_survivability_info(xe); - - return 0; -} - /** * xe_survivability_mode_is_boot_enabled- check if boot survivability mode is enabled * @xe: xe device instance @@ -325,9 +414,7 @@ int xe_survivability_mode_runtime_enable(struct xe_device *xe) return -EINVAL; } - ret = init_survivability_mode(xe); - if (ret) - return ret; + populate_survivability_info(xe); ret = create_survivability_sysfs(pdev); if (ret) @@ -356,17 +443,16 @@ int xe_survivability_mode_boot_enable(struct xe_device *xe) { struct xe_survivability *survivability = &xe->survivability; struct pci_dev *pdev = to_pci_dev(xe->drm.dev); - int ret; if (!xe_survivability_mode_is_requested(xe)) return 0; - ret = init_survivability_mode(xe); - if (ret) - return ret; + populate_survivability_info(xe); - /* Log breadcrumbs but do not enter survivability mode for Critical boot errors */ - if (survivability->boot_status == CRITICAL_FAILURE) { + /* + * v2 supports survivability mode for critical errors + */ + if (survivability->version < 2 && survivability->boot_status == CRITICAL_FAILURE) { log_survivability_info(pdev); return -ENXIO; } diff --git a/drivers/gpu/drm/xe/xe_survivability_mode_types.h b/drivers/gpu/drm/xe/xe_survivability_mode_types.h index cd65a5d167c9..bd5dc1c955ff 100644 --- a/drivers/gpu/drm/xe/xe_survivability_mode_types.h +++ b/drivers/gpu/drm/xe/xe_survivability_mode_types.h @@ -9,23 +9,29 @@ #include <linux/limits.h> #include <linux/types.h> +enum scratch_reg { + CAPABILITY_INFO, + POSTCODE_TRACE, + POSTCODE_TRACE_OVERFLOW, + AUX_INFO0, + AUX_INFO1, + AUX_INFO2, + AUX_INFO3, + AUX_INFO4, + MAX_SCRATCH_REG, +}; + enum xe_survivability_type { XE_SURVIVABILITY_TYPE_BOOT, XE_SURVIVABILITY_TYPE_RUNTIME, }; -struct xe_survivability_info { - char name[NAME_MAX]; - u32 reg; - u32 value; -}; - /** * struct xe_survivability: Contains survivability mode information */ struct xe_survivability { - /** @info: struct that holds survivability info from scratch registers */ - struct xe_survivability_info *info; + /** @info: survivability debug info */ + u32 info[MAX_SCRATCH_REG]; /** @size: number of scratch registers */ u32 size; @@ -38,6 +44,12 @@ struct xe_survivability { /** @type: survivability type */ enum xe_survivability_type type; + + /** @fdo_mode: indicates if FDO mode is enabled */ + bool fdo_mode; + + /** @version: breadcrumb version of survivability mode */ + u8 version; }; #endif /* _XE_SURVIVABILITY_MODE_TYPES_H_ */ diff --git a/drivers/gpu/drm/xe/xe_svm.c b/drivers/gpu/drm/xe/xe_svm.c index 55c5a0eb82e1..213f0334518a 100644 --- a/drivers/gpu/drm/xe/xe_svm.c +++ b/drivers/gpu/drm/xe/xe_svm.c @@ -3,7 +3,12 @@ * Copyright © 2024 Intel Corporation */ +#include <linux/pci-p2pdma.h> + #include <drm/drm_drv.h> +#include <drm/drm_managed.h> +#include <drm/drm_pagemap.h> +#include <drm/drm_pagemap_util.h> #include "xe_bo.h" #include "xe_exec_queue_types.h" @@ -19,6 +24,38 @@ #include "xe_vm_types.h" #include "xe_vram_types.h" +/* Identifies subclasses of struct drm_pagemap_peer */ +#define XE_PEER_PAGEMAP ((void *)0ul) +#define XE_PEER_VM ((void *)1ul) + +/** + * DOC: drm_pagemap reference-counting in xe: + * + * In addition to the drm_pagemap internal reference counting by its zone + * device data, the xe driver holds the following long-time references: + * + * - struct xe_pagemap: + * The xe_pagemap struct derives from struct drm_pagemap and uses its + * reference count. + * - SVM-enabled VMs: + * SVM-enabled VMs look up and keeps a reference to all xe_pagemaps on + * the same device. + * - VMAs: + * vmas keep a reference on the drm_pagemap indicated by a gpu_madvise() + * call. + * + * In addition, all drm_pagemap or xe_pagemap pointers where lifetime cannot + * be guaranteed by a vma reference under the vm lock should keep a reference. + * That includes the range->pages.dpagemap pointer. + */ + +static int xe_svm_get_pagemaps(struct xe_vm *vm); + +void *xe_svm_private_page_owner(struct xe_vm *vm, bool force_smem) +{ + return force_smem ? NULL : vm->svm.peer.owner; +} + static bool xe_svm_range_in_vram(struct xe_svm_range *range) { /* @@ -176,24 +213,13 @@ xe_svm_range_notifier_event_end(struct xe_vm *vm, struct drm_gpusvm_range *r, mmu_range); } -static s64 xe_svm_stats_ktime_us_delta(ktime_t start) -{ - return IS_ENABLED(CONFIG_DEBUG_FS) ? - ktime_us_delta(ktime_get(), start) : 0; -} - static void xe_svm_tlb_inval_us_stats_incr(struct xe_gt *gt, ktime_t start) { - s64 us_delta = xe_svm_stats_ktime_us_delta(start); + s64 us_delta = xe_gt_stats_ktime_us_delta(start); xe_gt_stats_incr(gt, XE_GT_STATS_ID_SVM_TLB_INVAL_US, us_delta); } -static ktime_t xe_svm_stats_ktime_get(void) -{ - return IS_ENABLED(CONFIG_DEBUG_FS) ? ktime_get() : 0; -} - static void xe_svm_invalidate(struct drm_gpusvm *gpusvm, struct drm_gpusvm_notifier *notifier, const struct mmu_notifier_range *mmu_range) @@ -202,7 +228,7 @@ static void xe_svm_invalidate(struct drm_gpusvm *gpusvm, struct xe_device *xe = vm->xe; struct drm_gpusvm_range *r, *first; struct xe_tile *tile; - ktime_t start = xe_svm_stats_ktime_get(); + ktime_t start = xe_gt_stats_ktime_get(); u64 adj_start = mmu_range->start, adj_end = mmu_range->end; u8 tile_mask = 0, id; long err; @@ -285,19 +311,25 @@ static int __xe_svm_garbage_collector(struct xe_vm *vm, return 0; } -static int xe_svm_range_set_default_attr(struct xe_vm *vm, u64 range_start, u64 range_end) +static void xe_vma_set_default_attributes(struct xe_vma *vma) { - struct xe_vma *vma; struct xe_vma_mem_attr default_attr = { - .preferred_loc = { - .devmem_fd = DRM_XE_PREFERRED_LOC_DEFAULT_DEVICE, - .migration_policy = DRM_XE_MIGRATE_ALL_PAGES, - }, + .preferred_loc.devmem_fd = DRM_XE_PREFERRED_LOC_DEFAULT_DEVICE, + .preferred_loc.migration_policy = DRM_XE_MIGRATE_ALL_PAGES, + .pat_index = vma->attr.default_pat_index, .atomic_access = DRM_XE_ATOMIC_UNDEFINED, }; - int err = 0; - vma = xe_vm_find_vma_by_addr(vm, range_start); + xe_vma_mem_attr_copy(&vma->attr, &default_attr); +} + +static int xe_svm_range_set_default_attr(struct xe_vm *vm, u64 start, u64 end) +{ + struct xe_vma *vma; + bool has_default_attr; + int err; + + vma = xe_vm_find_vma_by_addr(vm, start); if (!vma) return -EINVAL; @@ -306,25 +338,30 @@ static int xe_svm_range_set_default_attr(struct xe_vm *vm, u64 range_start, u64 return 0; } - if (xe_vma_has_default_mem_attrs(vma)) - return 0; - vm_dbg(&vm->xe->drm, "Existing VMA start=0x%016llx, vma_end=0x%016llx", xe_vma_start(vma), xe_vma_end(vma)); - if (xe_vma_start(vma) == range_start && xe_vma_end(vma) == range_end) { - default_attr.pat_index = vma->attr.default_pat_index; - default_attr.default_pat_index = vma->attr.default_pat_index; - vma->attr = default_attr; - } else { - vm_dbg(&vm->xe->drm, "Split VMA start=0x%016llx, vma_end=0x%016llx", - range_start, range_end); - err = xe_vm_alloc_cpu_addr_mirror_vma(vm, range_start, range_end - range_start); - if (err) { - drm_warn(&vm->xe->drm, "VMA SPLIT failed: %pe\n", ERR_PTR(err)); - xe_vm_kill(vm, true); - return err; - } + has_default_attr = xe_vma_has_default_mem_attrs(vma); + + if (has_default_attr) { + start = xe_vma_start(vma); + end = xe_vma_end(vma); + } else if (xe_vma_start(vma) == start && xe_vma_end(vma) == end) { + xe_vma_set_default_attributes(vma); + } + + xe_vm_find_cpu_addr_mirror_vma_range(vm, &start, &end); + + if (xe_vma_start(vma) == start && xe_vma_end(vma) == end && has_default_attr) + return 0; + + vm_dbg(&vm->xe->drm, "New VMA start=0x%016llx, vma_end=0x%016llx", start, end); + + err = xe_vm_alloc_cpu_addr_mirror_vma(vm, start, end - start); + if (err) { + drm_warn(&vm->xe->drm, "New VMA MAP failed: %pe\n", ERR_PTR(err)); + xe_vm_kill(vm, true); + return err; } /* @@ -394,27 +431,47 @@ static void xe_svm_garbage_collector_work_func(struct work_struct *w) #if IS_ENABLED(CONFIG_DRM_XE_PAGEMAP) -static struct xe_vram_region *page_to_vr(struct page *page) +static struct xe_vram_region *xe_pagemap_to_vr(struct xe_pagemap *xpagemap) { - return container_of(page_pgmap(page), struct xe_vram_region, pagemap); + return xpagemap->vr; } -static u64 xe_vram_region_page_to_dpa(struct xe_vram_region *vr, - struct page *page) +static struct xe_pagemap *xe_page_to_pagemap(struct page *page) { - u64 dpa; + return container_of(page_pgmap(page), struct xe_pagemap, pagemap); +} + +static struct xe_vram_region *xe_page_to_vr(struct page *page) +{ + return xe_pagemap_to_vr(xe_page_to_pagemap(page)); +} + +static u64 xe_page_to_dpa(struct page *page) +{ + struct xe_pagemap *xpagemap = xe_page_to_pagemap(page); + struct xe_vram_region *vr = xe_pagemap_to_vr(xpagemap); + u64 hpa_base = xpagemap->hpa_base; u64 pfn = page_to_pfn(page); u64 offset; + u64 dpa; xe_assert(vr->xe, is_device_private_page(page)); - xe_assert(vr->xe, (pfn << PAGE_SHIFT) >= vr->hpa_base); + xe_assert(vr->xe, (pfn << PAGE_SHIFT) >= hpa_base); - offset = (pfn << PAGE_SHIFT) - vr->hpa_base; + offset = (pfn << PAGE_SHIFT) - hpa_base; dpa = vr->dpa_base + offset; return dpa; } +static u64 xe_page_to_pcie(struct page *page) +{ + struct xe_pagemap *xpagemap = xe_page_to_pagemap(page); + struct xe_vram_region *vr = xe_pagemap_to_vr(xpagemap); + + return xe_page_to_dpa(page) - vr->dpa_base + vr->io_start; +} + enum xe_svm_copy_dir { XE_SVM_COPY_TO_VRAM, XE_SVM_COPY_TO_SRAM, @@ -435,7 +492,7 @@ static void xe_svm_copy_us_stats_incr(struct xe_gt *gt, unsigned long npages, ktime_t start) { - s64 us_delta = xe_svm_stats_ktime_us_delta(start); + s64 us_delta = xe_gt_stats_ktime_us_delta(start); if (dir == XE_SVM_COPY_TO_VRAM) { switch (npages) { @@ -476,7 +533,8 @@ static void xe_svm_copy_us_stats_incr(struct xe_gt *gt, static int xe_svm_copy(struct page **pages, struct drm_pagemap_addr *pagemap_addr, - unsigned long npages, const enum xe_svm_copy_dir dir) + unsigned long npages, const enum xe_svm_copy_dir dir, + struct dma_fence *pre_migrate_fence) { struct xe_vram_region *vr = NULL; struct xe_gt *gt = NULL; @@ -487,7 +545,7 @@ static int xe_svm_copy(struct page **pages, u64 vram_addr = XE_VRAM_ADDR_INVALID; int err = 0, pos = 0; bool sram = dir == XE_SVM_COPY_TO_SRAM; - ktime_t start = xe_svm_stats_ktime_get(); + ktime_t start = xe_gt_stats_ktime_get(); /* * This flow is complex: it locates physically contiguous device pages, @@ -513,11 +571,11 @@ static int xe_svm_copy(struct page **pages, continue; if (!vr && spage) { - vr = page_to_vr(spage); + vr = xe_page_to_vr(spage); gt = xe_migrate_exec_queue(vr->migrate)->gt; xe = vr->xe; } - XE_WARN_ON(spage && page_to_vr(spage) != vr); + XE_WARN_ON(spage && xe_page_to_vr(spage) != vr); /* * CPU page and device page valid, capture physical address on @@ -525,7 +583,7 @@ static int xe_svm_copy(struct page **pages, * device pages. */ if (pagemap_addr[i].addr && spage) { - __vram_addr = xe_vram_region_page_to_dpa(vr, spage); + __vram_addr = xe_page_to_dpa(spage); if (vram_addr == XE_VRAM_ADDR_INVALID) { vram_addr = __vram_addr; pos = i; @@ -565,7 +623,8 @@ static int xe_svm_copy(struct page **pages, __fence = xe_migrate_from_vram(vr->migrate, i - pos + incr, vram_addr, - &pagemap_addr[pos]); + &pagemap_addr[pos], + pre_migrate_fence); } else { vm_dbg(&xe->drm, "COPY TO VRAM - 0x%016llx -> 0x%016llx, NPAGES=%ld", @@ -574,13 +633,14 @@ static int xe_svm_copy(struct page **pages, __fence = xe_migrate_to_vram(vr->migrate, i - pos + incr, &pagemap_addr[pos], - vram_addr); + vram_addr, + pre_migrate_fence); } if (IS_ERR(__fence)) { err = PTR_ERR(__fence); goto err_out; } - + pre_migrate_fence = NULL; dma_fence_put(fence); fence = __fence; } @@ -603,20 +663,22 @@ static int xe_svm_copy(struct page **pages, vram_addr, (u64)pagemap_addr[pos].addr, 1); __fence = xe_migrate_from_vram(vr->migrate, 1, vram_addr, - &pagemap_addr[pos]); + &pagemap_addr[pos], + pre_migrate_fence); } else { vm_dbg(&xe->drm, "COPY TO VRAM - 0x%016llx -> 0x%016llx, NPAGES=%d", (u64)pagemap_addr[pos].addr, vram_addr, 1); __fence = xe_migrate_to_vram(vr->migrate, 1, &pagemap_addr[pos], - vram_addr); + vram_addr, + pre_migrate_fence); } if (IS_ERR(__fence)) { err = PTR_ERR(__fence); goto err_out; } - + pre_migrate_fence = NULL; dma_fence_put(fence); fence = __fence; } @@ -629,6 +691,8 @@ err_out: dma_fence_wait(fence, false); dma_fence_put(fence); } + if (pre_migrate_fence) + dma_fence_wait(pre_migrate_fence, false); /* * XXX: We can't derive the GT here (or anywhere in this functions, but @@ -645,16 +709,20 @@ err_out: static int xe_svm_copy_to_devmem(struct page **pages, struct drm_pagemap_addr *pagemap_addr, - unsigned long npages) + unsigned long npages, + struct dma_fence *pre_migrate_fence) { - return xe_svm_copy(pages, pagemap_addr, npages, XE_SVM_COPY_TO_VRAM); + return xe_svm_copy(pages, pagemap_addr, npages, XE_SVM_COPY_TO_VRAM, + pre_migrate_fence); } static int xe_svm_copy_to_ram(struct page **pages, struct drm_pagemap_addr *pagemap_addr, - unsigned long npages) + unsigned long npages, + struct dma_fence *pre_migrate_fence) { - return xe_svm_copy(pages, pagemap_addr, npages, XE_SVM_COPY_TO_SRAM); + return xe_svm_copy(pages, pagemap_addr, npages, XE_SVM_COPY_TO_SRAM, + pre_migrate_fence); } static struct xe_bo *to_xe_bo(struct drm_pagemap_devmem *devmem_allocation) @@ -667,13 +735,16 @@ static void xe_svm_devmem_release(struct drm_pagemap_devmem *devmem_allocation) struct xe_bo *bo = to_xe_bo(devmem_allocation); struct xe_device *xe = xe_bo_device(bo); + dma_fence_put(devmem_allocation->pre_migrate_fence); xe_bo_put_async(bo); xe_pm_runtime_put(xe); } -static u64 block_offset_to_pfn(struct xe_vram_region *vr, u64 offset) +static u64 block_offset_to_pfn(struct drm_pagemap *dpagemap, u64 offset) { - return PHYS_PFN(offset + vr->hpa_base); + struct xe_pagemap *xpagemap = container_of(dpagemap, typeof(*xpagemap), dpagemap); + + return PHYS_PFN(offset + xpagemap->hpa_base); } static struct drm_buddy *vram_to_buddy(struct xe_vram_region *vram) @@ -693,7 +764,8 @@ static int xe_svm_populate_devmem_pfn(struct drm_pagemap_devmem *devmem_allocati list_for_each_entry(block, blocks, link) { struct xe_vram_region *vr = block->private; struct drm_buddy *buddy = vram_to_buddy(vr); - u64 block_pfn = block_offset_to_pfn(vr, drm_buddy_block_offset(block)); + u64 block_pfn = block_offset_to_pfn(devmem_allocation->dpagemap, + drm_buddy_block_offset(block)); int i; for (i = 0; i < drm_buddy_block_size(buddy, block) >> PAGE_SHIFT; ++i) @@ -710,6 +782,11 @@ static const struct drm_pagemap_devmem_ops dpagemap_devmem_ops = { .copy_to_ram = xe_svm_copy_to_ram, }; +#else +static int xe_svm_get_pagemaps(struct xe_vm *vm) +{ + return 0; +} #endif static const struct drm_gpusvm_ops gpusvm_ops = { @@ -724,6 +801,48 @@ static const unsigned long fault_chunk_sizes[] = { SZ_4K, }; +static void xe_pagemap_put(struct xe_pagemap *xpagemap) +{ + drm_pagemap_put(&xpagemap->dpagemap); +} + +static void xe_svm_put_pagemaps(struct xe_vm *vm) +{ + struct xe_device *xe = vm->xe; + struct xe_tile *tile; + int id; + + for_each_tile(tile, xe, id) { + struct xe_pagemap *xpagemap = vm->svm.pagemaps[id]; + + if (xpagemap) + xe_pagemap_put(xpagemap); + vm->svm.pagemaps[id] = NULL; + } +} + +static struct device *xe_peer_to_dev(struct drm_pagemap_peer *peer) +{ + if (peer->private == XE_PEER_PAGEMAP) + return container_of(peer, struct xe_pagemap, peer)->dpagemap.drm->dev; + + return container_of(peer, struct xe_vm, svm.peer)->xe->drm.dev; +} + +static bool xe_has_interconnect(struct drm_pagemap_peer *peer1, + struct drm_pagemap_peer *peer2) +{ + struct device *dev1 = xe_peer_to_dev(peer1); + struct device *dev2 = xe_peer_to_dev(peer2); + + if (dev1 == dev2) + return true; + + return pci_p2pdma_distance(to_pci_dev(dev1), dev2, true) >= 0; +} + +static DRM_PAGEMAP_OWNER_LIST_DEFINE(xe_owner_list); + /** * xe_svm_init() - SVM initialize * @vm: The VM. @@ -742,12 +861,30 @@ int xe_svm_init(struct xe_vm *vm) INIT_WORK(&vm->svm.garbage_collector.work, xe_svm_garbage_collector_work_func); + vm->svm.peer.private = XE_PEER_VM; + err = drm_pagemap_acquire_owner(&vm->svm.peer, &xe_owner_list, + xe_has_interconnect); + if (err) + return err; + + err = xe_svm_get_pagemaps(vm); + if (err) { + drm_pagemap_release_owner(&vm->svm.peer); + return err; + } + err = drm_gpusvm_init(&vm->svm.gpusvm, "Xe SVM", &vm->xe->drm, current->mm, 0, vm->size, xe_modparam.svm_notifier_size * SZ_1M, &gpusvm_ops, fault_chunk_sizes, ARRAY_SIZE(fault_chunk_sizes)); drm_gpusvm_driver_set_lock(&vm->svm.gpusvm, &vm->lock); + + if (err) { + xe_svm_put_pagemaps(vm); + drm_pagemap_release_owner(&vm->svm.peer); + return err; + } } else { err = drm_gpusvm_init(&vm->svm.gpusvm, "Xe SVM (simple)", &vm->xe->drm, NULL, 0, 0, 0, NULL, @@ -767,6 +904,8 @@ void xe_svm_close(struct xe_vm *vm) { xe_assert(vm->xe, xe_vm_is_closed(vm)); flush_work(&vm->svm.garbage_collector.work); + xe_svm_put_pagemaps(vm); + drm_pagemap_release_owner(&vm->svm.peer); } /** @@ -782,13 +921,34 @@ void xe_svm_fini(struct xe_vm *vm) drm_gpusvm_fini(&vm->svm.gpusvm); } +static bool xe_svm_range_has_pagemap_locked(const struct xe_svm_range *range, + const struct drm_pagemap *dpagemap) +{ + return range->base.pages.dpagemap == dpagemap; +} + +static bool xe_svm_range_has_pagemap(struct xe_svm_range *range, + const struct drm_pagemap *dpagemap) +{ + struct xe_vm *vm = range_to_vm(&range->base); + bool ret; + + xe_svm_notifier_lock(vm); + ret = xe_svm_range_has_pagemap_locked(range, dpagemap); + xe_svm_notifier_unlock(vm); + + return ret; +} + static bool xe_svm_range_is_valid(struct xe_svm_range *range, struct xe_tile *tile, - bool devmem_only) + bool devmem_only, + const struct drm_pagemap *dpagemap) + { return (xe_vm_has_valid_gpu_mapping(tile, range->tile_present, range->tile_invalidated) && - (!devmem_only || xe_svm_range_in_vram(range))); + (!devmem_only || xe_svm_range_has_pagemap(range, dpagemap))); } /** xe_svm_range_migrate_to_smem() - Move range pages from VRAM to SMEM @@ -809,7 +969,8 @@ void xe_svm_range_migrate_to_smem(struct xe_vm *vm, struct xe_svm_range *range) * @vm: xe_vm pointer * @range: Pointer to the SVM range structure * @tile_mask: Mask representing the tiles to be checked - * @devmem_preferred : if true range needs to be in devmem + * @dpagemap: if !%NULL, the range is expected to be present + * in device memory identified by this parameter. * * The xe_svm_range_validate() function checks if a range is * valid and located in the desired memory region. @@ -818,14 +979,17 @@ void xe_svm_range_migrate_to_smem(struct xe_vm *vm, struct xe_svm_range *range) */ bool xe_svm_range_validate(struct xe_vm *vm, struct xe_svm_range *range, - u8 tile_mask, bool devmem_preferred) + u8 tile_mask, const struct drm_pagemap *dpagemap) { bool ret; xe_svm_notifier_lock(vm); - ret = (range->tile_present & ~range->tile_invalidated & tile_mask) == tile_mask && - (devmem_preferred == range->base.pages.flags.has_devmem_pages); + ret = (range->tile_present & ~range->tile_invalidated & tile_mask) == tile_mask; + if (dpagemap) + ret = ret && xe_svm_range_has_pagemap_locked(range, dpagemap); + else + ret = ret && !range->base.pages.dpagemap; xe_svm_notifier_unlock(vm); @@ -860,7 +1024,13 @@ static int xe_drm_pagemap_populate_mm(struct drm_pagemap *dpagemap, struct mm_struct *mm, unsigned long timeslice_ms) { - struct xe_vram_region *vr = container_of(dpagemap, typeof(*vr), dpagemap); + struct xe_pagemap *xpagemap = container_of(dpagemap, typeof(*xpagemap), dpagemap); + struct drm_pagemap_migrate_details mdetails = { + .timeslice_ms = timeslice_ms, + .source_peer_migrates = 1, + }; + struct xe_vram_region *vr = xe_pagemap_to_vr(xpagemap); + struct dma_fence *pre_migrate_fence = NULL; struct xe_device *xe = vr->xe; struct device *dev = xe->drm.dev; struct drm_buddy_block *block; @@ -887,8 +1057,20 @@ static int xe_drm_pagemap_populate_mm(struct drm_pagemap *dpagemap, break; } + /* Ensure that any clearing or async eviction will complete before migration. */ + if (!dma_resv_test_signaled(bo->ttm.base.resv, DMA_RESV_USAGE_KERNEL)) { + err = dma_resv_get_singleton(bo->ttm.base.resv, DMA_RESV_USAGE_KERNEL, + &pre_migrate_fence); + if (err) + dma_resv_wait_timeout(bo->ttm.base.resv, DMA_RESV_USAGE_KERNEL, + false, MAX_SCHEDULE_TIMEOUT); + else if (pre_migrate_fence) + dma_fence_enable_sw_signaling(pre_migrate_fence); + } + drm_pagemap_devmem_init(&bo->devmem_allocation, dev, mm, - &dpagemap_devmem_ops, dpagemap, end - start); + &dpagemap_devmem_ops, dpagemap, end - start, + pre_migrate_fence); blocks = &to_xe_ttm_vram_mgr_resource(bo->ttm.resource)->blocks; list_for_each_entry(block, blocks, link) @@ -898,11 +1080,9 @@ static int xe_drm_pagemap_populate_mm(struct drm_pagemap *dpagemap, /* Ensure the device has a pm ref while there are device pages active. */ xe_pm_runtime_get_noresume(xe); + /* Consumes the devmem allocation ref. */ err = drm_pagemap_migrate_to_devmem(&bo->devmem_allocation, mm, - start, end, timeslice_ms, - xe_svm_devm_owner(xe)); - if (err) - xe_svm_devmem_release(&bo->devmem_allocation); + start, end, &mdetails); xe_bo_unlock(bo); xe_bo_put(bo); } @@ -925,23 +1105,23 @@ static bool supports_4K_migration(struct xe_device *xe) * xe_svm_range_needs_migrate_to_vram() - SVM range needs migrate to VRAM or not * @range: SVM range for which migration needs to be decided * @vma: vma which has range - * @preferred_region_is_vram: preferred region for range is vram + * @dpagemap: The preferred struct drm_pagemap to migrate to. * * Return: True for range needing migration and migration is supported else false */ bool xe_svm_range_needs_migrate_to_vram(struct xe_svm_range *range, struct xe_vma *vma, - bool preferred_region_is_vram) + const struct drm_pagemap *dpagemap) { struct xe_vm *vm = range_to_vm(&range->base); u64 range_size = xe_svm_range_size(range); - if (!range->base.pages.flags.migrate_devmem || !preferred_region_is_vram) + if (!range->base.pages.flags.migrate_devmem || !dpagemap) return false; xe_assert(vm->xe, IS_DGFX(vm->xe)); - if (xe_svm_range_in_vram(range)) { - drm_info(&vm->xe->drm, "Range is already in VRAM\n"); + if (xe_svm_range_has_pagemap(range, dpagemap)) { + drm_dbg(&vm->xe->drm, "Range is already in VRAM\n"); return false; } @@ -979,7 +1159,7 @@ static void xe_svm_range_##elem##_us_stats_incr(struct xe_gt *gt, \ struct xe_svm_range *range, \ ktime_t start) \ { \ - s64 us_delta = xe_svm_stats_ktime_us_delta(start); \ + s64 us_delta = xe_gt_stats_ktime_us_delta(start); \ \ switch (xe_svm_range_size(range)) { \ case SZ_4K: \ @@ -1015,7 +1195,6 @@ static int __xe_svm_handle_pagefault(struct xe_vm *vm, struct xe_vma *vma, .devmem_only = need_vram && devmem_possible, .timeslice_ms = need_vram && devmem_possible ? vm->xe->atomic_svm_timeslice_ms : 0, - .device_private_page_owner = xe_svm_devm_owner(vm->xe), }; struct xe_validation_ctx vctx; struct drm_exec exec; @@ -1024,7 +1203,7 @@ static int __xe_svm_handle_pagefault(struct xe_vm *vm, struct xe_vma *vma, struct drm_pagemap *dpagemap; struct xe_tile *tile = gt_to_tile(gt); int migrate_try_count = ctx.devmem_only ? 3 : 1; - ktime_t start = xe_svm_stats_ktime_get(), bind_start, get_pages_start; + ktime_t start = xe_gt_stats_ktime_get(), bind_start, get_pages_start; int err; lockdep_assert_held_write(&vm->lock); @@ -1038,9 +1217,9 @@ retry: if (err) return err; - dpagemap = xe_vma_resolve_pagemap(vma, tile); - if (!dpagemap && !ctx.devmem_only) - ctx.device_private_page_owner = NULL; + dpagemap = ctx.devmem_only ? xe_tile_local_pagemap(tile) : + xe_vma_resolve_pagemap(vma, tile); + ctx.device_private_page_owner = xe_svm_private_page_owner(vm, !dpagemap); range = xe_svm_range_find_or_insert(vm, fault_addr, vma, &ctx); if (IS_ERR(range)) @@ -1053,7 +1232,7 @@ retry: goto out; } - if (xe_svm_range_is_valid(range, tile, ctx.devmem_only)) { + if (xe_svm_range_is_valid(range, tile, ctx.devmem_only, dpagemap)) { xe_svm_range_valid_fault_count_stats_incr(gt, range); range_debug(range, "PAGE FAULT - VALID"); goto out; @@ -1062,16 +1241,11 @@ retry: range_debug(range, "PAGE FAULT"); if (--migrate_try_count >= 0 && - xe_svm_range_needs_migrate_to_vram(range, vma, !!dpagemap || ctx.devmem_only)) { - ktime_t migrate_start = xe_svm_stats_ktime_get(); + xe_svm_range_needs_migrate_to_vram(range, vma, dpagemap)) { + ktime_t migrate_start = xe_gt_stats_ktime_get(); - /* TODO : For multi-device dpagemap will be used to find the - * remote tile and remote device. Will need to modify - * xe_svm_alloc_vram to use dpagemap for future multi-device - * support. - */ xe_svm_range_migrate_count_stats_incr(gt, range); - err = xe_svm_alloc_vram(tile, range, &ctx); + err = xe_svm_alloc_vram(range, &ctx, dpagemap); xe_svm_range_migrate_us_stats_incr(gt, range, migrate_start); ctx.timeslice_ms <<= 1; /* Double timeslice if we have to retry */ if (err) { @@ -1100,7 +1274,7 @@ retry: } get_pages: - get_pages_start = xe_svm_stats_ktime_get(); + get_pages_start = xe_gt_stats_ktime_get(); range_debug(range, "GET PAGES"); err = xe_svm_range_get_pages(vm, range, &ctx); @@ -1122,12 +1296,16 @@ get_pages: if (err) { range_debug(range, "PAGE FAULT - FAIL PAGE COLLECT"); goto out; + } else if (IS_ENABLED(CONFIG_DRM_XE_DEBUG_VM)) { + drm_dbg(&vm->xe->drm, "After page collect data location is %sin \"%s\".\n", + xe_svm_range_has_pagemap(range, dpagemap) ? "" : "NOT ", + dpagemap ? dpagemap->drm->unique : "System."); } xe_svm_range_get_pages_us_stats_incr(gt, range, get_pages_start); range_debug(range, "PAGE FAULT - BIND"); - bind_start = xe_svm_stats_ktime_get(); + bind_start = xe_gt_stats_ktime_get(); xe_validation_guard(&vctx, &vm->xe->val, &exec, (struct xe_val_flags) {}, err) { err = xe_vm_drm_exec_lock(vm, &exec); drm_exec_retry_on_contention(&exec); @@ -1369,11 +1547,6 @@ u8 xe_svm_ranges_zap_ptes_in_range(struct xe_vm *vm, u64 start, u64 end) #if IS_ENABLED(CONFIG_DRM_XE_PAGEMAP) -static struct drm_pagemap *tile_local_pagemap(struct xe_tile *tile) -{ - return &tile->mem.vram->dpagemap; -} - /** * xe_vma_resolve_pagemap - Resolve the appropriate DRM pagemap for a VMA * @vma: Pointer to the xe_vma structure containing memory attributes @@ -1393,40 +1566,87 @@ static struct drm_pagemap *tile_local_pagemap(struct xe_tile *tile) */ struct drm_pagemap *xe_vma_resolve_pagemap(struct xe_vma *vma, struct xe_tile *tile) { - s32 fd = (s32)vma->attr.preferred_loc.devmem_fd; + struct drm_pagemap *dpagemap = vma->attr.preferred_loc.dpagemap; + s32 fd; + + if (dpagemap) + return dpagemap; + + fd = (s32)vma->attr.preferred_loc.devmem_fd; if (fd == DRM_XE_PREFERRED_LOC_DEFAULT_SYSTEM) return NULL; if (fd == DRM_XE_PREFERRED_LOC_DEFAULT_DEVICE) - return IS_DGFX(tile_to_xe(tile)) ? tile_local_pagemap(tile) : NULL; + return IS_DGFX(tile_to_xe(tile)) ? xe_tile_local_pagemap(tile) : NULL; - /* TODO: Support multi-device with drm_pagemap_from_fd(fd) */ return NULL; } /** * xe_svm_alloc_vram()- Allocate device memory pages for range, * migrating existing data. - * @tile: tile to allocate vram from * @range: SVM range * @ctx: DRM GPU SVM context + * @dpagemap: The struct drm_pagemap representing the memory to allocate. * * Return: 0 on success, error code on failure. */ -int xe_svm_alloc_vram(struct xe_tile *tile, struct xe_svm_range *range, - const struct drm_gpusvm_ctx *ctx) +int xe_svm_alloc_vram(struct xe_svm_range *range, const struct drm_gpusvm_ctx *ctx, + struct drm_pagemap *dpagemap) { - struct drm_pagemap *dpagemap; + static DECLARE_RWSEM(driver_migrate_lock); + struct xe_vm *vm = range_to_vm(&range->base); + enum drm_gpusvm_scan_result migration_state; + struct xe_device *xe = vm->xe; + int err, retries = 1; + bool write_locked = false; - xe_assert(tile_to_xe(tile), range->base.pages.flags.migrate_devmem); + xe_assert(range_to_vm(&range->base)->xe, range->base.pages.flags.migrate_devmem); range_debug(range, "ALLOCATE VRAM"); - dpagemap = tile_local_pagemap(tile); - return drm_pagemap_populate_mm(dpagemap, xe_svm_range_start(range), - xe_svm_range_end(range), - range->base.gpusvm->mm, - ctx->timeslice_ms); + migration_state = drm_gpusvm_scan_mm(&range->base, + xe_svm_private_page_owner(vm, false), + dpagemap->pagemap); + + if (migration_state == DRM_GPUSVM_SCAN_EQUAL) { + if (IS_ENABLED(CONFIG_DRM_XE_DEBUG_VM)) + drm_dbg(dpagemap->drm, "Already migrated!\n"); + return 0; + } + + if (IS_ENABLED(CONFIG_DRM_XE_DEBUG_VM)) + drm_dbg(&xe->drm, "Request migration to device memory on \"%s\".\n", + dpagemap->drm->unique); + + err = down_read_interruptible(&driver_migrate_lock); + if (err) + return err; + do { + err = drm_pagemap_populate_mm(dpagemap, xe_svm_range_start(range), + xe_svm_range_end(range), + range->base.gpusvm->mm, + ctx->timeslice_ms); + + if (err == -EBUSY && retries) { + if (!write_locked) { + int lock_err; + + up_read(&driver_migrate_lock); + lock_err = down_write_killable(&driver_migrate_lock); + if (lock_err) + return lock_err; + write_locked = true; + } + drm_gpusvm_range_evict(range->base.gpusvm, &range->base); + } + } while (err == -EBUSY && retries--); + if (write_locked) + up_write(&driver_migrate_lock); + else + up_read(&driver_migrate_lock); + + return err; } static struct drm_pagemap_addr @@ -1436,92 +1656,363 @@ xe_drm_pagemap_device_map(struct drm_pagemap *dpagemap, unsigned int order, enum dma_data_direction dir) { - struct device *pgmap_dev = dpagemap->dev; + struct device *pgmap_dev = dpagemap->drm->dev; enum drm_interconnect_protocol prot; dma_addr_t addr; if (pgmap_dev == dev) { - addr = xe_vram_region_page_to_dpa(page_to_vr(page), page); + addr = xe_page_to_dpa(page); prot = XE_INTERCONNECT_VRAM; } else { - addr = DMA_MAPPING_ERROR; - prot = 0; + addr = dma_map_resource(dev, + xe_page_to_pcie(page), + PAGE_SIZE << order, dir, + DMA_ATTR_SKIP_CPU_SYNC); + prot = XE_INTERCONNECT_P2P; } return drm_pagemap_addr_encode(addr, prot, order, dir); } +static void xe_drm_pagemap_device_unmap(struct drm_pagemap *dpagemap, + struct device *dev, + struct drm_pagemap_addr addr) +{ + if (addr.proto != XE_INTERCONNECT_P2P) + return; + + dma_unmap_resource(dev, addr.addr, PAGE_SIZE << addr.order, + addr.dir, DMA_ATTR_SKIP_CPU_SYNC); +} + +static void xe_pagemap_destroy_work(struct work_struct *work) +{ + struct xe_pagemap *xpagemap = container_of(work, typeof(*xpagemap), destroy_work); + struct dev_pagemap *pagemap = &xpagemap->pagemap; + struct drm_device *drm = xpagemap->dpagemap.drm; + int idx; + + /* + * Only unmap / release if devm_ release hasn't run yet. + * Otherwise the devm_ callbacks have already released, or + * will do shortly. + */ + if (drm_dev_enter(drm, &idx)) { + devm_memunmap_pages(drm->dev, pagemap); + devm_release_mem_region(drm->dev, pagemap->range.start, + pagemap->range.end - pagemap->range.start + 1); + drm_dev_exit(idx); + } + + drm_pagemap_release_owner(&xpagemap->peer); + kfree(xpagemap); +} + +static void xe_pagemap_destroy(struct drm_pagemap *dpagemap, bool from_atomic_or_reclaim) +{ + struct xe_pagemap *xpagemap = container_of(dpagemap, typeof(*xpagemap), dpagemap); + struct xe_device *xe = to_xe_device(dpagemap->drm); + + if (from_atomic_or_reclaim) + queue_work(xe->destroy_wq, &xpagemap->destroy_work); + else + xe_pagemap_destroy_work(&xpagemap->destroy_work); +} + static const struct drm_pagemap_ops xe_drm_pagemap_ops = { .device_map = xe_drm_pagemap_device_map, + .device_unmap = xe_drm_pagemap_device_unmap, .populate_mm = xe_drm_pagemap_populate_mm, + .destroy = xe_pagemap_destroy, }; /** - * xe_devm_add: Remap and provide memmap backing for device memory - * @tile: tile that the memory region belongs to - * @vr: vram memory region to remap + * xe_pagemap_create() - Create a struct xe_pagemap object + * @xe: The xe device. + * @vr: Back-pointer to the struct xe_vram_region. * - * This remap device memory to host physical address space and create - * struct page to back device memory + * Allocate and initialize a struct xe_pagemap. On successful + * return, drm_pagemap_put() on the embedded struct drm_pagemap + * should be used to unreference. * - * Return: 0 on success standard error code otherwise + * Return: Pointer to a struct xe_pagemap if successful. Error pointer + * on failure. */ -int xe_devm_add(struct xe_tile *tile, struct xe_vram_region *vr) +static struct xe_pagemap *xe_pagemap_create(struct xe_device *xe, struct xe_vram_region *vr) { - struct xe_device *xe = tile_to_xe(tile); - struct device *dev = &to_pci_dev(xe->drm.dev)->dev; + struct device *dev = xe->drm.dev; + struct xe_pagemap *xpagemap; + struct dev_pagemap *pagemap; + struct drm_pagemap *dpagemap; struct resource *res; void *addr; - int ret; + int err; + + xpagemap = kzalloc(sizeof(*xpagemap), GFP_KERNEL); + if (!xpagemap) + return ERR_PTR(-ENOMEM); + + pagemap = &xpagemap->pagemap; + dpagemap = &xpagemap->dpagemap; + INIT_WORK(&xpagemap->destroy_work, xe_pagemap_destroy_work); + xpagemap->vr = vr; + xpagemap->peer.private = XE_PEER_PAGEMAP; + + err = drm_pagemap_init(dpagemap, pagemap, &xe->drm, &xe_drm_pagemap_ops); + if (err) + goto out_no_dpagemap; res = devm_request_free_mem_region(dev, &iomem_resource, vr->usable_size); if (IS_ERR(res)) { - ret = PTR_ERR(res); - return ret; + err = PTR_ERR(res); + goto out_err; } - vr->pagemap.type = MEMORY_DEVICE_PRIVATE; - vr->pagemap.range.start = res->start; - vr->pagemap.range.end = res->end; - vr->pagemap.nr_range = 1; - vr->pagemap.ops = drm_pagemap_pagemap_ops_get(); - vr->pagemap.owner = xe_svm_devm_owner(xe); - addr = devm_memremap_pages(dev, &vr->pagemap); + err = drm_pagemap_acquire_owner(&xpagemap->peer, &xe_owner_list, + xe_has_interconnect); + if (err) + goto out_no_owner; + + pagemap->type = MEMORY_DEVICE_PRIVATE; + pagemap->range.start = res->start; + pagemap->range.end = res->end; + pagemap->nr_range = 1; + pagemap->owner = xpagemap->peer.owner; + pagemap->ops = drm_pagemap_pagemap_ops_get(); + addr = devm_memremap_pages(dev, pagemap); + if (IS_ERR(addr)) { + err = PTR_ERR(addr); + goto out_no_pages; + } + xpagemap->hpa_base = res->start; + return xpagemap; + +out_no_pages: + drm_pagemap_release_owner(&xpagemap->peer); +out_no_owner: + devm_release_mem_region(dev, res->start, res->end - res->start + 1); +out_err: + drm_pagemap_put(dpagemap); + return ERR_PTR(err); + +out_no_dpagemap: + kfree(xpagemap); + return ERR_PTR(err); +} - vr->dpagemap.dev = dev; - vr->dpagemap.ops = &xe_drm_pagemap_ops; +/** + * xe_pagemap_find_or_create() - Find or create a struct xe_pagemap + * @xe: The xe device. + * @cache: The struct xe_pagemap_cache. + * @vr: The VRAM region. + * + * Check if there is an already used xe_pagemap for this tile, and in that case, + * return it. + * If not, check if there is a cached xe_pagemap for this tile, and in that case, + * cancel its destruction, re-initialize it and return it. + * Finally if there is no cached or already used pagemap, create one and + * register it in the tile's pagemap cache. + * + * Note that this function is typically called from within an IOCTL, and waits are + * therefore carried out interruptible if possible. + * + * Return: A pointer to a struct xe_pagemap if successful, Error pointer on failure. + */ +static struct xe_pagemap * +xe_pagemap_find_or_create(struct xe_device *xe, struct drm_pagemap_cache *cache, + struct xe_vram_region *vr) +{ + struct drm_pagemap *dpagemap; + struct xe_pagemap *xpagemap; + int err; - if (IS_ERR(addr)) { - devm_release_mem_region(dev, res->start, resource_size(res)); - ret = PTR_ERR(addr); - drm_err(&xe->drm, "Failed to remap tile %d memory, errno %pe\n", - tile->id, ERR_PTR(ret)); - return ret; + err = drm_pagemap_cache_lock_lookup(cache); + if (err) + return ERR_PTR(err); + + dpagemap = drm_pagemap_get_from_cache(cache); + if (IS_ERR(dpagemap)) { + xpagemap = ERR_CAST(dpagemap); + } else if (!dpagemap) { + xpagemap = xe_pagemap_create(xe, vr); + if (IS_ERR(xpagemap)) + goto out_unlock; + drm_pagemap_cache_set_pagemap(cache, &xpagemap->dpagemap); + } else { + xpagemap = container_of(dpagemap, typeof(*xpagemap), dpagemap); + } + +out_unlock: + drm_pagemap_cache_unlock_lookup(cache); + return xpagemap; +} + +static int xe_svm_get_pagemaps(struct xe_vm *vm) +{ + struct xe_device *xe = vm->xe; + struct xe_pagemap *xpagemap; + struct xe_tile *tile; + int id; + + for_each_tile(tile, xe, id) { + struct xe_vram_region *vr; + + if (!((BIT(id) << 1) & xe->info.mem_region_mask)) + continue; + + vr = xe_tile_to_vr(tile); + xpagemap = xe_pagemap_find_or_create(xe, vr->dpagemap_cache, vr); + if (IS_ERR(xpagemap)) + break; + vm->svm.pagemaps[id] = xpagemap; + } + + if (IS_ERR(xpagemap)) { + xe_svm_put_pagemaps(vm); + return PTR_ERR(xpagemap); } - vr->hpa_base = res->start; - drm_dbg(&xe->drm, "Added tile %d memory [%llx-%llx] to devm, remapped to %pr\n", - tile->id, vr->io_start, vr->io_start + vr->usable_size, res); return 0; } + +/** + * xe_pagemap_shrinker_create() - Create a drm_pagemap shrinker + * @xe: The xe device + * + * Create a drm_pagemap shrinker and register with the xe device. + * + * Return: %0 on success, negative error code on failure. + */ +int xe_pagemap_shrinker_create(struct xe_device *xe) +{ + xe->usm.dpagemap_shrinker = drm_pagemap_shrinker_create_devm(&xe->drm); + return PTR_ERR_OR_ZERO(xe->usm.dpagemap_shrinker); +} + +/** + * xe_pagemap_cache_create() - Create a drm_pagemap cache + * @tile: The tile to register the cache with + * + * Create a drm_pagemap cache and register with the tile. + * + * Return: %0 on success, negative error code on failure. + */ +int xe_pagemap_cache_create(struct xe_tile *tile) +{ + struct xe_device *xe = tile_to_xe(tile); + + if (IS_DGFX(xe)) { + struct drm_pagemap_cache *cache = + drm_pagemap_cache_create_devm(xe->usm.dpagemap_shrinker); + + if (IS_ERR(cache)) + return PTR_ERR(cache); + + tile->mem.vram->dpagemap_cache = cache; + } + + return 0; +} + +static struct drm_pagemap *xe_devmem_open(struct xe_device *xe, u32 region_instance) +{ + u32 tile_id = region_instance - 1; + struct xe_pagemap *xpagemap; + struct xe_vram_region *vr; + + if (tile_id >= xe->info.tile_count) + return ERR_PTR(-ENOENT); + + if (!((BIT(tile_id) << 1) & xe->info.mem_region_mask)) + return ERR_PTR(-ENOENT); + + vr = xe_tile_to_vr(&xe->tiles[tile_id]); + + /* Returns a reference-counted embedded struct drm_pagemap */ + xpagemap = xe_pagemap_find_or_create(xe, vr->dpagemap_cache, vr); + if (IS_ERR(xpagemap)) + return ERR_CAST(xpagemap); + + return &xpagemap->dpagemap; +} + +/** + * xe_drm_pagemap_from_fd() - Return a drm_pagemap pointer from a + * (file_descriptor, region_instance) pair. + * @fd: An fd opened against an xe device. + * @region_instance: The region instance representing the device memory + * on the opened xe device. + * + * Opens a struct drm_pagemap pointer on the + * indicated device and region_instance. + * + * Return: A reference-counted struct drm_pagemap pointer on success, + * negative error pointer on failure. + */ +struct drm_pagemap *xe_drm_pagemap_from_fd(int fd, u32 region_instance) +{ + struct drm_pagemap *dpagemap; + struct file *file; + struct drm_file *fpriv; + struct drm_device *drm; + int idx; + + if (fd <= 0) + return ERR_PTR(-EINVAL); + + file = fget(fd); + if (!file) + return ERR_PTR(-ENOENT); + + if (!xe_is_xe_file(file)) { + dpagemap = ERR_PTR(-ENOENT); + goto out; + } + + fpriv = file->private_data; + drm = fpriv->minor->dev; + if (!drm_dev_enter(drm, &idx)) { + dpagemap = ERR_PTR(-ENODEV); + goto out; + } + + dpagemap = xe_devmem_open(to_xe_device(drm), region_instance); + drm_dev_exit(idx); +out: + fput(file); + return dpagemap; +} + #else -int xe_svm_alloc_vram(struct xe_tile *tile, - struct xe_svm_range *range, - const struct drm_gpusvm_ctx *ctx) + +int xe_pagemap_shrinker_create(struct xe_device *xe) { - return -EOPNOTSUPP; + return 0; } -int xe_devm_add(struct xe_tile *tile, struct xe_vram_region *vr) +int xe_pagemap_cache_create(struct xe_tile *tile) { return 0; } +int xe_svm_alloc_vram(struct xe_svm_range *range, + const struct drm_gpusvm_ctx *ctx, + struct drm_pagemap *dpagemap) +{ + return -EOPNOTSUPP; +} + struct drm_pagemap *xe_vma_resolve_pagemap(struct xe_vma *vma, struct xe_tile *tile) { return NULL; } + +struct drm_pagemap *xe_drm_pagemap_from_fd(int fd, u32 region_instance) +{ + return ERR_PTR(-ENOENT); +} + #endif /** diff --git a/drivers/gpu/drm/xe/xe_svm.h b/drivers/gpu/drm/xe/xe_svm.h index 0955d2ac8d74..b7b8eeacf196 100644 --- a/drivers/gpu/drm/xe/xe_svm.h +++ b/drivers/gpu/drm/xe/xe_svm.h @@ -6,29 +6,22 @@ #ifndef _XE_SVM_H_ #define _XE_SVM_H_ -struct xe_device; - -/** - * xe_svm_devm_owner() - Return the owner of device private memory - * @xe: The xe device. - * - * Return: The owner of this device's device private memory to use in - * hmm_range_fault()- - */ -static inline void *xe_svm_devm_owner(struct xe_device *xe) -{ - return xe; -} - #if IS_ENABLED(CONFIG_DRM_XE_GPUSVM) #include <drm/drm_pagemap.h> #include <drm/drm_gpusvm.h> +#include <drm/drm_pagemap_util.h> #define XE_INTERCONNECT_VRAM DRM_INTERCONNECT_DRIVER +#define XE_INTERCONNECT_P2P (XE_INTERCONNECT_VRAM + 1) + +struct drm_device; +struct drm_file; struct xe_bo; struct xe_gt; +struct xe_device; +struct xe_vram_region; struct xe_tile; struct xe_vm; struct xe_vma; @@ -56,6 +49,24 @@ struct xe_svm_range { }; /** + * struct xe_pagemap - Manages xe device_private memory for SVM. + * @pagemap: The struct dev_pagemap providing the struct pages. + * @dpagemap: The drm_pagemap managing allocation and migration. + * @destroy_work: Handles asnynchronous destruction and caching. + * @peer: Used for pagemap owner computation. + * @hpa_base: The host physical address base for the managemd memory. + * @vr: Backpointer to the xe_vram region. + */ +struct xe_pagemap { + struct dev_pagemap pagemap; + struct drm_pagemap dpagemap; + struct work_struct destroy_work; + struct drm_pagemap_peer peer; + resource_size_t hpa_base; + struct xe_vram_region *vr; +}; + +/** * xe_svm_range_pages_valid() - SVM range pages valid * @range: SVM range * @@ -84,8 +95,8 @@ int xe_svm_bo_evict(struct xe_bo *bo); void xe_svm_range_debug(struct xe_svm_range *range, const char *operation); -int xe_svm_alloc_vram(struct xe_tile *tile, struct xe_svm_range *range, - const struct drm_gpusvm_ctx *ctx); +int xe_svm_alloc_vram(struct xe_svm_range *range, const struct drm_gpusvm_ctx *ctx, + struct drm_pagemap *dpagemap); struct xe_svm_range *xe_svm_range_find_or_insert(struct xe_vm *vm, u64 addr, struct xe_vma *vma, struct drm_gpusvm_ctx *ctx); @@ -94,13 +105,13 @@ int xe_svm_range_get_pages(struct xe_vm *vm, struct xe_svm_range *range, struct drm_gpusvm_ctx *ctx); bool xe_svm_range_needs_migrate_to_vram(struct xe_svm_range *range, struct xe_vma *vma, - bool preferred_region_is_vram); + const struct drm_pagemap *dpagemap); void xe_svm_range_migrate_to_smem(struct xe_vm *vm, struct xe_svm_range *range); bool xe_svm_range_validate(struct xe_vm *vm, struct xe_svm_range *range, - u8 tile_mask, bool devmem_preferred); + u8 tile_mask, const struct drm_pagemap *dpagemap); u64 xe_svm_find_vma_start(struct xe_vm *vm, u64 addr, u64 end, struct xe_vma *vma); @@ -110,6 +121,8 @@ u8 xe_svm_ranges_zap_ptes_in_range(struct xe_vm *vm, u64 start, u64 end); struct drm_pagemap *xe_vma_resolve_pagemap(struct xe_vma *vma, struct xe_tile *tile); +void *xe_svm_private_page_owner(struct xe_vm *vm, bool force_smem); + /** * xe_svm_range_has_dma_mapping() - SVM range has DMA mapping * @range: SVM range @@ -171,6 +184,12 @@ static inline unsigned long xe_svm_range_size(struct xe_svm_range *range) void xe_svm_flush(struct xe_vm *vm); +int xe_pagemap_shrinker_create(struct xe_device *xe); + +int xe_pagemap_cache_create(struct xe_tile *tile); + +struct drm_pagemap *xe_drm_pagemap_from_fd(int fd, u32 region_instance); + #else #include <linux/interval_tree.h> #include "xe_vm.h" @@ -179,13 +198,14 @@ struct drm_pagemap_addr; struct drm_gpusvm_ctx; struct drm_gpusvm_range; struct xe_bo; -struct xe_gt; +struct xe_device; struct xe_vm; struct xe_vma; struct xe_tile; struct xe_vram_region; #define XE_INTERCONNECT_VRAM 1 +#define XE_INTERCONNECT_P2P (XE_INTERCONNECT_VRAM + 1) struct xe_svm_range { struct { @@ -214,7 +234,7 @@ int xe_svm_init(struct xe_vm *vm) { #if IS_ENABLED(CONFIG_DRM_GPUSVM) return drm_gpusvm_init(&vm->svm.gpusvm, "Xe SVM (simple)", &vm->xe->drm, - NULL, NULL, 0, 0, 0, NULL, NULL, 0); + NULL, 0, 0, 0, NULL, NULL, 0); #else return 0; #endif @@ -260,8 +280,8 @@ void xe_svm_range_debug(struct xe_svm_range *range, const char *operation) } static inline int -xe_svm_alloc_vram(struct xe_tile *tile, struct xe_svm_range *range, - const struct drm_gpusvm_ctx *ctx) +xe_svm_alloc_vram(struct xe_svm_range *range, const struct drm_gpusvm_ctx *ctx, + struct drm_pagemap *dpagemap) { return -EOPNOTSUPP; } @@ -302,7 +322,7 @@ static inline unsigned long xe_svm_range_size(struct xe_svm_range *range) static inline bool xe_svm_range_needs_migrate_to_vram(struct xe_svm_range *range, struct xe_vma *vma, - u32 region) + const struct drm_pagemap *dpagemap) { return false; } @@ -343,9 +363,30 @@ struct drm_pagemap *xe_vma_resolve_pagemap(struct xe_vma *vma, struct xe_tile *t return NULL; } +static inline void *xe_svm_private_page_owner(struct xe_vm *vm, bool force_smem) +{ + return NULL; +} + static inline void xe_svm_flush(struct xe_vm *vm) { } + +static inline int xe_pagemap_shrinker_create(struct xe_device *xe) +{ + return 0; +} + +static inline int xe_pagemap_cache_create(struct xe_tile *tile) +{ + return 0; +} + +static inline struct drm_pagemap *xe_drm_pagemap_from_fd(int fd, u32 region_instance) +{ + return ERR_PTR(-ENOENT); +} + #define xe_svm_range_has_dma_mapping(...) false #endif /* CONFIG_DRM_XE_GPUSVM */ diff --git a/drivers/gpu/drm/xe/xe_sync.c b/drivers/gpu/drm/xe/xe_sync.c index ff74528ca0c6..c8fdcdbd6ae7 100644 --- a/drivers/gpu/drm/xe/xe_sync.c +++ b/drivers/gpu/drm/xe/xe_sync.c @@ -228,6 +228,32 @@ int xe_sync_entry_add_deps(struct xe_sync_entry *sync, struct xe_sched_job *job) return 0; } +/** + * xe_sync_entry_wait() - Wait on in-sync + * @sync: Sync object + * + * If the sync is in an in-sync, wait on the sync to signal. + * + * Return: 0 on success, -ERESTARTSYS on failure (interruption) + */ +int xe_sync_entry_wait(struct xe_sync_entry *sync) +{ + return xe_sync_needs_wait(sync) ? + dma_fence_wait(sync->fence, true) : 0; +} + +/** + * xe_sync_needs_wait() - Sync needs a wait (input dma-fence not signaled) + * @sync: Sync object + * + * Return: True if sync needs a wait, False otherwise + */ +bool xe_sync_needs_wait(struct xe_sync_entry *sync) +{ + return sync->fence && + !test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &sync->fence->flags); +} + void xe_sync_entry_signal(struct xe_sync_entry *sync, struct dma_fence *fence) { if (!(sync->flags & DRM_XE_SYNC_FLAG_SIGNAL)) @@ -311,8 +337,11 @@ xe_sync_in_fence_get(struct xe_sync_entry *sync, int num_sync, struct xe_tile *tile; u8 id; - for_each_tile(tile, vm->xe, id) - num_fence += (1 + XE_MAX_GT_PER_TILE); + for_each_tile(tile, vm->xe, id) { + num_fence++; + for_each_tlb_inval(i) + num_fence++; + } fences = kmalloc_array(num_fence, sizeof(*fences), GFP_KERNEL); diff --git a/drivers/gpu/drm/xe/xe_sync.h b/drivers/gpu/drm/xe/xe_sync.h index 51f2d803e977..6b949194acff 100644 --- a/drivers/gpu/drm/xe/xe_sync.h +++ b/drivers/gpu/drm/xe/xe_sync.h @@ -29,6 +29,8 @@ int xe_sync_entry_add_deps(struct xe_sync_entry *sync, struct xe_sched_job *job); void xe_sync_entry_signal(struct xe_sync_entry *sync, struct dma_fence *fence); +int xe_sync_entry_wait(struct xe_sync_entry *sync); +bool xe_sync_needs_wait(struct xe_sync_entry *sync); void xe_sync_entry_cleanup(struct xe_sync_entry *sync); struct dma_fence * xe_sync_in_fence_get(struct xe_sync_entry *sync, int num_sync, diff --git a/drivers/gpu/drm/xe/xe_tile.c b/drivers/gpu/drm/xe/xe_tile.c index 4f4f9a5c43af..c465aae7883c 100644 --- a/drivers/gpu/drm/xe/xe_tile.c +++ b/drivers/gpu/drm/xe/xe_tile.c @@ -6,11 +6,11 @@ #include <linux/fault-inject.h> #include <drm/drm_managed.h> +#include <drm/drm_pagemap_util.h> #include "xe_bo.h" -#include "xe_device.h" +#include "xe_device_types.h" #include "xe_ggtt.h" -#include "xe_gt.h" #include "xe_memirq.h" #include "xe_migrate.h" #include "xe_pcode.h" @@ -180,17 +180,19 @@ ALLOW_ERROR_INJECTION(xe_tile_init_early, ERRNO); /* See xe_pci_probe() */ int xe_tile_init_noalloc(struct xe_tile *tile) { struct xe_device *xe = tile_to_xe(tile); + int err; xe_wa_apply_tile_workarounds(tile); - if (xe->info.has_usm && IS_DGFX(xe)) - xe_devm_add(tile, tile->mem.vram); + err = xe_pagemap_cache_create(tile); + if (err) + return err; if (IS_DGFX(xe) && !ttm_resource_manager_used(&tile->mem.vram->ttm.manager)) { - int err = xe_ttm_vram_mgr_init(xe, tile->mem.vram); - + err = xe_ttm_vram_mgr_init(xe, tile->mem.vram); if (err) return err; + xe->info.mem_region_mask |= BIT(tile->mem.vram->id) << 1; } @@ -209,9 +211,37 @@ int xe_tile_init(struct xe_tile *tile) if (IS_ERR(tile->mem.kernel_bb_pool)) return PTR_ERR(tile->mem.kernel_bb_pool); + /* Optimistically anticipate at most 256 TLB fences with PRL */ + tile->mem.reclaim_pool = xe_sa_bo_manager_init(tile, SZ_1M, XE_PAGE_RECLAIM_LIST_MAX_SIZE); + if (IS_ERR(tile->mem.reclaim_pool)) + return PTR_ERR(tile->mem.reclaim_pool); + return 0; } void xe_tile_migrate_wait(struct xe_tile *tile) { xe_migrate_wait(tile->migrate); } + +#if IS_ENABLED(CONFIG_DRM_XE_PAGEMAP) +/** + * xe_tile_local_pagemap() - Return a pointer to the tile's local drm_pagemap if any + * @tile: The tile. + * + * Return: A pointer to the tile's local drm_pagemap, or NULL if local pagemap + * support has been compiled out. + */ +struct drm_pagemap *xe_tile_local_pagemap(struct xe_tile *tile) +{ + struct drm_pagemap *dpagemap = + drm_pagemap_get_from_cache_if_active(xe_tile_to_vr(tile)->dpagemap_cache); + + if (dpagemap) { + xe_assert(tile_to_xe(tile), kref_read(&dpagemap->ref) >= 2); + drm_pagemap_put(dpagemap); + } + + return dpagemap; +} +#endif + diff --git a/drivers/gpu/drm/xe/xe_tile.h b/drivers/gpu/drm/xe/xe_tile.h index dceb6297aa01..734132eddda5 100644 --- a/drivers/gpu/drm/xe/xe_tile.h +++ b/drivers/gpu/drm/xe/xe_tile.h @@ -8,6 +8,7 @@ #include "xe_device_types.h" +struct xe_pagemap; struct xe_tile; int xe_tile_init_early(struct xe_tile *tile, struct xe_device *xe, u8 id); @@ -23,4 +24,24 @@ static inline bool xe_tile_is_root(struct xe_tile *tile) return tile->id == 0; } +/** + * xe_tile_to_vr() - Return the struct xe_vram_region pointer from a + * struct xe_tile pointer + * @tile: Pointer to the struct xe_tile. + * + * Return: Pointer to the struct xe_vram_region embedded in *@tile. + */ +static inline struct xe_vram_region *xe_tile_to_vr(struct xe_tile *tile) +{ + return tile->mem.vram; +} + +#if IS_ENABLED(CONFIG_DRM_XE_PAGEMAP) +struct drm_pagemap *xe_tile_local_pagemap(struct xe_tile *tile); +#else +static inline struct drm_pagemap *xe_tile_local_pagemap(struct xe_tile *tile) +{ + return NULL; +} +#endif #endif diff --git a/drivers/gpu/drm/xe/xe_tile_debugfs.c b/drivers/gpu/drm/xe/xe_tile_debugfs.c index fff242a5ae56..5df2f461b7b7 100644 --- a/drivers/gpu/drm/xe/xe_tile_debugfs.c +++ b/drivers/gpu/drm/xe/xe_tile_debugfs.c @@ -82,13 +82,9 @@ int xe_tile_debugfs_show_with_rpm(struct seq_file *m, void *data) struct drm_info_node *node = m->private; struct xe_tile *tile = node_to_tile(node); struct xe_device *xe = tile_to_xe(tile); - int ret; - xe_pm_runtime_get(xe); - ret = xe_tile_debugfs_simple_show(m, data); - xe_pm_runtime_put(xe); - - return ret; + guard(xe_pm_runtime)(xe); + return xe_tile_debugfs_simple_show(m, data); } static int ggtt(struct xe_tile *tile, struct drm_printer *p) @@ -110,6 +106,13 @@ static const struct drm_info_list vf_safe_debugfs_list[] = { { "sa_info", .show = xe_tile_debugfs_show_with_rpm, .data = sa_info }, }; +static void tile_debugfs_create_vram_mm(struct xe_tile *tile) +{ + if (tile->mem.vram) + ttm_resource_manager_create_debugfs(&tile->mem.vram->ttm.manager, tile->debugfs, + "vram_mm"); +} + /** * xe_tile_debugfs_register - Register tile's debugfs attributes * @tile: the &xe_tile to register @@ -139,4 +142,6 @@ void xe_tile_debugfs_register(struct xe_tile *tile) drm_debugfs_create_files(vf_safe_debugfs_list, ARRAY_SIZE(vf_safe_debugfs_list), tile->debugfs, minor); + + tile_debugfs_create_vram_mm(tile); } diff --git a/drivers/gpu/drm/xe/xe_tile_sriov_pf_debugfs.c b/drivers/gpu/drm/xe/xe_tile_sriov_pf_debugfs.c index f3f478f14ff5..7f97db2f89bb 100644 --- a/drivers/gpu/drm/xe/xe_tile_sriov_pf_debugfs.c +++ b/drivers/gpu/drm/xe/xe_tile_sriov_pf_debugfs.c @@ -141,12 +141,11 @@ static int NAME##_set(void *data, u64 val) \ if (val > (TYPE)~0ull) \ return -EOVERFLOW; \ \ - xe_pm_runtime_get(xe); \ + guard(xe_pm_runtime)(xe); \ err = xe_sriov_pf_wait_ready(xe) ?: \ xe_gt_sriov_pf_config_set_##CONFIG(gt, vfid, val); \ if (!err) \ xe_sriov_pf_provision_set_custom_mode(xe); \ - xe_pm_runtime_put(xe); \ \ return err; \ } \ diff --git a/drivers/gpu/drm/xe/xe_tlb_inval.c b/drivers/gpu/drm/xe/xe_tlb_inval.c index 918a59e686ea..e837888367c4 100644 --- a/drivers/gpu/drm/xe/xe_tlb_inval.c +++ b/drivers/gpu/drm/xe/xe_tlb_inval.c @@ -5,13 +5,10 @@ #include <drm/drm_managed.h> -#include "abi/guc_actions_abi.h" -#include "xe_device.h" +#include "xe_device_types.h" #include "xe_force_wake.h" -#include "xe_gt.h" -#include "xe_gt_printk.h" #include "xe_gt_stats.h" -#include "xe_guc.h" +#include "xe_gt_types.h" #include "xe_guc_ct.h" #include "xe_guc_tlb_inval.h" #include "xe_mmio.h" @@ -94,7 +91,7 @@ static void xe_tlb_inval_fence_timeout(struct work_struct *work) xe_tlb_inval_fence_signal(fence); } if (!list_empty(&tlb_inval->pending_fences)) - queue_delayed_work(system_wq, &tlb_inval->fence_tdr, + queue_delayed_work(tlb_inval->timeout_wq, &tlb_inval->fence_tdr, timeout_delay); spin_unlock_irq(&tlb_inval->pending_lock); } @@ -146,6 +143,10 @@ int xe_gt_tlb_inval_init_early(struct xe_gt *gt) if (IS_ERR(tlb_inval->job_wq)) return PTR_ERR(tlb_inval->job_wq); + tlb_inval->timeout_wq = gt->ordered_wq; + if (IS_ERR(tlb_inval->timeout_wq)) + return PTR_ERR(tlb_inval->timeout_wq); + /* XXX: Blindly setting up backend to GuC */ xe_guc_tlb_inval_init_early(>->uc.guc, tlb_inval); @@ -199,6 +200,20 @@ void xe_tlb_inval_reset(struct xe_tlb_inval *tlb_inval) mutex_unlock(&tlb_inval->seqno_lock); } +/** + * xe_tlb_inval_reset_timeout() - Reset TLB inval fence timeout + * @tlb_inval: TLB invalidation client + * + * Reset the TLB invalidation timeout timer. + */ +static void xe_tlb_inval_reset_timeout(struct xe_tlb_inval *tlb_inval) +{ + lockdep_assert_held(&tlb_inval->pending_lock); + + mod_delayed_work(system_wq, &tlb_inval->fence_tdr, + tlb_inval->ops->timeout_delay(tlb_inval)); +} + static bool xe_tlb_inval_seqno_past(struct xe_tlb_inval *tlb_inval, int seqno) { int seqno_recv = READ_ONCE(tlb_inval->seqno_recv); @@ -226,7 +241,7 @@ static void xe_tlb_inval_fence_prep(struct xe_tlb_inval_fence *fence) list_add_tail(&fence->link, &tlb_inval->pending_fences); if (list_is_singular(&tlb_inval->pending_fences)) - queue_delayed_work(system_wq, &tlb_inval->fence_tdr, + queue_delayed_work(tlb_inval->timeout_wq, &tlb_inval->fence_tdr, tlb_inval->ops->timeout_delay(tlb_inval)); spin_unlock_irq(&tlb_inval->pending_lock); @@ -299,6 +314,7 @@ int xe_tlb_inval_ggtt(struct xe_tlb_inval *tlb_inval) * @start: start address * @end: end address * @asid: address space id + * @prl_sa: suballocation of page reclaim list if used, NULL indicates PPC flush * * Issue a range based TLB invalidation if supported, if not fallback to a full * TLB invalidation. Completion of TLB is asynchronous and caller can use @@ -308,10 +324,10 @@ int xe_tlb_inval_ggtt(struct xe_tlb_inval *tlb_inval) */ int xe_tlb_inval_range(struct xe_tlb_inval *tlb_inval, struct xe_tlb_inval_fence *fence, u64 start, u64 end, - u32 asid) + u32 asid, struct drm_suballoc *prl_sa) { return xe_tlb_inval_issue(tlb_inval, fence, tlb_inval->ops->ppgtt, - start, end, asid); + start, end, asid, prl_sa); } /** @@ -327,7 +343,7 @@ void xe_tlb_inval_vm(struct xe_tlb_inval *tlb_inval, struct xe_vm *vm) u64 range = 1ull << vm->xe->info.va_bits; xe_tlb_inval_fence_init(tlb_inval, &fence, true); - xe_tlb_inval_range(tlb_inval, &fence, 0, range, vm->usm.asid); + xe_tlb_inval_range(tlb_inval, &fence, 0, range, vm->usm.asid, NULL); xe_tlb_inval_fence_wait(&fence); } @@ -360,6 +376,12 @@ void xe_tlb_inval_done_handler(struct xe_tlb_inval *tlb_inval, int seqno) * process_g2h_msg(). */ spin_lock_irqsave(&tlb_inval->pending_lock, flags); + if (seqno == TLB_INVALIDATION_SEQNO_INVALID) { + xe_tlb_inval_reset_timeout(tlb_inval); + spin_unlock_irqrestore(&tlb_inval->pending_lock, flags); + return; + } + if (xe_tlb_inval_seqno_past(tlb_inval, seqno)) { spin_unlock_irqrestore(&tlb_inval->pending_lock, flags); return; @@ -378,7 +400,7 @@ void xe_tlb_inval_done_handler(struct xe_tlb_inval *tlb_inval, int seqno) } if (!list_empty(&tlb_inval->pending_fences)) - mod_delayed_work(system_wq, + mod_delayed_work(tlb_inval->timeout_wq, &tlb_inval->fence_tdr, tlb_inval->ops->timeout_delay(tlb_inval)); else diff --git a/drivers/gpu/drm/xe/xe_tlb_inval.h b/drivers/gpu/drm/xe/xe_tlb_inval.h index 05614915463a..858d0690f995 100644 --- a/drivers/gpu/drm/xe/xe_tlb_inval.h +++ b/drivers/gpu/drm/xe/xe_tlb_inval.h @@ -23,7 +23,7 @@ int xe_tlb_inval_ggtt(struct xe_tlb_inval *tlb_inval); void xe_tlb_inval_vm(struct xe_tlb_inval *tlb_inval, struct xe_vm *vm); int xe_tlb_inval_range(struct xe_tlb_inval *tlb_inval, struct xe_tlb_inval_fence *fence, - u64 start, u64 end, u32 asid); + u64 start, u64 end, u32 asid, struct drm_suballoc *prl_sa); void xe_tlb_inval_fence_init(struct xe_tlb_inval *tlb_inval, struct xe_tlb_inval_fence *fence, diff --git a/drivers/gpu/drm/xe/xe_tlb_inval_job.c b/drivers/gpu/drm/xe/xe_tlb_inval_job.c index 1ae0dec2cf31..6a7bd6315797 100644 --- a/drivers/gpu/drm/xe/xe_tlb_inval_job.c +++ b/drivers/gpu/drm/xe/xe_tlb_inval_job.c @@ -7,7 +7,9 @@ #include "xe_dep_job_types.h" #include "xe_dep_scheduler.h" #include "xe_exec_queue.h" +#include "xe_gt_printk.h" #include "xe_gt_types.h" +#include "xe_page_reclaim.h" #include "xe_tlb_inval.h" #include "xe_tlb_inval_job.h" #include "xe_migrate.h" @@ -24,6 +26,8 @@ struct xe_tlb_inval_job { struct xe_exec_queue *q; /** @vm: VM which TLB invalidation is being issued for */ struct xe_vm *vm; + /** @prl: Embedded copy of page reclaim list */ + struct xe_page_reclaim_list prl; /** @refcount: ref count of this job */ struct kref refcount; /** @@ -47,9 +51,16 @@ static struct dma_fence *xe_tlb_inval_job_run(struct xe_dep_job *dep_job) container_of(dep_job, typeof(*job), dep); struct xe_tlb_inval_fence *ifence = container_of(job->fence, typeof(*ifence), base); + struct drm_suballoc *prl_sa = NULL; + + if (xe_page_reclaim_list_valid(&job->prl)) { + prl_sa = xe_page_reclaim_create_prl_bo(job->tlb_inval, &job->prl, ifence); + if (IS_ERR(prl_sa)) + prl_sa = NULL; /* Indicate fall back PPC flush with NULL */ + } xe_tlb_inval_range(job->tlb_inval, ifence, job->start, - job->end, job->vm->usm.asid); + job->end, job->vm->usm.asid, prl_sa); return job->fence; } @@ -107,6 +118,7 @@ xe_tlb_inval_job_create(struct xe_exec_queue *q, struct xe_tlb_inval *tlb_inval, job->start = start; job->end = end; job->fence_armed = false; + xe_page_reclaim_list_init(&job->prl); job->dep.ops = &dep_job_ops; job->type = type; kref_init(&job->refcount); @@ -140,6 +152,25 @@ err_job: return ERR_PTR(err); } +/** + * xe_tlb_inval_job_add_page_reclaim() - Embed PRL into a TLB job + * @job: TLB invalidation job that may trigger reclamation + * @prl: Page reclaim list populated during unbind + * + * Copies @prl into the job and takes an extra reference to the entry page so + * ownership can transfer to the TLB fence when the job is pushed. + */ +void xe_tlb_inval_job_add_page_reclaim(struct xe_tlb_inval_job *job, + struct xe_page_reclaim_list *prl) +{ + struct xe_device *xe = gt_to_xe(job->q->gt); + + xe_gt_WARN_ON(job->q->gt, !xe->info.has_page_reclaim_hw_assist); + job->prl = *prl; + /* Pair with put in job_destroy */ + xe_page_reclaim_entries_get(job->prl.entries); +} + static void xe_tlb_inval_job_destroy(struct kref *ref) { struct xe_tlb_inval_job *job = container_of(ref, typeof(*job), @@ -150,6 +181,9 @@ static void xe_tlb_inval_job_destroy(struct kref *ref) struct xe_device *xe = gt_to_xe(q->gt); struct xe_vm *vm = job->vm; + /* BO creation retains a copy (if used), so no longer needed */ + xe_page_reclaim_entries_put(job->prl.entries); + if (!job->fence_armed) kfree(ifence); else diff --git a/drivers/gpu/drm/xe/xe_tlb_inval_job.h b/drivers/gpu/drm/xe/xe_tlb_inval_job.h index 4d6df1a6c6ca..03d6e21cd611 100644 --- a/drivers/gpu/drm/xe/xe_tlb_inval_job.h +++ b/drivers/gpu/drm/xe/xe_tlb_inval_job.h @@ -12,6 +12,7 @@ struct dma_fence; struct xe_dep_scheduler; struct xe_exec_queue; struct xe_migrate; +struct xe_page_reclaim_list; struct xe_tlb_inval; struct xe_tlb_inval_job; struct xe_vm; @@ -21,6 +22,9 @@ xe_tlb_inval_job_create(struct xe_exec_queue *q, struct xe_tlb_inval *tlb_inval, struct xe_dep_scheduler *dep_scheduler, struct xe_vm *vm, u64 start, u64 end, int type); +void xe_tlb_inval_job_add_page_reclaim(struct xe_tlb_inval_job *job, + struct xe_page_reclaim_list *prl); + int xe_tlb_inval_job_alloc_dep(struct xe_tlb_inval_job *job); struct dma_fence *xe_tlb_inval_job_push(struct xe_tlb_inval_job *job, diff --git a/drivers/gpu/drm/xe/xe_tlb_inval_types.h b/drivers/gpu/drm/xe/xe_tlb_inval_types.h index 8f8b060e9005..3b089f90f002 100644 --- a/drivers/gpu/drm/xe/xe_tlb_inval_types.h +++ b/drivers/gpu/drm/xe/xe_tlb_inval_types.h @@ -9,6 +9,7 @@ #include <linux/workqueue.h> #include <linux/dma-fence.h> +struct drm_suballoc; struct xe_tlb_inval; /** struct xe_tlb_inval_ops - TLB invalidation ops (backend) */ @@ -40,12 +41,13 @@ struct xe_tlb_inval_ops { * @start: Start address * @end: End address * @asid: Address space ID + * @prl_sa: Suballocation for page reclaim list * * Return 0 on success, -ECANCELED if backend is mid-reset, error on * failure */ int (*ppgtt)(struct xe_tlb_inval *tlb_inval, u32 seqno, u64 start, - u64 end, u32 asid); + u64 end, u32 asid, struct drm_suballoc *prl_sa); /** * @initialized: Backend is initialized @@ -80,6 +82,7 @@ struct xe_tlb_inval { const struct xe_tlb_inval_ops *ops; /** @tlb_inval.seqno: TLB invalidation seqno, protected by CT lock */ #define TLB_INVALIDATION_SEQNO_MAX 0x100000 +#define TLB_INVALIDATION_SEQNO_INVALID TLB_INVALIDATION_SEQNO_MAX int seqno; /** @tlb_invalidation.seqno_lock: protects @tlb_invalidation.seqno */ struct mutex seqno_lock; @@ -106,6 +109,8 @@ struct xe_tlb_inval { struct workqueue_struct *job_wq; /** @tlb_inval.lock: protects TLB invalidation fences */ spinlock_t lock; + /** @timeout_wq: schedules TLB invalidation fence timeouts */ + struct workqueue_struct *timeout_wq; }; /** diff --git a/drivers/gpu/drm/xe/xe_trace.h b/drivers/gpu/drm/xe/xe_trace.h index 79a97b086cb2..750fa32c13b2 100644 --- a/drivers/gpu/drm/xe/xe_trace.h +++ b/drivers/gpu/drm/xe/xe_trace.h @@ -13,6 +13,7 @@ #include <linux/types.h> #include "xe_exec_queue_types.h" +#include "xe_exec_queue.h" #include "xe_gpu_scheduler_types.h" #include "xe_gt_types.h" #include "xe_guc_exec_queue_types.h" @@ -97,11 +98,51 @@ DECLARE_EVENT_CLASS(xe_exec_queue, __entry->guc_state, __entry->flags) ); +DECLARE_EVENT_CLASS(xe_exec_queue_multi_queue, + TP_PROTO(struct xe_exec_queue *q), + TP_ARGS(q), + + TP_STRUCT__entry( + __string(dev, __dev_name_eq(q)) + __field(enum xe_engine_class, class) + __field(u32, logical_mask) + __field(u8, gt_id) + __field(u16, width) + __field(u32, guc_id) + __field(u32, guc_state) + __field(u32, flags) + __field(u32, primary) + ), + + TP_fast_assign( + __assign_str(dev); + __entry->class = q->class; + __entry->logical_mask = q->logical_mask; + __entry->gt_id = q->gt->info.id; + __entry->width = q->width; + __entry->guc_id = q->guc->id; + __entry->guc_state = atomic_read(&q->guc->state); + __entry->flags = q->flags; + __entry->primary = xe_exec_queue_multi_queue_primary(q)->guc->id; + ), + + TP_printk("dev=%s, %d:0x%x, gt=%d, width=%d guc_id=%d, guc_state=0x%x, flags=0x%x, primary=%d", + __get_str(dev), __entry->class, __entry->logical_mask, + __entry->gt_id, __entry->width, __entry->guc_id, + __entry->guc_state, __entry->flags, + __entry->primary) +); + DEFINE_EVENT(xe_exec_queue, xe_exec_queue_create, TP_PROTO(struct xe_exec_queue *q), TP_ARGS(q) ); +DEFINE_EVENT(xe_exec_queue_multi_queue, xe_exec_queue_create_multi_queue, + TP_PROTO(struct xe_exec_queue *q), + TP_ARGS(q) +); + DEFINE_EVENT(xe_exec_queue, xe_exec_queue_supress_resume, TP_PROTO(struct xe_exec_queue *q), TP_ARGS(q) @@ -172,17 +213,17 @@ DEFINE_EVENT(xe_exec_queue, xe_exec_queue_memory_cat_error, TP_ARGS(q) ); -DEFINE_EVENT(xe_exec_queue, xe_exec_queue_stop, +DEFINE_EVENT(xe_exec_queue, xe_exec_queue_cgp_context_error, TP_PROTO(struct xe_exec_queue *q), TP_ARGS(q) ); -DEFINE_EVENT(xe_exec_queue, xe_exec_queue_resubmit, +DEFINE_EVENT(xe_exec_queue, xe_exec_queue_stop, TP_PROTO(struct xe_exec_queue *q), TP_ARGS(q) ); -DEFINE_EVENT(xe_exec_queue, xe_exec_queue_lr_cleanup, +DEFINE_EVENT(xe_exec_queue, xe_exec_queue_resubmit, TP_PROTO(struct xe_exec_queue *q), TP_ARGS(q) ); diff --git a/drivers/gpu/drm/xe/xe_ttm_stolen_mgr.c b/drivers/gpu/drm/xe/xe_ttm_stolen_mgr.c index 1bddecfb723a..27c9d72222cf 100644 --- a/drivers/gpu/drm/xe/xe_ttm_stolen_mgr.c +++ b/drivers/gpu/drm/xe/xe_ttm_stolen_mgr.c @@ -17,7 +17,6 @@ #include "regs/xe_regs.h" #include "xe_bo.h" #include "xe_device.h" -#include "xe_gt.h" #include "xe_gt_printk.h" #include "xe_mmio.h" #include "xe_res_cursor.h" diff --git a/drivers/gpu/drm/xe/xe_ttm_sys_mgr.c b/drivers/gpu/drm/xe/xe_ttm_sys_mgr.c index 3e404eb8d098..99fb7e99eb7f 100644 --- a/drivers/gpu/drm/xe/xe_ttm_sys_mgr.c +++ b/drivers/gpu/drm/xe/xe_ttm_sys_mgr.c @@ -13,7 +13,6 @@ #include <drm/ttm/ttm_tt.h> #include "xe_bo.h" -#include "xe_gt.h" struct xe_ttm_sys_node { struct ttm_buffer_object *tbo; diff --git a/drivers/gpu/drm/xe/xe_ttm_vram_mgr.c b/drivers/gpu/drm/xe/xe_ttm_vram_mgr.c index 9f70802fce92..6553a19f7cf2 100644 --- a/drivers/gpu/drm/xe/xe_ttm_vram_mgr.c +++ b/drivers/gpu/drm/xe/xe_ttm_vram_mgr.c @@ -12,7 +12,6 @@ #include "xe_bo.h" #include "xe_device.h" -#include "xe_gt.h" #include "xe_res_cursor.h" #include "xe_ttm_vram_mgr.h" #include "xe_vram_types.h" diff --git a/drivers/gpu/drm/xe/xe_uc.c b/drivers/gpu/drm/xe/xe_uc.c index 465bda355443..3f63c2a7e86d 100644 --- a/drivers/gpu/drm/xe/xe_uc.c +++ b/drivers/gpu/drm/xe/xe_uc.c @@ -8,7 +8,6 @@ #include "xe_assert.h" #include "xe_device.h" #include "xe_gsc.h" -#include "xe_gsc_proxy.h" #include "xe_gt.h" #include "xe_gt_printk.h" #include "xe_gt_sriov_vf.h" @@ -17,7 +16,6 @@ #include "xe_guc_engine_activity.h" #include "xe_huc.h" #include "xe_sriov.h" -#include "xe_uc_fw.h" #include "xe_wopcm.h" static struct xe_gt * @@ -218,9 +216,12 @@ int xe_uc_load_hw(struct xe_uc *uc) xe_guc_engine_activity_enable_stats(&uc->guc); - /* We don't fail the driver load if HuC fails to auth, but let's warn */ + /* We don't fail the driver load if HuC fails to auth */ ret = xe_huc_auth(&uc->huc, XE_HUC_AUTH_VIA_GUC); - xe_gt_assert(uc_to_gt(uc), !ret); + if (ret) + xe_gt_err(uc_to_gt(uc), + "HuC authentication failed (%pe), continuing with no HuC\n", + ERR_PTR(ret)); /* GSC load is async */ xe_gsc_load_start(&uc->gsc); @@ -302,6 +303,34 @@ int xe_uc_suspend(struct xe_uc *uc) } /** + * xe_uc_runtime_suspend() - UC runtime suspend + * @uc: the UC object + * + * Runtime suspend all UCs. + */ +void xe_uc_runtime_suspend(struct xe_uc *uc) +{ + if (!xe_device_uc_enabled(uc_to_xe(uc))) + return; + + xe_guc_runtime_suspend(&uc->guc); +} + +/** + * xe_uc_runtime_resume() - UC runtime resume + * @uc: the UC object + * + * Runtime resume all UCs. + */ +void xe_uc_runtime_resume(struct xe_uc *uc) +{ + if (!xe_device_uc_enabled(uc_to_xe(uc))) + return; + + xe_guc_runtime_resume(&uc->guc); +} + +/** * xe_uc_declare_wedged() - Declare UC wedged * @uc: the UC object * diff --git a/drivers/gpu/drm/xe/xe_uc.h b/drivers/gpu/drm/xe/xe_uc.h index 21c9306098cf..5398da1a8097 100644 --- a/drivers/gpu/drm/xe/xe_uc.h +++ b/drivers/gpu/drm/xe/xe_uc.h @@ -14,6 +14,8 @@ int xe_uc_init_post_hwconfig(struct xe_uc *uc); int xe_uc_load_hw(struct xe_uc *uc); void xe_uc_gucrc_disable(struct xe_uc *uc); int xe_uc_reset_prepare(struct xe_uc *uc); +void xe_uc_runtime_resume(struct xe_uc *uc); +void xe_uc_runtime_suspend(struct xe_uc *uc); void xe_uc_stop_prepare(struct xe_uc *uc); void xe_uc_stop(struct xe_uc *uc); int xe_uc_start(struct xe_uc *uc); diff --git a/drivers/gpu/drm/xe/xe_uc_debugfs.c b/drivers/gpu/drm/xe/xe_uc_debugfs.c index 24a4209051ee..45119993f5cb 100644 --- a/drivers/gpu/drm/xe/xe_uc_debugfs.c +++ b/drivers/gpu/drm/xe/xe_uc_debugfs.c @@ -7,12 +7,12 @@ #include <drm/drm_debugfs.h> -#include "xe_gt.h" #include "xe_gsc_debugfs.h" #include "xe_guc_debugfs.h" #include "xe_huc_debugfs.h" #include "xe_macros.h" #include "xe_uc_debugfs.h" +#include "xe_uc_types.h" void xe_uc_debugfs_register(struct xe_uc *uc, struct dentry *parent) { diff --git a/drivers/gpu/drm/xe/xe_uc_fw.c b/drivers/gpu/drm/xe/xe_uc_fw.c index 622b76078567..d35bc4989144 100644 --- a/drivers/gpu/drm/xe/xe_uc_fw.c +++ b/drivers/gpu/drm/xe/xe_uc_fw.c @@ -14,9 +14,9 @@ #include "xe_device_types.h" #include "xe_force_wake.h" #include "xe_gsc.h" -#include "xe_gt.h" #include "xe_gt_printk.h" #include "xe_gt_sriov_vf.h" +#include "xe_gt_types.h" #include "xe_guc.h" #include "xe_map.h" #include "xe_mmio.h" @@ -115,11 +115,12 @@ struct fw_blobs_by_type { #define XE_GT_TYPE_ANY XE_GT_TYPE_UNINITIALIZED #define XE_GUC_FIRMWARE_DEFS(fw_def, mmp_ver, major_ver) \ - fw_def(PANTHERLAKE, GT_TYPE_ANY, major_ver(xe, guc, ptl, 70, 49, 4)) \ - fw_def(BATTLEMAGE, GT_TYPE_ANY, major_ver(xe, guc, bmg, 70, 49, 4)) \ - fw_def(LUNARLAKE, GT_TYPE_ANY, major_ver(xe, guc, lnl, 70, 45, 2)) \ - fw_def(METEORLAKE, GT_TYPE_ANY, major_ver(i915, guc, mtl, 70, 44, 1)) \ - fw_def(DG2, GT_TYPE_ANY, major_ver(i915, guc, dg2, 70, 45, 2)) \ + fw_def(NOVALAKE_S, GT_TYPE_ANY, mmp_ver(xe, guc, nvl, 70, 55, 4)) \ + fw_def(PANTHERLAKE, GT_TYPE_ANY, major_ver(xe, guc, ptl, 70, 54, 0)) \ + fw_def(BATTLEMAGE, GT_TYPE_ANY, major_ver(xe, guc, bmg, 70, 54, 0)) \ + fw_def(LUNARLAKE, GT_TYPE_ANY, major_ver(xe, guc, lnl, 70, 53, 0)) \ + fw_def(METEORLAKE, GT_TYPE_ANY, major_ver(i915, guc, mtl, 70, 53, 0)) \ + fw_def(DG2, GT_TYPE_ANY, major_ver(i915, guc, dg2, 70, 53, 0)) \ fw_def(DG1, GT_TYPE_ANY, major_ver(i915, guc, dg1, 70, 44, 1)) \ fw_def(ALDERLAKE_N, GT_TYPE_ANY, major_ver(i915, guc, tgl, 70, 44, 1)) \ fw_def(ALDERLAKE_P, GT_TYPE_ANY, major_ver(i915, guc, adlp, 70, 44, 1)) \ @@ -140,6 +141,7 @@ struct fw_blobs_by_type { /* for the GSC FW we match the compatibility version and not the release one */ #define XE_GSC_FIRMWARE_DEFS(fw_def, major_ver) \ + fw_def(PANTHERLAKE, GT_TYPE_ANY, major_ver(xe, gsc, ptl, 105, 1, 0)) \ fw_def(LUNARLAKE, GT_TYPE_ANY, major_ver(xe, gsc, lnl, 104, 1, 0)) \ fw_def(METEORLAKE, GT_TYPE_ANY, major_ver(i915, gsc, mtl, 102, 1, 0)) @@ -738,7 +740,7 @@ static int uc_fw_request(struct xe_uc_fw *uc_fw, const struct firmware **firmwar return 0; } - err = request_firmware(&fw, uc_fw->path, dev); + err = firmware_request_nowarn(&fw, uc_fw->path, dev); if (err) goto fail; @@ -767,8 +769,12 @@ fail: XE_UC_FIRMWARE_MISSING : XE_UC_FIRMWARE_ERROR); - xe_gt_notice(gt, "%s firmware %s: fetch failed with error %pe\n", - xe_uc_fw_type_repr(uc_fw->type), uc_fw->path, ERR_PTR(err)); + if (err == -ENOENT) + xe_gt_info(gt, "%s firmware %s not found\n", + xe_uc_fw_type_repr(uc_fw->type), uc_fw->path); + else + xe_gt_notice(gt, "%s firmware %s: fetch failed with error %pe\n", + xe_uc_fw_type_repr(uc_fw->type), uc_fw->path, ERR_PTR(err)); xe_gt_info(gt, "%s firmware(s) can be downloaded from %s\n", xe_uc_fw_type_repr(uc_fw->type), XE_UC_FIRMWARE_URL); diff --git a/drivers/gpu/drm/xe/xe_userptr.c b/drivers/gpu/drm/xe/xe_userptr.c index 0d9130b1958a..e120323c43bc 100644 --- a/drivers/gpu/drm/xe/xe_userptr.c +++ b/drivers/gpu/drm/xe/xe_userptr.c @@ -55,7 +55,7 @@ int xe_vma_userptr_pin_pages(struct xe_userptr_vma *uvma) struct xe_device *xe = vm->xe; struct drm_gpusvm_ctx ctx = { .read_only = xe_vma_read_only(vma), - .device_private_page_owner = xe_svm_devm_owner(xe), + .device_private_page_owner = xe_svm_private_page_owner(vm, false), .allow_mixed = true, }; diff --git a/drivers/gpu/drm/xe/xe_validation.c b/drivers/gpu/drm/xe/xe_validation.c index 826cd09966ef..a611438eaafe 100644 --- a/drivers/gpu/drm/xe/xe_validation.c +++ b/drivers/gpu/drm/xe/xe_validation.c @@ -2,7 +2,6 @@ /* * Copyright © 2024 Intel Corporation */ -#include "xe_bo.h" #include <drm/drm_exec.h> #include <drm/drm_gem.h> #include <drm/drm_gpuvm.h> diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c index 0e7f82eb38fc..dd6d99d7fca8 100644 --- a/drivers/gpu/drm/xe/xe_vm.c +++ b/drivers/gpu/drm/xe/xe_vm.c @@ -33,7 +33,6 @@ #include "xe_preempt_fence.h" #include "xe_pt.h" #include "xe_pxp.h" -#include "xe_res_cursor.h" #include "xe_sriov_vf.h" #include "xe_svm.h" #include "xe_sync.h" @@ -957,14 +956,37 @@ free_ops: return fence; } +static void xe_vma_mem_attr_fini(struct xe_vma_mem_attr *attr) +{ + drm_pagemap_put(attr->preferred_loc.dpagemap); +} + static void xe_vma_free(struct xe_vma *vma) { + xe_vma_mem_attr_fini(&vma->attr); + if (xe_vma_is_userptr(vma)) kfree(to_userptr_vma(vma)); else kfree(vma); } +/** + * xe_vma_mem_attr_copy() - copy an xe_vma_mem_attr structure. + * @to: Destination. + * @from: Source. + * + * Copies an xe_vma_mem_attr structure taking care to get reference + * counting of individual members right. + */ +void xe_vma_mem_attr_copy(struct xe_vma_mem_attr *to, struct xe_vma_mem_attr *from) +{ + xe_vma_mem_attr_fini(to); + *to = *from; + if (to->preferred_loc.dpagemap) + drm_pagemap_get(to->preferred_loc.dpagemap); +} + static struct xe_vma *xe_vma_create(struct xe_vm *vm, struct xe_bo *bo, u64 bo_offset_or_userptr, @@ -1015,8 +1037,7 @@ static struct xe_vma *xe_vma_create(struct xe_vm *vm, if (vm->xe->info.has_atomic_enable_pte_bit) vma->gpuva.flags |= XE_VMA_ATOMIC_PTE_BIT; - vma->attr = *attr; - + xe_vma_mem_attr_copy(&vma->attr, attr); if (bo) { struct drm_gpuvm_bo *vm_bo; @@ -1508,7 +1529,10 @@ struct xe_vm *xe_vm_create(struct xe_device *xe, u32 flags, struct xe_file *xef) INIT_WORK(&vm->destroy_work, vm_destroy_work_func); INIT_LIST_HEAD(&vm->preempt.exec_queues); - vm->preempt.min_run_period_ms = 10; /* FIXME: Wire up to uAPI */ + if (flags & XE_VM_FLAG_FAULT_MODE) + vm->preempt.min_run_period_ms = xe->min_run_period_pf_ms; + else + vm->preempt.min_run_period_ms = xe->min_run_period_lr_ms; for_each_tile(tile, xe, id) xe_range_fence_tree_init(&vm->rftree[id]); @@ -1614,7 +1638,7 @@ struct xe_vm *xe_vm_create(struct xe_device *xe, u32 flags, struct xe_file *xef) if (!vm->pt_root[id]) continue; - q = xe_exec_queue_create_bind(xe, tile, create_flags, 0); + q = xe_exec_queue_create_bind(xe, tile, vm, create_flags, 0); if (IS_ERR(q)) { err = PTR_ERR(q); goto err_close; @@ -2184,7 +2208,7 @@ static void print_op(struct xe_device *xe, struct drm_gpuva_op *op) (ULL)xe_vma_start(vma), (ULL)xe_vma_size(vma)); break; default: - drm_warn(&xe->drm, "NOT POSSIBLE"); + drm_warn(&xe->drm, "NOT POSSIBLE\n"); } } #else @@ -2233,6 +2257,7 @@ vm_bind_ioctl_ops_create(struct xe_vm *vm, struct xe_vma_ops *vops, struct drm_gpuva_ops *ops; struct drm_gpuva_op *__op; struct drm_gpuvm_bo *vm_bo; + u64 range_start = addr; u64 range_end = addr + range; int err; @@ -2245,10 +2270,16 @@ vm_bind_ioctl_ops_create(struct xe_vm *vm, struct xe_vma_ops *vops, switch (operation) { case DRM_XE_VM_BIND_OP_MAP: + if (flags & DRM_XE_VM_BIND_FLAG_CPU_ADDR_MIRROR) { + xe_vm_find_cpu_addr_mirror_vma_range(vm, &range_start, &range_end); + vops->flags |= XE_VMA_OPS_FLAG_ALLOW_SVM_UNMAP; + } + + fallthrough; case DRM_XE_VM_BIND_OP_MAP_USERPTR: { struct drm_gpuvm_map_req map_req = { - .map.va.addr = addr, - .map.va.range = range, + .map.va.addr = range_start, + .map.va.range = range_end - range_start, .map.gem.obj = obj, .map.gem.offset = bo_offset_or_userptr, }; @@ -2280,7 +2311,7 @@ vm_bind_ioctl_ops_create(struct xe_vm *vm, struct xe_vma_ops *vops, xe_bo_unlock(bo); break; default: - drm_warn(&vm->xe->drm, "NOT POSSIBLE"); + drm_warn(&vm->xe->drm, "NOT POSSIBLE\n"); ops = ERR_PTR(-EINVAL); } if (IS_ERR(ops)) @@ -2310,7 +2341,7 @@ vm_bind_ioctl_ops_create(struct xe_vm *vm, struct xe_vma_ops *vops, struct xe_tile *tile; struct xe_svm_range *svm_range; struct drm_gpusvm_ctx ctx = {}; - struct drm_pagemap *dpagemap; + struct drm_pagemap *dpagemap = NULL; u8 id, tile_mask = 0; u32 i; @@ -2328,23 +2359,17 @@ vm_bind_ioctl_ops_create(struct xe_vm *vm, struct xe_vma_ops *vops, xa_init_flags(&op->prefetch_range.range, XA_FLAGS_ALLOC); op->prefetch_range.ranges_count = 0; - tile = NULL; if (prefetch_region == DRM_XE_CONSULT_MEM_ADVISE_PREF_LOC) { dpagemap = xe_vma_resolve_pagemap(vma, xe_device_get_root_tile(vm->xe)); - /* - * TODO: Once multigpu support is enabled will need - * something to dereference tile from dpagemap. - */ - if (dpagemap) - tile = xe_device_get_root_tile(vm->xe); } else if (prefetch_region) { tile = &vm->xe->tiles[region_to_mem_type[prefetch_region] - XE_PL_VRAM0]; + dpagemap = xe_tile_local_pagemap(tile); } - op->prefetch_range.tile = tile; + op->prefetch_range.dpagemap = dpagemap; alloc_next_range: svm_range = xe_svm_range_find_or_insert(vm, addr, vma, &ctx); @@ -2363,7 +2388,7 @@ alloc_next_range: goto unwind_prefetch_ops; } - if (xe_svm_range_validate(vm, svm_range, tile_mask, !!tile)) { + if (xe_svm_range_validate(vm, svm_range, tile_mask, dpagemap)) { xe_svm_range_debug(svm_range, "PREFETCH - RANGE IS VALID"); goto check_next_range; } @@ -2448,8 +2473,17 @@ static struct xe_vma *new_vma(struct xe_vm *vm, struct drm_gpuva_op_map *op, if (IS_ERR(vma)) return vma; - if (xe_vma_is_userptr(vma)) + if (xe_vma_is_userptr(vma)) { err = xe_vma_userptr_pin_pages(to_userptr_vma(vma)); + /* + * -EBUSY has dedicated meaning that a user fence + * attached to the VMA is busy, in practice + * xe_vma_userptr_pin_pages can only fail with -EBUSY if + * we are low on memory so convert this to -ENOMEM. + */ + if (err == -EBUSY) + err = -ENOMEM; + } } if (err) { prep_vma_destroy(vm, vma, false); @@ -2549,7 +2583,7 @@ static int xe_vma_op_commit(struct xe_vm *vm, struct xe_vma_op *op) op->flags |= XE_VMA_OP_COMMITTED; break; default: - drm_warn(&vm->xe->drm, "NOT POSSIBLE"); + drm_warn(&vm->xe->drm, "NOT POSSIBLE\n"); } return err; @@ -2724,7 +2758,8 @@ static int vm_bind_ioctl_ops_parse(struct xe_vm *vm, struct drm_gpuva_ops *ops, if (xe_vma_is_cpu_addr_mirror(vma) && xe_svm_has_mapping(vm, xe_vma_start(vma), - xe_vma_end(vma))) + xe_vma_end(vma)) && + !(vops->flags & XE_VMA_OPS_FLAG_ALLOW_SVM_UNMAP)) return -EBUSY; if (!xe_vma_is_cpu_addr_mirror(vma)) @@ -2747,7 +2782,7 @@ static int vm_bind_ioctl_ops_parse(struct xe_vm *vm, struct drm_gpuva_ops *ops, break; default: - drm_warn(&vm->xe->drm, "NOT POSSIBLE"); + drm_warn(&vm->xe->drm, "NOT POSSIBLE\n"); } err = xe_vma_op_commit(vm, op); @@ -2809,7 +2844,7 @@ static void xe_vma_op_unwind(struct xe_vm *vm, struct xe_vma_op *op, /* Nothing to do */ break; default: - drm_warn(&vm->xe->drm, "NOT POSSIBLE"); + drm_warn(&vm->xe->drm, "NOT POSSIBLE\n"); } } @@ -2875,7 +2910,7 @@ static int prefetch_ranges(struct xe_vm *vm, struct xe_vma_op *op) { bool devmem_possible = IS_DGFX(vm->xe) && IS_ENABLED(CONFIG_DRM_XE_PAGEMAP); struct xe_vma *vma = gpuva_to_vma(op->base.prefetch.va); - struct xe_tile *tile = op->prefetch_range.tile; + struct drm_pagemap *dpagemap = op->prefetch_range.dpagemap; int err = 0; struct xe_svm_range *svm_range; @@ -2888,15 +2923,22 @@ static int prefetch_ranges(struct xe_vm *vm, struct xe_vma_op *op) ctx.read_only = xe_vma_read_only(vma); ctx.devmem_possible = devmem_possible; ctx.check_pages_threshold = devmem_possible ? SZ_64K : 0; - ctx.device_private_page_owner = xe_svm_devm_owner(vm->xe); + ctx.device_private_page_owner = xe_svm_private_page_owner(vm, !dpagemap); /* TODO: Threading the migration */ xa_for_each(&op->prefetch_range.range, i, svm_range) { - if (!tile) + if (!dpagemap) xe_svm_range_migrate_to_smem(vm, svm_range); - if (xe_svm_range_needs_migrate_to_vram(svm_range, vma, !!tile)) { - err = xe_svm_alloc_vram(tile, svm_range, &ctx); + if (IS_ENABLED(CONFIG_DRM_XE_DEBUG_VM)) { + drm_dbg(&vm->xe->drm, + "Prefetch pagemap is %s start 0x%016lx end 0x%016lx\n", + dpagemap ? dpagemap->drm->unique : "system", + xe_svm_range_start(svm_range), xe_svm_range_end(svm_range)); + } + + if (xe_svm_range_needs_migrate_to_vram(svm_range, vma, dpagemap)) { + err = xe_svm_alloc_vram(svm_range, &ctx, dpagemap); if (err) { drm_dbg(&vm->xe->drm, "VRAM allocation failed, retry from userspace, asid=%u, gpusvm=%p, errno=%pe\n", vm->usm.asid, &vm->svm.gpusvm, ERR_PTR(err)); @@ -2986,7 +3028,7 @@ static int op_lock_and_prep(struct drm_exec *exec, struct xe_vm *vm, break; } default: - drm_warn(&vm->xe->drm, "NOT POSSIBLE"); + drm_warn(&vm->xe->drm, "NOT POSSIBLE\n"); } return err; @@ -3104,19 +3146,19 @@ static struct dma_fence *ops_execute(struct xe_vm *vm, struct dma_fence *fence = NULL; struct dma_fence **fences = NULL; struct dma_fence_array *cf = NULL; - int number_tiles = 0, current_fence = 0, n_fence = 0, err; + int number_tiles = 0, current_fence = 0, n_fence = 0, err, i; u8 id; number_tiles = vm_ops_setup_tile_args(vm, vops); if (number_tiles == 0) return ERR_PTR(-ENODATA); - if (vops->flags & XE_VMA_OPS_FLAG_SKIP_TLB_WAIT) { - for_each_tile(tile, vm->xe, id) - ++n_fence; - } else { - for_each_tile(tile, vm->xe, id) - n_fence += (1 + XE_MAX_GT_PER_TILE); + for_each_tile(tile, vm->xe, id) { + ++n_fence; + + if (!(vops->flags & XE_VMA_OPS_FLAG_SKIP_TLB_WAIT)) + for_each_tlb_inval(i) + ++n_fence; } fences = kmalloc_array(n_fence, sizeof(*fences), GFP_KERNEL); @@ -3146,7 +3188,6 @@ static struct dma_fence *ops_execute(struct xe_vm *vm, for_each_tile(tile, vm->xe, id) { struct xe_exec_queue *q = vops->pt_update_ops[tile->id].q; - int i; fence = NULL; if (!vops->pt_update_ops[id].num_ops) @@ -3211,7 +3252,8 @@ static void op_add_ufence(struct xe_vm *vm, struct xe_vma_op *op, { switch (op->base.op) { case DRM_GPUVA_OP_MAP: - vma_add_ufence(op->map.vma, ufence); + if (!xe_vma_is_cpu_addr_mirror(op->map.vma)) + vma_add_ufence(op->map.vma, ufence); break; case DRM_GPUVA_OP_REMAP: if (op->remap.prev) @@ -3225,7 +3267,7 @@ static void op_add_ufence(struct xe_vm *vm, struct xe_vma_op *op, vma_add_ufence(gpuva_to_vma(op->base.prefetch.va), ufence); break; default: - drm_warn(&vm->xe->drm, "NOT POSSIBLE"); + drm_warn(&vm->xe->drm, "NOT POSSIBLE\n"); } } @@ -3324,6 +3366,9 @@ static int vm_bind_ioctl_check_args(struct xe_device *xe, struct xe_vm *vm, if (XE_IOCTL_DBG(xe, args->extensions)) return -EINVAL; + if (XE_IOCTL_DBG(xe, args->num_syncs > DRM_XE_MAX_SYNCS)) + return -EINVAL; + if (args->num_binds > 1) { u64 __user *bind_user = u64_to_user_ptr(args->vector_of_binds); @@ -3359,6 +3404,7 @@ static int vm_bind_ioctl_check_args(struct xe_device *xe, struct xe_vm *vm, DRM_XE_VM_BIND_FLAG_CPU_ADDR_MIRROR; u16 pat_index = (*bind_ops)[i].pat_index; u16 coh_mode; + bool comp_en; if (XE_IOCTL_DBG(xe, is_cpu_addr_mirror && (!xe_vm_in_fault_mode(vm) || @@ -3375,6 +3421,7 @@ static int vm_bind_ioctl_check_args(struct xe_device *xe, struct xe_vm *vm, pat_index = array_index_nospec(pat_index, xe->pat.n_entries); (*bind_ops)[i].pat_index = pat_index; coh_mode = xe_pat_index_get_coh_mode(xe, pat_index); + comp_en = xe_pat_index_get_comp_en(xe, pat_index); if (XE_IOCTL_DBG(xe, !coh_mode)) { /* hw reserved */ err = -EINVAL; goto free_bind_ops; @@ -3405,6 +3452,8 @@ static int vm_bind_ioctl_check_args(struct xe_device *xe, struct xe_vm *vm, op == DRM_XE_VM_BIND_OP_MAP_USERPTR) || XE_IOCTL_DBG(xe, coh_mode == XE_COH_NONE && op == DRM_XE_VM_BIND_OP_MAP_USERPTR) || + XE_IOCTL_DBG(xe, comp_en && + op == DRM_XE_VM_BIND_OP_MAP_USERPTR) || XE_IOCTL_DBG(xe, op == DRM_XE_VM_BIND_OP_MAP_USERPTR && !IS_ENABLED(CONFIG_DRM_GPUSVM)) || XE_IOCTL_DBG(xe, obj && @@ -3483,6 +3532,11 @@ static int xe_vm_bind_ioctl_validate_bo(struct xe_device *xe, struct xe_bo *bo, u16 pat_index, u32 op, u32 bind_flags) { u16 coh_mode; + bool comp_en; + + if (XE_IOCTL_DBG(xe, (bo->flags & XE_BO_FLAG_NO_COMPRESSION) && + xe_pat_index_get_comp_en(xe, pat_index))) + return -EINVAL; if (XE_IOCTL_DBG(xe, range > xe_bo_size(bo)) || XE_IOCTL_DBG(xe, obj_offset > @@ -3524,6 +3578,14 @@ static int xe_vm_bind_ioctl_validate_bo(struct xe_device *xe, struct xe_bo *bo, return -EINVAL; } + /* + * Ensures that imported buffer objects (dma-bufs) are not mapped + * with a PAT index that enables compression. + */ + comp_en = xe_pat_index_get_comp_en(xe, pat_index); + if (XE_IOCTL_DBG(xe, bo->ttm.base.import_attach && comp_en)) + return -EINVAL; + /* If a BO is protected it can only be mapped if the key is still valid */ if ((bind_flags & DRM_XE_VM_BIND_FLAG_CHECK_PXP) && xe_bo_is_protected(bo) && op != DRM_XE_VM_BIND_OP_UNMAP && op != DRM_XE_VM_BIND_OP_UNMAP_ALL) @@ -3572,6 +3634,11 @@ int xe_vm_bind_ioctl(struct drm_device *dev, void *data, struct drm_file *file) } } + if (XE_IOCTL_DBG(xe, q && vm != q->user_vm)) { + err = -EINVAL; + goto put_exec_queue; + } + /* Ensure all UNMAPs visible */ xe_svm_flush(vm); @@ -3907,7 +3974,7 @@ int xe_vm_range_tilemask_tlb_inval(struct xe_vm *vm, u64 start, err = xe_tlb_inval_range(&tile->primary_gt->tlb_inval, &fence[fence_id], start, end, - vm->usm.asid); + vm->usm.asid, NULL); if (err) goto wait; ++fence_id; @@ -3920,7 +3987,7 @@ int xe_vm_range_tilemask_tlb_inval(struct xe_vm *vm, u64 start, err = xe_tlb_inval_range(&tile->media_gt->tlb_inval, &fence[fence_id], start, end, - vm->usm.asid); + vm->usm.asid, NULL); if (err) goto wait; ++fence_id; @@ -4026,10 +4093,18 @@ int xe_vm_validate_protected(struct xe_vm *vm) } struct xe_vm_snapshot { + int uapi_flags; unsigned long num_snaps; struct { u64 ofs, bo_ofs; unsigned long len; +#define XE_VM_SNAP_FLAG_USERPTR BIT(0) +#define XE_VM_SNAP_FLAG_READ_ONLY BIT(1) +#define XE_VM_SNAP_FLAG_IS_NULL BIT(2) + unsigned long flags; + int uapi_mem_region; + int pat_index; + int cpu_caching; struct xe_bo *bo; void *data; struct mm_struct *mm; @@ -4058,6 +4133,13 @@ struct xe_vm_snapshot *xe_vm_snapshot_capture(struct xe_vm *vm) goto out_unlock; } + if (vm->flags & XE_VM_FLAG_FAULT_MODE) + snap->uapi_flags |= DRM_XE_VM_CREATE_FLAG_FAULT_MODE; + if (vm->flags & XE_VM_FLAG_LR_MODE) + snap->uapi_flags |= DRM_XE_VM_CREATE_FLAG_LR_MODE; + if (vm->flags & XE_VM_FLAG_SCRATCH_PAGE) + snap->uapi_flags |= DRM_XE_VM_CREATE_FLAG_SCRATCH_PAGE; + snap->num_snaps = num_snaps; i = 0; drm_gpuvm_for_each_va(gpuva, &vm->gpuvm) { @@ -4070,9 +4152,25 @@ struct xe_vm_snapshot *xe_vm_snapshot_capture(struct xe_vm *vm) snap->snap[i].ofs = xe_vma_start(vma); snap->snap[i].len = xe_vma_size(vma); + snap->snap[i].flags = xe_vma_read_only(vma) ? + XE_VM_SNAP_FLAG_READ_ONLY : 0; + snap->snap[i].pat_index = vma->attr.pat_index; if (bo) { + snap->snap[i].cpu_caching = bo->cpu_caching; snap->snap[i].bo = xe_bo_get(bo); snap->snap[i].bo_ofs = xe_vma_bo_offset(vma); + switch (bo->ttm.resource->mem_type) { + case XE_PL_SYSTEM: + case XE_PL_TT: + snap->snap[i].uapi_mem_region = 0; + break; + case XE_PL_VRAM0: + snap->snap[i].uapi_mem_region = 1; + break; + case XE_PL_VRAM1: + snap->snap[i].uapi_mem_region = 2; + break; + } } else if (xe_vma_is_userptr(vma)) { struct mm_struct *mm = to_userptr_vma(vma)->userptr.notifier.mm; @@ -4083,8 +4181,14 @@ struct xe_vm_snapshot *xe_vm_snapshot_capture(struct xe_vm *vm) snap->snap[i].data = ERR_PTR(-EFAULT); snap->snap[i].bo_ofs = xe_vma_userptr(vma); + snap->snap[i].flags |= XE_VM_SNAP_FLAG_USERPTR; + snap->snap[i].uapi_mem_region = 0; + } else if (xe_vma_is_null(vma)) { + snap->snap[i].flags |= XE_VM_SNAP_FLAG_IS_NULL; + snap->snap[i].uapi_mem_region = -1; } else { snap->snap[i].data = ERR_PTR(-ENOENT); + snap->snap[i].uapi_mem_region = -1; } i++; } @@ -4103,7 +4207,8 @@ void xe_vm_snapshot_capture_delayed(struct xe_vm_snapshot *snap) struct xe_bo *bo = snap->snap[i].bo; int err; - if (IS_ERR(snap->snap[i].data)) + if (IS_ERR(snap->snap[i].data) || + snap->snap[i].flags & XE_VM_SNAP_FLAG_IS_NULL) continue; snap->snap[i].data = kvmalloc(snap->snap[i].len, GFP_USER); @@ -4149,15 +4254,32 @@ void xe_vm_snapshot_print(struct xe_vm_snapshot *snap, struct drm_printer *p) return; } + drm_printf(p, "VM.uapi_flags: 0x%x\n", snap->uapi_flags); for (i = 0; i < snap->num_snaps; i++) { drm_printf(p, "[%llx].length: 0x%lx\n", snap->snap[i].ofs, snap->snap[i].len); + drm_printf(p, "[%llx].properties: %s|%s|mem_region=0x%lx|pat_index=%d|cpu_caching=%d\n", + snap->snap[i].ofs, + snap->snap[i].flags & XE_VM_SNAP_FLAG_READ_ONLY ? + "read_only" : "read_write", + snap->snap[i].flags & XE_VM_SNAP_FLAG_IS_NULL ? + "null_sparse" : + snap->snap[i].flags & XE_VM_SNAP_FLAG_USERPTR ? + "userptr" : "bo", + snap->snap[i].uapi_mem_region == -1 ? 0 : + BIT(snap->snap[i].uapi_mem_region), + snap->snap[i].pat_index, + snap->snap[i].cpu_caching); + if (IS_ERR(snap->snap[i].data)) { drm_printf(p, "[%llx].error: %li\n", snap->snap[i].ofs, PTR_ERR(snap->snap[i].data)); continue; } + if (snap->snap[i].flags & XE_VM_SNAP_FLAG_IS_NULL) + continue; + drm_printf(p, "[%llx].data: ", snap->snap[i].ofs); for (j = 0; j < snap->snap[i].len; j += sizeof(u32)) { @@ -4242,7 +4364,7 @@ static int xe_vm_alloc_vma(struct xe_vm *vm, struct drm_gpuva_op *__op; unsigned int vma_flags = 0; bool remap_op = false; - struct xe_vma_mem_attr tmp_attr; + struct xe_vma_mem_attr tmp_attr = {}; u16 default_pat; int err; @@ -4311,6 +4433,8 @@ static int xe_vm_alloc_vma(struct xe_vm *vm, if (is_madvise) vops.flags |= XE_VMA_OPS_FLAG_MADVISE; + else + vops.flags |= XE_VMA_OPS_FLAG_ALLOW_SVM_UNMAP; err = vm_bind_ioctl_ops_parse(vm, ops, &vops); if (err) @@ -4335,7 +4459,7 @@ static int xe_vm_alloc_vma(struct xe_vm *vm, * VMA, so they can be assigned to newly MAP created vma. */ if (is_madvise) - tmp_attr = vma->attr; + xe_vma_mem_attr_copy(&tmp_attr, &vma->attr); xe_vma_destroy(gpuva_to_vma(op->base.remap.unmap->va), NULL); } else if (__op->op == DRM_GPUVA_OP_MAP) { @@ -4345,12 +4469,13 @@ static int xe_vm_alloc_vma(struct xe_vm *vm, * copy them to new vma. */ if (is_madvise) - vma->attr = tmp_attr; + xe_vma_mem_attr_copy(&vma->attr, &tmp_attr); } } xe_vm_unlock(vm); drm_gpuva_ops_free(&vm->gpuvm, ops); + xe_vma_mem_attr_fini(&tmp_attr); return 0; unwind_ops: @@ -4384,6 +4509,46 @@ int xe_vm_alloc_madvise_vma(struct xe_vm *vm, uint64_t start, uint64_t range) return xe_vm_alloc_vma(vm, &map_req, true); } +static bool is_cpu_addr_vma_with_default_attr(struct xe_vma *vma) +{ + return vma && xe_vma_is_cpu_addr_mirror(vma) && + xe_vma_has_default_mem_attrs(vma); +} + +/** + * xe_vm_find_cpu_addr_mirror_vma_range - Extend a VMA range to include adjacent CPU-mirrored VMAs + * @vm: VM to search within + * @start: Input/output pointer to the starting address of the range + * @end: Input/output pointer to the end address of the range + * + * Given a range defined by @start and @range, this function checks the VMAs + * immediately before and after the range. If those neighboring VMAs are + * CPU-address-mirrored and have default memory attributes, the function + * updates @start and @range to include them. This extended range can then + * be used for merging or other operations that require a unified VMA. + * + * The function does not perform the merge itself; it only computes the + * mergeable boundaries. + */ +void xe_vm_find_cpu_addr_mirror_vma_range(struct xe_vm *vm, u64 *start, u64 *end) +{ + struct xe_vma *prev, *next; + + lockdep_assert_held(&vm->lock); + + if (*start >= SZ_4K) { + prev = xe_vm_find_vma_by_addr(vm, *start - SZ_4K); + if (is_cpu_addr_vma_with_default_attr(prev)) + *start = xe_vma_start(prev); + } + + if (*end < vm->size) { + next = xe_vm_find_vma_by_addr(vm, *end + 1); + if (is_cpu_addr_vma_with_default_attr(next)) + *end = xe_vma_end(next); + } +} + /** * xe_vm_alloc_cpu_addr_mirror_vma - Allocate CPU addr mirror vma * @vm: Pointer to the xe_vm structure @@ -4408,3 +4573,4 @@ int xe_vm_alloc_cpu_addr_mirror_vma(struct xe_vm *vm, uint64_t start, uint64_t r return xe_vm_alloc_vma(vm, &map_req, false); } + diff --git a/drivers/gpu/drm/xe/xe_vm.h b/drivers/gpu/drm/xe/xe_vm.h index ef8a5019574e..6cc98df47291 100644 --- a/drivers/gpu/drm/xe/xe_vm.h +++ b/drivers/gpu/drm/xe/xe_vm.h @@ -68,6 +68,9 @@ xe_vm_find_overlapping_vma(struct xe_vm *vm, u64 start, u64 range); bool xe_vma_has_default_mem_attrs(struct xe_vma *vma); +void xe_vm_find_cpu_addr_mirror_vma_range(struct xe_vm *vm, + u64 *start, + u64 *end); /** * xe_vm_has_scratch() - Whether the vm is configured for scratch PTEs * @vm: The vm @@ -379,7 +382,7 @@ static inline void xe_vm_set_validation_exec(struct xe_vm *vm, struct drm_exec * } /** - * xe_vm_set_validation_exec() - Accessor to read the drm_exec object + * xe_vm_validation_exec() - Accessor to read the drm_exec object * @vm: The vm we want to register a drm_exec object with. * * Return: The drm_exec object used to lock the vm's resv. The value @@ -411,4 +414,5 @@ static inline struct drm_exec *xe_vm_validation_exec(struct xe_vm *vm) #define xe_vm_has_valid_gpu_mapping(tile, tile_present, tile_invalidated) \ ((READ_ONCE(tile_present) & ~READ_ONCE(tile_invalidated)) & BIT((tile)->id)) +void xe_vma_mem_attr_copy(struct xe_vma_mem_attr *to, struct xe_vma_mem_attr *from); #endif diff --git a/drivers/gpu/drm/xe/xe_vm_madvise.c b/drivers/gpu/drm/xe/xe_vm_madvise.c index cad3cf627c3f..add9a6ca2390 100644 --- a/drivers/gpu/drm/xe/xe_vm_madvise.c +++ b/drivers/gpu/drm/xe/xe_vm_madvise.c @@ -22,6 +22,19 @@ struct xe_vmas_in_madvise_range { bool has_svm_userptr_vmas; }; +/** + * struct xe_madvise_details - Argument to madvise_funcs + * @dpagemap: Reference-counted pointer to a struct drm_pagemap. + * + * The madvise IOCTL handler may, in addition to the user-space + * args, have additional info to pass into the madvise_func that + * handles the madvise type. Use a struct_xe_madvise_details + * for that and extend the struct as necessary. + */ +struct xe_madvise_details { + struct drm_pagemap *dpagemap; +}; + static int get_vmas(struct xe_vm *vm, struct xe_vmas_in_madvise_range *madvise_range) { u64 addr = madvise_range->addr; @@ -74,34 +87,41 @@ static int get_vmas(struct xe_vm *vm, struct xe_vmas_in_madvise_range *madvise_r static void madvise_preferred_mem_loc(struct xe_device *xe, struct xe_vm *vm, struct xe_vma **vmas, int num_vmas, - struct drm_xe_madvise *op) + struct drm_xe_madvise *op, + struct xe_madvise_details *details) { int i; xe_assert(vm->xe, op->type == DRM_XE_MEM_RANGE_ATTR_PREFERRED_LOC); for (i = 0; i < num_vmas; i++) { + struct xe_vma *vma = vmas[i]; + struct xe_vma_preferred_loc *loc = &vma->attr.preferred_loc; + /*TODO: Extend attributes to bo based vmas */ - if ((vmas[i]->attr.preferred_loc.devmem_fd == op->preferred_mem_loc.devmem_fd && - vmas[i]->attr.preferred_loc.migration_policy == - op->preferred_mem_loc.migration_policy) || - !xe_vma_is_cpu_addr_mirror(vmas[i])) { - vmas[i]->skip_invalidation = true; + if ((loc->devmem_fd == op->preferred_mem_loc.devmem_fd && + loc->migration_policy == op->preferred_mem_loc.migration_policy) || + !xe_vma_is_cpu_addr_mirror(vma)) { + vma->skip_invalidation = true; } else { - vmas[i]->skip_invalidation = false; - vmas[i]->attr.preferred_loc.devmem_fd = op->preferred_mem_loc.devmem_fd; + vma->skip_invalidation = false; + loc->devmem_fd = op->preferred_mem_loc.devmem_fd; /* Till multi-device support is not added migration_policy * is of no use and can be ignored. */ - vmas[i]->attr.preferred_loc.migration_policy = - op->preferred_mem_loc.migration_policy; + loc->migration_policy = op->preferred_mem_loc.migration_policy; + drm_pagemap_put(loc->dpagemap); + loc->dpagemap = NULL; + if (details->dpagemap) + loc->dpagemap = drm_pagemap_get(details->dpagemap); } } } static void madvise_atomic(struct xe_device *xe, struct xe_vm *vm, struct xe_vma **vmas, int num_vmas, - struct drm_xe_madvise *op) + struct drm_xe_madvise *op, + struct xe_madvise_details *details) { struct xe_bo *bo; int i; @@ -142,7 +162,8 @@ static void madvise_atomic(struct xe_device *xe, struct xe_vm *vm, static void madvise_pat_index(struct xe_device *xe, struct xe_vm *vm, struct xe_vma **vmas, int num_vmas, - struct drm_xe_madvise *op) + struct drm_xe_madvise *op, + struct xe_madvise_details *details) { int i; @@ -160,7 +181,8 @@ static void madvise_pat_index(struct xe_device *xe, struct xe_vm *vm, typedef void (*madvise_func)(struct xe_device *xe, struct xe_vm *vm, struct xe_vma **vmas, int num_vmas, - struct drm_xe_madvise *op); + struct drm_xe_madvise *op, + struct xe_madvise_details *details); static const madvise_func madvise_funcs[] = { [DRM_XE_MEM_RANGE_ATTR_PREFERRED_LOC] = madvise_preferred_mem_loc, @@ -244,11 +266,12 @@ static bool madvise_args_are_sane(struct xe_device *xe, const struct drm_xe_madv if (XE_IOCTL_DBG(xe, fd < DRM_XE_PREFERRED_LOC_DEFAULT_SYSTEM)) return false; - if (XE_IOCTL_DBG(xe, args->preferred_mem_loc.migration_policy > - DRM_XE_MIGRATE_ONLY_SYSTEM_PAGES)) + if (XE_IOCTL_DBG(xe, fd <= DRM_XE_PREFERRED_LOC_DEFAULT_DEVICE && + args->preferred_mem_loc.region_instance != 0)) return false; - if (XE_IOCTL_DBG(xe, args->preferred_mem_loc.pad)) + if (XE_IOCTL_DBG(xe, args->preferred_mem_loc.migration_policy > + DRM_XE_MIGRATE_ONLY_SYSTEM_PAGES)) return false; if (XE_IOCTL_DBG(xe, args->preferred_mem_loc.reserved)) @@ -294,6 +317,41 @@ static bool madvise_args_are_sane(struct xe_device *xe, const struct drm_xe_madv return true; } +static int xe_madvise_details_init(struct xe_vm *vm, const struct drm_xe_madvise *args, + struct xe_madvise_details *details) +{ + struct xe_device *xe = vm->xe; + + memset(details, 0, sizeof(*details)); + + if (args->type == DRM_XE_MEM_RANGE_ATTR_PREFERRED_LOC) { + int fd = args->preferred_mem_loc.devmem_fd; + struct drm_pagemap *dpagemap; + + if (fd <= 0) + return 0; + + dpagemap = xe_drm_pagemap_from_fd(args->preferred_mem_loc.devmem_fd, + args->preferred_mem_loc.region_instance); + if (XE_IOCTL_DBG(xe, IS_ERR(dpagemap))) + return PTR_ERR(dpagemap); + + /* Don't allow a foreign placement without a fast interconnect! */ + if (XE_IOCTL_DBG(xe, dpagemap->pagemap->owner != vm->svm.peer.owner)) { + drm_pagemap_put(dpagemap); + return -ENOLINK; + } + details->dpagemap = dpagemap; + } + + return 0; +} + +static void xe_madvise_details_fini(struct xe_madvise_details *details) +{ + drm_pagemap_put(details->dpagemap); +} + static bool check_bo_args_are_sane(struct xe_vm *vm, struct xe_vma **vmas, int num_vmas, u32 atomic_val) { @@ -347,6 +405,7 @@ int xe_vm_madvise_ioctl(struct drm_device *dev, void *data, struct drm_file *fil struct drm_xe_madvise *args = data; struct xe_vmas_in_madvise_range madvise_range = {.addr = args->start, .range = args->range, }; + struct xe_madvise_details details; struct xe_vm *vm; struct drm_exec exec; int err, attr_type; @@ -371,13 +430,17 @@ int xe_vm_madvise_ioctl(struct drm_device *dev, void *data, struct drm_file *fil goto unlock_vm; } - err = xe_vm_alloc_madvise_vma(vm, args->start, args->range); + err = xe_madvise_details_init(vm, args, &details); if (err) goto unlock_vm; + err = xe_vm_alloc_madvise_vma(vm, args->start, args->range); + if (err) + goto madv_fini; + err = get_vmas(vm, &madvise_range); if (err || !madvise_range.num_vmas) - goto unlock_vm; + goto madv_fini; if (madvise_range.has_bo_vmas) { if (args->type == DRM_XE_MEM_RANGE_ATTR_ATOMIC) { @@ -385,7 +448,7 @@ int xe_vm_madvise_ioctl(struct drm_device *dev, void *data, struct drm_file *fil madvise_range.num_vmas, args->atomic.val)) { err = -EINVAL; - goto unlock_vm; + goto madv_fini; } } @@ -411,7 +474,8 @@ int xe_vm_madvise_ioctl(struct drm_device *dev, void *data, struct drm_file *fil } attr_type = array_index_nospec(args->type, ARRAY_SIZE(madvise_funcs)); - madvise_funcs[attr_type](xe, vm, madvise_range.vmas, madvise_range.num_vmas, args); + madvise_funcs[attr_type](xe, vm, madvise_range.vmas, madvise_range.num_vmas, args, + &details); err = xe_vm_invalidate_madvise_range(vm, args->start, args->start + args->range); @@ -423,6 +487,8 @@ err_fini: drm_exec_fini(&exec); kfree(madvise_range.vmas); madvise_range.vmas = NULL; +madv_fini: + xe_madvise_details_fini(&details); unlock_vm: up_write(&vm->lock); put_vm: diff --git a/drivers/gpu/drm/xe/xe_vm_types.h b/drivers/gpu/drm/xe/xe_vm_types.h index ccd6cc090309..437f64202f3b 100644 --- a/drivers/gpu/drm/xe/xe_vm_types.h +++ b/drivers/gpu/drm/xe/xe_vm_types.h @@ -8,6 +8,7 @@ #include <drm/drm_gpusvm.h> #include <drm/drm_gpuvm.h> +#include <drm/drm_pagemap_util.h> #include <linux/dma-resv.h> #include <linux/kref.h> @@ -19,6 +20,8 @@ #include "xe_range_fence.h" #include "xe_userptr.h" +struct drm_pagemap; + struct xe_bo; struct xe_svm_range; struct xe_sync_entry; @@ -53,7 +56,7 @@ struct xe_vm_pgtable_update_op; */ struct xe_vma_mem_attr { /** @preferred_loc: preferred memory_location */ - struct { + struct xe_vma_preferred_loc { /** @preferred_loc.migration_policy: Pages migration policy */ u32 migration_policy; @@ -64,6 +67,13 @@ struct xe_vma_mem_attr { * closest device memory respectively. */ u32 devmem_fd; + /** + * @preferred_loc.dpagemap: Reference-counted pointer to the drm_pagemap preferred + * for migration on a SVM page-fault. The pointer is protected by the + * vm lock, and is %NULL if @devmem_fd should be consulted for special + * values. + */ + struct drm_pagemap *dpagemap; } preferred_loc; /** @@ -191,6 +201,9 @@ struct xe_vm { */ struct work_struct work; } garbage_collector; + struct xe_pagemap *pagemaps[XE_MAX_TILES_PER_DEVICE]; + /** @svm.peer: Used for pagemap connectivity computations. */ + struct drm_pagemap_peer peer; } svm; struct xe_device *xe; @@ -263,7 +276,7 @@ struct xe_vm { * @min_run_period_ms: The minimum run period before preempting * an engine again */ - s64 min_run_period_ms; + unsigned int min_run_period_ms; /** @exec_queues: list of exec queues attached to this VM */ struct list_head exec_queues; /** @num_exec_queues: number exec queues attached to this VM */ @@ -395,10 +408,10 @@ struct xe_vma_op_prefetch_range { /** @ranges_count: number of svm ranges to map */ u32 ranges_count; /** - * @tile: Pointer to the tile structure containing memory to prefetch. - * NULL if prefetch requested region is smem + * @dpagemap: Pointer to the dpagemap structure containing memory to prefetch. + * NULL if prefetch requested region is smem */ - struct xe_tile *tile; + struct drm_pagemap *dpagemap; }; /** enum xe_vma_op_flags - flags for VMA operation */ @@ -467,6 +480,7 @@ struct xe_vma_ops { #define XE_VMA_OPS_FLAG_MADVISE BIT(1) #define XE_VMA_OPS_ARRAY_OF_BINDS BIT(2) #define XE_VMA_OPS_FLAG_SKIP_TLB_WAIT BIT(3) +#define XE_VMA_OPS_FLAG_ALLOW_SVM_UNMAP BIT(4) u32 flags; #ifdef TEST_VM_OPS_ERROR /** @inject_error: inject error to test error handling */ diff --git a/drivers/gpu/drm/xe/xe_vram.c b/drivers/gpu/drm/xe/xe_vram.c index d50baefcd124..0538dcb8b18c 100644 --- a/drivers/gpu/drm/xe/xe_vram.c +++ b/drivers/gpu/drm/xe/xe_vram.c @@ -13,109 +13,16 @@ #include "regs/xe_gt_regs.h" #include "regs/xe_regs.h" #include "xe_assert.h" -#include "xe_bo.h" #include "xe_device.h" #include "xe_force_wake.h" #include "xe_gt_mcr.h" #include "xe_mmio.h" -#include "xe_module.h" #include "xe_sriov.h" #include "xe_tile_sriov_vf.h" #include "xe_ttm_vram_mgr.h" #include "xe_vram.h" #include "xe_vram_types.h" -static void resize_bar(struct xe_device *xe, int resno, resource_size_t size) -{ - struct pci_dev *pdev = to_pci_dev(xe->drm.dev); - int bar_size = pci_rebar_bytes_to_size(size); - int ret; - - ret = pci_resize_resource(pdev, resno, bar_size, 0); - if (ret) { - drm_info(&xe->drm, "Failed to resize BAR%d to %dM (%pe). Consider enabling 'Resizable BAR' support in your BIOS\n", - resno, 1 << bar_size, ERR_PTR(ret)); - return; - } - - drm_info(&xe->drm, "BAR%d resized to %dM\n", resno, 1 << bar_size); -} - -/* - * if force_vram_bar_size is set, attempt to set to the requested size - * else set to maximum possible size - */ -void xe_vram_resize_bar(struct xe_device *xe) -{ - int force_vram_bar_size = xe_modparam.force_vram_bar_size; - struct pci_dev *pdev = to_pci_dev(xe->drm.dev); - struct pci_bus *root = pdev->bus; - resource_size_t current_size; - resource_size_t rebar_size; - struct resource *root_res; - int max_size, i; - u32 pci_cmd; - - /* gather some relevant info */ - current_size = pci_resource_len(pdev, LMEM_BAR); - - if (force_vram_bar_size < 0) - return; - - /* set to a specific size? */ - if (force_vram_bar_size) { - rebar_size = pci_rebar_bytes_to_size(force_vram_bar_size * - (resource_size_t)SZ_1M); - - if (!pci_rebar_size_supported(pdev, LMEM_BAR, rebar_size)) { - drm_info(&xe->drm, - "Requested size: %lluMiB is not supported by rebar sizes: 0x%llx. Leaving default: %lluMiB\n", - (u64)pci_rebar_size_to_bytes(rebar_size) >> 20, - pci_rebar_get_possible_sizes(pdev, LMEM_BAR), - (u64)current_size >> 20); - return; - } - - rebar_size = pci_rebar_size_to_bytes(rebar_size); - if (rebar_size == current_size) - return; - } else { - max_size = pci_rebar_get_max_size(pdev, LMEM_BAR); - if (max_size < 0) - return; - rebar_size = pci_rebar_size_to_bytes(max_size); - - /* only resize if larger than current */ - if (rebar_size <= current_size) - return; - } - - drm_info(&xe->drm, "Attempting to resize bar from %lluMiB -> %lluMiB\n", - (u64)current_size >> 20, (u64)rebar_size >> 20); - - while (root->parent) - root = root->parent; - - pci_bus_for_each_resource(root, root_res, i) { - if (root_res && root_res->flags & (IORESOURCE_MEM | IORESOURCE_MEM_64) && - (u64)root_res->start > 0x100000000ul) - break; - } - - if (!root_res) { - drm_info(&xe->drm, "Can't resize VRAM BAR - platform support is missing. Consider enabling 'Resizable BAR' support in your BIOS\n"); - return; - } - - pci_read_config_dword(pdev, PCI_COMMAND, &pci_cmd); - pci_write_config_dword(pdev, PCI_COMMAND, pci_cmd & ~PCI_COMMAND_MEMORY); - - resize_bar(xe, LMEM_BAR, rebar_size); - - pci_assign_unassigned_bus_resources(pdev->bus); - pci_write_config_dword(pdev, PCI_COMMAND, pci_cmd); -} - static bool resource_is_valid(struct pci_dev *pdev, int bar) { if (!pci_resource_flags(pdev, bar)) @@ -156,12 +63,11 @@ static int determine_lmem_bar_size(struct xe_device *xe, struct xe_vram_region * static int get_flat_ccs_offset(struct xe_gt *gt, u64 tile_size, u64 *poffset) { struct xe_device *xe = gt_to_xe(gt); - unsigned int fw_ref; u64 offset; u32 reg; - fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT); - if (!fw_ref) + CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT); + if (!fw_ref.domains) return -ETIMEDOUT; if (GRAPHICS_VER(xe) >= 20) { @@ -193,7 +99,6 @@ static int get_flat_ccs_offset(struct xe_gt *gt, u64 tile_size, u64 *poffset) offset = (u64)REG_FIELD_GET(XEHP_FLAT_CCS_PTR, reg) * SZ_64K; } - xe_force_wake_put(gt_to_fw(gt), fw_ref); *poffset = offset; return 0; @@ -248,7 +153,7 @@ static int tile_vram_size(struct xe_tile *tile, u64 *vram_size, *tile_offset = 0; } else { reg = xe_mmio_read32(&tile->mmio, SG_TILE_ADDR_RANGE(tile->id)); - *tile_size = (u64)REG_FIELD_GET(GENMASK(14, 8), reg) * SZ_1G; + *tile_size = (u64)REG_FIELD_GET(GENMASK(17, 8), reg) * SZ_1G; *tile_offset = (u64)REG_FIELD_GET(GENMASK(7, 1), reg) * SZ_1G; } diff --git a/drivers/gpu/drm/xe/xe_vram.h b/drivers/gpu/drm/xe/xe_vram.h index 13505cfb184d..72860f714fc6 100644 --- a/drivers/gpu/drm/xe/xe_vram.h +++ b/drivers/gpu/drm/xe/xe_vram.h @@ -11,7 +11,6 @@ struct xe_device; struct xe_vram_region; -void xe_vram_resize_bar(struct xe_device *xe); int xe_vram_probe(struct xe_device *xe); struct xe_vram_region *xe_vram_region_alloc(struct xe_device *xe, u8 id, u32 placement); diff --git a/drivers/gpu/drm/xe/xe_vram_freq.c b/drivers/gpu/drm/xe/xe_vram_freq.c index 17bc84da4cdc..6f8281e0b96a 100644 --- a/drivers/gpu/drm/xe/xe_vram_freq.c +++ b/drivers/gpu/drm/xe/xe_vram_freq.c @@ -5,7 +5,6 @@ #include <linux/sysfs.h> #include <drm/drm_managed.h> -#include "xe_gt_types.h" #include "xe_pcode.h" #include "xe_pcode_api.h" #include "xe_tile.h" diff --git a/drivers/gpu/drm/xe/xe_vram_types.h b/drivers/gpu/drm/xe/xe_vram_types.h index 83772dcbf1af..646e3c12ae9f 100644 --- a/drivers/gpu/drm/xe/xe_vram_types.h +++ b/drivers/gpu/drm/xe/xe_vram_types.h @@ -66,19 +66,8 @@ struct xe_vram_region { #if IS_ENABLED(CONFIG_DRM_XE_PAGEMAP) /** @migrate: Back pointer to migrate */ struct xe_migrate *migrate; - /** @pagemap: Used to remap device memory as ZONE_DEVICE */ - struct dev_pagemap pagemap; - /** - * @dpagemap: The struct drm_pagemap of the ZONE_DEVICE memory - * pages of this tile. - */ - struct drm_pagemap dpagemap; - /** - * @hpa_base: base host physical address - * - * This is generated when remap device memory as ZONE_DEVICE - */ - resource_size_t hpa_base; + /** @dpagemap_cache: drm_pagemap cache. */ + struct drm_pagemap_cache *dpagemap_cache; #endif }; diff --git a/drivers/gpu/drm/xe/xe_vsec.c b/drivers/gpu/drm/xe/xe_vsec.c index 8f23a27871b6..4ebb4dbe1c9b 100644 --- a/drivers/gpu/drm/xe/xe_vsec.c +++ b/drivers/gpu/drm/xe/xe_vsec.c @@ -12,7 +12,6 @@ #include "xe_device.h" #include "xe_device_types.h" -#include "xe_drv.h" #include "xe_mmio.h" #include "xe_platform_types.h" #include "xe_pm.h" @@ -158,13 +157,15 @@ int xe_pmt_telem_read(struct pci_dev *pdev, u32 guid, u64 *data, loff_t user_off guard(mutex)(&xe->pmt.lock); + if (!xe->soc_remapper.set_telem_region) + return -ENODEV; + /* indicate that we are not at an appropriate power level */ if (!xe_pm_runtime_get_if_active(xe)) return -ENODATA; /* set SoC re-mapper index register based on GUID memory region */ - xe_mmio_rmw32(xe_root_tile_mmio(xe), SG_REMAP_INDEX1, SG_REMAP_BITS, - REG_FIELD_PREP(SG_REMAP_BITS, mem_region)); + xe->soc_remapper.set_telem_region(xe, mem_region); memcpy_fromio(data, telem_addr, count); xe_pm_runtime_put(xe); diff --git a/drivers/gpu/drm/xe/xe_wa.c b/drivers/gpu/drm/xe/xe_wa.c index 3764abca3d4f..a991ee2b8781 100644 --- a/drivers/gpu/drm/xe/xe_wa.c +++ b/drivers/gpu/drm/xe/xe_wa.c @@ -15,10 +15,11 @@ #include "regs/xe_engine_regs.h" #include "regs/xe_gt_regs.h" +#include "regs/xe_guc_regs.h" #include "regs/xe_regs.h" #include "xe_device_types.h" #include "xe_force_wake.h" -#include "xe_gt.h" +#include "xe_gt_types.h" #include "xe_hw_engine_types.h" #include "xe_mmio.h" #include "xe_platform_types.h" @@ -216,20 +217,6 @@ static const struct xe_rtp_entry_sr gt_was[] = { XE_RTP_ACTIONS(SET(XELPMP_SQCNT1, ENFORCE_RAR)) }, - /* Xe2_LPG */ - - { XE_RTP_NAME("16020975621"), - XE_RTP_RULES(GRAPHICS_VERSION(2004), GRAPHICS_STEP(A0, B0)), - XE_RTP_ACTIONS(SET(XEHP_SLICE_UNIT_LEVEL_CLKGATE, SBEUNIT_CLKGATE_DIS)) - }, - { XE_RTP_NAME("14018157293"), - XE_RTP_RULES(GRAPHICS_VERSION(2004), GRAPHICS_STEP(A0, B0)), - XE_RTP_ACTIONS(SET(XEHPC_L3CLOS_MASK(0), ~0), - SET(XEHPC_L3CLOS_MASK(1), ~0), - SET(XEHPC_L3CLOS_MASK(2), ~0), - SET(XEHPC_L3CLOS_MASK(3), ~0)) - }, - /* Xe2_LPM */ { XE_RTP_NAME("14017421178"), @@ -270,14 +257,6 @@ static const struct xe_rtp_entry_sr gt_was[] = { XE_RTP_ACTIONS(SET(VDBOX_CGCTL3F1C(0), MFXPIPE_CLKGATE_DIS)), XE_RTP_ENTRY_FLAG(FOREACH_ENGINE), }, - { XE_RTP_NAME("14020316580"), - XE_RTP_RULES(MEDIA_VERSION(1301)), - XE_RTP_ACTIONS(CLR(POWERGATE_ENABLE, - VDN_HCP_POWERGATE_ENABLE(0) | - VDN_MFXVDENC_POWERGATE_ENABLE(0) | - VDN_HCP_POWERGATE_ENABLE(2) | - VDN_MFXVDENC_POWERGATE_ENABLE(2))), - }, { XE_RTP_NAME("14019449301"), XE_RTP_RULES(MEDIA_VERSION(1301), ENGINE_CLASS(VIDEO_DECODE)), XE_RTP_ACTIONS(SET(VDBOX_CGCTL3F08(0), CG3DDISHRS_CLKGATE_DIS)), @@ -323,6 +302,10 @@ static const struct xe_rtp_entry_sr gt_was[] = { XE_RTP_ACTIONS(SET(VDBOX_CGCTL3F10(0), RAMDFTUNIT_CLKGATE_DIS)), XE_RTP_ENTRY_FLAG(FOREACH_ENGINE), }, + { XE_RTP_NAME("16028005424"), + XE_RTP_RULES(GRAPHICS_VERSION_RANGE(3000, 3005)), + XE_RTP_ACTIONS(SET(GUC_INTR_CHICKEN, DISABLE_SIGNALING_ENGINES)) + }, }; static const struct xe_rtp_entry_sr engine_was[] = { @@ -512,11 +495,6 @@ static const struct xe_rtp_entry_sr engine_was[] = { XE_RTP_RULES(GRAPHICS_VERSION(2004), FUNC(xe_rtp_match_first_render_or_compute)), XE_RTP_ACTIONS(SET(LSC_CHICKEN_BIT_0_UDW, XE2_ALLOC_DPA_STARVE_FIX_DIS)) }, - { XE_RTP_NAME("14018957109"), - XE_RTP_RULES(GRAPHICS_VERSION(2004), GRAPHICS_STEP(A0, B0), - FUNC(xe_rtp_match_first_render_or_compute)), - XE_RTP_ACTIONS(SET(HALF_SLICE_CHICKEN5, DISABLE_SAMPLE_G_PERFORMANCE)) - }, { XE_RTP_NAME("14020338487"), XE_RTP_RULES(GRAPHICS_VERSION(2004), FUNC(xe_rtp_match_first_render_or_compute)), XE_RTP_ACTIONS(SET(ROW_CHICKEN3, XE2_EUPEND_CHK_FLUSH_DIS)) @@ -526,11 +504,6 @@ static const struct xe_rtp_entry_sr engine_was[] = { FUNC(xe_rtp_match_first_render_or_compute)), XE_RTP_ACTIONS(SET(ROW_CHICKEN4, DISABLE_TDL_PUSH)) }, - { XE_RTP_NAME("14019322943"), - XE_RTP_RULES(GRAPHICS_VERSION(2004), GRAPHICS_STEP(A0, B0), - FUNC(xe_rtp_match_first_render_or_compute)), - XE_RTP_ACTIONS(SET(LSC_CHICKEN_BIT_0, TGM_WRITE_EOM_FORCE)) - }, { XE_RTP_NAME("14018471104"), XE_RTP_RULES(GRAPHICS_VERSION(2004), FUNC(xe_rtp_match_first_render_or_compute)), XE_RTP_ACTIONS(SET(LSC_CHICKEN_BIT_0_UDW, ENABLE_SMP_LD_RENDER_SURFACE_CONTROL)) @@ -701,7 +674,7 @@ static const struct xe_rtp_entry_sr engine_was[] = { XE_RTP_ACTIONS(SET(HALF_SLICE_CHICKEN7, CLEAR_OPTIMIZATION_DISABLE)) }, { XE_RTP_NAME("18041344222"), - XE_RTP_RULES(GRAPHICS_VERSION_RANGE(3000, 3001), + XE_RTP_RULES(GRAPHICS_VERSION(3000), FUNC(xe_rtp_match_first_render_or_compute), FUNC(xe_rtp_match_not_sriov_vf), FUNC(xe_rtp_match_gt_has_discontiguous_dss_groups)), @@ -807,17 +780,6 @@ static const struct xe_rtp_entry_sr lrc_was[] = { /* Xe2_LPG */ - { XE_RTP_NAME("16020518922"), - XE_RTP_RULES(GRAPHICS_VERSION(2004), GRAPHICS_STEP(A0, B0), - ENGINE_CLASS(RENDER)), - XE_RTP_ACTIONS(SET(FF_MODE, - DIS_TE_AUTOSTRIP | - DIS_MESH_PARTIAL_AUTOSTRIP | - DIS_MESH_AUTOSTRIP), - SET(VFLSKPD, - DIS_PARTIAL_AUTOSTRIP | - DIS_AUTOSTRIP)) - }, { XE_RTP_NAME("14019386621"), XE_RTP_RULES(GRAPHICS_VERSION(2004), ENGINE_CLASS(RENDER)), XE_RTP_ACTIONS(SET(VF_SCRATCHPAD, XE2_VFG_TED_CREDIT_INTERFACE_DISABLE)) @@ -826,20 +788,10 @@ static const struct xe_rtp_entry_sr lrc_was[] = { XE_RTP_RULES(GRAPHICS_VERSION(2004), ENGINE_CLASS(RENDER)), XE_RTP_ACTIONS(SET(XEHP_PSS_CHICKEN, FD_END_COLLECT)) }, - { XE_RTP_NAME("14020013138"), - XE_RTP_RULES(GRAPHICS_VERSION(2004), GRAPHICS_STEP(A0, B0), - ENGINE_CLASS(RENDER)), - XE_RTP_ACTIONS(SET(WM_CHICKEN3, HIZ_PLANE_COMPRESSION_DIS)) - }, { XE_RTP_NAME("14019988906"), XE_RTP_RULES(GRAPHICS_VERSION(2004), ENGINE_CLASS(RENDER)), XE_RTP_ACTIONS(SET(XEHP_PSS_CHICKEN, FLSH_IGNORES_PSD)) }, - { XE_RTP_NAME("16020183090"), - XE_RTP_RULES(GRAPHICS_VERSION(2004), GRAPHICS_STEP(A0, B0), - ENGINE_CLASS(RENDER)), - XE_RTP_ACTIONS(SET(INSTPM(RENDER_RING_BASE), ENABLE_SEMAPHORE_POLL_BIT)) - }, { XE_RTP_NAME("18033852989"), XE_RTP_RULES(GRAPHICS_VERSION(2004), ENGINE_CLASS(RENDER)), XE_RTP_ACTIONS(SET(COMMON_SLICE_CHICKEN1, DISABLE_BOTTOM_CLIP_RECTANGLE_TEST)) diff --git a/drivers/gpu/drm/xe/xe_wa_oob.rules b/drivers/gpu/drm/xe/xe_wa_oob.rules index fb38eb3d6e9a..5cd7fa6d2a5c 100644 --- a/drivers/gpu/drm/xe/xe_wa_oob.rules +++ b/drivers/gpu/drm/xe/xe_wa_oob.rules @@ -16,15 +16,11 @@ 16017236439 PLATFORM(PVC) 14019821291 MEDIA_VERSION_RANGE(1300, 2000) 14015076503 MEDIA_VERSION(1300) -16020292621 GRAPHICS_VERSION(2004), GRAPHICS_STEP(A0, B0) -14018913170 GRAPHICS_VERSION(2004), GRAPHICS_STEP(A0, B0) - MEDIA_VERSION(2000), GRAPHICS_STEP(A0, A1) - GRAPHICS_VERSION_RANGE(1270, 1274) +14018913170 GRAPHICS_VERSION_RANGE(1270, 1274) MEDIA_VERSION(1300) PLATFORM(DG2) 14018094691 GRAPHICS_VERSION_RANGE(2001, 2002) GRAPHICS_VERSION(2004) -14019882105 GRAPHICS_VERSION(2004), GRAPHICS_STEP(A0, B0) 18024947630 GRAPHICS_VERSION(2001) GRAPHICS_VERSION(2004) MEDIA_VERSION(2000) @@ -76,3 +72,4 @@ 15015404425_disable PLATFORM(PANTHERLAKE), MEDIA_STEP(B0, FOREVER) 16026007364 MEDIA_VERSION(3000) +14020316580 MEDIA_VERSION(1301) diff --git a/drivers/gpu/drm/xe/xe_wait_user_fence.c b/drivers/gpu/drm/xe/xe_wait_user_fence.c index 5b4264ea38bd..51eb940ceb4e 100644 --- a/drivers/gpu/drm/xe/xe_wait_user_fence.c +++ b/drivers/gpu/drm/xe/xe_wait_user_fence.c @@ -11,7 +11,6 @@ #include <uapi/drm/xe_drm.h> #include "xe_device.h" -#include "xe_gt.h" #include "xe_macros.h" #include "xe_exec_queue.h" diff --git a/drivers/gpu/drm/xe/xe_wopcm.c b/drivers/gpu/drm/xe/xe_wopcm.c index ada0d0aa6b74..dde4f4967ca3 100644 --- a/drivers/gpu/drm/xe/xe_wopcm.c +++ b/drivers/gpu/drm/xe/xe_wopcm.c @@ -10,7 +10,7 @@ #include "regs/xe_guc_regs.h" #include "xe_device.h" #include "xe_force_wake.h" -#include "xe_gt.h" +#include "xe_gt_types.h" #include "xe_mmio.h" #include "xe_uc_fw.h" |
