summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/gpu/xe/index.rst1
-rw-r--r--Documentation/gpu/xe/xe_exec_queue.rst20
-rw-r--r--drivers/gpu/drm/drm_gpusvm.c6
-rw-r--r--drivers/gpu/drm/xe/Kconfig.debug16
-rw-r--r--drivers/gpu/drm/xe/Makefile6
-rw-r--r--drivers/gpu/drm/xe/abi/guc_actions_abi.h8
-rw-r--r--drivers/gpu/drm/xe/display/intel_fbdev_fb.c4
-rw-r--r--drivers/gpu/drm/xe/display/xe_display.c5
-rw-r--r--drivers/gpu/drm/xe/display/xe_display.h4
-rw-r--r--drivers/gpu/drm/xe/display/xe_display_wa.c3
-rw-r--r--drivers/gpu/drm/xe/display/xe_plane_initial.c4
-rw-r--r--drivers/gpu/drm/xe/regs/xe_engine_regs.h4
-rw-r--r--drivers/gpu/drm/xe/regs/xe_gt_regs.h7
-rw-r--r--drivers/gpu/drm/xe/regs/xe_i2c_regs.h3
-rw-r--r--drivers/gpu/drm/xe/regs/xe_irq_regs.h8
-rw-r--r--drivers/gpu/drm/xe/tests/xe_dma_buf.c17
-rw-r--r--drivers/gpu/drm/xe/tests/xe_pci.c6
-rw-r--r--drivers/gpu/drm/xe/tests/xe_rtp_test.c6
-rw-r--r--drivers/gpu/drm/xe/xe_bo.c78
-rw-r--r--drivers/gpu/drm/xe/xe_bo.h3
-rw-r--r--drivers/gpu/drm/xe/xe_bo_evict.c9
-rw-r--r--drivers/gpu/drm/xe/xe_configfs.c282
-rw-r--r--drivers/gpu/drm/xe/xe_configfs.h8
-rw-r--r--drivers/gpu/drm/xe/xe_debugfs.c15
-rw-r--r--drivers/gpu/drm/xe/xe_device.c78
-rw-r--r--drivers/gpu/drm/xe/xe_device_sysfs.c10
-rw-r--r--drivers/gpu/drm/xe/xe_device_types.h15
-rw-r--r--drivers/gpu/drm/xe/xe_device_wa_oob.rules3
-rw-r--r--drivers/gpu/drm/xe/xe_dma_buf.c41
-rw-r--r--drivers/gpu/drm/xe/xe_eu_stall.c28
-rw-r--r--drivers/gpu/drm/xe/xe_exec.c15
-rw-r--r--drivers/gpu/drm/xe/xe_exec_queue.c118
-rw-r--r--drivers/gpu/drm/xe/xe_exec_queue.h5
-rw-r--r--drivers/gpu/drm/xe/xe_exec_queue_types.h3
-rw-r--r--drivers/gpu/drm/xe/xe_execlist.c2
-rw-r--r--drivers/gpu/drm/xe/xe_ggtt.c70
-rw-r--r--drivers/gpu/drm/xe/xe_gpu_scheduler.c27
-rw-r--r--drivers/gpu/drm/xe/xe_gpu_scheduler.h29
-rw-r--r--drivers/gpu/drm/xe/xe_gsc.c4
-rw-r--r--drivers/gpu/drm/xe/xe_gt.c53
-rw-r--r--drivers/gpu/drm/xe/xe_gt.h19
-rw-r--r--drivers/gpu/drm/xe/xe_gt_clock.c19
-rw-r--r--drivers/gpu/drm/xe/xe_gt_debugfs.c159
-rw-r--r--drivers/gpu/drm/xe/xe_gt_debugfs.h1
-rw-r--r--drivers/gpu/drm/xe/xe_gt_freq.c7
-rw-r--r--drivers/gpu/drm/xe/xe_gt_mcr.c67
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_config.c6
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_control.c98
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_control.h2
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_control_types.h2
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_debugfs.c436
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_debugfs.h1
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_printk.h7
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_vf.c470
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_vf.h11
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_vf_types.h34
-rw-r--r--drivers/gpu/drm/xe/xe_gt_topology.c17
-rw-r--r--drivers/gpu/drm/xe/xe_gt_topology.h2
-rw-r--r--drivers/gpu/drm/xe/xe_gt_types.h25
-rw-r--r--drivers/gpu/drm/xe/xe_guc.c269
-rw-r--r--drivers/gpu/drm/xe/xe_guc_ads.c2
-rw-r--r--drivers/gpu/drm/xe/xe_guc_capture.c29
-rw-r--r--drivers/gpu/drm/xe/xe_guc_ct.c371
-rw-r--r--drivers/gpu/drm/xe/xe_guc_ct.h13
-rw-r--r--drivers/gpu/drm/xe/xe_guc_exec_queue_types.h15
-rw-r--r--drivers/gpu/drm/xe/xe_guc_pc.c47
-rw-r--r--drivers/gpu/drm/xe/xe_guc_relay.c17
-rw-r--r--drivers/gpu/drm/xe/xe_guc_relay_types.h4
-rw-r--r--drivers/gpu/drm/xe/xe_guc_submit.c604
-rw-r--r--drivers/gpu/drm/xe/xe_guc_submit.h7
-rw-r--r--drivers/gpu/drm/xe/xe_huc.c10
-rw-r--r--drivers/gpu/drm/xe/xe_hw_engine.c62
-rw-r--r--drivers/gpu/drm/xe/xe_hwmon.c8
-rw-r--r--drivers/gpu/drm/xe/xe_i2c.c28
-rw-r--r--drivers/gpu/drm/xe/xe_i2c.h4
-rw-r--r--drivers/gpu/drm/xe/xe_irq.c138
-rw-r--r--drivers/gpu/drm/xe/xe_lmtt.c11
-rw-r--r--drivers/gpu/drm/xe/xe_lrc.c20
-rw-r--r--drivers/gpu/drm/xe/xe_lrc.h16
-rw-r--r--drivers/gpu/drm/xe/xe_map.h18
-rw-r--r--drivers/gpu/drm/xe/xe_memirq.c57
-rw-r--r--drivers/gpu/drm/xe/xe_memirq.h2
-rw-r--r--drivers/gpu/drm/xe/xe_migrate.c107
-rw-r--r--drivers/gpu/drm/xe/xe_mocs.c16
-rw-r--r--drivers/gpu/drm/xe/xe_mocs.h8
-rw-r--r--drivers/gpu/drm/xe/xe_oa.c3
-rw-r--r--drivers/gpu/drm/xe/xe_pat.c136
-rw-r--r--drivers/gpu/drm/xe/xe_pat.h7
-rw-r--r--drivers/gpu/drm/xe/xe_pci.c264
-rw-r--r--drivers/gpu/drm/xe/xe_pci_sriov.c53
-rw-r--r--drivers/gpu/drm/xe/xe_pci_types.h10
-rw-r--r--drivers/gpu/drm/xe/xe_platform_types.h2
-rw-r--r--drivers/gpu/drm/xe/xe_pm.c66
-rw-r--r--drivers/gpu/drm/xe/xe_pm.h2
-rw-r--r--drivers/gpu/drm/xe/xe_pmu.c11
-rw-r--r--drivers/gpu/drm/xe/xe_preempt_fence.c11
-rw-r--r--drivers/gpu/drm/xe/xe_psmi.c4
-rw-r--r--drivers/gpu/drm/xe/xe_pt.c6
-rw-r--r--drivers/gpu/drm/xe/xe_query.c2
-rw-r--r--drivers/gpu/drm/xe/xe_reg_whitelist.c3
-rw-r--r--drivers/gpu/drm/xe/xe_ring_ops.c23
-rw-r--r--drivers/gpu/drm/xe/xe_rtp.c31
-rw-r--r--drivers/gpu/drm/xe/xe_rtp.h30
-rw-r--r--drivers/gpu/drm/xe/xe_rtp_types.h4
-rw-r--r--drivers/gpu/drm/xe/xe_sched_job_types.h9
-rw-r--r--drivers/gpu/drm/xe/xe_sriov.c2
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_pf.c70
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_pf.h18
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_pf_control.c151
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_pf_control.h17
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_pf_debugfs.c264
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_pf_debugfs.h18
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_pf_helpers.h11
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_pf_provision.c154
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_pf_provision.h31
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_pf_provision_types.h36
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_pf_types.h4
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_printk.h12
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_vf.c243
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_vf.h1
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_vf_ccs.c28
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_vf_ccs.h1
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_vf_types.h4
-rw-r--r--drivers/gpu/drm/xe/xe_svm.c5
-rw-r--r--drivers/gpu/drm/xe/xe_tile.c14
-rw-r--r--drivers/gpu/drm/xe/xe_tile_debugfs.c19
-rw-r--r--drivers/gpu/drm/xe/xe_tile_debugfs.h3
-rw-r--r--drivers/gpu/drm/xe/xe_tile_sriov_pf_debugfs.c253
-rw-r--r--drivers/gpu/drm/xe/xe_tile_sriov_pf_debugfs.h15
-rw-r--r--drivers/gpu/drm/xe/xe_tile_sriov_printk.h33
-rw-r--r--drivers/gpu/drm/xe/xe_tile_sriov_vf.c112
-rw-r--r--drivers/gpu/drm/xe/xe_tile_sriov_vf.h9
-rw-r--r--drivers/gpu/drm/xe/xe_tile_sriov_vf_types.h23
-rw-r--r--drivers/gpu/drm/xe/xe_ttm_stolen_mgr.c8
-rw-r--r--drivers/gpu/drm/xe/xe_ttm_sys_mgr.c6
-rw-r--r--drivers/gpu/drm/xe/xe_ttm_vram_mgr.c6
-rw-r--r--drivers/gpu/drm/xe/xe_tuning.c26
-rw-r--r--drivers/gpu/drm/xe/xe_tuning.h2
-rw-r--r--drivers/gpu/drm/xe/xe_userptr.c4
-rw-r--r--drivers/gpu/drm/xe/xe_vm.c127
-rw-r--r--drivers/gpu/drm/xe/xe_vm_types.h10
-rw-r--r--drivers/gpu/drm/xe/xe_vram.c12
-rw-r--r--drivers/gpu/drm/xe/xe_wa.c27
-rw-r--r--drivers/gpu/drm/xe/xe_wa.h2
-rw-r--r--drivers/gpu/drm/xe/xe_wa_oob.rules17
-rw-r--r--include/drm/drm_gpusvm.h4
-rw-r--r--include/drm/intel/pciids.h16
-rw-r--r--include/uapi/drm/xe_drm.h21
148 files changed, 4704 insertions, 2102 deletions
diff --git a/Documentation/gpu/xe/index.rst b/Documentation/gpu/xe/index.rst
index 88b22fad880e..bc432c95d1a3 100644
--- a/Documentation/gpu/xe/index.rst
+++ b/Documentation/gpu/xe/index.rst
@@ -14,6 +14,7 @@ DG2, etc is provided to prototype the driver.
xe_mm
xe_map
xe_migrate
+ xe_exec_queue
xe_cs
xe_pm
xe_gt_freq
diff --git a/Documentation/gpu/xe/xe_exec_queue.rst b/Documentation/gpu/xe/xe_exec_queue.rst
new file mode 100644
index 000000000000..6076569e311c
--- /dev/null
+++ b/Documentation/gpu/xe/xe_exec_queue.rst
@@ -0,0 +1,20 @@
+.. SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+
+===============
+Execution Queue
+===============
+
+.. kernel-doc:: drivers/gpu/drm/xe/xe_exec_queue.c
+ :doc: Execution Queue
+
+Internal API
+============
+
+.. kernel-doc:: drivers/gpu/drm/xe/xe_exec_queue_types.h
+ :internal:
+
+.. kernel-doc:: drivers/gpu/drm/xe/xe_exec_queue.h
+ :internal:
+
+.. kernel-doc:: drivers/gpu/drm/xe/xe_exec_queue.c
+ :internal:
diff --git a/drivers/gpu/drm/drm_gpusvm.c b/drivers/gpu/drm/drm_gpusvm.c
index cb906765897e..73e550c8ff8c 100644
--- a/drivers/gpu/drm/drm_gpusvm.c
+++ b/drivers/gpu/drm/drm_gpusvm.c
@@ -1363,7 +1363,8 @@ map_pages:
order = drm_gpusvm_hmm_pfn_to_order(pfns[i], i, npages);
if (is_device_private_page(page) ||
is_device_coherent_page(page)) {
- if (zdd != page->zone_device_data && i > 0) {
+ if (!ctx->allow_mixed &&
+ zdd != page->zone_device_data && i > 0) {
err = -EOPNOTSUPP;
goto err_unmap;
}
@@ -1399,7 +1400,8 @@ map_pages:
} else {
dma_addr_t addr;
- if (is_zone_device_page(page) || pagemap) {
+ if (is_zone_device_page(page) ||
+ (pagemap && !ctx->allow_mixed)) {
err = -EOPNOTSUPP;
goto err_unmap;
}
diff --git a/drivers/gpu/drm/xe/Kconfig.debug b/drivers/gpu/drm/xe/Kconfig.debug
index 87902b4bd6d3..01227c77f6d7 100644
--- a/drivers/gpu/drm/xe/Kconfig.debug
+++ b/drivers/gpu/drm/xe/Kconfig.debug
@@ -40,23 +40,23 @@ config DRM_XE_DEBUG_VM
If in doubt, say "N".
-config DRM_XE_DEBUG_MEMIRQ
- bool "Enable extra memirq debugging"
+config DRM_XE_DEBUG_SRIOV
+ bool "Enable extra SR-IOV debugging"
default n
+ imply DRM_XE_DEBUG_MEMIRQ
help
- Choose this option to enable additional debugging info for
- memory based interrupts.
+ Enable extra SR-IOV debugging info.
Recommended for driver developers only.
If in doubt, say "N".
-config DRM_XE_DEBUG_SRIOV
- bool "Enable extra SR-IOV debugging"
+config DRM_XE_DEBUG_MEMIRQ
+ bool "Enable extra memirq debugging"
default n
- select DRM_XE_DEBUG_MEMIRQ
help
- Enable extra SR-IOV debugging info.
+ Choose this option to enable additional debugging info for
+ memory based interrupts.
Recommended for driver developers only.
diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile
index 5d6e80d541e0..82c6b3d29676 100644
--- a/drivers/gpu/drm/xe/Makefile
+++ b/drivers/gpu/drm/xe/Makefile
@@ -174,7 +174,11 @@ xe-$(CONFIG_PCI_IOV) += \
xe_lmtt_ml.o \
xe_pci_sriov.o \
xe_sriov_pf.o \
- xe_sriov_pf_service.o
+ xe_sriov_pf_control.o \
+ xe_sriov_pf_debugfs.o \
+ xe_sriov_pf_provision.o \
+ xe_sriov_pf_service.o \
+ xe_tile_sriov_pf_debugfs.o
# include helpers for tests even when XE is built-in
ifdef CONFIG_DRM_XE_KUNIT_TEST
diff --git a/drivers/gpu/drm/xe/abi/guc_actions_abi.h b/drivers/gpu/drm/xe/abi/guc_actions_abi.h
index 31090c69dfbe..47756e4674a1 100644
--- a/drivers/gpu/drm/xe/abi/guc_actions_abi.h
+++ b/drivers/gpu/drm/xe/abi/guc_actions_abi.h
@@ -196,14 +196,6 @@ enum xe_guc_register_context_multi_lrc_param_offsets {
XE_GUC_REGISTER_CONTEXT_MULTI_LRC_MSG_MIN_LEN = 11,
};
-enum xe_guc_context_wq_item_offsets {
- XE_GUC_CONTEXT_WQ_HEADER_DATA_0_TYPE_LEN = 0,
- XE_GUC_CONTEXT_WQ_EL_INFO_DATA_1_CTX_DESC_LOW,
- XE_GUC_CONTEXT_WQ_EL_INFO_DATA_2_GUCCTX_RINGTAIL_FREEZEPOCS,
- XE_GUC_CONTEXT_WQ_EL_INFO_DATA_3_WI_FENCE_ID,
- XE_GUC_CONTEXT_WQ_EL_CHILD_LIST_DATA_4_RINGTAIL,
-};
-
enum xe_guc_report_status {
XE_GUC_REPORT_STATUS_UNKNOWN = 0x0,
XE_GUC_REPORT_STATUS_ACKED = 0x1,
diff --git a/drivers/gpu/drm/xe/display/intel_fbdev_fb.c b/drivers/gpu/drm/xe/display/intel_fbdev_fb.c
index 35a5b07eeba4..af72f7305e5a 100644
--- a/drivers/gpu/drm/xe/display/intel_fbdev_fb.c
+++ b/drivers/gpu/drm/xe/display/intel_fbdev_fb.c
@@ -10,7 +10,7 @@
#include "xe_ttm_stolen_mgr.h"
#include "xe_wa.h"
-#include <generated/xe_wa_oob.h>
+#include <generated/xe_device_wa_oob.h>
struct drm_gem_object *intel_fbdev_fb_bo_create(struct drm_device *drm, int size)
{
@@ -19,7 +19,7 @@ struct drm_gem_object *intel_fbdev_fb_bo_create(struct drm_device *drm, int size
obj = ERR_PTR(-ENODEV);
- if (!IS_DGFX(xe) && !XE_GT_WA(xe_root_mmio_gt(xe), 22019338487_display)) {
+ if (!IS_DGFX(xe) && !XE_DEVICE_WA(xe, 22019338487_display)) {
obj = xe_bo_create_pin_map_novm(xe, xe_device_get_root_tile(xe),
size,
ttm_bo_type_kernel, XE_BO_FLAG_SCANOUT |
diff --git a/drivers/gpu/drm/xe/display/xe_display.c b/drivers/gpu/drm/xe/display/xe_display.c
index ba6411c48c17..5a02754d0610 100644
--- a/drivers/gpu/drm/xe/display/xe_display.c
+++ b/drivers/gpu/drm/xe/display/xe_display.c
@@ -227,15 +227,14 @@ void xe_display_irq_reset(struct xe_device *xe)
gen11_display_irq_reset(display);
}
-void xe_display_irq_postinstall(struct xe_device *xe, struct xe_gt *gt)
+void xe_display_irq_postinstall(struct xe_device *xe)
{
struct intel_display *display = xe->display;
if (!xe->info.probe_display)
return;
- if (gt->info.id == XE_GT0)
- gen11_de_irq_postinstall(display);
+ gen11_de_irq_postinstall(display);
}
static bool suspend_to_idle(void)
diff --git a/drivers/gpu/drm/xe/display/xe_display.h b/drivers/gpu/drm/xe/display/xe_display.h
index e533aa4750bc..76db95c25f7e 100644
--- a/drivers/gpu/drm/xe/display/xe_display.h
+++ b/drivers/gpu/drm/xe/display/xe_display.h
@@ -26,7 +26,7 @@ void xe_display_unregister(struct xe_device *xe);
void xe_display_irq_handler(struct xe_device *xe, u32 master_ctl);
void xe_display_irq_enable(struct xe_device *xe, u32 gu_misc_iir);
void xe_display_irq_reset(struct xe_device *xe);
-void xe_display_irq_postinstall(struct xe_device *xe, struct xe_gt *gt);
+void xe_display_irq_postinstall(struct xe_device *xe);
void xe_display_pm_suspend(struct xe_device *xe);
void xe_display_pm_shutdown(struct xe_device *xe);
@@ -55,7 +55,7 @@ static inline void xe_display_unregister(struct xe_device *xe) {}
static inline void xe_display_irq_handler(struct xe_device *xe, u32 master_ctl) {}
static inline void xe_display_irq_enable(struct xe_device *xe, u32 gu_misc_iir) {}
static inline void xe_display_irq_reset(struct xe_device *xe) {}
-static inline void xe_display_irq_postinstall(struct xe_device *xe, struct xe_gt *gt) {}
+static inline void xe_display_irq_postinstall(struct xe_device *xe) {}
static inline void xe_display_pm_suspend(struct xe_device *xe) {}
static inline void xe_display_pm_shutdown(struct xe_device *xe) {}
diff --git a/drivers/gpu/drm/xe/display/xe_display_wa.c b/drivers/gpu/drm/xe/display/xe_display_wa.c
index 8ada1cbcb16c..2aa1b8c03411 100644
--- a/drivers/gpu/drm/xe/display/xe_display_wa.c
+++ b/drivers/gpu/drm/xe/display/xe_display_wa.c
@@ -13,6 +13,7 @@
bool intel_display_needs_wa_16023588340(struct intel_display *display)
{
struct xe_device *xe = to_xe_device(display->drm);
+ struct xe_gt *wa_gt = xe_root_mmio_gt(xe);
- return XE_GT_WA(xe_root_mmio_gt(xe), 16023588340);
+ return wa_gt && XE_GT_WA(wa_gt, 16023588340);
}
diff --git a/drivers/gpu/drm/xe/display/xe_plane_initial.c b/drivers/gpu/drm/xe/display/xe_plane_initial.c
index 94f00def811b..12d25c5290fd 100644
--- a/drivers/gpu/drm/xe/display/xe_plane_initial.c
+++ b/drivers/gpu/drm/xe/display/xe_plane_initial.c
@@ -25,7 +25,7 @@
#include "xe_vram_types.h"
#include "xe_wa.h"
-#include <generated/xe_wa_oob.h>
+#include <generated/xe_device_wa_oob.h>
void intel_plane_initial_vblank_wait(struct intel_crtc *crtc)
{
@@ -123,7 +123,7 @@ initial_plane_bo(struct xe_device *xe,
phys_base = base;
flags |= XE_BO_FLAG_STOLEN;
- if (XE_GT_WA(xe_root_mmio_gt(xe), 22019338487_display))
+ if (XE_DEVICE_WA(xe, 22019338487_display))
return NULL;
/*
diff --git a/drivers/gpu/drm/xe/regs/xe_engine_regs.h b/drivers/gpu/drm/xe/regs/xe_engine_regs.h
index f4c3e1187a00..68172b0248a6 100644
--- a/drivers/gpu/drm/xe/regs/xe_engine_regs.h
+++ b/drivers/gpu/drm/xe/regs/xe_engine_regs.h
@@ -141,6 +141,8 @@
#define INHIBIT_SWITCH_UNTIL_PREEMPTED REG_BIT(31)
#define IDLE_DELAY REG_GENMASK(20, 0)
+#define RING_CURRENT_LRCA(base) XE_REG((base) + 0x240)
+
#define RING_CONTEXT_CONTROL(base) XE_REG((base) + 0x244, XE_REG_OPTION_MASKED)
#define CTX_CTRL_PXP_ENABLE REG_BIT(10)
#define CTX_CTRL_OAC_CONTEXT_ENABLE REG_BIT(8)
@@ -153,6 +155,8 @@
#define GFX_DISABLE_LEGACY_MODE REG_BIT(3)
#define GFX_MSIX_INTERRUPT_ENABLE REG_BIT(13)
+#define RING_CSMQDEBUG(base) XE_REG((base) + 0x2b0)
+
#define RING_TIMESTAMP(base) XE_REG((base) + 0x358)
#define RING_TIMESTAMP_UDW(base) XE_REG((base) + 0x358 + 4)
diff --git a/drivers/gpu/drm/xe/regs/xe_gt_regs.h b/drivers/gpu/drm/xe/regs/xe_gt_regs.h
index 51f2a03847f9..228de47c0f3f 100644
--- a/drivers/gpu/drm/xe/regs/xe_gt_regs.h
+++ b/drivers/gpu/drm/xe/regs/xe_gt_regs.h
@@ -239,6 +239,9 @@
#define XE2_GT_GEOMETRY_DSS_1 XE_REG(0x9150)
#define XE2_GT_GEOMETRY_DSS_2 XE_REG(0x9154)
+#define SERVICE_COPY_ENABLE XE_REG(0x9170)
+#define FUSE_SERVICE_COPY_ENABLE_MASK REG_GENMASK(7, 0)
+
#define GDRST XE_REG(0x941c)
#define GRDOM_GUC REG_BIT(3)
#define GRDOM_FULL REG_BIT(0)
@@ -346,10 +349,6 @@
#define VDN_HCP_POWERGATE_ENABLE(n) REG_BIT(3 + 2 * (n))
#define VDN_MFXVDENC_POWERGATE_ENABLE(n) REG_BIT(4 + 2 * (n))
-#define CTC_MODE XE_REG(0xa26c)
-#define CTC_SHIFT_PARAMETER_MASK REG_GENMASK(2, 1)
-#define CTC_SOURCE_DIVIDE_LOGIC REG_BIT(0)
-
#define FORCEWAKE_RENDER XE_REG(0xa278)
#define POWERGATE_DOMAIN_STATUS XE_REG(0xa2a0)
diff --git a/drivers/gpu/drm/xe/regs/xe_i2c_regs.h b/drivers/gpu/drm/xe/regs/xe_i2c_regs.h
index af781c8e4a80..f2e455e2bfe4 100644
--- a/drivers/gpu/drm/xe/regs/xe_i2c_regs.h
+++ b/drivers/gpu/drm/xe/regs/xe_i2c_regs.h
@@ -14,6 +14,9 @@
#define REG_SG_REMAP_ADDR_PREFIX XE_REG(SOC_BASE + 0x0164)
#define REG_SG_REMAP_ADDR_POSTFIX XE_REG(SOC_BASE + 0x0168)
+#define I2C_BRIDGE_PCICFGCTL XE_REG(I2C_BRIDGE_OFFSET + 0x200)
+#define ACPI_INTR_EN REG_BIT(1)
+
#define I2C_CONFIG_CMD XE_REG(I2C_CONFIG_SPACE_OFFSET + PCI_COMMAND)
#define I2C_CONFIG_PMCSR XE_REG(I2C_CONFIG_SPACE_OFFSET + 0x84)
diff --git a/drivers/gpu/drm/xe/regs/xe_irq_regs.h b/drivers/gpu/drm/xe/regs/xe_irq_regs.h
index 7c2a3a140142..2f97662d958d 100644
--- a/drivers/gpu/drm/xe/regs/xe_irq_regs.h
+++ b/drivers/gpu/drm/xe/regs/xe_irq_regs.h
@@ -65,7 +65,10 @@
#define BCS_RSVD_INTR_MASK XE_REG(0x1900a0, XE_REG_OPTION_VF)
#define VCS0_VCS1_INTR_MASK XE_REG(0x1900a8, XE_REG_OPTION_VF)
#define VCS2_VCS3_INTR_MASK XE_REG(0x1900ac, XE_REG_OPTION_VF)
+#define VCS4_VCS5_INTR_MASK XE_REG(0x1900b0, XE_REG_OPTION_VF)
+#define VCS6_VCS7_INTR_MASK XE_REG(0x1900b4, XE_REG_OPTION_VF)
#define VECS0_VECS1_INTR_MASK XE_REG(0x1900d0, XE_REG_OPTION_VF)
+#define VECS2_VECS3_INTR_MASK XE_REG(0x1900d4, XE_REG_OPTION_VF)
#define HECI2_RSVD_INTR_MASK XE_REG(0x1900e4)
#define GUC_SG_INTR_MASK XE_REG(0x1900e8, XE_REG_OPTION_VF)
#define GPM_WGBOXPERF_INTR_MASK XE_REG(0x1900ec, XE_REG_OPTION_VF)
@@ -80,9 +83,10 @@
#define GT_WAIT_SEMAPHORE_INTERRUPT REG_BIT(11)
#define GT_CONTEXT_SWITCH_INTERRUPT REG_BIT(8)
#define GSC_ER_COMPLETE REG_BIT(5)
-#define GT_RENDER_PIPECTL_NOTIFY_INTERRUPT REG_BIT(4)
+#define GT_FLUSH_COMPLETE_INTERRUPT REG_BIT(4)
#define GT_CS_MASTER_ERROR_INTERRUPT REG_BIT(3)
-#define GT_RENDER_USER_INTERRUPT REG_BIT(0)
+#define GT_COMPUTE_WALKER_INTERRUPT REG_BIT(2)
+#define GT_MI_USER_INTERRUPT REG_BIT(0)
/* irqs for OTHER_KCR_INSTANCE */
#define KCR_PXP_STATE_TERMINATED_INTERRUPT REG_BIT(1)
diff --git a/drivers/gpu/drm/xe/tests/xe_dma_buf.c b/drivers/gpu/drm/xe/tests/xe_dma_buf.c
index a7e548a2bdfb..5df98de5ba3c 100644
--- a/drivers/gpu/drm/xe/tests/xe_dma_buf.c
+++ b/drivers/gpu/drm/xe/tests/xe_dma_buf.c
@@ -31,6 +31,7 @@ static void check_residency(struct kunit *test, struct xe_bo *exported,
struct drm_exec *exec)
{
struct dma_buf_test_params *params = to_dma_buf_test_params(test->priv);
+ struct dma_buf_attachment *attach;
u32 mem_type;
int ret;
@@ -46,7 +47,7 @@ static void check_residency(struct kunit *test, struct xe_bo *exported,
mem_type = XE_PL_TT;
else if (params->force_different_devices && !is_dynamic(params) &&
(params->mem_mask & XE_BO_FLAG_SYSTEM))
- /* Pin migrated to TT */
+ /* Pin migrated to TT on non-dynamic attachments. */
mem_type = XE_PL_TT;
if (!xe_bo_is_mem_type(exported, mem_type)) {
@@ -88,6 +89,18 @@ static void check_residency(struct kunit *test, struct xe_bo *exported,
KUNIT_EXPECT_TRUE(test, xe_bo_is_mem_type(exported, mem_type));
+ /* Check that we can pin without migrating. */
+ attach = list_first_entry_or_null(&dmabuf->attachments, typeof(*attach), node);
+ if (attach) {
+ int err = dma_buf_pin(attach);
+
+ if (!err) {
+ KUNIT_EXPECT_TRUE(test, xe_bo_is_mem_type(exported, mem_type));
+ dma_buf_unpin(attach);
+ }
+ KUNIT_EXPECT_EQ(test, err, 0);
+ }
+
if (params->force_different_devices)
KUNIT_EXPECT_TRUE(test, xe_bo_is_mem_type(imported, XE_PL_TT));
else
@@ -150,7 +163,7 @@ static void xe_test_dmabuf_import_same_driver(struct xe_device *xe)
xe_bo_lock(import_bo, false);
err = xe_bo_validate(import_bo, NULL, false, exec);
- /* Pinning in VRAM is not allowed. */
+ /* Pinning in VRAM is not allowed for non-dynamic attachments */
if (!is_dynamic(params) &&
params->force_different_devices &&
!(params->mem_mask & XE_BO_FLAG_SYSTEM))
diff --git a/drivers/gpu/drm/xe/tests/xe_pci.c b/drivers/gpu/drm/xe/tests/xe_pci.c
index 663a79ec960d..f3179b31f13e 100644
--- a/drivers/gpu/drm/xe/tests/xe_pci.c
+++ b/drivers/gpu/drm/xe/tests/xe_pci.c
@@ -311,8 +311,8 @@ const void *xe_pci_id_gen_param(struct kunit *test, const void *prev, char *desc
}
EXPORT_SYMBOL_IF_KUNIT(xe_pci_id_gen_param);
-static void fake_read_gmdid(struct xe_device *xe, enum xe_gmdid_type type,
- u32 *ver, u32 *revid)
+static int fake_read_gmdid(struct xe_device *xe, enum xe_gmdid_type type,
+ u32 *ver, u32 *revid)
{
struct kunit *test = kunit_get_current_test();
struct xe_pci_fake_data *data = test->priv;
@@ -324,6 +324,8 @@ static void fake_read_gmdid(struct xe_device *xe, enum xe_gmdid_type type,
*ver = data->graphics_verx100;
*revid = xe_step_to_gmdid(data->step.graphics);
}
+
+ return 0;
}
static void fake_xe_info_probe_tile_count(struct xe_device *xe)
diff --git a/drivers/gpu/drm/xe/tests/xe_rtp_test.c b/drivers/gpu/drm/xe/tests/xe_rtp_test.c
index b0254b014fe4..d2255a59e58f 100644
--- a/drivers/gpu/drm/xe/tests/xe_rtp_test.c
+++ b/drivers/gpu/drm/xe/tests/xe_rtp_test.c
@@ -48,12 +48,14 @@ struct rtp_test_case {
const struct xe_rtp_entry *entries;
};
-static bool match_yes(const struct xe_gt *gt, const struct xe_hw_engine *hwe)
+static bool match_yes(const struct xe_device *xe, const struct xe_gt *gt,
+ const struct xe_hw_engine *hwe)
{
return true;
}
-static bool match_no(const struct xe_gt *gt, const struct xe_hw_engine *hwe)
+static bool match_no(const struct xe_device *xe, const struct xe_gt *gt,
+ const struct xe_hw_engine *hwe)
{
return false;
}
diff --git a/drivers/gpu/drm/xe/xe_bo.c b/drivers/gpu/drm/xe/xe_bo.c
index fbe81cda15c2..7b6502081873 100644
--- a/drivers/gpu/drm/xe/xe_bo.c
+++ b/drivers/gpu/drm/xe/xe_bo.c
@@ -35,6 +35,7 @@
#include "xe_res_cursor.h"
#include "xe_shrinker.h"
#include "xe_sriov_vf_ccs.h"
+#include "xe_tile.h"
#include "xe_trace_bo.h"
#include "xe_ttm_stolen_mgr.h"
#include "xe_vm.h"
@@ -82,6 +83,10 @@ static struct ttm_placement tt_placement = {
.placement = tt_placement_flags,
};
+#define for_each_set_bo_vram_flag(bit__, bo_flags__) \
+ for (unsigned int __bit_tmp = BIT(0); __bit_tmp <= XE_BO_FLAG_VRAM_MASK; __bit_tmp <<= 1) \
+ for_each_if(((bit__) = __bit_tmp) & (bo_flags__) & XE_BO_FLAG_VRAM_MASK)
+
bool mem_type_is_vram(u32 mem_type)
{
return mem_type >= XE_PL_VRAM0 && mem_type != XE_PL_STOLEN;
@@ -214,6 +219,27 @@ static bool force_contiguous(u32 bo_flags)
bo_flags & XE_BO_FLAG_PINNED;
}
+static u8 vram_bo_flag_to_tile_id(struct xe_device *xe, u32 vram_bo_flag)
+{
+ xe_assert(xe, vram_bo_flag & XE_BO_FLAG_VRAM_MASK);
+ xe_assert(xe, (vram_bo_flag & (vram_bo_flag - 1)) == 0);
+
+ return __ffs(vram_bo_flag >> (__ffs(XE_BO_FLAG_VRAM0) - 1)) - 1;
+}
+
+static u32 bo_vram_flags_to_vram_placement(struct xe_device *xe, u32 bo_flags, u32 vram_flag,
+ enum ttm_bo_type type)
+{
+ u8 tile_id = vram_bo_flag_to_tile_id(xe, vram_flag);
+
+ xe_assert(xe, tile_id < xe->info.tile_count);
+
+ if (type == ttm_bo_type_kernel && !(bo_flags & XE_BO_FLAG_FORCE_USER_VRAM))
+ return xe->tiles[tile_id].mem.kernel_vram->placement;
+ else
+ return xe->tiles[tile_id].mem.vram->placement;
+}
+
static void add_vram(struct xe_device *xe, struct xe_bo *bo,
struct ttm_place *places, u32 bo_flags, u32 mem_type, u32 *c)
{
@@ -246,12 +272,15 @@ static void add_vram(struct xe_device *xe, struct xe_bo *bo,
}
static void try_add_vram(struct xe_device *xe, struct xe_bo *bo,
- u32 bo_flags, u32 *c)
+ u32 bo_flags, enum ttm_bo_type type, u32 *c)
{
- if (bo_flags & XE_BO_FLAG_VRAM0)
- add_vram(xe, bo, bo->placements, bo_flags, XE_PL_VRAM0, c);
- if (bo_flags & XE_BO_FLAG_VRAM1)
- add_vram(xe, bo, bo->placements, bo_flags, XE_PL_VRAM1, c);
+ u32 vram_flag;
+
+ for_each_set_bo_vram_flag(vram_flag, bo_flags) {
+ u32 pl = bo_vram_flags_to_vram_placement(xe, bo_flags, vram_flag, type);
+
+ add_vram(xe, bo, bo->placements, bo_flags, pl, c);
+ }
}
static void try_add_stolen(struct xe_device *xe, struct xe_bo *bo,
@@ -270,11 +299,11 @@ static void try_add_stolen(struct xe_device *xe, struct xe_bo *bo,
}
static int __xe_bo_placement_for_flags(struct xe_device *xe, struct xe_bo *bo,
- u32 bo_flags)
+ u32 bo_flags, enum ttm_bo_type type)
{
u32 c = 0;
- try_add_vram(xe, bo, bo_flags, &c);
+ try_add_vram(xe, bo, bo_flags, type, &c);
try_add_system(xe, bo, bo_flags, &c);
try_add_stolen(xe, bo, bo_flags, &c);
@@ -290,10 +319,10 @@ static int __xe_bo_placement_for_flags(struct xe_device *xe, struct xe_bo *bo,
}
int xe_bo_placement_for_flags(struct xe_device *xe, struct xe_bo *bo,
- u32 bo_flags)
+ u32 bo_flags, enum ttm_bo_type type)
{
xe_bo_assert_held(bo);
- return __xe_bo_placement_for_flags(xe, bo, bo_flags);
+ return __xe_bo_placement_for_flags(xe, bo, bo_flags, type);
}
static void xe_evict_flags(struct ttm_buffer_object *tbo,
@@ -2165,7 +2194,7 @@ struct xe_bo *xe_bo_init_locked(struct xe_device *xe, struct xe_bo *bo,
xe_validation_assert_exec(xe, exec, &bo->ttm.base);
if (!(flags & XE_BO_FLAG_FIXED_PLACEMENT)) {
- err = __xe_bo_placement_for_flags(xe, bo, bo->flags);
+ err = __xe_bo_placement_for_flags(xe, bo, bo->flags, type);
if (WARN_ON(err)) {
xe_ttm_bo_destroy(&bo->ttm);
return ERR_PTR(err);
@@ -2223,34 +2252,31 @@ struct xe_bo *xe_bo_init_locked(struct xe_device *xe, struct xe_bo *bo,
}
static int __xe_bo_fixed_placement(struct xe_device *xe,
- struct xe_bo *bo,
+ struct xe_bo *bo, enum ttm_bo_type type,
u32 flags,
u64 start, u64 end, u64 size)
{
struct ttm_place *place = bo->placements;
+ u32 vram_flag, vram_stolen_flags;
if (flags & (XE_BO_FLAG_USER | XE_BO_FLAG_SYSTEM))
return -EINVAL;
+ vram_flag = flags & XE_BO_FLAG_VRAM_MASK;
+ vram_stolen_flags = (flags & (XE_BO_FLAG_STOLEN)) | vram_flag;
+
+ /* check if more than one VRAM/STOLEN flag is set */
+ if (hweight32(vram_stolen_flags) > 1)
+ return -EINVAL;
+
place->flags = TTM_PL_FLAG_CONTIGUOUS;
place->fpfn = start >> PAGE_SHIFT;
place->lpfn = end >> PAGE_SHIFT;
- switch (flags & (XE_BO_FLAG_STOLEN | XE_BO_FLAG_VRAM_MASK)) {
- case XE_BO_FLAG_VRAM0:
- place->mem_type = XE_PL_VRAM0;
- break;
- case XE_BO_FLAG_VRAM1:
- place->mem_type = XE_PL_VRAM1;
- break;
- case XE_BO_FLAG_STOLEN:
+ if (flags & XE_BO_FLAG_STOLEN)
place->mem_type = XE_PL_STOLEN;
- break;
-
- default:
- /* 0 or multiple of the above set */
- return -EINVAL;
- }
+ else
+ place->mem_type = bo_vram_flags_to_vram_placement(xe, flags, vram_flag, type);
bo->placement = (struct ttm_placement) {
.num_placement = 1,
@@ -2279,7 +2305,7 @@ __xe_bo_create_locked(struct xe_device *xe,
return bo;
flags |= XE_BO_FLAG_FIXED_PLACEMENT;
- err = __xe_bo_fixed_placement(xe, bo, flags, start, end, size);
+ err = __xe_bo_fixed_placement(xe, bo, type, flags, start, end, size);
if (err) {
xe_bo_free(bo);
return ERR_PTR(err);
diff --git a/drivers/gpu/drm/xe/xe_bo.h b/drivers/gpu/drm/xe/xe_bo.h
index a77af42b5f9e..353d607d301d 100644
--- a/drivers/gpu/drm/xe/xe_bo.h
+++ b/drivers/gpu/drm/xe/xe_bo.h
@@ -49,6 +49,7 @@
#define XE_BO_FLAG_GGTT2 BIT(22)
#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)
/* this one is trigger internally only */
#define XE_BO_FLAG_INTERNAL_TEST BIT(30)
@@ -122,7 +123,7 @@ struct xe_bo *xe_managed_bo_create_from_data(struct xe_device *xe, struct xe_til
int xe_managed_bo_reinit_in_vram(struct xe_device *xe, struct xe_tile *tile, struct xe_bo **src);
int xe_bo_placement_for_flags(struct xe_device *xe, struct xe_bo *bo,
- u32 bo_flags);
+ u32 bo_flags, enum ttm_bo_type type);
static inline struct xe_bo *ttm_to_xe_bo(const struct ttm_buffer_object *bo)
{
diff --git a/drivers/gpu/drm/xe/xe_bo_evict.c b/drivers/gpu/drm/xe/xe_bo_evict.c
index bc5b4c5fab81..7661fca7f278 100644
--- a/drivers/gpu/drm/xe/xe_bo_evict.c
+++ b/drivers/gpu/drm/xe/xe_bo_evict.c
@@ -73,6 +73,11 @@ int xe_bo_notifier_prepare_all_pinned(struct xe_device *xe)
&xe->pinned.late.kernel_bo_present,
xe_bo_notifier_prepare_pinned);
+ if (!ret)
+ ret = xe_bo_apply_to_pinned(xe, &xe->pinned.late.external,
+ &xe->pinned.late.external,
+ xe_bo_notifier_prepare_pinned);
+
return ret;
}
@@ -93,6 +98,10 @@ void xe_bo_notifier_unprepare_all_pinned(struct xe_device *xe)
(void)xe_bo_apply_to_pinned(xe, &xe->pinned.late.kernel_bo_present,
&xe->pinned.late.kernel_bo_present,
xe_bo_notifier_unprepare_pinned);
+
+ (void)xe_bo_apply_to_pinned(xe, &xe->pinned.late.external,
+ &xe->pinned.late.external,
+ xe_bo_notifier_unprepare_pinned);
}
/**
diff --git a/drivers/gpu/drm/xe/xe_configfs.c b/drivers/gpu/drm/xe/xe_configfs.c
index 139663423185..c1419a270fa4 100644
--- a/drivers/gpu/drm/xe/xe_configfs.c
+++ b/drivers/gpu/drm/xe/xe_configfs.c
@@ -15,9 +15,11 @@
#include "instructions/xe_mi_commands.h"
#include "xe_configfs.h"
+#include "xe_gt_types.h"
#include "xe_hw_engine_types.h"
#include "xe_module.h"
#include "xe_pci_types.h"
+#include "xe_sriov_types.h"
/**
* DOC: Xe Configfs
@@ -56,6 +58,7 @@
* :
* └── 0000:03:00.0
* ├── survivability_mode
+ * ├── gt_types_allowed
* ├── engines_allowed
* └── enable_psmi
*
@@ -79,6 +82,44 @@
*
* This attribute can only be set before binding to the device.
*
+ * Allowed GT types:
+ * -----------------
+ *
+ * Allow only specific types of GTs to be detected and initialized by the
+ * driver. Any combination of GT types can be enabled/disabled, although
+ * some settings will cause the device to fail to probe.
+ *
+ * Writes support both comma- and newline-separated input format. Reads
+ * will always return one GT type per line. "primary" and "media" are the
+ * GT type names supported by this interface.
+ *
+ * This attribute can only be set before binding to the device.
+ *
+ * Examples:
+ *
+ * Allow both primary and media GTs to be initialized and used. This matches
+ * the driver's default behavior::
+ *
+ * # echo 'primary,media' > /sys/kernel/config/xe/0000:03:00.0/gt_types_allowed
+ *
+ * Allow only the primary GT of each tile to be initialized and used,
+ * effectively disabling the media GT if it exists on the platform::
+ *
+ * # echo 'primary' > /sys/kernel/config/xe/0000:03:00.0/gt_types_allowed
+ *
+ * Allow only the media GT of each tile to be initialized and used,
+ * effectively disabling the primary GT. **This configuration will cause
+ * device probe failure on all current platforms, but may be allowed on
+ * igpu platforms in the future**::
+ *
+ * # echo 'media' > /sys/kernel/config/xe/0000:03:00.0/gt_types_allowed
+ *
+ * Disable all GTs. Only other GPU IP (such as display) is potentially usable.
+ * **This configuration will cause device probe failure on all current
+ * platforms, but may be allowed on igpu platforms in the future**::
+ *
+ * # echo '' > /sys/kernel/config/xe/0000:03:00.0/gt_types_allowed
+ *
* Allowed engines:
* ----------------
*
@@ -169,6 +210,32 @@
* Currently this is implemented only for post and mid context restore and
* these attributes can only be set before binding to the device.
*
+ * Max SR-IOV Virtual Functions
+ * ----------------------------
+ *
+ * This config allows to limit number of the Virtual Functions (VFs) that can
+ * be managed by the Physical Function (PF) driver, where value 0 disables the
+ * PF mode (no VFs).
+ *
+ * The default max_vfs config value is taken from the max_vfs modparam.
+ *
+ * How to enable PF with support with unlimited (up to HW limit) number of VFs::
+ *
+ * # echo unlimited > /sys/kernel/config/xe/0000:00:02.0/sriov/max_vfs
+ * # echo 0000:00:02.0 > /sys/bus/pci/drivers/xe/bind
+ *
+ * How to enable PF with support up to 3 VFs::
+ *
+ * # echo 3 > /sys/kernel/config/xe/0000:00:02.0/sriov/max_vfs
+ * # echo 0000:00:02.0 > /sys/bus/pci/drivers/xe/bind
+ *
+ * How to disable PF mode and always run as native::
+ *
+ * # echo 0 > /sys/kernel/config/xe/0000:00:02.0/sriov/max_vfs
+ * # echo 0000:00:02.0 > /sys/bus/pci/drivers/xe/bind
+ *
+ * This setting only takes effect when probing the device.
+ *
* Remove devices
* ==============
*
@@ -185,30 +252,44 @@ struct wa_bb {
struct xe_config_group_device {
struct config_group group;
+ struct config_group sriov;
struct xe_config_device {
+ u64 gt_types_allowed;
u64 engines_allowed;
struct wa_bb ctx_restore_post_bb[XE_ENGINE_CLASS_MAX];
struct wa_bb ctx_restore_mid_bb[XE_ENGINE_CLASS_MAX];
bool survivability_mode;
bool enable_psmi;
+ struct {
+ unsigned int max_vfs;
+ } sriov;
} config;
/* protects attributes */
struct mutex lock;
/* matching descriptor */
const struct xe_device_desc *desc;
+ /* tentative SR-IOV mode */
+ enum xe_sriov_mode mode;
};
static const struct xe_config_device device_defaults = {
+ .gt_types_allowed = U64_MAX,
.engines_allowed = U64_MAX,
.survivability_mode = false,
.enable_psmi = false,
+ .sriov = {
+ .max_vfs = UINT_MAX,
+ },
};
static void set_device_defaults(struct xe_config_device *config)
{
*config = device_defaults;
+#ifdef CONFIG_PCI_IOV
+ config->sriov.max_vfs = xe_modparam.max_vfs;
+#endif
}
struct engine_info {
@@ -220,6 +301,7 @@ struct engine_info {
/* Some helpful macros to aid on the sizing of buffer allocation when parsing */
#define MAX_ENGINE_CLASS_CHARS 5
#define MAX_ENGINE_INSTANCE_CHARS 2
+#define MAX_GT_TYPE_CHARS 7
static const struct engine_info engine_info[] = {
{ .cls = "rcs", .mask = XE_HW_ENGINE_RCS_MASK, .engine_class = XE_ENGINE_CLASS_RENDER },
@@ -230,6 +312,14 @@ static const struct engine_info engine_info[] = {
{ .cls = "gsccs", .mask = XE_HW_ENGINE_GSCCS_MASK, .engine_class = XE_ENGINE_CLASS_OTHER },
};
+static const struct {
+ const char name[MAX_GT_TYPE_CHARS + 1];
+ enum xe_gt_type type;
+} gt_types[] = {
+ { .name = "primary", .type = XE_GT_TYPE_MAIN },
+ { .name = "media", .type = XE_GT_TYPE_MEDIA },
+};
+
static struct xe_config_group_device *to_xe_config_group_device(struct config_item *item)
{
return container_of(to_config_group(item), struct xe_config_group_device, group);
@@ -292,6 +382,57 @@ static ssize_t survivability_mode_store(struct config_item *item, const char *pa
return len;
}
+static ssize_t gt_types_allowed_show(struct config_item *item, char *page)
+{
+ struct xe_config_device *dev = to_xe_config_device(item);
+ char *p = page;
+
+ for (size_t i = 0; i < ARRAY_SIZE(gt_types); i++)
+ if (dev->gt_types_allowed & BIT_ULL(gt_types[i].type))
+ p += sprintf(p, "%s\n", gt_types[i].name);
+
+ return p - page;
+}
+
+static ssize_t gt_types_allowed_store(struct config_item *item, const char *page,
+ size_t len)
+{
+ struct xe_config_group_device *dev = to_xe_config_group_device(item);
+ char *buf __free(kfree) = kstrdup(page, GFP_KERNEL);
+ char *p = buf;
+ u64 typemask = 0;
+
+ if (!buf)
+ return -ENOMEM;
+
+ while (p) {
+ char *typename = strsep(&p, ",\n");
+ bool matched = false;
+
+ if (typename[0] == '\0')
+ continue;
+
+ for (size_t i = 0; i < ARRAY_SIZE(gt_types); i++) {
+ if (strcmp(typename, gt_types[i].name) == 0) {
+ typemask |= BIT(gt_types[i].type);
+ matched = true;
+ break;
+ }
+ }
+
+ if (!matched)
+ return -EINVAL;
+ }
+
+ guard(mutex)(&dev->lock);
+ if (is_bound(dev))
+ return -EBUSY;
+
+ dev->config.gt_types_allowed = typemask;
+
+ return len;
+}
+
static ssize_t engines_allowed_show(struct config_item *item, char *page)
{
struct xe_config_device *dev = to_xe_config_device(item);
@@ -672,6 +813,7 @@ CONFIGFS_ATTR(, ctx_restore_mid_bb);
CONFIGFS_ATTR(, ctx_restore_post_bb);
CONFIGFS_ATTR(, enable_psmi);
CONFIGFS_ATTR(, engines_allowed);
+CONFIGFS_ATTR(, gt_types_allowed);
CONFIGFS_ATTR(, survivability_mode);
static struct configfs_attribute *xe_config_device_attrs[] = {
@@ -679,6 +821,7 @@ static struct configfs_attribute *xe_config_device_attrs[] = {
&attr_ctx_restore_post_bb,
&attr_enable_psmi,
&attr_engines_allowed,
+ &attr_gt_types_allowed,
&attr_survivability_mode,
NULL,
};
@@ -721,6 +864,68 @@ static const struct config_item_type xe_config_device_type = {
.ct_owner = THIS_MODULE,
};
+static ssize_t sriov_max_vfs_show(struct config_item *item, char *page)
+{
+ struct xe_config_group_device *dev = to_xe_config_group_device(item->ci_parent);
+
+ guard(mutex)(&dev->lock);
+
+ if (dev->config.sriov.max_vfs == UINT_MAX)
+ return sprintf(page, "%s\n", "unlimited");
+ else
+ return sprintf(page, "%u\n", dev->config.sriov.max_vfs);
+}
+
+static ssize_t sriov_max_vfs_store(struct config_item *item, const char *page, size_t len)
+{
+ struct xe_config_group_device *dev = to_xe_config_group_device(item->ci_parent);
+ unsigned int max_vfs;
+ int ret;
+
+ guard(mutex)(&dev->lock);
+
+ if (is_bound(dev))
+ return -EBUSY;
+
+ ret = kstrtouint(page, 0, &max_vfs);
+ if (ret) {
+ if (!sysfs_streq(page, "unlimited"))
+ return ret;
+ max_vfs = UINT_MAX;
+ }
+
+ dev->config.sriov.max_vfs = max_vfs;
+ return len;
+}
+
+CONFIGFS_ATTR(sriov_, max_vfs);
+
+static struct configfs_attribute *xe_config_sriov_attrs[] = {
+ &sriov_attr_max_vfs,
+ NULL,
+};
+
+static bool xe_config_sriov_is_visible(struct config_item *item,
+ struct configfs_attribute *attr, int n)
+{
+ struct xe_config_group_device *dev = to_xe_config_group_device(item->ci_parent);
+
+ if (attr == &sriov_attr_max_vfs && dev->mode != XE_SRIOV_MODE_PF)
+ return false;
+
+ return true;
+}
+
+static struct configfs_group_operations xe_config_sriov_group_ops = {
+ .is_visible = xe_config_sriov_is_visible,
+};
+
+static const struct config_item_type xe_config_sriov_type = {
+ .ct_owner = THIS_MODULE,
+ .ct_group_ops = &xe_config_sriov_group_ops,
+ .ct_attrs = xe_config_sriov_attrs,
+};
+
static const struct xe_device_desc *xe_match_desc(struct pci_dev *pdev)
{
struct device_driver *driver = driver_find("xe", &pci_bus_type);
@@ -746,6 +951,7 @@ static struct config_group *xe_config_make_device_group(struct config_group *gro
unsigned int domain, bus, slot, function;
struct xe_config_group_device *dev;
const struct xe_device_desc *match;
+ enum xe_sriov_mode mode;
struct pci_dev *pdev;
char canonical[16];
int vfnumber = 0;
@@ -762,6 +968,9 @@ static struct config_group *xe_config_make_device_group(struct config_group *gro
return ERR_PTR(-EINVAL);
pdev = pci_get_domain_bus_and_slot(domain, bus, PCI_DEVFN(slot, function));
+ mode = pdev ? dev_is_pf(&pdev->dev) ?
+ XE_SRIOV_MODE_PF : XE_SRIOV_MODE_NONE : XE_SRIOV_MODE_VF;
+
if (!pdev && function)
pdev = pci_get_domain_bus_and_slot(domain, bus, PCI_DEVFN(slot, 0));
if (!pdev && slot)
@@ -796,9 +1005,15 @@ static struct config_group *xe_config_make_device_group(struct config_group *gro
return ERR_PTR(-ENOMEM);
dev->desc = match;
+ dev->mode = match->has_sriov ? mode : XE_SRIOV_MODE_NONE;
+
set_device_defaults(&dev->config);
config_group_init_type_name(&dev->group, name, &xe_config_device_type);
+ if (dev->mode != XE_SRIOV_MODE_NONE) {
+ config_group_init_type_name(&dev->sriov, "sriov", &xe_config_sriov_type);
+ configfs_add_default_group(&dev->sriov, &dev->group);
+ }
mutex_init(&dev->lock);
@@ -846,6 +1061,7 @@ static void dump_custom_dev_config(struct pci_dev *pdev,
dev->config.attr_); \
} while (0)
+ PRI_CUSTOM_ATTR("%llx", gt_types_allowed);
PRI_CUSTOM_ATTR("%llx", engines_allowed);
PRI_CUSTOM_ATTR("%d", enable_psmi);
PRI_CUSTOM_ATTR("%d", survivability_mode);
@@ -896,6 +1112,44 @@ bool xe_configfs_get_survivability_mode(struct pci_dev *pdev)
return mode;
}
+static u64 get_gt_types_allowed(struct pci_dev *pdev)
+{
+ struct xe_config_group_device *dev = find_xe_config_group_device(pdev);
+ u64 mask;
+
+ if (!dev)
+ return device_defaults.gt_types_allowed;
+
+ mask = dev->config.gt_types_allowed;
+ config_group_put(&dev->group);
+
+ return mask;
+}
+
+/**
+ * xe_configfs_primary_gt_allowed - determine whether primary GTs are supported
+ * @pdev: pci device
+ *
+ * Return: True if primary GTs are enabled, false if they have been disabled via
+ * configfs.
+ */
+bool xe_configfs_primary_gt_allowed(struct pci_dev *pdev)
+{
+ return get_gt_types_allowed(pdev) & BIT_ULL(XE_GT_TYPE_MAIN);
+}
+
+/**
+ * xe_configfs_media_gt_allowed - determine whether media GTs are supported
+ * @pdev: pci device
+ *
+ * Return: True if the media GTs are enabled, false if they have been disabled
+ * via configfs.
+ */
+bool xe_configfs_media_gt_allowed(struct pci_dev *pdev)
+{
+ return get_gt_types_allowed(pdev) & BIT_ULL(XE_GT_TYPE_MEDIA);
+}
+
/**
* xe_configfs_get_engines_allowed - get engine allowed mask from configfs
* @pdev: pci device
@@ -988,6 +1242,34 @@ u32 xe_configfs_get_ctx_restore_post_bb(struct pci_dev *pdev,
return len;
}
+#ifdef CONFIG_PCI_IOV
+/**
+ * xe_configfs_get_max_vfs() - Get number of VFs that could be managed
+ * @pdev: the &pci_dev device
+ *
+ * Find the configfs group that belongs to the PCI device and return maximum
+ * number of Virtual Functions (VFs) that could be managed by this device.
+ * If configfs group is not present, use value of max_vfs module parameter.
+ *
+ * Return: maximum number of VFs that could be managed.
+ */
+unsigned int xe_configfs_get_max_vfs(struct pci_dev *pdev)
+{
+ struct xe_config_group_device *dev = find_xe_config_group_device(pdev);
+ unsigned int max_vfs;
+
+ if (!dev)
+ return xe_modparam.max_vfs;
+
+ scoped_guard(mutex, &dev->lock)
+ max_vfs = dev->config.sriov.max_vfs;
+
+ config_group_put(&dev->group);
+
+ return max_vfs;
+}
+#endif
+
int __init xe_configfs_init(void)
{
int ret;
diff --git a/drivers/gpu/drm/xe/xe_configfs.h b/drivers/gpu/drm/xe/xe_configfs.h
index c61e0e47ed94..fed57be0b90e 100644
--- a/drivers/gpu/drm/xe/xe_configfs.h
+++ b/drivers/gpu/drm/xe/xe_configfs.h
@@ -17,23 +17,31 @@ int xe_configfs_init(void);
void xe_configfs_exit(void);
void xe_configfs_check_device(struct pci_dev *pdev);
bool xe_configfs_get_survivability_mode(struct pci_dev *pdev);
+bool xe_configfs_primary_gt_allowed(struct pci_dev *pdev);
+bool xe_configfs_media_gt_allowed(struct pci_dev *pdev);
u64 xe_configfs_get_engines_allowed(struct pci_dev *pdev);
bool xe_configfs_get_psmi_enabled(struct pci_dev *pdev);
u32 xe_configfs_get_ctx_restore_mid_bb(struct pci_dev *pdev, enum xe_engine_class,
const u32 **cs);
u32 xe_configfs_get_ctx_restore_post_bb(struct pci_dev *pdev, enum xe_engine_class,
const u32 **cs);
+#ifdef CONFIG_PCI_IOV
+unsigned int xe_configfs_get_max_vfs(struct pci_dev *pdev);
+#endif
#else
static inline int xe_configfs_init(void) { return 0; }
static inline void xe_configfs_exit(void) { }
static inline void xe_configfs_check_device(struct pci_dev *pdev) { }
static inline bool xe_configfs_get_survivability_mode(struct pci_dev *pdev) { return false; }
+static inline bool xe_configfs_primary_gt_allowed(struct pci_dev *pdev) { return true; }
+static inline bool xe_configfs_media_gt_allowed(struct pci_dev *pdev) { return true; }
static inline u64 xe_configfs_get_engines_allowed(struct pci_dev *pdev) { return U64_MAX; }
static inline bool xe_configfs_get_psmi_enabled(struct pci_dev *pdev) { return false; }
static inline u32 xe_configfs_get_ctx_restore_mid_bb(struct pci_dev *pdev, enum xe_engine_class,
const u32 **cs) { return 0; }
static inline u32 xe_configfs_get_ctx_restore_post_bb(struct pci_dev *pdev, enum xe_engine_class,
const u32 **cs) { return 0; }
+static inline unsigned int xe_configfs_get_max_vfs(struct pci_dev *pdev) { return UINT_MAX; }
#endif
#endif
diff --git a/drivers/gpu/drm/xe/xe_debugfs.c b/drivers/gpu/drm/xe/xe_debugfs.c
index cd977dbd1ef6..1c3c9557a9bd 100644
--- a/drivers/gpu/drm/xe/xe_debugfs.c
+++ b/drivers/gpu/drm/xe/xe_debugfs.c
@@ -23,12 +23,12 @@
#include "xe_psmi.h"
#include "xe_pxp_debugfs.h"
#include "xe_sriov.h"
-#include "xe_sriov_pf.h"
+#include "xe_sriov_pf_debugfs.h"
#include "xe_sriov_vf.h"
#include "xe_step.h"
#include "xe_tile_debugfs.h"
-#include "xe_wa.h"
#include "xe_vsec.h"
+#include "xe_wa.h"
#ifdef CONFIG_DRM_XE_DEBUG
#include "xe_bo_evict.h"
@@ -349,17 +349,14 @@ static ssize_t disable_late_binding_set(struct file *f, const char __user *ubuf,
{
struct xe_device *xe = file_inode(f)->i_private;
struct xe_late_bind *late_bind = &xe->late_bind;
- u32 uval;
- ssize_t ret;
+ bool val;
+ int ret;
- ret = kstrtouint_from_user(ubuf, size, sizeof(uval), &uval);
+ ret = kstrtobool_from_user(ubuf, size, &val);
if (ret)
return ret;
- if (uval > 1)
- return -EINVAL;
-
- late_bind->disable = !!uval;
+ late_bind->disable = val;
return size;
}
diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c
index 34d33965eac2..5f6a412b571c 100644
--- a/drivers/gpu/drm/xe/xe_device.c
+++ b/drivers/gpu/drm/xe/xe_device.c
@@ -8,6 +8,7 @@
#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>
@@ -630,16 +631,22 @@ mask_err:
return err;
}
-static bool verify_lmem_ready(struct xe_device *xe)
+static int lmem_initializing(struct xe_device *xe)
{
- u32 val = xe_mmio_read32(xe_root_tile_mmio(xe), GU_CNTL) & LMEM_INIT;
+ if (xe_mmio_read32(xe_root_tile_mmio(xe), GU_CNTL) & LMEM_INIT)
+ return 0;
+
+ if (signal_pending(current))
+ return -EINTR;
- return !!val;
+ return 1;
}
static int wait_for_lmem_ready(struct xe_device *xe)
{
- unsigned long timeout, start;
+ const unsigned long TIMEOUT_SEC = 60;
+ unsigned long prev_jiffies;
+ int initializing;
if (!IS_DGFX(xe))
return 0;
@@ -647,39 +654,35 @@ static int wait_for_lmem_ready(struct xe_device *xe)
if (IS_SRIOV_VF(xe))
return 0;
- if (verify_lmem_ready(xe))
+ if (!lmem_initializing(xe))
return 0;
drm_dbg(&xe->drm, "Waiting for lmem initialization\n");
+ prev_jiffies = jiffies;
- start = jiffies;
- timeout = start + secs_to_jiffies(60); /* 60 sec! */
-
- do {
- if (signal_pending(current))
- return -EINTR;
-
- /*
- * 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.
- */
- if (time_after(jiffies, timeout)) {
- drm_dbg(&xe->drm, "lmem not initialized by firmware\n");
- return -EPROBE_DEFER;
- }
-
- msleep(20);
-
- } while (!verify_lmem_ready(xe));
+ /*
+ * 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 - start));
+ jiffies_to_msecs(jiffies - prev_jiffies));
return 0;
}
@@ -779,6 +782,8 @@ static int probe_has_flat_ccs(struct xe_device *xe)
return 0;
gt = xe_root_mmio_gt(xe);
+ if (!gt)
+ return 0;
fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
if (!fw_ref)
@@ -983,12 +988,12 @@ void xe_device_remove(struct xe_device *xe)
void xe_device_shutdown(struct xe_device *xe)
{
- struct xe_gt *gt;
- u8 id;
-
drm_dbg(&xe->drm, "Shutting down device\n");
if (xe_driver_flr_disabled(xe)) {
+ struct xe_gt *gt;
+ u8 id;
+
xe_display_pm_shutdown(xe);
xe_irq_suspend(xe);
@@ -1059,6 +1064,8 @@ void xe_device_l2_flush(struct xe_device *xe)
unsigned int fw_ref;
gt = xe_root_mmio_gt(xe);
+ if (!gt)
+ return;
if (!XE_GT_WA(gt, 16023588340))
return;
@@ -1104,6 +1111,9 @@ void xe_device_td_flush(struct xe_device *xe)
return;
root_gt = xe_root_mmio_gt(xe);
+ if (!root_gt)
+ return;
+
if (XE_GT_WA(root_gt, 16023588340)) {
/* A transient flush is not sufficient: flush the L2 */
xe_device_l2_flush(xe);
diff --git a/drivers/gpu/drm/xe/xe_device_sysfs.c b/drivers/gpu/drm/xe/xe_device_sysfs.c
index c5151c86a98a..ec9c06b06fb5 100644
--- a/drivers/gpu/drm/xe/xe_device_sysfs.c
+++ b/drivers/gpu/drm/xe/xe_device_sysfs.c
@@ -38,13 +38,8 @@ vram_d3cold_threshold_show(struct device *dev,
{
struct pci_dev *pdev = to_pci_dev(dev);
struct xe_device *xe = pdev_to_xe_device(pdev);
- int ret;
-
- xe_pm_runtime_get(xe);
- ret = sysfs_emit(buf, "%d\n", xe->d3cold.vram_threshold);
- xe_pm_runtime_put(xe);
- return ret;
+ return sysfs_emit(buf, "%d\n", xe->d3cold.vram_threshold);
}
static ssize_t
@@ -173,11 +168,8 @@ static umode_t late_bind_attr_is_visible(struct kobject *kobj,
u32 cap = 0;
int ret;
- xe_pm_runtime_get(xe);
-
ret = xe_pcode_read(root, PCODE_MBOX(PCODE_LATE_BINDING, GET_CAPABILITY_STATUS, 0),
&cap, NULL);
- xe_pm_runtime_put(xe);
if (ret)
return 0;
diff --git a/drivers/gpu/drm/xe/xe_device_types.h b/drivers/gpu/drm/xe/xe_device_types.h
index 53264b2bb832..02c04ad7296e 100644
--- a/drivers/gpu/drm/xe/xe_device_types.h
+++ b/drivers/gpu/drm/xe/xe_device_types.h
@@ -27,6 +27,7 @@
#include "xe_sriov_vf_ccs_types.h"
#include "xe_step_types.h"
#include "xe_survivability_mode_types.h"
+#include "xe_tile_sriov_vf_types.h"
#include "xe_validation.h"
#if IS_ENABLED(CONFIG_DRM_XE_DEBUG)
@@ -158,7 +159,15 @@ struct xe_tile {
/** @mem: memory management info for tile */
struct {
/**
- * @mem.vram: VRAM info for tile.
+ * @mem.kernel_vram: kernel-dedicated VRAM info for tile.
+ *
+ * Although VRAM is associated with a specific tile, it can
+ * still be accessed by all tiles' GTs.
+ */
+ struct xe_vram_region *kernel_vram;
+
+ /**
+ * @mem.vram: general purpose VRAM info for tile.
*
* Although VRAM is associated with a specific tile, it can
* still be accessed by all tiles' GTs.
@@ -185,6 +194,8 @@ struct xe_tile {
struct {
/** @sriov.vf.ggtt_balloon: GGTT regions excluded from use. */
struct xe_ggtt_node *ggtt_balloon[2];
+ /** @sriov.vf.self_config: VF configuration data */
+ struct xe_tile_sriov_vf_selfconfig self_config;
} vf;
} sriov;
@@ -323,6 +334,8 @@ struct xe_device {
u8 skip_mtcfg:1;
/** @info.skip_pcode: skip access to PCODE uC */
u8 skip_pcode:1;
+ /** @info.needs_shared_vf_gt_wq: needs shared GT WQ on VF */
+ u8 needs_shared_vf_gt_wq:1;
} info;
/** @wa_active: keep track of active workarounds */
diff --git a/drivers/gpu/drm/xe/xe_device_wa_oob.rules b/drivers/gpu/drm/xe/xe_device_wa_oob.rules
index 3a0c4ccc4224..55ba01bc8f38 100644
--- a/drivers/gpu/drm/xe/xe_device_wa_oob.rules
+++ b/drivers/gpu/drm/xe/xe_device_wa_oob.rules
@@ -1,2 +1,5 @@
+22010954014 PLATFORM(DG2)
15015404425 PLATFORM(LUNARLAKE)
PLATFORM(PANTHERLAKE)
+22019338487_display PLATFORM(LUNARLAKE)
+14022085890 SUBPLATFORM(BATTLEMAGE, G21)
diff --git a/drivers/gpu/drm/xe/xe_dma_buf.c b/drivers/gpu/drm/xe/xe_dma_buf.c
index a7d67725c3ee..54e42960daad 100644
--- a/drivers/gpu/drm/xe/xe_dma_buf.c
+++ b/drivers/gpu/drm/xe/xe_dma_buf.c
@@ -48,32 +48,43 @@ static void xe_dma_buf_detach(struct dma_buf *dmabuf,
static int xe_dma_buf_pin(struct dma_buf_attachment *attach)
{
- struct drm_gem_object *obj = attach->dmabuf->priv;
+ struct dma_buf *dmabuf = attach->dmabuf;
+ struct drm_gem_object *obj = dmabuf->priv;
struct xe_bo *bo = gem_to_xe_bo(obj);
struct xe_device *xe = xe_bo_device(bo);
struct drm_exec *exec = XE_VALIDATION_UNSUPPORTED;
+ bool allow_vram = true;
int ret;
- /*
- * For now only support pinning in TT memory, for two reasons:
- * 1) Avoid pinning in a placement not accessible to some importers.
- * 2) Pinning in VRAM requires PIN accounting which is a to-do.
- */
- if (xe_bo_is_pinned(bo) && !xe_bo_is_mem_type(bo, XE_PL_TT)) {
+ if (!IS_ENABLED(CONFIG_DMABUF_MOVE_NOTIFY)) {
+ allow_vram = false;
+ } else {
+ list_for_each_entry(attach, &dmabuf->attachments, node) {
+ if (!attach->peer2peer) {
+ allow_vram = false;
+ break;
+ }
+ }
+ }
+
+ if (xe_bo_is_pinned(bo) && !xe_bo_is_mem_type(bo, XE_PL_TT) &&
+ !(xe_bo_is_vram(bo) && allow_vram)) {
drm_dbg(&xe->drm, "Can't migrate pinned bo for dma-buf pin.\n");
return -EINVAL;
}
- ret = xe_bo_migrate(bo, XE_PL_TT, NULL, exec);
- if (ret) {
- if (ret != -EINTR && ret != -ERESTARTSYS)
- drm_dbg(&xe->drm,
- "Failed migrating dma-buf to TT memory: %pe\n",
- ERR_PTR(ret));
- return ret;
+ if (!allow_vram) {
+ ret = xe_bo_migrate(bo, XE_PL_TT, NULL, exec);
+ if (ret) {
+ if (ret != -EINTR && ret != -ERESTARTSYS)
+ drm_dbg(&xe->drm,
+ "Failed migrating dma-buf to TT memory: %pe\n",
+ ERR_PTR(ret));
+ return ret;
+ }
}
- ret = xe_bo_pin_external(bo, true, exec);
+ ret = xe_bo_pin_external(bo, !allow_vram, exec);
xe_assert(xe, !ret);
return 0;
diff --git a/drivers/gpu/drm/xe/xe_eu_stall.c b/drivers/gpu/drm/xe/xe_eu_stall.c
index f5cfdf29fde3..650e45f6a7c7 100644
--- a/drivers/gpu/drm/xe/xe_eu_stall.c
+++ b/drivers/gpu/drm/xe/xe_eu_stall.c
@@ -124,6 +124,27 @@ struct xe_eu_stall_data_xe2 {
__u64 unused[6];
} __packed;
+/*
+ * EU stall data format for Xe3p arch GPUs.
+ */
+struct xe_eu_stall_data_xe3p {
+ __u64 ip_addr:61; /* Bits 0 to 60 */
+ __u64 tdr_count:8; /* Bits 61 to 68 */
+ __u64 other_count:8; /* Bits 69 to 76 */
+ __u64 control_count:8; /* Bits 77 to 84 */
+ __u64 pipestall_count:8; /* Bits 85 to 92 */
+ __u64 send_count:8; /* Bits 93 to 100 */
+ __u64 dist_acc_count:8; /* Bits 101 to 108 */
+ __u64 sbid_count:8; /* Bits 109 to 116 */
+ __u64 sync_count:8; /* Bits 117 to 124 */
+ __u64 inst_fetch_count:8; /* Bits 125 to 132 */
+ __u64 active_count:8; /* Bits 133 to 140 */
+ __u64 ex_id:3; /* Bits 141 to 143 */
+ __u64 end_flag:1; /* Bit 144 */
+ __u64 unused_bits:47;
+ __u64 unused[5];
+} __packed;
+
const u64 eu_stall_sampling_rates[] = {251, 251 * 2, 251 * 3, 251 * 4, 251 * 5, 251 * 6, 251 * 7};
/**
@@ -167,10 +188,13 @@ size_t xe_eu_stall_data_record_size(struct xe_device *xe)
{
size_t record_size = 0;
- if (xe->info.platform == XE_PVC)
- record_size = sizeof(struct xe_eu_stall_data_pvc);
+ if (GRAPHICS_VER(xe) >= 35)
+ record_size = sizeof(struct xe_eu_stall_data_xe3p);
else if (GRAPHICS_VER(xe) >= 20)
record_size = sizeof(struct xe_eu_stall_data_xe2);
+ else if (xe->info.platform == XE_PVC)
+ record_size = sizeof(struct xe_eu_stall_data_pvc);
+
xe_assert(xe, is_power_of_2(record_size));
diff --git a/drivers/gpu/drm/xe/xe_exec.c b/drivers/gpu/drm/xe/xe_exec.c
index 7715e74bb945..0dc27476832b 100644
--- a/drivers/gpu/drm/xe/xe_exec.c
+++ b/drivers/gpu/drm/xe/xe_exec.c
@@ -16,6 +16,7 @@
#include "xe_exec_queue.h"
#include "xe_hw_engine_group.h"
#include "xe_macros.h"
+#include "xe_pm.h"
#include "xe_ring_ops_types.h"
#include "xe_sched_job.h"
#include "xe_sync.h"
@@ -123,7 +124,7 @@ int xe_exec_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
struct xe_validation_ctx ctx;
struct xe_sched_job *job;
struct xe_vm *vm;
- bool write_locked, skip_retry = false;
+ bool write_locked;
int err = 0;
struct xe_hw_engine_group *group;
enum xe_hw_engine_group_execution_mode mode, previous_mode;
@@ -247,7 +248,7 @@ retry:
* on task freezing during suspend / hibernate, the call will
* return -ERESTARTSYS and the IOCTL will be rerun.
*/
- err = wait_for_completion_interruptible(&xe->pm_block);
+ err = xe_pm_block_on_suspend(xe);
if (err)
goto err_unlock_list;
@@ -265,12 +266,6 @@ retry:
goto err_exec;
}
- if (xe_exec_queue_is_lr(q) && xe_exec_queue_ring_full(q)) {
- err = -EWOULDBLOCK; /* Aliased to -EAGAIN */
- skip_retry = true;
- goto err_exec;
- }
-
if (xe_exec_queue_uses_pxp(q)) {
err = xe_vm_validate_protected(q->vm);
if (err)
@@ -327,8 +322,6 @@ retry:
xe_sched_job_init_user_fence(job, &syncs[i]);
}
- if (xe_exec_queue_is_lr(q))
- q->ring_ops->emit_job(job);
if (!xe_vm_in_lr_mode(vm))
xe_exec_queue_last_fence_set(q, vm, &job->drm.s_fence->finished);
xe_sched_job_push(job);
@@ -354,7 +347,7 @@ err_exec:
xe_validation_ctx_fini(&ctx);
err_unlock_list:
up_read(&vm->lock);
- if (err == -EAGAIN && !skip_retry)
+ if (err == -EAGAIN)
goto retry;
err_hw_exec_mode:
if (mode == EXEC_MODE_DMA_FENCE)
diff --git a/drivers/gpu/drm/xe/xe_exec_queue.c b/drivers/gpu/drm/xe/xe_exec_queue.c
index 37b2b93b73d6..90cbc95f8e2e 100644
--- a/drivers/gpu/drm/xe/xe_exec_queue.c
+++ b/drivers/gpu/drm/xe/xe_exec_queue.c
@@ -15,6 +15,7 @@
#include "xe_dep_scheduler.h"
#include "xe_device.h"
#include "xe_gt.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"
@@ -28,6 +29,29 @@
#include "xe_vm.h"
#include "xe_pxp.h"
+/**
+ * DOC: Execution Queue
+ *
+ * An Execution queue is an interface for the HW context of execution.
+ * The user creates an execution queue, submits the GPU jobs through those
+ * queues and in the end destroys them.
+ *
+ * Execution queues can also be created by XeKMD itself for driver internal
+ * operations like object migration etc.
+ *
+ * An execution queue is associated with a specified HW engine or a group of
+ * engines (belonging to the same tile and engine class) and any GPU job
+ * submitted on the queue will be run on one of these engines.
+ *
+ * An execution queue is tied to an address space (VM). It holds a reference
+ * of the associated VM and the underlying Logical Ring Context/s (LRC/s)
+ * until the queue is destroyed.
+ *
+ * The execution queue sits on top of the submission backend. It opaquely
+ * handles the GuC and Execlist backends whichever the platform uses, and
+ * the ring operations the different engine classes support.
+ */
+
enum xe_exec_queue_sched_prop {
XE_EXEC_QUEUE_JOB_TIMEOUT = 0,
XE_EXEC_QUEUE_TIMESLICE = 1,
@@ -160,7 +184,7 @@ static struct xe_exec_queue *__xe_exec_queue_alloc(struct xe_device *xe,
return q;
}
-static int __xe_exec_queue_init(struct xe_exec_queue *q)
+static int __xe_exec_queue_init(struct xe_exec_queue *q, u32 exec_queue_flags)
{
int i, err;
u32 flags = 0;
@@ -179,17 +203,37 @@ static int __xe_exec_queue_init(struct xe_exec_queue *q)
flags |= XE_LRC_CREATE_RUNALONE;
}
+ if (!(exec_queue_flags & EXEC_QUEUE_FLAG_KERNEL))
+ flags |= XE_LRC_CREATE_USER_CTX;
+
+ err = q->ops->init(q);
+ if (err)
+ return err;
+
+ /*
+ * This must occur after q->ops->init to avoid race conditions during VF
+ * post-migration recovery, as the fixups for the LRC GGTT addresses
+ * depend on the queue being present in the backend tracking structure.
+ *
+ * In addition to above, we must wait on inflight GGTT changes to avoid
+ * writing out stale values here. Such wait provides a solid solution
+ * (without a race) only if the function can detect migration instantly
+ * from the moment vCPU resumes execution.
+ */
for (i = 0; i < q->width; ++i) {
- q->lrc[i] = xe_lrc_create(q->hwe, q->vm, SZ_16K, q->msix_vec, flags);
- if (IS_ERR(q->lrc[i])) {
- err = PTR_ERR(q->lrc[i]);
+ 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);
+ if (IS_ERR(lrc)) {
+ err = PTR_ERR(lrc);
goto err_lrc;
}
- }
- err = q->ops->init(q);
- if (err)
- goto err_lrc;
+ /* Pairs with READ_ONCE to xe_exec_queue_contexts_hwsp_rebase */
+ WRITE_ONCE(q->lrc[i], lrc);
+ }
return 0;
@@ -225,7 +269,7 @@ struct xe_exec_queue *xe_exec_queue_create(struct xe_device *xe, struct xe_vm *v
if (IS_ERR(q))
return q;
- err = __xe_exec_queue_init(q);
+ err = __xe_exec_queue_init(q, flags);
if (err)
goto err_post_alloc;
@@ -824,25 +868,6 @@ bool xe_exec_queue_is_lr(struct xe_exec_queue *q)
!(q->flags & EXEC_QUEUE_FLAG_VM);
}
-static s32 xe_exec_queue_num_job_inflight(struct xe_exec_queue *q)
-{
- return q->lrc[0]->fence_ctx.next_seqno - xe_lrc_seqno(q->lrc[0]) - 1;
-}
-
-/**
- * xe_exec_queue_ring_full() - Whether an exec_queue's ring is full
- * @q: The exec_queue
- *
- * Return: True if the exec_queue's ring is full, false otherwise.
- */
-bool xe_exec_queue_ring_full(struct xe_exec_queue *q)
-{
- struct xe_lrc *lrc = q->lrc[0];
- s32 max_job = lrc->ring.size / MAX_JOB_SIZE_BYTES;
-
- return xe_exec_queue_num_job_inflight(q) >= max_job;
-}
-
/**
* xe_exec_queue_is_idle() - Whether an exec_queue is idle.
* @q: The exec_queue
@@ -1114,36 +1139,19 @@ int xe_exec_queue_contexts_hwsp_rebase(struct xe_exec_queue *q, void *scratch)
int err = 0;
for (i = 0; i < q->width; ++i) {
- xe_lrc_update_memirq_regs_with_address(q->lrc[i], q->hwe, scratch);
- xe_lrc_update_hwctx_regs_with_address(q->lrc[i]);
- err = xe_lrc_setup_wa_bb_with_scratch(q->lrc[i], q->hwe, scratch);
+ struct xe_lrc *lrc;
+
+ /* Pairs with WRITE_ONCE in __xe_exec_queue_init */
+ lrc = READ_ONCE(q->lrc[i]);
+ if (!lrc)
+ continue;
+
+ xe_lrc_update_memirq_regs_with_address(lrc, q->hwe, scratch);
+ xe_lrc_update_hwctx_regs_with_address(lrc);
+ err = xe_lrc_setup_wa_bb_with_scratch(lrc, q->hwe, scratch);
if (err)
break;
}
return err;
}
-
-/**
- * xe_exec_queue_jobs_ring_restore - Re-emit ring commands of requests pending on given queue.
- * @q: the &xe_exec_queue struct instance
- */
-void xe_exec_queue_jobs_ring_restore(struct xe_exec_queue *q)
-{
- struct xe_gpu_scheduler *sched = &q->guc->sched;
- struct xe_sched_job *job;
-
- /*
- * This routine is used within VF migration recovery. This means
- * using the lock here introduces a restriction: we cannot wait
- * for any GFX HW response while the lock is taken.
- */
- spin_lock(&sched->base.job_list_lock);
- list_for_each_entry(job, &sched->base.pending_list, drm.list) {
- if (xe_sched_job_is_error(job))
- continue;
-
- q->ring_ops->emit_job(job);
- }
- spin_unlock(&sched->base.job_list_lock);
-}
diff --git a/drivers/gpu/drm/xe/xe_exec_queue.h b/drivers/gpu/drm/xe/xe_exec_queue.h
index 15ec852e7f7e..a4dfbe858bda 100644
--- a/drivers/gpu/drm/xe/xe_exec_queue.h
+++ b/drivers/gpu/drm/xe/xe_exec_queue.h
@@ -64,8 +64,6 @@ static inline bool xe_exec_queue_uses_pxp(struct xe_exec_queue *q)
bool xe_exec_queue_is_lr(struct xe_exec_queue *q);
-bool xe_exec_queue_ring_full(struct xe_exec_queue *q);
-
bool xe_exec_queue_is_idle(struct xe_exec_queue *q);
void xe_exec_queue_kill(struct xe_exec_queue *q);
@@ -92,7 +90,6 @@ void xe_exec_queue_update_run_ticks(struct xe_exec_queue *q);
int xe_exec_queue_contexts_hwsp_rebase(struct xe_exec_queue *q, void *scratch);
-void xe_exec_queue_jobs_ring_restore(struct xe_exec_queue *q);
-
struct xe_lrc *xe_exec_queue_lrc(struct xe_exec_queue *q);
+
#endif
diff --git a/drivers/gpu/drm/xe/xe_exec_queue_types.h b/drivers/gpu/drm/xe/xe_exec_queue_types.h
index 27b76cf9da89..282505fa1377 100644
--- a/drivers/gpu/drm/xe/xe_exec_queue_types.h
+++ b/drivers/gpu/drm/xe/xe_exec_queue_types.h
@@ -207,6 +207,9 @@ struct xe_exec_queue_ops {
* call after suspend. In dma-fencing path thus must return within a
* reasonable amount of time. -ETIME return shall indicate an error
* waiting for suspend resulting in associated VM getting killed.
+ * -EAGAIN return indicates the wait should be tried again, if the wait
+ * is within a work item, the work item should be requeued as deadlock
+ * avoidance mechanism.
*/
int (*suspend_wait)(struct xe_exec_queue *q);
/**
diff --git a/drivers/gpu/drm/xe/xe_execlist.c b/drivers/gpu/drm/xe/xe_execlist.c
index f83d421ac9d3..769d05517f93 100644
--- a/drivers/gpu/drm/xe/xe_execlist.c
+++ b/drivers/gpu/drm/xe/xe_execlist.c
@@ -339,7 +339,7 @@ static int execlist_exec_queue_init(struct xe_exec_queue *q)
const struct drm_sched_init_args args = {
.ops = &drm_sched_ops,
.num_rqs = 1,
- .credit_limit = q->lrc[0]->ring.size / MAX_JOB_SIZE_BYTES,
+ .credit_limit = xe_lrc_ring_size() / MAX_JOB_SIZE_BYTES,
.hang_limit = XE_SCHED_HANG_LIMIT,
.timeout = XE_SCHED_JOB_TIMEOUT,
.name = q->hwe->name,
diff --git a/drivers/gpu/drm/xe/xe_ggtt.c b/drivers/gpu/drm/xe/xe_ggtt.c
index 7fdd0a97a628..40680f0c49a1 100644
--- a/drivers/gpu/drm/xe/xe_ggtt.c
+++ b/drivers/gpu/drm/xe/xe_ggtt.c
@@ -107,10 +107,23 @@ static unsigned int probe_gsm_size(struct pci_dev *pdev)
static void ggtt_update_access_counter(struct xe_ggtt *ggtt)
{
struct xe_tile *tile = ggtt->tile;
- struct xe_gt *affected_gt = XE_GT_WA(tile->primary_gt, 22019338487) ?
- tile->primary_gt : tile->media_gt;
- struct xe_mmio *mmio = &affected_gt->mmio;
- u32 max_gtt_writes = XE_GT_WA(ggtt->tile->primary_gt, 22019338487) ? 1100 : 63;
+ struct xe_gt *affected_gt;
+ u32 max_gtt_writes;
+
+ if (tile->primary_gt && XE_GT_WA(tile->primary_gt, 22019338487)) {
+ affected_gt = tile->primary_gt;
+ max_gtt_writes = 1100;
+
+ /* Only expected to apply to primary GT on dgpu platforms */
+ xe_tile_assert(tile, IS_DGFX(tile_to_xe(tile)));
+ } else {
+ affected_gt = tile->media_gt;
+ max_gtt_writes = 63;
+
+ /* Only expected to apply to media GT on igpu platforms */
+ xe_tile_assert(tile, !IS_DGFX(tile_to_xe(tile)));
+ }
+
/*
* Wa_22019338487: GMD_ID is a RO register, a dummy write forces gunit
* to wait for completion of prior GTT writes before letting this through.
@@ -119,7 +132,7 @@ static void ggtt_update_access_counter(struct xe_ggtt *ggtt)
lockdep_assert_held(&ggtt->lock);
if ((++ggtt->access_count % max_gtt_writes) == 0) {
- xe_mmio_write32(mmio, GMD_ID, 0x0);
+ xe_mmio_write32(&affected_gt->mmio, GMD_ID, 0x0);
ggtt->access_count = 0;
}
}
@@ -159,6 +172,16 @@ static void xe_ggtt_clear(struct xe_ggtt *ggtt, u64 start, u64 size)
}
}
+static void primelockdep(struct xe_ggtt *ggtt)
+{
+ if (!IS_ENABLED(CONFIG_LOCKDEP))
+ return;
+
+ fs_reclaim_acquire(GFP_KERNEL);
+ might_lock(&ggtt->lock);
+ fs_reclaim_release(GFP_KERNEL);
+}
+
/**
* xe_ggtt_alloc - Allocate a GGTT for a given &xe_tile
* @tile: &xe_tile
@@ -169,9 +192,19 @@ static void xe_ggtt_clear(struct xe_ggtt *ggtt, u64 start, u64 size)
*/
struct xe_ggtt *xe_ggtt_alloc(struct xe_tile *tile)
{
- struct xe_ggtt *ggtt = drmm_kzalloc(&tile_to_xe(tile)->drm, sizeof(*ggtt), GFP_KERNEL);
- if (ggtt)
- ggtt->tile = tile;
+ struct xe_device *xe = tile_to_xe(tile);
+ struct xe_ggtt *ggtt;
+
+ ggtt = drmm_kzalloc(&xe->drm, sizeof(*ggtt), GFP_KERNEL);
+ if (!ggtt)
+ return NULL;
+
+ if (drmm_mutex_init(&xe->drm, &ggtt->lock))
+ return NULL;
+
+ primelockdep(ggtt);
+ ggtt->tile = tile;
+
return ggtt;
}
@@ -180,7 +213,6 @@ static void ggtt_fini_early(struct drm_device *drm, void *arg)
struct xe_ggtt *ggtt = arg;
destroy_workqueue(ggtt->wq);
- mutex_destroy(&ggtt->lock);
drm_mm_takedown(&ggtt->mm);
}
@@ -198,16 +230,6 @@ void xe_ggtt_might_lock(struct xe_ggtt *ggtt)
}
#endif
-static void primelockdep(struct xe_ggtt *ggtt)
-{
- if (!IS_ENABLED(CONFIG_LOCKDEP))
- return;
-
- fs_reclaim_acquire(GFP_KERNEL);
- might_lock(&ggtt->lock);
- fs_reclaim_release(GFP_KERNEL);
-}
-
static const struct xe_ggtt_pt_ops xelp_pt_ops = {
.pte_encode_flags = xelp_ggtt_pte_flags,
.ggtt_set_pte = xe_ggtt_set_pte,
@@ -227,8 +249,6 @@ static void __xe_ggtt_init_early(struct xe_ggtt *ggtt, u32 reserved)
{
drm_mm_init(&ggtt->mm, reserved,
ggtt->size - reserved);
- mutex_init(&ggtt->lock);
- primelockdep(ggtt);
}
int xe_ggtt_init_kunit(struct xe_ggtt *ggtt, u32 reserved, u32 size)
@@ -284,10 +304,10 @@ int xe_ggtt_init_early(struct xe_ggtt *ggtt)
ggtt->size = GUC_GGTT_TOP;
if (GRAPHICS_VERx100(xe) >= 1270)
- ggtt->pt_ops = (ggtt->tile->media_gt &&
- XE_GT_WA(ggtt->tile->media_gt, 22019338487)) ||
- XE_GT_WA(ggtt->tile->primary_gt, 22019338487) ?
- &xelpg_pt_wa_ops : &xelpg_pt_ops;
+ ggtt->pt_ops =
+ (ggtt->tile->media_gt && XE_GT_WA(ggtt->tile->media_gt, 22019338487)) ||
+ (ggtt->tile->primary_gt && XE_GT_WA(ggtt->tile->primary_gt, 22019338487)) ?
+ &xelpg_pt_wa_ops : &xelpg_pt_ops;
else
ggtt->pt_ops = &xelp_pt_ops;
diff --git a/drivers/gpu/drm/xe/xe_gpu_scheduler.c b/drivers/gpu/drm/xe/xe_gpu_scheduler.c
index 455ccaf17314..f91e06d03511 100644
--- a/drivers/gpu/drm/xe/xe_gpu_scheduler.c
+++ b/drivers/gpu/drm/xe/xe_gpu_scheduler.c
@@ -101,19 +101,6 @@ void xe_sched_submission_stop(struct xe_gpu_scheduler *sched)
cancel_work_sync(&sched->work_process_msg);
}
-/**
- * xe_sched_submission_stop_async - Stop further runs of submission tasks on a scheduler.
- * @sched: the &xe_gpu_scheduler struct instance
- *
- * This call disables further runs of scheduling work queue. It does not wait
- * for any in-progress runs to finish, only makes sure no further runs happen
- * afterwards.
- */
-void xe_sched_submission_stop_async(struct xe_gpu_scheduler *sched)
-{
- drm_sched_wqueue_stop(&sched->base);
-}
-
void xe_sched_submission_resume_tdr(struct xe_gpu_scheduler *sched)
{
drm_sched_resume_timeout(&sched->base, sched->base.timeout);
@@ -135,3 +122,17 @@ void xe_sched_add_msg_locked(struct xe_gpu_scheduler *sched,
list_add_tail(&msg->link, &sched->msgs);
xe_sched_process_msg_queue(sched);
}
+
+/**
+ * xe_sched_add_msg_head() - Xe GPU scheduler add message to head of list
+ * @sched: Xe GPU scheduler
+ * @msg: Message to add
+ */
+void xe_sched_add_msg_head(struct xe_gpu_scheduler *sched,
+ struct xe_sched_msg *msg)
+{
+ lockdep_assert_held(&sched->base.job_list_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 e548b2aed95a..9955397aaaa9 100644
--- a/drivers/gpu/drm/xe/xe_gpu_scheduler.h
+++ b/drivers/gpu/drm/xe/xe_gpu_scheduler.h
@@ -7,7 +7,7 @@
#define _XE_GPU_SCHEDULER_H_
#include "xe_gpu_scheduler_types.h"
-#include "xe_sched_job_types.h"
+#include "xe_sched_job.h"
int xe_sched_init(struct xe_gpu_scheduler *sched,
const struct drm_sched_backend_ops *ops,
@@ -21,7 +21,6 @@ void xe_sched_fini(struct xe_gpu_scheduler *sched);
void xe_sched_submission_start(struct xe_gpu_scheduler *sched);
void xe_sched_submission_stop(struct xe_gpu_scheduler *sched);
-void xe_sched_submission_stop_async(struct xe_gpu_scheduler *sched);
void xe_sched_submission_resume_tdr(struct xe_gpu_scheduler *sched);
@@ -29,6 +28,8 @@ void xe_sched_add_msg(struct xe_gpu_scheduler *sched,
struct xe_sched_msg *msg);
void xe_sched_add_msg_locked(struct xe_gpu_scheduler *sched,
struct xe_sched_msg *msg);
+void xe_sched_add_msg_head(struct xe_gpu_scheduler *sched,
+ struct xe_sched_msg *msg);
static inline void xe_sched_msg_lock(struct xe_gpu_scheduler *sched)
{
@@ -58,7 +59,8 @@ static inline void xe_sched_resubmit_jobs(struct xe_gpu_scheduler *sched)
struct drm_sched_fence *s_fence = s_job->s_fence;
struct dma_fence *hw_fence = s_fence->parent;
- if (hw_fence && !dma_fence_is_signaled(hw_fence))
+ if (to_xe_sched_job(s_job)->skip_emit ||
+ (hw_fence && !dma_fence_is_signaled(hw_fence)))
sched->base.ops->run_job(s_job);
}
}
@@ -77,17 +79,30 @@ static inline void xe_sched_add_pending_job(struct xe_gpu_scheduler *sched,
spin_unlock(&sched->base.job_list_lock);
}
+/**
+ * xe_sched_first_pending_job() - Find first pending job which is unsignaled
+ * @sched: Xe GPU scheduler
+ *
+ * Return first unsignaled job in pending list or NULL
+ */
static inline
struct xe_sched_job *xe_sched_first_pending_job(struct xe_gpu_scheduler *sched)
{
- struct xe_sched_job *job;
+ struct xe_sched_job *job, *r_job = NULL;
spin_lock(&sched->base.job_list_lock);
- job = list_first_entry_or_null(&sched->base.pending_list,
- struct xe_sched_job, drm.list);
+ 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;
+
+ if (hw_fence && !dma_fence_is_signaled(hw_fence)) {
+ r_job = job;
+ break;
+ }
+ }
spin_unlock(&sched->base.job_list_lock);
- return job;
+ return r_job;
}
static inline int
diff --git a/drivers/gpu/drm/xe/xe_gsc.c b/drivers/gpu/drm/xe/xe_gsc.c
index 83d61bf8ec62..dd69cb834f8e 100644
--- a/drivers/gpu/drm/xe/xe_gsc.c
+++ b/drivers/gpu/drm/xe/xe_gsc.c
@@ -266,7 +266,7 @@ static int gsc_upload_and_init(struct xe_gsc *gsc)
unsigned int fw_ref;
int ret;
- if (XE_GT_WA(tile->primary_gt, 14018094691)) {
+ if (tile->primary_gt && XE_GT_WA(tile->primary_gt, 14018094691)) {
fw_ref = xe_force_wake_get(gt_to_fw(tile->primary_gt), XE_FORCEWAKE_ALL);
/*
@@ -281,7 +281,7 @@ static int gsc_upload_and_init(struct xe_gsc *gsc)
ret = gsc_upload(gsc);
- if (XE_GT_WA(tile->primary_gt, 14018094691))
+ if (tile->primary_gt && XE_GT_WA(tile->primary_gt, 14018094691))
xe_force_wake_put(gt_to_fw(tile->primary_gt), fw_ref);
if (ret)
diff --git a/drivers/gpu/drm/xe/xe_gt.c b/drivers/gpu/drm/xe/xe_gt.c
index 3e0ad7e5b5df..d8e94fb8b9bd 100644
--- a/drivers/gpu/drm/xe/xe_gt.c
+++ b/drivers/gpu/drm/xe/xe_gt.c
@@ -65,29 +65,29 @@
#include "xe_wa.h"
#include "xe_wopcm.h"
-static void gt_fini(struct drm_device *drm, void *arg)
-{
- struct xe_gt *gt = arg;
-
- destroy_workqueue(gt->ordered_wq);
-}
-
struct xe_gt *xe_gt_alloc(struct xe_tile *tile)
{
+ struct xe_device *xe = tile_to_xe(tile);
+ struct drm_device *drm = &xe->drm;
+ bool shared_wq = xe->info.needs_shared_vf_gt_wq && tile->primary_gt &&
+ IS_SRIOV_VF(xe);
+ struct workqueue_struct *ordered_wq;
struct xe_gt *gt;
- int err;
- gt = drmm_kzalloc(&tile_to_xe(tile)->drm, sizeof(*gt), GFP_KERNEL);
+ gt = drmm_kzalloc(drm, sizeof(*gt), GFP_KERNEL);
if (!gt)
return ERR_PTR(-ENOMEM);
gt->tile = tile;
- gt->ordered_wq = alloc_ordered_workqueue("gt-ordered-wq",
- WQ_MEM_RECLAIM);
+ if (shared_wq && tile->primary_gt->ordered_wq)
+ ordered_wq = tile->primary_gt->ordered_wq;
+ else
+ ordered_wq = drmm_alloc_ordered_workqueue(drm, "gt-ordered-wq",
+ WQ_MEM_RECLAIM);
+ if (IS_ERR(ordered_wq))
+ return ERR_CAST(ordered_wq);
- err = drmm_add_action_or_reset(&gt_to_xe(gt)->drm, gt_fini, gt);
- if (err)
- return ERR_PTR(err);
+ gt->ordered_wq = ordered_wq;
return gt;
}
@@ -398,6 +398,12 @@ int xe_gt_init_early(struct xe_gt *gt)
return err;
}
+ if (IS_SRIOV_VF(gt_to_xe(gt))) {
+ err = xe_gt_sriov_vf_init_early(gt);
+ if (err)
+ return err;
+ }
+
xe_reg_sr_init(&gt->reg_sr, "GT", gt_to_xe(gt));
err = xe_wa_gt_init(gt);
@@ -583,10 +589,8 @@ static int gt_init_with_all_forcewake(struct xe_gt *gt)
if (IS_SRIOV_PF(gt_to_xe(gt)) && xe_gt_is_main_type(gt))
xe_lmtt_init_hw(&gt_to_tile(gt)->sriov.pf.lmtt);
- if (IS_SRIOV_PF(gt_to_xe(gt))) {
- xe_gt_sriov_pf_init(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);
@@ -657,6 +661,12 @@ int xe_gt_init(struct xe_gt *gt)
if (err)
return err;
+ if (IS_SRIOV_VF(gt_to_xe(gt))) {
+ err = xe_gt_sriov_vf_init(gt);
+ if (err)
+ return err;
+ }
+
return 0;
}
@@ -803,11 +813,6 @@ static int do_gt_restart(struct xe_gt *gt)
return 0;
}
-static int gt_wait_reset_unblock(struct xe_gt *gt)
-{
- return xe_guc_wait_reset_unblock(&gt->uc.guc);
-}
-
static int gt_reset(struct xe_gt *gt)
{
unsigned int fw_ref;
@@ -822,10 +827,6 @@ static int gt_reset(struct xe_gt *gt)
xe_gt_info(gt, "reset started\n");
- err = gt_wait_reset_unblock(gt);
- if (!err)
- xe_gt_warn(gt, "reset block failed to get lifted");
-
xe_pm_runtime_get(gt_to_xe(gt));
if (xe_fault_inject_gt_reset()) {
diff --git a/drivers/gpu/drm/xe/xe_gt.h b/drivers/gpu/drm/xe/xe_gt.h
index 41880979f4de..9d710049da45 100644
--- a/drivers/gpu/drm/xe/xe_gt.h
+++ b/drivers/gpu/drm/xe/xe_gt.h
@@ -12,6 +12,7 @@
#include "xe_device.h"
#include "xe_device_types.h"
+#include "xe_gt_sriov_vf.h"
#include "xe_hw_engine.h"
#define for_each_hw_engine(hwe__, gt__, id__) \
@@ -21,6 +22,12 @@
#define CCS_MASK(gt) (((gt)->info.engine_mask & XE_HW_ENGINE_CCS_MASK) >> XE_HW_ENGINE_CCS0)
+#define GT_VER(gt) ({ \
+ typeof(gt) gt_ = (gt); \
+ struct xe_device *xe = gt_to_xe(gt_); \
+ xe_gt_is_media_type(gt_) ? MEDIA_VER(xe) : GRAPHICS_VER(xe); \
+})
+
extern struct fault_attr gt_reset_failure;
static inline bool xe_fault_inject_gt_reset(void)
{
@@ -124,4 +131,16 @@ static inline bool xe_gt_is_usm_hwe(struct xe_gt *gt, struct xe_hw_engine *hwe)
hwe->instance == gt->usm.reserved_bcs_instance;
}
+/**
+ * xe_gt_recovery_pending() - GT recovery pending
+ * @gt: the &xe_gt
+ *
+ * Return: True if GT recovery in pending, False otherwise
+ */
+static inline bool xe_gt_recovery_pending(struct xe_gt *gt)
+{
+ return IS_SRIOV_VF(gt_to_xe(gt)) &&
+ xe_gt_sriov_vf_recovery_pending(gt);
+}
+
#endif
diff --git a/drivers/gpu/drm/xe/xe_gt_clock.c b/drivers/gpu/drm/xe/xe_gt_clock.c
index 4f011d1573c6..00f5972c14dc 100644
--- a/drivers/gpu/drm/xe/xe_gt_clock.c
+++ b/drivers/gpu/drm/xe/xe_gt_clock.c
@@ -55,30 +55,11 @@ static void read_crystal_clock(struct xe_gt *gt, u32 rpm_config_reg, u32 *freq,
}
}
-static void check_ctc_mode(struct xe_gt *gt)
-{
- /*
- * CTC_MODE[0] = 1 is definitely not supported for Xe2 and later
- * platforms. In theory it could be a valid setting for pre-Xe2
- * platforms, but there's no documentation on how to properly handle
- * this case. Reading TIMESTAMP_OVERRIDE, as the driver attempted in
- * the past has been confirmed as incorrect by the hardware architects.
- *
- * For now just warn if we ever encounter hardware in the wild that
- * has this setting and move on as if it hadn't been set.
- */
- if (xe_mmio_read32(&gt->mmio, CTC_MODE) & CTC_SOURCE_DIVIDE_LOGIC)
- xe_gt_warn(gt, "CTC_MODE[0] is set; this is unexpected and undocumented\n");
-}
-
int xe_gt_clock_init(struct xe_gt *gt)
{
u32 freq;
u32 c0;
- if (!IS_SRIOV_VF(gt_to_xe(gt)))
- check_ctc_mode(gt);
-
c0 = xe_mmio_read32(&gt->mmio, RPM_CONFIG0);
read_crystal_clock(gt, c0, &freq, &gt->info.timestamp_base);
diff --git a/drivers/gpu/drm/xe/xe_gt_debugfs.c b/drivers/gpu/drm/xe/xe_gt_debugfs.c
index f253e2df4907..e4fd632f43cf 100644
--- a/drivers/gpu/drm/xe/xe_gt_debugfs.c
+++ b/drivers/gpu/drm/xe/xe_gt_debugfs.c
@@ -12,7 +12,6 @@
#include "xe_device.h"
#include "xe_force_wake.h"
-#include "xe_ggtt.h"
#include "xe_gt.h"
#include "xe_gt_mcr.h"
#include "xe_gt_idle.h"
@@ -36,6 +35,11 @@
#include "xe_uc_debugfs.h"
#include "xe_wa.h"
+static struct xe_gt *node_to_gt(struct drm_info_node *node)
+{
+ return node->dent->d_parent->d_inode->i_private;
+}
+
/**
* xe_gt_debugfs_simple_show - A show callback for struct drm_info_list
* @m: the &seq_file
@@ -78,8 +82,7 @@ int xe_gt_debugfs_simple_show(struct seq_file *m, void *data)
{
struct drm_printer p = drm_seq_file_printer(m);
struct drm_info_node *node = m->private;
- struct dentry *parent = node->dent->d_parent;
- struct xe_gt *gt = parent->d_inode->i_private;
+ struct xe_gt *gt = node_to_gt(node);
int (*print)(struct xe_gt *, struct drm_printer *) = node->info_ent->data;
if (WARN_ON(!print))
@@ -88,15 +91,36 @@ int xe_gt_debugfs_simple_show(struct seq_file *m, void *data)
return print(gt, &p);
}
-static int hw_engines(struct xe_gt *gt, struct drm_printer *p)
+/**
+ * xe_gt_debugfs_show_with_rpm - A show callback for struct drm_info_list
+ * @m: the &seq_file
+ * @data: data used by the drm debugfs helpers
+ *
+ * Similar to xe_gt_debugfs_simple_show() but implicitly takes a RPM ref.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+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;
+}
+
+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;
- xe_pm_runtime_get(xe);
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;
@@ -108,58 +132,21 @@ static int hw_engines(struct xe_gt *gt, struct drm_printer *p)
fw_put:
xe_force_wake_put(gt_to_fw(gt), fw_ref);
- xe_pm_runtime_put(xe);
-
- return ret;
-}
-
-static int powergate_info(struct xe_gt *gt, struct drm_printer *p)
-{
- int ret;
-
- xe_pm_runtime_get(gt_to_xe(gt));
- ret = xe_gt_idle_pg_print(gt, p);
- xe_pm_runtime_put(gt_to_xe(gt));
return ret;
}
-static int topology(struct xe_gt *gt, struct drm_printer *p)
-{
- xe_pm_runtime_get(gt_to_xe(gt));
- xe_gt_topology_dump(gt, p);
- xe_pm_runtime_put(gt_to_xe(gt));
-
- return 0;
-}
-
static int steering(struct xe_gt *gt, struct drm_printer *p)
{
- xe_pm_runtime_get(gt_to_xe(gt));
xe_gt_mcr_steering_dump(gt, p);
- xe_pm_runtime_put(gt_to_xe(gt));
-
return 0;
}
-static int ggtt(struct xe_gt *gt, struct drm_printer *p)
-{
- int ret;
-
- xe_pm_runtime_get(gt_to_xe(gt));
- ret = xe_ggtt_dump(gt_to_tile(gt)->mem.ggtt, p);
- xe_pm_runtime_put(gt_to_xe(gt));
-
- return ret;
-}
-
static int register_save_restore(struct xe_gt *gt, struct drm_printer *p)
{
struct xe_hw_engine *hwe;
enum xe_hw_engine_id id;
- xe_pm_runtime_get(gt_to_xe(gt));
-
xe_reg_sr_dump(&gt->reg_sr, p);
drm_printf(p, "\n");
@@ -177,98 +164,42 @@ static int register_save_restore(struct xe_gt *gt, struct drm_printer *p)
for_each_hw_engine(hwe, gt, id)
xe_reg_whitelist_dump(&hwe->reg_whitelist, p);
- xe_pm_runtime_put(gt_to_xe(gt));
-
- return 0;
-}
-
-static int workarounds(struct xe_gt *gt, struct drm_printer *p)
-{
- xe_pm_runtime_get(gt_to_xe(gt));
- xe_wa_dump(gt, p);
- xe_pm_runtime_put(gt_to_xe(gt));
-
- return 0;
-}
-
-static int tunings(struct xe_gt *gt, struct drm_printer *p)
-{
- xe_pm_runtime_get(gt_to_xe(gt));
- xe_tuning_dump(gt, p);
- xe_pm_runtime_put(gt_to_xe(gt));
-
- return 0;
-}
-
-static int pat(struct xe_gt *gt, struct drm_printer *p)
-{
- xe_pm_runtime_get(gt_to_xe(gt));
- xe_pat_dump(gt, p);
- xe_pm_runtime_put(gt_to_xe(gt));
-
- return 0;
-}
-
-static int mocs(struct xe_gt *gt, struct drm_printer *p)
-{
- xe_pm_runtime_get(gt_to_xe(gt));
- xe_mocs_dump(gt, p);
- xe_pm_runtime_put(gt_to_xe(gt));
-
return 0;
}
static int rcs_default_lrc(struct xe_gt *gt, struct drm_printer *p)
{
- xe_pm_runtime_get(gt_to_xe(gt));
xe_lrc_dump_default(p, gt, XE_ENGINE_CLASS_RENDER);
- xe_pm_runtime_put(gt_to_xe(gt));
-
return 0;
}
static int ccs_default_lrc(struct xe_gt *gt, struct drm_printer *p)
{
- xe_pm_runtime_get(gt_to_xe(gt));
xe_lrc_dump_default(p, gt, XE_ENGINE_CLASS_COMPUTE);
- xe_pm_runtime_put(gt_to_xe(gt));
-
return 0;
}
static int bcs_default_lrc(struct xe_gt *gt, struct drm_printer *p)
{
- xe_pm_runtime_get(gt_to_xe(gt));
xe_lrc_dump_default(p, gt, XE_ENGINE_CLASS_COPY);
- xe_pm_runtime_put(gt_to_xe(gt));
-
return 0;
}
static int vcs_default_lrc(struct xe_gt *gt, struct drm_printer *p)
{
- xe_pm_runtime_get(gt_to_xe(gt));
xe_lrc_dump_default(p, gt, XE_ENGINE_CLASS_VIDEO_DECODE);
- xe_pm_runtime_put(gt_to_xe(gt));
-
return 0;
}
static int vecs_default_lrc(struct xe_gt *gt, struct drm_printer *p)
{
- xe_pm_runtime_get(gt_to_xe(gt));
xe_lrc_dump_default(p, gt, XE_ENGINE_CLASS_VIDEO_ENHANCE);
- xe_pm_runtime_put(gt_to_xe(gt));
-
return 0;
}
static int hwconfig(struct xe_gt *gt, struct drm_printer *p)
{
- xe_pm_runtime_get(gt_to_xe(gt));
xe_guc_hwconfig_dump(&gt->uc.guc, p);
- xe_pm_runtime_put(gt_to_xe(gt));
-
return 0;
}
@@ -278,26 +209,26 @@ static int hwconfig(struct xe_gt *gt, struct drm_printer *p)
* - without access to the PF specific data
*/
static const struct drm_info_list vf_safe_debugfs_list[] = {
- {"topology", .show = xe_gt_debugfs_simple_show, .data = topology},
- {"ggtt", .show = xe_gt_debugfs_simple_show, .data = ggtt},
- {"register-save-restore", .show = xe_gt_debugfs_simple_show, .data = register_save_restore},
- {"workarounds", .show = xe_gt_debugfs_simple_show, .data = workarounds},
- {"tunings", .show = xe_gt_debugfs_simple_show, .data = tunings},
- {"default_lrc_rcs", .show = xe_gt_debugfs_simple_show, .data = rcs_default_lrc},
- {"default_lrc_ccs", .show = xe_gt_debugfs_simple_show, .data = ccs_default_lrc},
- {"default_lrc_bcs", .show = xe_gt_debugfs_simple_show, .data = bcs_default_lrc},
- {"default_lrc_vcs", .show = xe_gt_debugfs_simple_show, .data = vcs_default_lrc},
- {"default_lrc_vecs", .show = xe_gt_debugfs_simple_show, .data = vecs_default_lrc},
- {"hwconfig", .show = xe_gt_debugfs_simple_show, .data = hwconfig},
+ { "topology", .show = xe_gt_debugfs_show_with_rpm, .data = xe_gt_topology_dump },
+ { "register-save-restore",
+ .show = xe_gt_debugfs_show_with_rpm, .data = register_save_restore },
+ { "workarounds", .show = xe_gt_debugfs_show_with_rpm, .data = xe_wa_gt_dump },
+ { "tunings", .show = xe_gt_debugfs_show_with_rpm, .data = xe_tuning_dump },
+ { "default_lrc_rcs", .show = xe_gt_debugfs_show_with_rpm, .data = rcs_default_lrc },
+ { "default_lrc_ccs", .show = xe_gt_debugfs_show_with_rpm, .data = ccs_default_lrc },
+ { "default_lrc_bcs", .show = xe_gt_debugfs_show_with_rpm, .data = bcs_default_lrc },
+ { "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 },
};
/* everything else should be added here */
static const struct drm_info_list pf_only_debugfs_list[] = {
- {"hw_engines", .show = xe_gt_debugfs_simple_show, .data = hw_engines},
- {"mocs", .show = xe_gt_debugfs_simple_show, .data = mocs},
- {"pat", .show = xe_gt_debugfs_simple_show, .data = pat},
- {"powergate_info", .show = xe_gt_debugfs_simple_show, .data = powergate_info},
- {"steering", .show = xe_gt_debugfs_simple_show, .data = steering},
+ { "hw_engines", .show = xe_gt_debugfs_show_with_rpm, .data = hw_engines },
+ { "mocs", .show = xe_gt_debugfs_show_with_rpm, .data = xe_mocs_dump },
+ { "pat", .show = xe_gt_debugfs_show_with_rpm, .data = xe_pat_dump },
+ { "powergate_info", .show = xe_gt_debugfs_show_with_rpm, .data = xe_gt_idle_pg_print },
+ { "steering", .show = xe_gt_debugfs_show_with_rpm, .data = steering },
};
static ssize_t write_to_gt_call(const char __user *userbuf, size_t count, loff_t *ppos,
diff --git a/drivers/gpu/drm/xe/xe_gt_debugfs.h b/drivers/gpu/drm/xe/xe_gt_debugfs.h
index 05a6cc93c78c..32ee3264051b 100644
--- a/drivers/gpu/drm/xe/xe_gt_debugfs.h
+++ b/drivers/gpu/drm/xe/xe_gt_debugfs.h
@@ -11,5 +11,6 @@ struct xe_gt;
void xe_gt_debugfs_register(struct xe_gt *gt);
int xe_gt_debugfs_simple_show(struct seq_file *m, void *data);
+int xe_gt_debugfs_show_with_rpm(struct seq_file *m, void *data);
#endif
diff --git a/drivers/gpu/drm/xe/xe_gt_freq.c b/drivers/gpu/drm/xe/xe_gt_freq.c
index 4ff1b6b58d6b..701349251bbc 100644
--- a/drivers/gpu/drm/xe/xe_gt_freq.c
+++ b/drivers/gpu/drm/xe/xe_gt_freq.c
@@ -99,13 +99,8 @@ static ssize_t rp0_freq_show(struct kobject *kobj,
{
struct device *dev = kobj_to_dev(kobj);
struct xe_guc_pc *pc = dev_to_pc(dev);
- u32 freq;
- xe_pm_runtime_get(dev_to_xe(dev));
- freq = xe_guc_pc_get_rp0_freq(pc);
- xe_pm_runtime_put(dev_to_xe(dev));
-
- return sysfs_emit(buf, "%d\n", freq);
+ return sysfs_emit(buf, "%d\n", xe_guc_pc_get_rp0_freq(pc));
}
static struct kobj_attribute attr_rp0_freq = __ATTR_RO(rp0_freq);
diff --git a/drivers/gpu/drm/xe/xe_gt_mcr.c b/drivers/gpu/drm/xe/xe_gt_mcr.c
index 8fb1cae91724..81ecd9382635 100644
--- a/drivers/gpu/drm/xe/xe_gt_mcr.c
+++ b/drivers/gpu/drm/xe/xe_gt_mcr.c
@@ -169,6 +169,15 @@ static const struct xe_mmio_range xelpg_dss_steering_table[] = {
{},
};
+static const struct xe_mmio_range xe3p_xpc_xecore_steering_table[] = {
+ { 0x008140, 0x00817F }, /* SLICE, XeCore, SLICE */
+ { 0x009480, 0x00955F }, /* SLICE, XeCore */
+ { 0x00D800, 0x00D87F }, /* SLICE */
+ { 0x00DC00, 0x00E9FF }, /* SLICE, rsvd, XeCore, rsvd, XeCore, rsvd, XeCore */
+ { 0x013000, 0x0135FF }, /* XeCore, SLICE */
+ {},
+};
+
static const struct xe_mmio_range xelpmp_oaddrm_steering_table[] = {
{ 0x393200, 0x39323F },
{ 0x393400, 0x3934FF },
@@ -236,16 +245,41 @@ static const struct xe_mmio_range xe2lpm_instance0_steering_table[] = {
};
static const struct xe_mmio_range xe3lpm_instance0_steering_table[] = {
- { 0x384000, 0x3847DF }, /* GAM, rsvd, GAM */
+ { 0x384000, 0x3841FF }, /* GAM */
+ { 0x384400, 0x3847DF }, /* GAM */
{ 0x384900, 0x384AFF }, /* GAM */
{ 0x389560, 0x3895FF }, /* MEDIAINF */
{ 0x38B600, 0x38B8FF }, /* L3BANK */
{ 0x38C800, 0x38D07F }, /* GAM, MEDIAINF */
- { 0x38D0D0, 0x38F0FF }, /* MEDIAINF, GAM */
+ { 0x38D0D0, 0x38F0FF }, /* MEDIAINF, rsvd, GAM */
{ 0x393C00, 0x393C7F }, /* MEDIAINF */
{},
};
+/*
+ * Different "GAM" ranges have different rules; GAMWKRS, STLB, and GAMREQSTRM
+ * range subtypes need to be steered to (1,0), while all other GAM subtypes
+ * are steered to (0,0) and are included in the "INSTANCE0" table farther
+ * down.
+ */
+static const struct xe_mmio_range xe3p_xpc_gam_grp1_steering_table[] = {
+ { 0x004000, 0x004AFF }, /* GAMREQSTRM, rsvd, STLB, GAMWKRS, GAMREQSTRM */
+ { 0x00F100, 0x00FFFF }, /* GAMWKRS */
+ {},
+};
+
+static const struct xe_mmio_range xe3p_xpc_psmi_grp19_steering_table[] = {
+ { 0x00B500, 0x00B5FF },
+ {},
+};
+
+static const struct xe_mmio_range xe3p_xpc_instance0_steering_table[] = {
+ { 0x00B600, 0x00B6FF }, /* PSMI0 */
+ { 0x00C800, 0x00CFFF }, /* GAMCTRL */
+ { 0x00F000, 0x00F0FF }, /* GAMCTRL */
+ {},
+};
+
static void init_steering_l3bank(struct xe_gt *gt)
{
struct xe_mmio *mmio = &gt->mmio;
@@ -418,6 +452,18 @@ static void init_steering_sqidi_psmi(struct xe_gt *gt)
gt->steering[SQIDI_PSMI].instance_target = select & 0x1;
}
+static void init_steering_psmi(struct xe_gt *gt)
+{
+ gt->steering[PSMI19].group_target = 19;
+ gt->steering[PSMI19].instance_target = 0;
+}
+
+static void init_steering_gam1(struct xe_gt *gt)
+{
+ gt->steering[GAM1].group_target = 1;
+ gt->steering[GAM1].instance_target = 0;
+}
+
static const struct {
const char *name;
void (*init)(struct xe_gt *gt);
@@ -425,9 +471,11 @@ static const struct {
[L3BANK] = { "L3BANK", init_steering_l3bank },
[MSLICE] = { "MSLICE", init_steering_mslice },
[LNCF] = { "LNCF", NULL }, /* initialized by mslice init */
- [DSS] = { "DSS", init_steering_dss },
+ [DSS] = { "DSS / XeCore", init_steering_dss },
[OADDRM] = { "OADDRM / GPMXMT", init_steering_oaddrm },
[SQIDI_PSMI] = { "SQIDI_PSMI", init_steering_sqidi_psmi },
+ [PSMI19] = { "PSMI[19]", init_steering_psmi },
+ [GAM1] = { "GAMWKRS / STLB / GAMREQSTRM", init_steering_gam1 },
[INSTANCE0] = { "INSTANCE 0", NULL },
[IMPLICIT_STEERING] = { "IMPLICIT", NULL },
};
@@ -466,7 +514,18 @@ void xe_gt_mcr_init_early(struct xe_gt *gt)
gt->steering[OADDRM].ranges = xelpmp_oaddrm_steering_table;
}
} else {
- if (GRAPHICS_VER(xe) >= 20) {
+ if (GRAPHICS_VERx100(xe) == 3511) {
+ /*
+ * TODO: there are some ranges in bspec with missing
+ * termination: [0x00B000, 0x00B0FF] and
+ * [0x00D880, 0x00D8FF] (NODE); [0x00B100, 0x00B3FF]
+ * (L3BANK). Update them here once bspec is updated.
+ */
+ gt->steering[DSS].ranges = xe3p_xpc_xecore_steering_table;
+ gt->steering[GAM1].ranges = xe3p_xpc_gam_grp1_steering_table;
+ gt->steering[INSTANCE0].ranges = xe3p_xpc_instance0_steering_table;
+ gt->steering[PSMI19].ranges = xe3p_xpc_psmi_grp19_steering_table;
+ } else if (GRAPHICS_VER(xe) >= 20) {
gt->steering[DSS].ranges = xe2lpg_dss_steering_table;
gt->steering[SQIDI_PSMI].ranges = xe2lpg_sqidi_psmi_steering_table;
gt->steering[INSTANCE0].ranges = xe2lpg_instance0_steering_table;
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 6344b5205c08..c0c0215c0703 100644
--- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_config.c
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_config.c
@@ -1484,7 +1484,8 @@ static int pf_provision_vf_lmem(struct xe_gt *gt, unsigned int vfid, u64 size)
XE_BO_FLAG_VRAM_IF_DGFX(tile) |
XE_BO_FLAG_NEEDS_2M |
XE_BO_FLAG_PINNED |
- XE_BO_FLAG_PINNED_LATE_RESTORE);
+ XE_BO_FLAG_PINNED_LATE_RESTORE |
+ XE_BO_FLAG_FORCE_USER_VRAM);
if (IS_ERR(bo))
return PTR_ERR(bo);
@@ -1547,7 +1548,8 @@ int xe_gt_sriov_pf_config_set_lmem(struct xe_gt *gt, unsigned int vfid, u64 size
{
int err;
- xe_gt_assert(gt, xe_device_has_lmtt(gt_to_xe(gt)));
+ if (!xe_device_has_lmtt(gt_to_xe(gt)))
+ return -EPERM;
mutex_lock(xe_gt_sriov_pf_master_mutex(gt));
if (vfid)
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_control.c b/drivers/gpu/drm/xe/xe_gt_sriov_pf_control.c
index 4f7fff892bc0..2e6bd3d1fe1d 100644
--- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_control.c
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_control.c
@@ -18,6 +18,7 @@
#include "xe_gt_sriov_printk.h"
#include "xe_guc_ct.h"
#include "xe_sriov.h"
+#include "xe_sriov_pf_control.h"
#include "xe_sriov_pf_service.h"
#include "xe_tile.h"
@@ -170,6 +171,7 @@ static const char *control_bit_to_string(enum xe_gt_sriov_control_bits bit)
CASE2STR(FLR_SEND_START);
CASE2STR(FLR_WAIT_GUC);
CASE2STR(FLR_GUC_DONE);
+ CASE2STR(FLR_SYNC);
CASE2STR(FLR_RESET_CONFIG);
CASE2STR(FLR_RESET_DATA);
CASE2STR(FLR_RESET_MMIO);
@@ -271,12 +273,19 @@ static bool pf_expect_vf_not_state(struct xe_gt *gt, unsigned int vfid,
return result;
}
+static void pf_track_vf_state(struct xe_gt *gt, unsigned int vfid,
+ enum xe_gt_sriov_control_bits bit,
+ const char *what)
+{
+ xe_gt_sriov_dbg_verbose(gt, "VF%u state %s(%d) %s\n",
+ vfid, control_bit_to_string(bit), bit, what);
+}
+
static bool pf_enter_vf_state(struct xe_gt *gt, unsigned int vfid,
enum xe_gt_sriov_control_bits bit)
{
if (!test_and_set_bit(bit, pf_peek_vf_state(gt, vfid))) {
- xe_gt_sriov_dbg_verbose(gt, "VF%u state %s(%d) enter\n",
- vfid, control_bit_to_string(bit), bit);
+ pf_track_vf_state(gt, vfid, bit, "enter");
return true;
}
return false;
@@ -286,8 +295,7 @@ static bool pf_exit_vf_state(struct xe_gt *gt, unsigned int vfid,
enum xe_gt_sriov_control_bits bit)
{
if (test_and_clear_bit(bit, pf_peek_vf_state(gt, vfid))) {
- xe_gt_sriov_dbg_verbose(gt, "VF%u state %s(%d) exit\n",
- vfid, control_bit_to_string(bit), bit);
+ pf_track_vf_state(gt, vfid, bit, "exit");
return true;
}
return false;
@@ -616,7 +624,7 @@ int xe_gt_sriov_pf_control_pause_vf(struct xe_gt *gt, unsigned int vfid)
}
if (pf_expect_vf_state(gt, vfid, XE_GT_SRIOV_STATE_PAUSED)) {
- xe_gt_sriov_info(gt, "VF%u paused!\n", vfid);
+ xe_gt_sriov_dbg(gt, "VF%u paused!\n", vfid);
return 0;
}
@@ -755,7 +763,7 @@ int xe_gt_sriov_pf_control_resume_vf(struct xe_gt *gt, unsigned int vfid)
return err;
if (pf_expect_vf_state(gt, vfid, XE_GT_SRIOV_STATE_RESUMED)) {
- xe_gt_sriov_info(gt, "VF%u resumed!\n", vfid);
+ xe_gt_sriov_dbg(gt, "VF%u resumed!\n", vfid);
return 0;
}
@@ -896,7 +904,7 @@ int xe_gt_sriov_pf_control_stop_vf(struct xe_gt *gt, unsigned int vfid)
return err;
if (pf_expect_vf_state(gt, vfid, XE_GT_SRIOV_STATE_STOPPED)) {
- xe_gt_sriov_info(gt, "VF%u stopped!\n", vfid);
+ xe_gt_sriov_dbg(gt, "VF%u stopped!\n", vfid);
return 0;
}
@@ -934,6 +942,10 @@ int xe_gt_sriov_pf_control_stop_vf(struct xe_gt *gt, unsigned int vfid)
* : v : | |
* : FLR_GUC_DONE : | |
* : | : | |
+ * : | o--<--sync : | |
+ * : |/ / : | |
+ * : FLR_SYNC--o : | |
+ * : | : | |
* : FLR_RESET_CONFIG---failed--->-----------o--------+-----------o
* : | : | |
* : FLR_RESET_DATA : | |
@@ -1141,12 +1153,38 @@ static bool pf_exit_vf_flr_send_start(struct xe_gt *gt, unsigned int vfid)
return true;
}
+static bool pf_exit_vf_flr_sync(struct xe_gt *gt, unsigned int vfid)
+{
+ if (!pf_exit_vf_state(gt, vfid, XE_GT_SRIOV_STATE_FLR_SYNC))
+ return false;
+
+ pf_enter_vf_flr_reset_config(gt, vfid);
+ return true;
+}
+
+static void pf_enter_vf_flr_sync(struct xe_gt *gt, unsigned int vfid)
+{
+ int ret;
+
+ if (!pf_enter_vf_state(gt, vfid, XE_GT_SRIOV_STATE_FLR_SYNC))
+ pf_enter_vf_state_machine_bug(gt, vfid);
+
+ ret = xe_sriov_pf_control_sync_flr(gt_to_xe(gt), vfid);
+ if (ret < 0) {
+ xe_gt_sriov_dbg_verbose(gt, "FLR checkpoint %pe\n", ERR_PTR(ret));
+ pf_expect_vf_state(gt, vfid, XE_GT_SRIOV_STATE_FLR_SYNC);
+ } else {
+ xe_gt_sriov_dbg_verbose(gt, "FLR checkpoint pass\n");
+ pf_expect_vf_not_state(gt, vfid, XE_GT_SRIOV_STATE_FLR_SYNC);
+ }
+}
+
static bool pf_exit_vf_flr_guc_done(struct xe_gt *gt, unsigned int vfid)
{
if (!pf_exit_vf_state(gt, vfid, XE_GT_SRIOV_STATE_FLR_GUC_DONE))
return false;
- pf_enter_vf_flr_reset_config(gt, vfid);
+ pf_enter_vf_flr_sync(gt, vfid);
return true;
}
@@ -1167,10 +1205,52 @@ static void pf_enter_vf_flr_guc_done(struct xe_gt *gt, unsigned int vfid)
*/
int xe_gt_sriov_pf_control_trigger_flr(struct xe_gt *gt, unsigned int vfid)
{
+ pf_enter_vf_flr_wip(gt, vfid);
+
+ return 0;
+}
+
+/**
+ * xe_gt_sriov_pf_control_sync_flr() - Synchronize on the VF FLR checkpoint.
+ * @gt: the &xe_gt
+ * @vfid: the VF identifier
+ * @sync: if true it will allow to exit the checkpoint
+ *
+ * Return: non-zero if FLR checkpoint has been reached, zero if the is no FLR
+ * in progress, or a negative error code on the FLR busy or failed.
+ */
+int xe_gt_sriov_pf_control_sync_flr(struct xe_gt *gt, unsigned int vfid, bool sync)
+{
+ if (sync && pf_exit_vf_flr_sync(gt, vfid))
+ return 1;
+ if (pf_check_vf_state(gt, vfid, XE_GT_SRIOV_STATE_FLR_SYNC))
+ return 1;
+ if (pf_check_vf_state(gt, vfid, XE_GT_SRIOV_STATE_FLR_WIP))
+ return -EBUSY;
+ if (pf_check_vf_state(gt, vfid, XE_GT_SRIOV_STATE_FLR_FAILED))
+ return -EIO;
+ return 0;
+}
+
+/**
+ * xe_gt_sriov_pf_control_wait_flr() - Wait for a VF FLR to complete.
+ * @gt: the &xe_gt
+ * @vfid: the VF identifier
+ *
+ * This function is for PF only.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_gt_sriov_pf_control_wait_flr(struct xe_gt *gt, unsigned int vfid)
+{
unsigned long timeout = pf_get_default_timeout(XE_GT_SRIOV_STATE_FLR_WIP);
int err;
- pf_enter_vf_flr_wip(gt, vfid);
+ if (pf_check_vf_state(gt, vfid, XE_GT_SRIOV_STATE_FLR_FAILED))
+ return -EIO;
+
+ if (!pf_check_vf_state(gt, vfid, XE_GT_SRIOV_STATE_FLR_WIP))
+ return 0;
err = pf_wait_vf_wip_done(gt, vfid, timeout);
if (err) {
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_control.h b/drivers/gpu/drm/xe/xe_gt_sriov_pf_control.h
index c85e64f099cc..8a72ef3778d4 100644
--- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_control.h
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_control.h
@@ -18,6 +18,8 @@ int xe_gt_sriov_pf_control_pause_vf(struct xe_gt *gt, unsigned int vfid);
int xe_gt_sriov_pf_control_resume_vf(struct xe_gt *gt, unsigned int vfid);
int xe_gt_sriov_pf_control_stop_vf(struct xe_gt *gt, unsigned int vfid);
int xe_gt_sriov_pf_control_trigger_flr(struct xe_gt *gt, unsigned int vfid);
+int xe_gt_sriov_pf_control_sync_flr(struct xe_gt *gt, unsigned int vfid, bool sync);
+int xe_gt_sriov_pf_control_wait_flr(struct xe_gt *gt, unsigned int vfid);
#ifdef CONFIG_PCI_IOV
int xe_gt_sriov_pf_control_process_guc2pf(struct xe_gt *gt, const u32 *msg, u32 len);
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_control_types.h b/drivers/gpu/drm/xe/xe_gt_sriov_pf_control_types.h
index f02f941b4ad2..c80b7e77f1ad 100644
--- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_control_types.h
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_control_types.h
@@ -18,6 +18,7 @@
* @XE_GT_SRIOV_STATE_FLR_SEND_START: indicates that the PF wants to send a FLR START command.
* @XE_GT_SRIOV_STATE_FLR_WAIT_GUC: indicates that the PF awaits for a response from the GuC.
* @XE_GT_SRIOV_STATE_FLR_GUC_DONE: indicates that the PF has received a response from the GuC.
+ * @XE_GT_SRIOV_STATE_FLR_SYNC: indicates that the PF awaits to synchronize with other GuCs.
* @XE_GT_SRIOV_STATE_FLR_RESET_CONFIG: indicates that the PF needs to clear VF's resources.
* @XE_GT_SRIOV_STATE_FLR_RESET_DATA: indicates that the PF needs to clear VF's data.
* @XE_GT_SRIOV_STATE_FLR_RESET_MMIO: indicates that the PF needs to reset VF's registers.
@@ -47,6 +48,7 @@ enum xe_gt_sriov_control_bits {
XE_GT_SRIOV_STATE_FLR_SEND_START,
XE_GT_SRIOV_STATE_FLR_WAIT_GUC,
XE_GT_SRIOV_STATE_FLR_GUC_DONE,
+ XE_GT_SRIOV_STATE_FLR_SYNC,
XE_GT_SRIOV_STATE_FLR_RESET_CONFIG,
XE_GT_SRIOV_STATE_FLR_RESET_DATA,
XE_GT_SRIOV_STATE_FLR_RESET_MMIO,
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 3ed245e04d0c..838beb7f6327 100644
--- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_debugfs.c
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_debugfs.c
@@ -23,14 +23,25 @@
#include "xe_gt_sriov_pf_service.h"
#include "xe_pm.h"
#include "xe_sriov_pf.h"
+#include "xe_sriov_pf_provision.h"
/*
- * /sys/kernel/debug/dri/0/
- * ├── gt0 # d_inode->i_private = gt
- * │   ├── pf # d_inode->i_private = gt
- * │   ├── vf1 # d_inode->i_private = VFID(1)
- * :   :
- * │   ├── vfN # d_inode->i_private = VFID(N)
+ * /sys/kernel/debug/dri/BDF/
+ * ├── sriov # d_inode->i_private = (xe_device*)
+ * │ ├── pf # d_inode->i_private = (xe_device*)
+ * │ │ ├── tile0 # d_inode->i_private = (xe_tile*)
+ * │ │ │ ├── gt0 # d_inode->i_private = (xe_gt*)
+ * │ │ │ ├── gt1 # d_inode->i_private = (xe_gt*)
+ * │ │ ├── tile1
+ * │ │ │ :
+ * │ ├── vf1 # d_inode->i_private = VFID(1)
+ * │ │ ├── tile0 # d_inode->i_private = (xe_tile*)
+ * │ │ │ ├── gt0 # d_inode->i_private = (xe_gt*)
+ * │ │ │ ├── gt1 # d_inode->i_private = (xe_gt*)
+ * │ │ ├── tile1
+ * │ │ │ :
+ * : :
+ * │ ├── vfN # d_inode->i_private = VFID(N)
*/
static void *extract_priv(struct dentry *d)
@@ -40,26 +51,31 @@ static void *extract_priv(struct dentry *d)
static struct xe_gt *extract_gt(struct dentry *d)
{
- return extract_priv(d->d_parent);
+ return extract_priv(d);
+}
+
+static struct xe_device *extract_xe(struct dentry *d)
+{
+ return extract_priv(d->d_parent->d_parent->d_parent);
}
static unsigned int extract_vfid(struct dentry *d)
{
- return extract_priv(d) == extract_gt(d) ? PFID : (uintptr_t)extract_priv(d);
+ void *priv = extract_priv(d->d_parent->d_parent);
+
+ return priv == extract_xe(d) ? PFID : (uintptr_t)priv;
}
/*
- * /sys/kernel/debug/dri/0/
- * ├── gt0
- * │   ├── pf
- * │   │   ├── contexts_provisioned
- * │   │   ├── doorbells_provisioned
- * │   │   ├── runtime_registers
- * │   │   ├── negotiated_versions
- * │   │   ├── adverse_events
- * ├── gt1
- * │   ├── pf
- * │   │   ├── ...
+ * /sys/kernel/debug/dri/BDF/
+ * ├── sriov
+ * : ├── pf
+ * : ├── tile0
+ * : ├── gt0
+ * : ├── contexts_provisioned
+ * ├── doorbells_provisioned
+ * ├── runtime_registers
+ * ├── adverse_events
*/
static const struct drm_info_list pf_info[] = {
@@ -86,48 +102,14 @@ static const struct drm_info_list pf_info[] = {
};
/*
- * /sys/kernel/debug/dri/0/
- * ├── gt0
- * │   ├── pf
- * │   │   ├── ggtt_available
- * │   │   ├── ggtt_provisioned
- */
-
-static const struct drm_info_list pf_ggtt_info[] = {
- {
- "ggtt_available",
- .show = xe_gt_debugfs_simple_show,
- .data = xe_gt_sriov_pf_config_print_available_ggtt,
- },
- {
- "ggtt_provisioned",
- .show = xe_gt_debugfs_simple_show,
- .data = xe_gt_sriov_pf_config_print_ggtt,
- },
-};
-
-/*
- * /sys/kernel/debug/dri/0/
- * ├── gt0
- * │   ├── pf
- * │   │   ├── lmem_provisioned
- */
-
-static const struct drm_info_list pf_lmem_info[] = {
- {
- "lmem_provisioned",
- .show = xe_gt_debugfs_simple_show,
- .data = xe_gt_sriov_pf_config_print_lmem,
- },
-};
-
-/*
- * /sys/kernel/debug/dri/0/
- * ├── gt0
- * │   ├── pf
- * │   │   ├── reset_engine
- * │   │   ├── sample_period
- * │   │   ├── sched_if_idle
+ * /sys/kernel/debug/dri/BDF/
+ * ├── sriov
+ * : ├── pf
+ * : ├── tile0
+ * : ├── gt0
+ * : ├── reset_engine
+ * ├── sample_period
+ * ├── sched_if_idle
*/
#define DEFINE_SRIOV_GT_POLICY_DEBUGFS_ATTRIBUTE(POLICY, TYPE, FORMAT) \
@@ -143,6 +125,8 @@ static int POLICY##_set(void *data, u64 val) \
\
xe_pm_runtime_get(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; \
@@ -173,24 +157,24 @@ static void pf_add_policy_attrs(struct xe_gt *gt, struct dentry *parent)
}
/*
- * /sys/kernel/debug/dri/0/
- * ├── gt0
- * │   ├── pf
- * │   │   ├── ggtt_spare
- * │   │   ├── lmem_spare
- * │   │   ├── doorbells_spare
- * │   │   ├── contexts_spare
- * │   │   ├── exec_quantum_ms
- * │   │   ├── preempt_timeout_us
- * │   │   ├── sched_priority
- * │   ├── vf1
- * │   │   ├── ggtt_quota
- * │   │   ├── lmem_quota
- * │   │   ├── doorbells_quota
- * │   │   ├── contexts_quota
- * │   │   ├── exec_quantum_ms
- * │   │   ├── preempt_timeout_us
- * │   │   ├── sched_priority
+ * /sys/kernel/debug/dri/BDF/
+ * ├── sriov
+ * : ├── pf
+ * │ ├── tile0
+ * │ : ├── gt0
+ * │ : ├── doorbells_spare
+ * │ ├── contexts_spare
+ * │ ├── exec_quantum_ms
+ * │ ├── preempt_timeout_us
+ * │ ├── sched_priority
+ * ├── vf1
+ * : ├── tile0
+ * : ├── gt0
+ * : ├── doorbells_quota
+ * ├── contexts_quota
+ * ├── exec_quantum_ms
+ * ├── preempt_timeout_us
+ * ├── sched_priority
*/
#define DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(CONFIG, TYPE, FORMAT) \
@@ -208,6 +192,8 @@ static int CONFIG##_set(void *data, u64 val) \
xe_pm_runtime_get(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; \
@@ -224,8 +210,6 @@ static int CONFIG##_get(void *data, u64 *val) \
\
DEFINE_DEBUGFS_ATTRIBUTE(CONFIG##_fops, CONFIG##_get, CONFIG##_set, FORMAT)
-DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(ggtt, u64, "%llu\n");
-DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(lmem, u64, "%llu\n");
DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(ctxs, u32, "%llu\n");
DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(dbs, u32, "%llu\n");
DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(exec_quantum, u32, "%llu\n");
@@ -233,22 +217,26 @@ DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(preempt_timeout, u32, "%llu\n");
DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(sched_priority, u32, "%llu\n");
/*
- * /sys/kernel/debug/dri/0/
- * ├── gt0
- * │   ├── pf
- * │   │   ├── threshold_cat_error_count
- * │   │   ├── threshold_doorbell_time_us
- * │   │   ├── threshold_engine_reset_count
- * │   │   ├── threshold_guc_time_us
- * │   │   ├── threshold_irq_time_us
- * │   │   ├── threshold_page_fault_count
- * │   ├── vf1
- * │   │   ├── threshold_cat_error_count
- * │   │   ├── threshold_doorbell_time_us
- * │   │   ├── threshold_engine_reset_count
- * │   │   ├── threshold_guc_time_us
- * │   │   ├── threshold_irq_time_us
- * │   │   ├── threshold_page_fault_count
+ * /sys/kernel/debug/dri/BDF/
+ * ├── sriov
+ * : ├── pf
+ * │ ├── tile0
+ * │ : ├── gt0
+ * │ : ├── threshold_cat_error_count
+ * │ ├── threshold_doorbell_time_us
+ * │ ├── threshold_engine_reset_count
+ * │ ├── threshold_guc_time_us
+ * │ ├── threshold_irq_time_us
+ * │ ├── threshold_page_fault_count
+ * ├── vf1
+ * : ├── tile0
+ * : ├── gt0
+ * : ├── threshold_cat_error_count
+ * ├── threshold_doorbell_time_us
+ * ├── threshold_engine_reset_count
+ * ├── threshold_guc_time_us
+ * ├── threshold_irq_time_us
+ * ├── threshold_page_fault_count
*/
static int set_threshold(void *data, u64 val, enum xe_guc_klv_threshold_index index)
@@ -263,6 +251,8 @@ static int set_threshold(void *data, u64 val, enum xe_guc_klv_threshold_index in
xe_pm_runtime_get(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;
@@ -302,13 +292,6 @@ static void pf_add_config_attrs(struct xe_gt *gt, struct dentry *parent, unsigne
xe_gt_assert(gt, gt == extract_gt(parent));
xe_gt_assert(gt, vfid == extract_vfid(parent));
- if (xe_gt_is_main_type(gt)) {
- debugfs_create_file_unsafe(vfid ? "ggtt_quota" : "ggtt_spare",
- 0644, parent, parent, &ggtt_fops);
- if (xe_device_has_lmtt(gt_to_xe(gt)))
- debugfs_create_file_unsafe(vfid ? "lmem_quota" : "lmem_spare",
- 0644, parent, parent, &lmem_fops);
- }
debugfs_create_file_unsafe(vfid ? "doorbells_quota" : "doorbells_spare",
0644, parent, parent, &dbs_fops);
debugfs_create_file_unsafe(vfid ? "contexts_quota" : "contexts_spare",
@@ -329,10 +312,12 @@ static void pf_add_config_attrs(struct xe_gt *gt, struct dentry *parent, unsigne
}
/*
- * /sys/kernel/debug/dri/0/
- * ├── gt0
- * │   ├── vf1
- * │   │   ├── control { stop, pause, resume }
+ * /sys/kernel/debug/dri/BDF/
+ * ├── sriov
+ * : ├── vf1
+ * : ├── tile0
+ * : ├── gt0
+ * : ├── control { stop, pause, resume }
*/
static const struct {
@@ -409,11 +394,14 @@ static const struct file_operations control_ops = {
};
/*
- * /sys/kernel/debug/dri/0/
- * ├── gt0
- * │   ├── vf1
- * │   │   ├── guc_state
+ * /sys/kernel/debug/dri/BDF/
+ * ├── sriov
+ * : ├── vf1
+ * : ├── tile0
+ * : ├── gt0
+ * : ├── guc_state
*/
+
static ssize_t guc_state_read(struct file *file, char __user *buf,
size_t count, loff_t *pos)
{
@@ -447,20 +435,27 @@ static const struct file_operations guc_state_ops = {
};
/*
- * /sys/kernel/debug/dri/0/
- * ├── gt0
- * │   ├── vf1
- * │   │   ├── config_blob
+ * /sys/kernel/debug/dri/BDF/
+ * ├── sriov
+ * : ├── vf1
+ * : ├── tile0
+ * : ├── gt0
+ * : ├── config_blob
*/
-static ssize_t config_blob_read(struct file *file, char __user *buf,
- size_t count, loff_t *pos)
+
+struct config_blob_data {
+ size_t size;
+ u8 blob[];
+};
+
+static int config_blob_open(struct inode *inode, struct file *file)
{
struct dentry *dent = file_dentry(file);
struct dentry *parent = dent->d_parent;
struct xe_gt *gt = extract_gt(parent);
unsigned int vfid = extract_vfid(parent);
+ struct config_blob_data *cbd;
ssize_t ret;
- void *tmp;
ret = xe_gt_sriov_pf_config_save(gt, vfid, NULL, 0);
if (!ret)
@@ -468,16 +463,27 @@ static ssize_t config_blob_read(struct file *file, char __user *buf,
if (ret < 0)
return ret;
- tmp = kzalloc(ret, GFP_KERNEL);
- if (!tmp)
+ cbd = kzalloc(struct_size(cbd, blob, ret), GFP_KERNEL);
+ if (!cbd)
return -ENOMEM;
- ret = xe_gt_sriov_pf_config_save(gt, vfid, tmp, ret);
- if (ret > 0)
- ret = simple_read_from_buffer(buf, count, pos, tmp, ret);
+ ret = xe_gt_sriov_pf_config_save(gt, vfid, cbd->blob, ret);
+ if (ret < 0) {
+ kfree(cbd);
+ return ret;
+ }
- kfree(tmp);
- return ret;
+ cbd->size = ret;
+ file->private_data = cbd;
+ return nonseekable_open(inode, file);
+}
+
+static ssize_t config_blob_read(struct file *file, char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct config_blob_data *cbd = file->private_data;
+
+ return simple_read_from_buffer(buf, count, pos, cbd->blob, cbd->size);
}
static ssize_t config_blob_write(struct file *file, const char __user *buf,
@@ -514,80 +520,150 @@ static ssize_t config_blob_write(struct file *file, const char __user *buf,
return ret;
}
+static int config_blob_release(struct inode *inode, struct file *file)
+{
+ kfree(file->private_data);
+ return 0;
+}
+
static const struct file_operations config_blob_ops = {
.owner = THIS_MODULE,
+ .open = config_blob_open,
.read = config_blob_read,
.write = config_blob_write,
- .llseek = default_llseek,
+ .release = config_blob_release,
};
-/**
- * xe_gt_sriov_pf_debugfs_register - Register SR-IOV PF specific entries in GT debugfs.
- * @gt: the &xe_gt to register
- * @root: the &dentry that represents the GT directory
- *
- * Register SR-IOV PF entries that are GT related and must be shown under GT debugfs.
- */
-void xe_gt_sriov_pf_debugfs_register(struct xe_gt *gt, struct dentry *root)
+static void pf_add_compat_attrs(struct xe_gt *gt, struct dentry *dent, unsigned int vfid)
{
struct xe_device *xe = gt_to_xe(gt);
- struct drm_minor *minor = xe->drm.primary;
- int n, totalvfs = xe_sriov_pf_get_totalvfs(xe);
- struct dentry *pfdentry;
- struct dentry *vfdentry;
- char buf[14]; /* should be enough up to "vf%u\0" for 2^32 - 1 */
-
- xe_gt_assert(gt, IS_SRIOV_PF(xe));
- xe_gt_assert(gt, root->d_inode->i_private == gt);
- /*
- * /sys/kernel/debug/dri/0/
- * ├── gt0
- * │   ├── pf
- */
- pfdentry = debugfs_create_dir("pf", root);
- if (IS_ERR(pfdentry))
+ if (!xe_gt_is_main_type(gt))
return;
- pfdentry->d_inode->i_private = gt;
-
- drm_debugfs_create_files(pf_info, ARRAY_SIZE(pf_info), pfdentry, minor);
- if (xe_gt_is_main_type(gt)) {
- drm_debugfs_create_files(pf_ggtt_info,
- ARRAY_SIZE(pf_ggtt_info),
- pfdentry, minor);
- if (xe_device_has_lmtt(gt_to_xe(gt)))
- drm_debugfs_create_files(pf_lmem_info,
- ARRAY_SIZE(pf_lmem_info),
- pfdentry, minor);
+
+ if (vfid) {
+ debugfs_create_symlink("ggtt_quota", dent, "../ggtt_quota");
+ if (xe_device_has_lmtt(xe))
+ debugfs_create_symlink("lmem_quota", dent, "../vram_quota");
+ } else {
+ debugfs_create_symlink("ggtt_spare", dent, "../ggtt_spare");
+ debugfs_create_symlink("ggtt_available", dent, "../ggtt_available");
+ debugfs_create_symlink("ggtt_provisioned", dent, "../ggtt_provisioned");
+ if (xe_device_has_lmtt(xe)) {
+ debugfs_create_symlink("lmem_spare", dent, "../vram_spare");
+ debugfs_create_symlink("lmem_provisioned", dent, "../vram_provisioned");
+ }
}
+}
- pf_add_policy_attrs(gt, pfdentry);
- pf_add_config_attrs(gt, pfdentry, PFID);
-
- for (n = 1; n <= totalvfs; n++) {
- /*
- * /sys/kernel/debug/dri/0/
- * ├── gt0
- * │   ├── vf1
- * │   ├── vf2
- */
- snprintf(buf, sizeof(buf), "vf%u", n);
- vfdentry = debugfs_create_dir(buf, root);
- if (IS_ERR(vfdentry))
- break;
- vfdentry->d_inode->i_private = (void *)(uintptr_t)n;
+static void pf_populate_gt(struct xe_gt *gt, struct dentry *dent, unsigned int vfid)
+{
+ struct xe_device *xe = gt_to_xe(gt);
+ struct drm_minor *minor = xe->drm.primary;
- pf_add_config_attrs(gt, vfdentry, VFID(n));
- debugfs_create_file("control", 0600, vfdentry, NULL, &control_ops);
+ if (vfid) {
+ pf_add_config_attrs(gt, dent, vfid);
+
+ debugfs_create_file("control", 0600, dent, NULL, &control_ops);
/* for testing/debugging purposes only! */
if (IS_ENABLED(CONFIG_DRM_XE_DEBUG)) {
debugfs_create_file("guc_state",
IS_ENABLED(CONFIG_DRM_XE_DEBUG_SRIOV) ? 0600 : 0400,
- vfdentry, NULL, &guc_state_ops);
+ dent, NULL, &guc_state_ops);
debugfs_create_file("config_blob",
IS_ENABLED(CONFIG_DRM_XE_DEBUG_SRIOV) ? 0600 : 0400,
- vfdentry, NULL, &config_blob_ops);
+ dent, NULL, &config_blob_ops);
}
+
+ } else {
+ pf_add_config_attrs(gt, dent, PFID);
+ pf_add_policy_attrs(gt, dent);
+
+ drm_debugfs_create_files(pf_info, ARRAY_SIZE(pf_info), dent, minor);
+ }
+
+ /* for backward compatibility only */
+ pf_add_compat_attrs(gt, dent, vfid);
+}
+
+/**
+ * xe_gt_sriov_pf_debugfs_populate() - Create SR-IOV GT-level debugfs directories and files.
+ * @gt: the &xe_gt to register
+ * @parent: the parent &dentry that represents a &xe_tile
+ * @vfid: the VF identifier
+ *
+ * Add to the @parent directory new debugfs directory that will represent a @gt and
+ * populate it with GT files that are related to the SR-IOV @vfid function.
+ *
+ * This function can only be called on PF.
+ */
+void xe_gt_sriov_pf_debugfs_populate(struct xe_gt *gt, struct dentry *parent, unsigned int vfid)
+{
+ struct dentry *dent;
+ char name[8]; /* should be enough up to "gt%u\0" for 2^8 - 1 */
+
+ xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
+ xe_gt_assert(gt, extract_priv(parent) == gt->tile);
+ xe_gt_assert(gt, extract_priv(parent->d_parent) == gt_to_xe(gt) ||
+ (uintptr_t)extract_priv(parent->d_parent) == vfid);
+
+ /*
+ * /sys/kernel/debug/dri/BDF/
+ * ├── sriov
+ * │ ├── pf
+ * │ │ ├── tile0 # parent
+ * │ │ │ ├── gt0 # d_inode->i_private = (xe_gt*)
+ * │ │ │ ├── gt1
+ * │ │ : :
+ * │ ├── vf1
+ * │ │ ├── tile0 # parent
+ * │ │ │ ├── gt0 # d_inode->i_private = (xe_gt*)
+ * │ │ │ ├── gt1
+ * │ : : :
+ */
+ snprintf(name, sizeof(name), "gt%u", gt->info.id);
+ dent = debugfs_create_dir(name, parent);
+ if (IS_ERR(dent))
+ return;
+ dent->d_inode->i_private = gt;
+
+ xe_gt_assert(gt, extract_gt(dent) == gt);
+ xe_gt_assert(gt, extract_vfid(dent) == vfid);
+
+ pf_populate_gt(gt, dent, vfid);
+}
+
+static void pf_add_links(struct xe_gt *gt, struct dentry *dent)
+{
+ unsigned int totalvfs = xe_gt_sriov_pf_get_totalvfs(gt);
+ unsigned int vfid;
+ char name[16]; /* should be more than enough for "vf%u\0" and VFID(UINT_MAX) */
+ char symlink[64]; /* should be more enough for "../../sriov/vf%u/tile%u/gt%u\0" */
+
+ for (vfid = 0; vfid <= totalvfs; vfid++) {
+ if (vfid)
+ snprintf(name, sizeof(name), "vf%u", vfid);
+ else
+ snprintf(name, sizeof(name), "pf");
+ snprintf(symlink, sizeof(symlink), "../../sriov/%s/tile%u/gt%u",
+ name, gt->tile->id, gt->info.id);
+ debugfs_create_symlink(name, dent, symlink);
}
}
+
+/**
+ * xe_gt_sriov_pf_debugfs_register - Register SR-IOV PF specific entries in GT debugfs.
+ * @gt: the &xe_gt to register
+ * @dent: the &dentry that represents the GT directory
+ *
+ * Instead of actual files, create symlinks for PF and each VF to their GT specific
+ * attributes that should be already exposed in the dedicated debugfs SR-IOV tree.
+ */
+void xe_gt_sriov_pf_debugfs_register(struct xe_gt *gt, struct dentry *dent)
+{
+ xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
+ xe_gt_assert(gt, dent->d_inode->i_private == gt);
+
+ pf_add_links(gt, dent);
+}
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_debugfs.h b/drivers/gpu/drm/xe/xe_gt_sriov_pf_debugfs.h
index 038cc8ddc244..82ff3b7f0532 100644
--- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_debugfs.h
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_debugfs.h
@@ -11,6 +11,7 @@ struct dentry;
#ifdef CONFIG_PCI_IOV
void xe_gt_sriov_pf_debugfs_register(struct xe_gt *gt, struct dentry *root);
+void xe_gt_sriov_pf_debugfs_populate(struct xe_gt *gt, struct dentry *parent, unsigned int vfid);
#else
static inline void xe_gt_sriov_pf_debugfs_register(struct xe_gt *gt, struct dentry *root) { }
#endif
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_printk.h b/drivers/gpu/drm/xe/xe_gt_sriov_printk.h
index 17624b16300a..d3457d608db8 100644
--- a/drivers/gpu/drm/xe/xe_gt_sriov_printk.h
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_printk.h
@@ -7,10 +7,13 @@
#define _XE_GT_SRIOV_PRINTK_H_
#include "xe_gt_printk.h"
-#include "xe_sriov_printk.h"
+#include "xe_tile_sriov_printk.h"
+
+#define __XE_GT_SRIOV_PRINTK_FMT(_gt, _fmt, ...) \
+ __XE_TILE_SRIOV_PRINTK_FMT((_gt)->tile, __XE_GT_PRINTK_FMT((_gt), _fmt, ##__VA_ARGS__))
#define __xe_gt_sriov_printk(gt, _level, fmt, ...) \
- xe_gt_printk((gt), _level, "%s" fmt, xe_sriov_printk_prefix(gt_to_xe(gt)), ##__VA_ARGS__)
+ xe_sriov_##_level(gt_to_xe(gt), __XE_GT_SRIOV_PRINTK_FMT((gt), fmt, ##__VA_ARGS__))
#define xe_gt_sriov_err(_gt, _fmt, ...) \
__xe_gt_sriov_printk(_gt, err, _fmt, ##__VA_ARGS__)
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_vf.c b/drivers/gpu/drm/xe/xe_gt_sriov_vf.c
index 0461d5513487..46518e629ba3 100644
--- a/drivers/gpu/drm/xe/xe_gt_sriov_vf.c
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_vf.c
@@ -23,12 +23,20 @@
#include "xe_gt_sriov_vf.h"
#include "xe_gt_sriov_vf_types.h"
#include "xe_guc.h"
+#include "xe_guc_ct.h"
#include "xe_guc_hxg_helpers.h"
#include "xe_guc_relay.h"
+#include "xe_guc_submit.h"
+#include "xe_irq.h"
#include "xe_lrc.h"
+#include "xe_memirq.h"
#include "xe_mmio.h"
+#include "xe_pm.h"
#include "xe_sriov.h"
#include "xe_sriov_vf.h"
+#include "xe_sriov_vf_ccs.h"
+#include "xe_tile_sriov_vf.h"
+#include "xe_tlb_inval.h"
#include "xe_uc_fw.h"
#include "xe_wopcm.h"
@@ -307,13 +315,13 @@ static int guc_action_vf_notify_resfix_done(struct xe_guc *guc)
}
/**
- * xe_gt_sriov_vf_notify_resfix_done - Notify GuC about resource fixups apply completed.
+ * 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.
*/
-int xe_gt_sriov_vf_notify_resfix_done(struct xe_gt *gt)
+static int vf_notify_resfix_done(struct xe_gt *gt)
{
struct xe_guc *guc = &gt->uc.guc;
int err;
@@ -433,13 +441,17 @@ u32 xe_gt_sriov_vf_gmdid(struct xe_gt *gt)
static int vf_get_ggtt_info(struct xe_gt *gt)
{
- struct xe_gt_sriov_vf_selfconfig *config = &gt->sriov.vf.self_config;
+ struct xe_tile *tile = gt_to_tile(gt);
+ struct xe_ggtt *ggtt = tile->mem.ggtt;
struct xe_guc *guc = &gt->uc.guc;
- u64 start, size;
+ u64 start, size, ggtt_size;
+ s64 shift;
int err;
xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
+ guard(mutex)(&ggtt->lock);
+
err = guc_action_query_single_klv64(guc, GUC_KLV_VF_CFG_GGTT_START_KEY, &start);
if (unlikely(err))
return err;
@@ -448,28 +460,44 @@ static int vf_get_ggtt_info(struct xe_gt *gt)
if (unlikely(err))
return err;
- if (config->ggtt_size && config->ggtt_size != size) {
+ if (!size)
+ return -ENODATA;
+
+ ggtt_size = xe_tile_sriov_vf_ggtt(tile);
+ if (ggtt_size && ggtt_size != size) {
xe_gt_sriov_err(gt, "Unexpected GGTT reassignment: %lluK != %lluK\n",
- size / SZ_1K, config->ggtt_size / SZ_1K);
+ size / SZ_1K, ggtt_size / SZ_1K);
return -EREMCHG;
}
xe_gt_sriov_dbg_verbose(gt, "GGTT %#llx-%#llx = %lluK\n",
start, start + size - 1, size / SZ_1K);
- config->ggtt_shift = start - (s64)config->ggtt_base;
- config->ggtt_base = start;
- config->ggtt_size = size;
+ shift = start - (s64)xe_tile_sriov_vf_ggtt_base(tile);
+ xe_tile_sriov_vf_ggtt_base_store(tile, start);
+ xe_tile_sriov_vf_ggtt_store(tile, size);
- return config->ggtt_size ? 0 : -ENODATA;
+ if (shift && shift != start) {
+ xe_gt_sriov_info(gt, "Shifting GGTT base by %lld to 0x%016llx\n",
+ shift, start);
+ xe_tile_sriov_vf_fixup_ggtt_nodes_locked(gt_to_tile(gt), shift);
+ }
+
+ if (xe_sriov_vf_migration_supported(gt_to_xe(gt))) {
+ WRITE_ONCE(gt->sriov.vf.migration.ggtt_need_fixes, false);
+ smp_wmb(); /* Ensure above write visible before wake */
+ wake_up_all(&gt->sriov.vf.migration.wq);
+ }
+
+ return 0;
}
static int vf_get_lmem_info(struct xe_gt *gt)
{
- struct xe_gt_sriov_vf_selfconfig *config = &gt->sriov.vf.self_config;
+ struct xe_tile *tile = gt_to_tile(gt);
struct xe_guc *guc = &gt->uc.guc;
char size_str[10];
- u64 size;
+ u64 size, lmem_size;
int err;
xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
@@ -478,18 +506,19 @@ static int vf_get_lmem_info(struct xe_gt *gt)
if (unlikely(err))
return err;
- if (config->lmem_size && config->lmem_size != size) {
+ lmem_size = xe_tile_sriov_vf_lmem(tile);
+ if (lmem_size && lmem_size != size) {
xe_gt_sriov_err(gt, "Unexpected LMEM reassignment: %lluM != %lluM\n",
- size / SZ_1M, config->lmem_size / SZ_1M);
+ size / SZ_1M, lmem_size / SZ_1M);
return -EREMCHG;
}
string_get_size(size, 1, STRING_UNITS_2, size_str, sizeof(size_str));
xe_gt_sriov_dbg_verbose(gt, "LMEM %lluM %s\n", size / SZ_1M, size_str);
- config->lmem_size = size;
+ xe_tile_sriov_vf_lmem_store(tile, size);
- return config->lmem_size ? 0 : -ENODATA;
+ return size ? 0 : -ENODATA;
}
static int vf_get_submission_cfg(struct xe_gt *gt)
@@ -540,7 +569,9 @@ static void vf_cache_gmdid(struct xe_gt *gt)
* xe_gt_sriov_vf_query_config - Query SR-IOV config data over MMIO.
* @gt: the &xe_gt
*
- * This function is for VF use only.
+ * This function is for VF use only. This function may shift the GGTT and is
+ * performed under GGTT lock, making this step visible to all GTs that share a
+ * GGTT.
*
* Return: 0 on success or a negative error code on failure.
*/
@@ -586,75 +617,6 @@ u16 xe_gt_sriov_vf_guc_ids(struct xe_gt *gt)
return gt->sriov.vf.self_config.num_ctxs;
}
-/**
- * xe_gt_sriov_vf_lmem - VF LMEM configuration.
- * @gt: the &xe_gt
- *
- * This function is for VF use only.
- *
- * Return: size of the LMEM assigned to VF.
- */
-u64 xe_gt_sriov_vf_lmem(struct xe_gt *gt)
-{
- xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
- xe_gt_assert(gt, gt->sriov.vf.guc_version.major);
- xe_gt_assert(gt, gt->sriov.vf.self_config.lmem_size);
-
- return gt->sriov.vf.self_config.lmem_size;
-}
-
-/**
- * xe_gt_sriov_vf_ggtt - VF GGTT configuration.
- * @gt: the &xe_gt
- *
- * This function is for VF use only.
- *
- * Return: size of the GGTT assigned to VF.
- */
-u64 xe_gt_sriov_vf_ggtt(struct xe_gt *gt)
-{
- xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
- xe_gt_assert(gt, gt->sriov.vf.guc_version.major);
- xe_gt_assert(gt, gt->sriov.vf.self_config.ggtt_size);
-
- return gt->sriov.vf.self_config.ggtt_size;
-}
-
-/**
- * xe_gt_sriov_vf_ggtt_base - VF GGTT base offset.
- * @gt: the &xe_gt
- *
- * This function is for VF use only.
- *
- * Return: base offset of the GGTT assigned to VF.
- */
-u64 xe_gt_sriov_vf_ggtt_base(struct xe_gt *gt)
-{
- xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
- xe_gt_assert(gt, gt->sriov.vf.guc_version.major);
- xe_gt_assert(gt, gt->sriov.vf.self_config.ggtt_size);
-
- return gt->sriov.vf.self_config.ggtt_base;
-}
-
-/**
- * xe_gt_sriov_vf_ggtt_shift - Return shift in GGTT range due to VF migration
- * @gt: the &xe_gt struct instance
- *
- * This function is for VF use only.
- *
- * Return: The shift value; could be negative
- */
-s64 xe_gt_sriov_vf_ggtt_shift(struct xe_gt *gt)
-{
- struct xe_gt_sriov_vf_selfconfig *config = &gt->sriov.vf.self_config;
-
- xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
- xe_gt_assert(gt, xe_gt_is_main_type(gt));
-
- return config->ggtt_shift;
-}
-
static int relay_action_handshake(struct xe_gt *gt, u32 *major, u32 *minor)
{
u32 request[VF2PF_HANDSHAKE_REQUEST_MSG_LEN] = {
@@ -755,7 +717,7 @@ failed:
* xe_gt_sriov_vf_default_lrcs_hwsp_rebase - Update GGTT references in HWSP of default LRCs.
* @gt: the &xe_gt struct instance
*/
-void xe_gt_sriov_vf_default_lrcs_hwsp_rebase(struct xe_gt *gt)
+static void xe_gt_sriov_vf_default_lrcs_hwsp_rebase(struct xe_gt *gt)
{
struct xe_hw_engine *hwe;
enum xe_hw_engine_id id;
@@ -764,6 +726,31 @@ void xe_gt_sriov_vf_default_lrcs_hwsp_rebase(struct xe_gt *gt)
xe_default_lrc_update_memirq_regs_with_address(hwe);
}
+static void vf_start_migration_recovery(struct xe_gt *gt)
+{
+ bool started;
+
+ xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
+
+ spin_lock(&gt->sriov.vf.migration.lock);
+
+ 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);
+ WRITE_ONCE(gt->sriov.vf.migration.ggtt_need_fixes, true);
+ smp_wmb(); /* Ensure above writes visable before wake */
+
+ xe_guc_ct_wake_waiters(&gt->uc.guc.ct);
+
+ started = queue_work(gt->ordered_wq, &gt->sriov.vf.migration.worker);
+ xe_gt_sriov_info(gt, "VF migration recovery %s\n", started ?
+ "scheduled" : "already in progress");
+ }
+
+ spin_unlock(&gt->sriov.vf.migration.lock);
+}
+
/**
* xe_gt_sriov_vf_migrated_event_handler - Start a VF migration recovery,
* or just mark that a GuC is ready for it.
@@ -776,16 +763,15 @@ void xe_gt_sriov_vf_migrated_event_handler(struct xe_gt *gt)
struct xe_device *xe = gt_to_xe(gt);
xe_gt_assert(gt, IS_SRIOV_VF(xe));
+ xe_gt_assert(gt, xe_gt_sriov_vf_recovery_pending(gt));
- set_bit(gt->info.id, &xe->sriov.vf.migration.gt_flags);
- /*
- * We need to be certain that if all flags were set, at least one
- * thread will notice that and schedule the recovery.
- */
- smp_mb__after_atomic();
+ if (!xe_sriov_vf_migration_supported(xe)) {
+ xe_gt_sriov_err(gt, "migration not supported\n");
+ return;
+ }
xe_gt_sriov_info(gt, "ready for recovery after migration\n");
- xe_sriov_vf_start_migration_recovery(xe);
+ vf_start_migration_recovery(gt);
}
static bool vf_is_negotiated(struct xe_gt *gt, u16 major, u16 minor)
@@ -1040,22 +1026,25 @@ void xe_gt_sriov_vf_print_config(struct xe_gt *gt, struct drm_printer *p)
{
struct xe_gt_sriov_vf_selfconfig *config = &gt->sriov.vf.self_config;
struct xe_device *xe = gt_to_xe(gt);
+ u64 lmem_size;
char buf[10];
xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
- drm_printf(p, "GGTT range:\t%#llx-%#llx\n",
- config->ggtt_base,
- config->ggtt_base + config->ggtt_size - 1);
-
- string_get_size(config->ggtt_size, 1, STRING_UNITS_2, buf, sizeof(buf));
- drm_printf(p, "GGTT size:\t%llu (%s)\n", config->ggtt_size, buf);
+ if (xe_gt_is_main_type(gt)) {
+ u64 ggtt_size = xe_tile_sriov_vf_ggtt(gt_to_tile(gt));
+ u64 ggtt_base = xe_tile_sriov_vf_ggtt_base(gt_to_tile(gt));
- drm_printf(p, "GGTT shift on last restore:\t%lld\n", config->ggtt_shift);
+ drm_printf(p, "GGTT range:\t%#llx-%#llx\n",
+ ggtt_base, ggtt_base + ggtt_size - 1);
+ string_get_size(ggtt_size, 1, STRING_UNITS_2, buf, sizeof(buf));
+ drm_printf(p, "GGTT size:\t%llu (%s)\n", ggtt_size, buf);
- if (IS_DGFX(xe) && xe_gt_is_main_type(gt)) {
- string_get_size(config->lmem_size, 1, STRING_UNITS_2, buf, sizeof(buf));
- drm_printf(p, "LMEM size:\t%llu (%s)\n", config->lmem_size, buf);
+ if (IS_DGFX(xe)) {
+ lmem_size = xe_tile_sriov_vf_lmem(gt_to_tile(gt));
+ string_get_size(lmem_size, 1, STRING_UNITS_2, buf, sizeof(buf));
+ drm_printf(p, "LMEM size:\t%llu (%s)\n", lmem_size, buf);
+ }
}
drm_printf(p, "GuC contexts:\t%u\n", config->num_ctxs);
@@ -1118,3 +1107,276 @@ void xe_gt_sriov_vf_print_version(struct xe_gt *gt, struct drm_printer *p)
drm_printf(p, "\thandshake:\t%u.%u\n",
pf_version->major, pf_version->minor);
}
+
+static bool vf_post_migration_shutdown(struct xe_gt *gt)
+{
+ struct xe_device *xe = gt_to_xe(gt);
+
+ /*
+ * On platforms where CCS must be restored by the primary GT, the media
+ * GT's VF post-migration recovery must run afterward. Detect this case
+ * and re-queue the media GT's restore work item if necessary.
+ */
+ if (xe->info.needs_shared_vf_gt_wq && xe_gt_is_media_type(gt)) {
+ struct xe_gt *primary_gt = gt_to_tile(gt)->primary_gt;
+
+ if (xe_gt_sriov_vf_recovery_pending(primary_gt))
+ return true;
+ }
+
+ spin_lock_irq(&gt->sriov.vf.migration.lock);
+ gt->sriov.vf.migration.recovery_queued = false;
+ spin_unlock_irq(&gt->sriov.vf.migration.lock);
+
+ xe_guc_ct_flush_and_stop(&gt->uc.guc.ct);
+ xe_guc_submit_pause(&gt->uc.guc);
+ xe_tlb_inval_reset(&gt->tlb_inval);
+
+ return false;
+}
+
+static size_t post_migration_scratch_size(struct xe_device *xe)
+{
+ return max(xe_lrc_reg_size(xe), LRC_WA_BB_SIZE);
+}
+
+static int vf_post_migration_fixups(struct xe_gt *gt)
+{
+ void *buf = gt->sriov.vf.migration.scratch;
+ int err;
+
+ /* xe_gt_sriov_vf_query_config will fixup the GGTT addresses */
+ err = xe_gt_sriov_vf_query_config(gt);
+ if (err)
+ return err;
+
+ if (xe_gt_is_main_type(gt))
+ xe_sriov_vf_ccs_rebase(gt_to_xe(gt));
+
+ xe_gt_sriov_vf_default_lrcs_hwsp_rebase(gt);
+ err = xe_guc_contexts_hwsp_rebase(&gt->uc.guc, buf);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static void vf_post_migration_rearm(struct xe_gt *gt)
+{
+ xe_guc_ct_restart(&gt->uc.guc.ct);
+ xe_guc_submit_unpause_prepare(&gt->uc.guc);
+}
+
+static void vf_post_migration_kickstart(struct xe_gt *gt)
+{
+ xe_guc_submit_unpause(&gt->uc.guc);
+}
+
+static void vf_post_migration_abort(struct xe_gt *gt)
+{
+ spin_lock_irq(&gt->sriov.vf.migration.lock);
+ WRITE_ONCE(gt->sriov.vf.migration.recovery_inprogress, false);
+ WRITE_ONCE(gt->sriov.vf.migration.ggtt_need_fixes, false);
+ spin_unlock_irq(&gt->sriov.vf.migration.lock);
+
+ wake_up_all(&gt->sriov.vf.migration.wq);
+
+ xe_guc_submit_pause_abort(&gt->uc.guc);
+}
+
+static int vf_post_migration_notify_resfix_done(struct xe_gt *gt)
+{
+ bool skip_resfix = false;
+
+ spin_lock_irq(&gt->sriov.vf.migration.lock);
+ if (gt->sriov.vf.migration.recovery_queued) {
+ skip_resfix = true;
+ xe_gt_sriov_dbg(gt, "another recovery imminent, resfix skipped\n");
+ } else {
+ WRITE_ONCE(gt->sriov.vf.migration.recovery_inprogress, false);
+ }
+ spin_unlock_irq(&gt->sriov.vf.migration.lock);
+
+ if (skip_resfix)
+ return -EAGAIN;
+
+ /*
+ * 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));
+
+ return vf_notify_resfix_done(gt);
+}
+
+static void vf_post_migration_recovery(struct xe_gt *gt)
+{
+ struct xe_device *xe = gt_to_xe(gt);
+ int err;
+ bool retry;
+
+ xe_gt_sriov_dbg(gt, "migration recovery in progress\n");
+
+ xe_pm_runtime_get(xe);
+ retry = vf_post_migration_shutdown(gt);
+ if (retry)
+ goto queue;
+
+ if (!xe_sriov_vf_migration_supported(xe)) {
+ xe_gt_sriov_err(gt, "migration is not supported\n");
+ err = -ENOTRECOVERABLE;
+ 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)
+ goto fail;
+
+ vf_post_migration_kickstart(gt);
+
+ xe_pm_runtime_put(xe);
+ xe_gt_sriov_notice(gt, "migration recovery ended\n");
+ return;
+fail:
+ vf_post_migration_abort(gt);
+ xe_pm_runtime_put(xe);
+ xe_gt_sriov_err(gt, "migration recovery failed (%pe)\n", ERR_PTR(err));
+ xe_device_declare_wedged(xe);
+ return;
+
+queue:
+ xe_gt_sriov_info(gt, "Re-queuing migration recovery\n");
+ queue_work(gt->ordered_wq, &gt->sriov.vf.migration.worker);
+ xe_pm_runtime_put(xe);
+}
+
+static void migration_worker_func(struct work_struct *w)
+{
+ struct xe_gt *gt = container_of(w, struct xe_gt,
+ sriov.vf.migration.worker);
+
+ vf_post_migration_recovery(gt);
+}
+
+static void vf_migration_fini(void *arg)
+{
+ struct xe_gt *gt = arg;
+
+ spin_lock_irq(&gt->sriov.vf.migration.lock);
+ gt->sriov.vf.migration.recovery_teardown = true;
+ spin_unlock_irq(&gt->sriov.vf.migration.lock);
+
+ cancel_work_sync(&gt->sriov.vf.migration.worker);
+}
+
+/**
+ * xe_gt_sriov_vf_init_early() - GT VF init early
+ * @gt: the &xe_gt
+ *
+ * Return 0 on success, errno on failure
+ */
+int xe_gt_sriov_vf_init_early(struct xe_gt *gt)
+{
+ void *buf;
+
+ if (!xe_sriov_vf_migration_supported(gt_to_xe(gt)))
+ return 0;
+
+ buf = drmm_kmalloc(&gt_to_xe(gt)->drm,
+ post_migration_scratch_size(gt_to_xe(gt)),
+ GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ gt->sriov.vf.migration.scratch = buf;
+ spin_lock_init(&gt->sriov.vf.migration.lock);
+ INIT_WORK(&gt->sriov.vf.migration.worker, migration_worker_func);
+ init_waitqueue_head(&gt->sriov.vf.migration.wq);
+
+ return 0;
+}
+
+/**
+ * xe_gt_sriov_vf_init() - GT VF init
+ * @gt: the &xe_gt
+ *
+ * Return 0 on success, errno on failure
+ */
+int xe_gt_sriov_vf_init(struct xe_gt *gt)
+{
+ if (!xe_sriov_vf_migration_supported(gt_to_xe(gt)))
+ return 0;
+
+ /*
+ * We want to tear down the VF post-migration early during driver
+ * unload; therefore, we add this finalization action later during
+ * driver load.
+ */
+ return devm_add_action_or_reset(gt_to_xe(gt)->drm.dev,
+ vf_migration_fini, gt);
+}
+
+/**
+ * xe_gt_sriov_vf_recovery_pending() - VF post migration recovery pending
+ * @gt: the &xe_gt
+ *
+ * The return value of this function must be immediately visible upon vCPU
+ * unhalt and must persist until RESFIX_DONE is issued. This guarantee is
+ * currently implemented only for platforms that support memirq. If non-memirq
+ * platforms begin to support VF migration, this function will need to be
+ * updated accordingly.
+ *
+ * Return: True if VF post migration recovery is pending, False otherwise
+ */
+bool xe_gt_sriov_vf_recovery_pending(struct xe_gt *gt)
+{
+ struct xe_memirq *memirq = &gt_to_tile(gt)->memirq;
+
+ xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
+
+ /* early detection until recovery starts */
+ if (xe_device_uses_memirq(gt_to_xe(gt)) &&
+ xe_memirq_guc_sw_int_0_irq_pending(memirq, &gt->uc.guc))
+ return true;
+
+ return READ_ONCE(gt->sriov.vf.migration.recovery_inprogress);
+}
+
+static bool vf_valid_ggtt(struct xe_gt *gt)
+{
+ struct xe_memirq *memirq = &gt_to_tile(gt)->memirq;
+ bool irq_pending = xe_device_uses_memirq(gt_to_xe(gt)) &&
+ xe_memirq_guc_sw_int_0_irq_pending(memirq, &gt->uc.guc);
+
+ xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
+
+ if (irq_pending || READ_ONCE(gt->sriov.vf.migration.ggtt_need_fixes))
+ return false;
+
+ return true;
+}
+
+/**
+ * xe_gt_sriov_vf_wait_valid_ggtt() - VF wait for valid GGTT addresses
+ * @gt: the &xe_gt
+ */
+void xe_gt_sriov_vf_wait_valid_ggtt(struct xe_gt *gt)
+{
+ int ret;
+
+ if (!IS_SRIOV_VF(gt_to_xe(gt)) ||
+ !xe_sriov_vf_migration_supported(gt_to_xe(gt)))
+ return;
+
+ ret = wait_event_interruptible_timeout(gt->sriov.vf.migration.wq,
+ vf_valid_ggtt(gt),
+ HZ * 5);
+ xe_gt_WARN_ON(gt, !ret);
+}
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_vf.h b/drivers/gpu/drm/xe/xe_gt_sriov_vf.h
index 0af1dc769fe0..af40276790fa 100644
--- a/drivers/gpu/drm/xe/xe_gt_sriov_vf.h
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_vf.h
@@ -21,16 +21,15 @@ void xe_gt_sriov_vf_guc_versions(struct xe_gt *gt,
int xe_gt_sriov_vf_query_config(struct xe_gt *gt);
int xe_gt_sriov_vf_connect(struct xe_gt *gt);
int xe_gt_sriov_vf_query_runtime(struct xe_gt *gt);
-void xe_gt_sriov_vf_default_lrcs_hwsp_rebase(struct xe_gt *gt);
-int xe_gt_sriov_vf_notify_resfix_done(struct xe_gt *gt);
void xe_gt_sriov_vf_migrated_event_handler(struct xe_gt *gt);
+int xe_gt_sriov_vf_init_early(struct xe_gt *gt);
+int xe_gt_sriov_vf_init(struct xe_gt *gt);
+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);
-u64 xe_gt_sriov_vf_ggtt(struct xe_gt *gt);
-u64 xe_gt_sriov_vf_ggtt_base(struct xe_gt *gt);
-s64 xe_gt_sriov_vf_ggtt_shift(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);
@@ -39,4 +38,6 @@ void xe_gt_sriov_vf_print_config(struct xe_gt *gt, struct drm_printer *p);
void xe_gt_sriov_vf_print_runtime(struct xe_gt *gt, struct drm_printer *p);
void xe_gt_sriov_vf_print_version(struct xe_gt *gt, struct drm_printer *p);
+void xe_gt_sriov_vf_wait_valid_ggtt(struct xe_gt *gt);
+
#endif
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_vf_types.h b/drivers/gpu/drm/xe/xe_gt_sriov_vf_types.h
index 298dedf4b009..420b0e6089de 100644
--- a/drivers/gpu/drm/xe/xe_gt_sriov_vf_types.h
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_vf_types.h
@@ -7,20 +7,14 @@
#define _XE_GT_SRIOV_VF_TYPES_H_
#include <linux/types.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
#include "xe_uc_fw_types.h"
/**
* struct xe_gt_sriov_vf_selfconfig - VF configuration data.
*/
struct xe_gt_sriov_vf_selfconfig {
- /** @ggtt_base: assigned base offset of the GGTT region. */
- u64 ggtt_base;
- /** @ggtt_size: assigned size of the GGTT region. */
- u64 ggtt_size;
- /** @ggtt_shift: difference in ggtt_base on last migration */
- s64 ggtt_shift;
- /** @lmem_size: assigned size of the LMEM. */
- u64 lmem_size;
/** @num_ctxs: assigned number of GuC submission context IDs. */
u16 num_ctxs;
/** @num_dbs: assigned number of GuC doorbells IDs. */
@@ -47,6 +41,28 @@ struct xe_gt_sriov_vf_runtime {
};
/**
+ * xe_gt_sriov_vf_migration - VF migration data.
+ */
+struct xe_gt_sriov_vf_migration {
+ /** @migration: VF migration recovery worker */
+ struct work_struct worker;
+ /** @lock: Protects recovery_queued, teardown */
+ spinlock_t lock;
+ /** @wq: wait queue for migration fixes */
+ wait_queue_head_t wq;
+ /** @scratch: Scratch memory for VF recovery */
+ void *scratch;
+ /** @recovery_teardown: VF post migration recovery is being torn down */
+ bool recovery_teardown;
+ /** @recovery_queued: VF post migration recovery in queued */
+ bool recovery_queued;
+ /** @recovery_inprogress: VF post migration recovery in progress */
+ bool recovery_inprogress;
+ /** @ggtt_need_fixes: VF GGTT needs fixes */
+ bool ggtt_need_fixes;
+};
+
+/**
* struct xe_gt_sriov_vf - GT level VF virtualization data.
*/
struct xe_gt_sriov_vf {
@@ -58,6 +74,8 @@ struct xe_gt_sriov_vf {
struct xe_gt_sriov_vf_selfconfig self_config;
/** @runtime: runtime data retrieved from the PF. */
struct xe_gt_sriov_vf_runtime runtime;
+ /** @migration: migration data for the VF. */
+ struct xe_gt_sriov_vf_migration migration;
};
#endif
diff --git a/drivers/gpu/drm/xe/xe_gt_topology.c b/drivers/gpu/drm/xe/xe_gt_topology.c
index 4e61c5e39bcb..1e0516ba7422 100644
--- a/drivers/gpu/drm/xe/xe_gt_topology.c
+++ b/drivers/gpu/drm/xe/xe_gt_topology.c
@@ -148,7 +148,11 @@ load_l3_bank_mask(struct xe_gt *gt, xe_l3_bank_mask_t l3_bank_mask)
if (!xe_gt_topology_report_l3(gt))
return;
- if (GRAPHICS_VER(xe) >= 30) {
+ if (GRAPHICS_VER(xe) >= 35) {
+ u32 fuse_val = xe_mmio_read32(mmio, MIRROR_L3BANK_ENABLE);
+
+ bitmap_from_arr32(l3_bank_mask, &fuse_val, 32);
+ } else if (GRAPHICS_VER(xe) >= 30) {
xe_l3_bank_mask_t per_node = {};
u32 meml3_en = REG_FIELD_GET(XE2_NODE_ENABLE_MASK, fuse3);
u32 mirror_l3bank_enable = xe_mmio_read32(mmio, MIRROR_L3BANK_ENABLE);
@@ -269,8 +273,14 @@ static const char *eu_type_to_str(enum xe_gt_eu_type eu_type)
return NULL;
}
-void
-xe_gt_topology_dump(struct xe_gt *gt, struct drm_printer *p)
+/**
+ * xe_gt_topology_dump() - Dump GT topology into a drm printer.
+ * @gt: the &xe_gt
+ * @p: the &drm_printer
+ *
+ * Return: always 0.
+ */
+int xe_gt_topology_dump(struct xe_gt *gt, struct drm_printer *p)
{
drm_printf(p, "dss mask (geometry): %*pb\n", XE_MAX_DSS_FUSE_BITS,
gt->fuse_topo.g_dss_mask);
@@ -285,6 +295,7 @@ xe_gt_topology_dump(struct xe_gt *gt, struct drm_printer *p)
if (xe_gt_topology_report_l3(gt))
drm_printf(p, "L3 bank mask: %*pb\n", XE_MAX_L3_BANK_MASK_BITS,
gt->fuse_topo.l3_bank_mask);
+ return 0;
}
/*
diff --git a/drivers/gpu/drm/xe/xe_gt_topology.h b/drivers/gpu/drm/xe/xe_gt_topology.h
index 5e62f5949b7b..3ff40f44bf2a 100644
--- a/drivers/gpu/drm/xe/xe_gt_topology.h
+++ b/drivers/gpu/drm/xe/xe_gt_topology.h
@@ -23,7 +23,7 @@ struct drm_printer;
void xe_gt_topology_init(struct xe_gt *gt);
-void xe_gt_topology_dump(struct xe_gt *gt, struct drm_printer *p);
+int xe_gt_topology_dump(struct xe_gt *gt, struct drm_printer *p);
/**
* xe_gt_topology_mask_last_dss() - Returns the index of the last DSS in a mask.
diff --git a/drivers/gpu/drm/xe/xe_gt_types.h b/drivers/gpu/drm/xe/xe_gt_types.h
index 66158105aca5..d93faa1eedef 100644
--- a/drivers/gpu/drm/xe/xe_gt_types.h
+++ b/drivers/gpu/drm/xe/xe_gt_types.h
@@ -73,6 +73,21 @@ enum xe_steering_type {
SQIDI_PSMI,
/*
+ * The bspec lists multiple ranges as "PSMI," but the different
+ * ranges with that label have different grpid steering values so we
+ * treat them independently in code. Note that the ranges with grpid=0
+ * are included in the INSTANCE0 group above.
+ */
+ PSMI19,
+
+ /*
+ * Although most GAM ranges must be steered to (0,0) and thus use the
+ * INSTANCE0 type farther down, some platforms have special rules
+ * for specific subtypes that require steering to (1,0) instead.
+ */
+ GAM1,
+
+ /*
* On some platforms there are multiple types of MCR registers that
* will always return a non-terminated value at instance (0, 0). We'll
* lump those all into a single category to keep things simple.
@@ -202,14 +217,14 @@ struct xe_gt {
/**
* @usm.bb_pool: Pool from which batchbuffers, for USM operations
* (e.g. migrations, fixing page tables), are allocated.
- * Dedicated pool needed so USM operations to not get blocked
+ * Dedicated pool needed so USM operations do not get blocked
* behind any user operations which may have resulted in a
* fault.
*/
struct xe_sa_manager *bb_pool;
/**
* @usm.reserved_bcs_instance: reserved BCS instance used for USM
- * operations (e.g. mmigrations, fixing page tables)
+ * operations (e.g. migrations, fixing page tables)
*/
u16 reserved_bcs_instance;
/** @usm.pf_wq: page fault work queue, unbound, high priority */
@@ -220,8 +235,8 @@ struct xe_gt {
* @usm.pf_queue: Page fault queue used to sync faults so faults can
* be processed not under the GuC CT lock. The queue is sized so
* it can sync all possible faults (1 per physical engine).
- * Multiple queues exists for page faults from different VMs are
- * be processed in parallel.
+ * Multiple queues exist for page faults from different VMs to be
+ * processed in parallel.
*/
struct pf_queue {
/** @usm.pf_queue.gt: back pointer to GT */
@@ -387,7 +402,7 @@ struct xe_gt {
/**
* @wa_active.oob_initialized: mark oob as initialized to help
* detecting misuse of XE_GT_WA() - it can only be called on
- * initialization after OOB WAs have being processed
+ * initialization after OOB WAs have been processed
*/
bool oob_initialized;
} wa_active;
diff --git a/drivers/gpu/drm/xe/xe_guc.c b/drivers/gpu/drm/xe/xe_guc.c
index 00789844ea4d..d94490979adc 100644
--- a/drivers/gpu/drm/xe/xe_guc.c
+++ b/drivers/gpu/drm/xe/xe_guc.c
@@ -5,6 +5,7 @@
#include "xe_guc.h"
+#include <linux/iopoll.h>
#include <drm/drm_managed.h>
#include <generated/xe_wa_oob.h>
@@ -971,20 +972,93 @@ static int guc_xfer_rsa(struct xe_guc *guc)
}
/*
- * Check a previously read GuC status register (GUC_STATUS) looking for
- * known terminal states (either completion or failure) of either the
- * microkernel status field or the boot ROM status field. Returns +1 for
- * successful completion, -1 for failure and 0 for any intermediate state.
+ * Wait for the GuC to start up.
+ *
+ * Measurements indicate this should take no more than 20ms (assuming the GT
+ * clock is at maximum frequency). However, thermal throttling and other issues
+ * can prevent the clock hitting max and thus making the load take significantly
+ * longer. Allow up to 3s as a safety margin in normal builds. For
+ * CONFIG_DRM_XE_DEBUG allow up to 10s to account for slower execution, issues
+ * in PCODE, driver, fan, etc.
+ *
+ * Keep checking the GUC_STATUS every 10ms with a debug message every 100
+ * attempts as a "I'm slow, but alive" message. Regardless, if it takes more
+ * than 200ms, emit a warning.
*/
-static int guc_load_done(u32 status)
+
+#if IS_ENABLED(CONFIG_DRM_XE_DEBUG)
+#define GUC_LOAD_TIMEOUT_SEC 20
+#else
+#define GUC_LOAD_TIMEOUT_SEC 3
+#endif
+#define GUC_LOAD_TIME_WARN_MSEC 200
+
+static void print_load_status_err(struct xe_gt *gt, u32 status)
{
- u32 uk_val = REG_FIELD_GET(GS_UKERNEL_MASK, status);
- u32 br_val = REG_FIELD_GET(GS_BOOTROM_MASK, status);
+ struct xe_mmio *mmio = &gt->mmio;
+ u32 ukernel = REG_FIELD_GET(GS_UKERNEL_MASK, status);
+ u32 bootrom = REG_FIELD_GET(GS_BOOTROM_MASK, status);
+
+ xe_gt_err(gt, "load failed: status: Reset = %d, BootROM = 0x%02X, UKernel = 0x%02X, MIA = 0x%02X, Auth = 0x%02X\n",
+ REG_FIELD_GET(GS_MIA_IN_RESET, status),
+ bootrom, ukernel,
+ REG_FIELD_GET(GS_MIA_MASK, status),
+ REG_FIELD_GET(GS_AUTH_STATUS_MASK, status));
- switch (uk_val) {
+ switch (bootrom) {
+ case XE_BOOTROM_STATUS_NO_KEY_FOUND:
+ xe_gt_err(gt, "invalid key requested, header = 0x%08X\n",
+ xe_mmio_read32(mmio, GUC_HEADER_INFO));
+ break;
+ case XE_BOOTROM_STATUS_RSA_FAILED:
+ xe_gt_err(gt, "firmware signature verification failed\n");
+ break;
+ case XE_BOOTROM_STATUS_PROD_KEY_CHECK_FAILURE:
+ xe_gt_err(gt, "firmware production part check failure\n");
+ break;
+ }
+
+ switch (ukernel) {
+ case XE_GUC_LOAD_STATUS_HWCONFIG_START:
+ xe_gt_err(gt, "still extracting hwconfig table.\n");
+ break;
+ case XE_GUC_LOAD_STATUS_EXCEPTION:
+ xe_gt_err(gt, "firmware exception. EIP: %#x\n",
+ xe_mmio_read32(mmio, SOFT_SCRATCH(13)));
+ break;
+ case XE_GUC_LOAD_STATUS_INIT_DATA_INVALID:
+ xe_gt_err(gt, "illegal init/ADS data\n");
+ break;
+ case XE_GUC_LOAD_STATUS_INIT_MMIO_SAVE_RESTORE_INVALID:
+ xe_gt_err(gt, "illegal register in save/restore workaround list\n");
+ break;
+ case XE_GUC_LOAD_STATUS_KLV_WORKAROUND_INIT_ERROR:
+ xe_gt_err(gt, "illegal workaround KLV data\n");
+ break;
+ case XE_GUC_LOAD_STATUS_INVALID_FTR_FLAG:
+ xe_gt_err(gt, "illegal feature flag specified\n");
+ break;
+ }
+}
+
+/*
+ * Check GUC_STATUS looking for known terminal states (either completion or
+ * failure) of either the microkernel status field or the boot ROM status field.
+ *
+ * Returns 1 for successful completion, -1 for failure and 0 for any
+ * intermediate state.
+ */
+static int guc_load_done(struct xe_gt *gt, u32 *status, u32 *tries)
+{
+ u32 ukernel, bootrom;
+
+ *status = xe_mmio_read32(&gt->mmio, GUC_STATUS);
+ ukernel = REG_FIELD_GET(GS_UKERNEL_MASK, *status);
+ bootrom = REG_FIELD_GET(GS_BOOTROM_MASK, *status);
+
+ switch (ukernel) {
case XE_GUC_LOAD_STATUS_READY:
return 1;
-
case XE_GUC_LOAD_STATUS_ERROR_DEVID_BUILD_MISMATCH:
case XE_GUC_LOAD_STATUS_GUC_PREPROD_BUILD_MISMATCH:
case XE_GUC_LOAD_STATUS_ERROR_DEVID_INVALID_GUCTYPE:
@@ -1000,7 +1074,7 @@ static int guc_load_done(u32 status)
return -1;
}
- switch (br_val) {
+ switch (bootrom) {
case XE_BOOTROM_STATUS_NO_KEY_FOUND:
case XE_BOOTROM_STATUS_RSA_FAILED:
case XE_BOOTROM_STATUS_PAVPC_FAILED:
@@ -1014,165 +1088,58 @@ static int guc_load_done(u32 status)
return -1;
}
- return 0;
-}
+ if (++*tries >= 100) {
+ struct xe_guc_pc *guc_pc = &gt->uc.guc.pc;
-static s32 guc_pc_get_cur_freq(struct xe_guc_pc *guc_pc)
-{
- u32 freq;
- int ret = xe_guc_pc_get_cur_freq(guc_pc, &freq);
+ *tries = 0;
+ xe_gt_dbg(gt, "GuC load still in progress, freq = %dMHz (req %dMHz), status = 0x%08X [0x%02X/%02X]\n",
+ xe_guc_pc_get_act_freq(guc_pc),
+ xe_guc_pc_get_cur_freq_fw(guc_pc),
+ *status, ukernel, bootrom);
+ }
- return ret ? ret : freq;
+ return 0;
}
-/*
- * Wait for the GuC to start up.
- *
- * Measurements indicate this should take no more than 20ms (assuming the GT
- * clock is at maximum frequency). However, thermal throttling and other issues
- * can prevent the clock hitting max and thus making the load take significantly
- * longer. Allow up to 200ms as a safety margin for real world worst case situations.
- *
- * However, bugs anywhere from KMD to GuC to PCODE to fan failure in a CI farm can
- * lead to even longer times. E.g. if the GT is clamped to minimum frequency then
- * the load times can be in the seconds range. So the timeout is increased for debug
- * builds to ensure that problems can be correctly analysed. For release builds, the
- * timeout is kept short so that users don't wait forever to find out that there is a
- * problem. In either case, if the load took longer than is reasonable even with some
- * 'sensible' throttling, then flag a warning because something is not right.
- *
- * Note that there is a limit on how long an individual usleep_range() can wait for,
- * hence longer waits require wrapping a shorter wait in a loop.
- *
- * Note that the only reason an end user should hit the shorter timeout is in case of
- * extreme thermal throttling. And a system that is that hot during boot is probably
- * dead anyway!
- */
-#if IS_ENABLED(CONFIG_DRM_XE_DEBUG)
-#define GUC_LOAD_RETRY_LIMIT 20
-#else
-#define GUC_LOAD_RETRY_LIMIT 3
-#endif
-#define GUC_LOAD_TIME_WARN_MS 200
-
static int guc_wait_ucode(struct xe_guc *guc)
{
struct xe_gt *gt = guc_to_gt(guc);
- struct xe_mmio *mmio = &gt->mmio;
struct xe_guc_pc *guc_pc = &gt->uc.guc.pc;
- ktime_t before, after, delta;
- int load_done;
- u32 status = 0;
- int count = 0;
+ u32 before_freq, act_freq, cur_freq;
+ u32 status = 0, tries = 0;
+ ktime_t before;
u64 delta_ms;
- u32 before_freq;
+ int ret;
before_freq = xe_guc_pc_get_act_freq(guc_pc);
before = ktime_get();
- /*
- * Note, can't use any kind of timing information from the call to xe_mmio_wait.
- * It could return a thousand intermediate stages at random times. Instead, must
- * manually track the total time taken and locally implement the timeout.
- */
- do {
- u32 last_status = status & (GS_UKERNEL_MASK | GS_BOOTROM_MASK);
- int ret;
-
- /*
- * Wait for any change (intermediate or terminal) in the status register.
- * Note, the return value is a don't care. The only failure code is timeout
- * but the timeouts need to be accumulated over all the intermediate partial
- * timeouts rather than allowing a huge timeout each time. So basically, need
- * to treat a timeout no different to a value change.
- */
- ret = xe_mmio_wait32_not(mmio, GUC_STATUS, GS_UKERNEL_MASK | GS_BOOTROM_MASK,
- last_status, 1000 * 1000, &status, false);
- if (ret < 0)
- count++;
- after = ktime_get();
- delta = ktime_sub(after, before);
- delta_ms = ktime_to_ms(delta);
-
- load_done = guc_load_done(status);
- if (load_done != 0)
- break;
- if (delta_ms >= (GUC_LOAD_RETRY_LIMIT * 1000))
- break;
+ ret = poll_timeout_us(ret = guc_load_done(gt, &status, &tries), ret,
+ 10 * USEC_PER_MSEC,
+ GUC_LOAD_TIMEOUT_SEC * USEC_PER_SEC, false);
- xe_gt_dbg(gt, "load still in progress, timeouts = %d, freq = %dMHz (req %dMHz), status = 0x%08X [0x%02X/%02X]\n",
- count, xe_guc_pc_get_act_freq(guc_pc),
- guc_pc_get_cur_freq(guc_pc), status,
- REG_FIELD_GET(GS_BOOTROM_MASK, status),
- REG_FIELD_GET(GS_UKERNEL_MASK, status));
- } while (1);
+ delta_ms = ktime_to_ms(ktime_sub(ktime_get(), before));
+ act_freq = xe_guc_pc_get_act_freq(guc_pc);
+ cur_freq = xe_guc_pc_get_cur_freq_fw(guc_pc);
- if (load_done != 1) {
- u32 ukernel = REG_FIELD_GET(GS_UKERNEL_MASK, status);
- u32 bootrom = REG_FIELD_GET(GS_BOOTROM_MASK, status);
-
- xe_gt_err(gt, "load failed: status = 0x%08X, time = %lldms, freq = %dMHz (req %dMHz), done = %d\n",
+ if (ret) {
+ xe_gt_err(gt, "load failed: status = 0x%08X, time = %lldms, freq = %dMHz (req %dMHz)\n",
status, delta_ms, xe_guc_pc_get_act_freq(guc_pc),
- guc_pc_get_cur_freq(guc_pc), load_done);
- xe_gt_err(gt, "load failed: status: Reset = %d, BootROM = 0x%02X, UKernel = 0x%02X, MIA = 0x%02X, Auth = 0x%02X\n",
- REG_FIELD_GET(GS_MIA_IN_RESET, status),
- bootrom, ukernel,
- REG_FIELD_GET(GS_MIA_MASK, status),
- REG_FIELD_GET(GS_AUTH_STATUS_MASK, status));
-
- switch (bootrom) {
- case XE_BOOTROM_STATUS_NO_KEY_FOUND:
- xe_gt_err(gt, "invalid key requested, header = 0x%08X\n",
- xe_mmio_read32(mmio, GUC_HEADER_INFO));
- break;
-
- case XE_BOOTROM_STATUS_RSA_FAILED:
- xe_gt_err(gt, "firmware signature verification failed\n");
- break;
-
- case XE_BOOTROM_STATUS_PROD_KEY_CHECK_FAILURE:
- xe_gt_err(gt, "firmware production part check failure\n");
- break;
- }
-
- switch (ukernel) {
- case XE_GUC_LOAD_STATUS_HWCONFIG_START:
- xe_gt_err(gt, "still extracting hwconfig table.\n");
- break;
-
- case XE_GUC_LOAD_STATUS_EXCEPTION:
- xe_gt_err(gt, "firmware exception. EIP: %#x\n",
- xe_mmio_read32(mmio, SOFT_SCRATCH(13)));
- break;
-
- case XE_GUC_LOAD_STATUS_INIT_DATA_INVALID:
- xe_gt_err(gt, "illegal init/ADS data\n");
- break;
-
- case XE_GUC_LOAD_STATUS_INIT_MMIO_SAVE_RESTORE_INVALID:
- xe_gt_err(gt, "illegal register in save/restore workaround list\n");
- break;
-
- case XE_GUC_LOAD_STATUS_KLV_WORKAROUND_INIT_ERROR:
- xe_gt_err(gt, "illegal workaround KLV data\n");
- break;
-
- case XE_GUC_LOAD_STATUS_INVALID_FTR_FLAG:
- xe_gt_err(gt, "illegal feature flag specified\n");
- break;
- }
+ xe_guc_pc_get_cur_freq_fw(guc_pc));
+ print_load_status_err(gt, status);
return -EPROTO;
- } else if (delta_ms > GUC_LOAD_TIME_WARN_MS) {
- xe_gt_warn(gt, "excessive init time: %lldms! [status = 0x%08X, timeouts = %d]\n",
- delta_ms, status, count);
- xe_gt_warn(gt, "excessive init time: [freq = %dMHz (req = %dMHz), before = %dMHz, perf_limit_reasons = 0x%08X]\n",
- xe_guc_pc_get_act_freq(guc_pc), guc_pc_get_cur_freq(guc_pc),
- before_freq, xe_gt_throttle_get_limit_reasons(gt));
+ }
+
+ if (delta_ms > GUC_LOAD_TIME_WARN_MSEC) {
+ xe_gt_warn(gt, "GuC load: excessive init time: %lldms! [status = 0x%08X]\n",
+ delta_ms, status);
+ xe_gt_warn(gt, "GuC load: excessive init time: [freq = %dMHz (req = %dMHz), before = %dMHz, perf_limit_reasons = 0x%08X]\n",
+ act_freq, cur_freq, before_freq,
+ xe_gt_throttle_get_limit_reasons(gt));
} else {
- xe_gt_dbg(gt, "init took %lldms, freq = %dMHz (req = %dMHz), before = %dMHz, status = 0x%08X, timeouts = %d\n",
- delta_ms, xe_guc_pc_get_act_freq(guc_pc), guc_pc_get_cur_freq(guc_pc),
- before_freq, status, count);
+ xe_gt_dbg(gt, "GuC load: init took %lldms, freq = %dMHz (req = %dMHz), before = %dMHz, status = 0x%08X\n",
+ delta_ms, act_freq, cur_freq, before_freq, status);
}
return 0;
@@ -1472,7 +1439,7 @@ timeout:
BUILD_BUG_ON((GUC_HXG_TYPE_RESPONSE_SUCCESS ^ GUC_HXG_TYPE_RESPONSE_FAILURE) != 1);
ret = xe_mmio_wait32(mmio, reply_reg, resp_mask, resp_mask,
- 1000000, &header, false);
+ 2000000, &header, false);
if (unlikely(FIELD_GET(GUC_HXG_MSG_0_ORIGIN, header) !=
GUC_HXG_ORIGIN_GUC))
diff --git a/drivers/gpu/drm/xe/xe_guc_ads.c b/drivers/gpu/drm/xe/xe_guc_ads.c
index 58e0b0294a5b..22ac2a8b74c8 100644
--- a/drivers/gpu/drm/xe/xe_guc_ads.c
+++ b/drivers/gpu/drm/xe/xe_guc_ads.c
@@ -18,6 +18,7 @@
#include "xe_bo.h"
#include "xe_gt.h"
#include "xe_gt_ccs_mode.h"
+#include "xe_gt_mcr.h"
#include "xe_gt_printk.h"
#include "xe_guc.h"
#include "xe_guc_buf.h"
@@ -30,7 +31,6 @@
#include "xe_platform_types.h"
#include "xe_uc_fw.h"
#include "xe_wa.h"
-#include "xe_gt_mcr.h"
/* Slack of a few additional entries per engine */
#define ADS_REGSET_EXTRA_MAX 8
diff --git a/drivers/gpu/drm/xe/xe_guc_capture.c b/drivers/gpu/drm/xe/xe_guc_capture.c
index 243dad3e2418..0c1fbe97b8bf 100644
--- a/drivers/gpu/drm/xe/xe_guc_capture.c
+++ b/drivers/gpu/drm/xe/xe_guc_capture.c
@@ -122,6 +122,7 @@ struct __guc_capture_parsed_output {
{ RING_IPEHR(0), REG_32BIT, 0, 0, 0, "IPEHR"}, \
{ RING_INSTDONE(0), REG_32BIT, 0, 0, 0, "RING_INSTDONE"}, \
{ INDIRECT_RING_STATE(0), REG_32BIT, 0, 0, 0, "INDIRECT_RING_STATE"}, \
+ { RING_CURRENT_LRCA(0), REG_32BIT, 0, 0, 0, "CURRENT_LRCA"}, \
{ RING_ACTHD(0), REG_64BIT_LOW_DW, 0, 0, 0, NULL}, \
{ RING_ACTHD_UDW(0), REG_64BIT_HI_DW, 0, 0, 0, "ACTHD"}, \
{ RING_BBADDR(0), REG_64BIT_LOW_DW, 0, 0, 0, NULL}, \
@@ -149,6 +150,9 @@ struct __guc_capture_parsed_output {
{ SFC_DONE(2), 0, 0, 0, 0, "SFC_DONE[2]"}, \
{ SFC_DONE(3), 0, 0, 0, 0, "SFC_DONE[3]"}
+#define XE3P_BASE_ENGINE_INSTANCE \
+ { RING_CSMQDEBUG(0), REG_32BIT, 0, 0, 0, "CSMQDEBUG"}
+
/* XE_LP Global */
static const struct __guc_mmio_reg_descr xe_lp_global_regs[] = {
COMMON_XELP_BASE_GLOBAL,
@@ -195,6 +199,12 @@ static const struct __guc_mmio_reg_descr xe_lp_gsc_inst_regs[] = {
COMMON_BASE_ENGINE_INSTANCE,
};
+/* Render / Compute Per-Engine-Instance */
+static const struct __guc_mmio_reg_descr xe3p_rc_inst_regs[] = {
+ COMMON_BASE_ENGINE_INSTANCE,
+ XE3P_BASE_ENGINE_INSTANCE,
+};
+
/*
* Empty list to prevent warnings about unknown class/instance types
* as not all class/instance types have entries on all platforms.
@@ -245,6 +255,21 @@ static const struct __guc_mmio_reg_descr_group xe_hpg_lists[] = {
{}
};
+ /* List of lists for Xe3p and beyond */
+static const struct __guc_mmio_reg_descr_group xe3p_lists[] = {
+ MAKE_REGLIST(xe_lp_global_regs, PF, GLOBAL, 0),
+ MAKE_REGLIST(xe_hpg_rc_class_regs, PF, ENGINE_CLASS, GUC_CAPTURE_LIST_CLASS_RENDER_COMPUTE),
+ MAKE_REGLIST(xe3p_rc_inst_regs, PF, ENGINE_INSTANCE, GUC_CAPTURE_LIST_CLASS_RENDER_COMPUTE),
+ MAKE_REGLIST(empty_regs_list, PF, ENGINE_CLASS, GUC_CAPTURE_LIST_CLASS_VIDEO),
+ MAKE_REGLIST(xe_vd_inst_regs, PF, ENGINE_INSTANCE, GUC_CAPTURE_LIST_CLASS_VIDEO),
+ MAKE_REGLIST(xe_vec_class_regs, PF, ENGINE_CLASS, GUC_CAPTURE_LIST_CLASS_VIDEOENHANCE),
+ MAKE_REGLIST(xe_vec_inst_regs, PF, ENGINE_INSTANCE, GUC_CAPTURE_LIST_CLASS_VIDEOENHANCE),
+ MAKE_REGLIST(empty_regs_list, PF, ENGINE_CLASS, GUC_CAPTURE_LIST_CLASS_BLITTER),
+ MAKE_REGLIST(xe_blt_inst_regs, PF, ENGINE_INSTANCE, GUC_CAPTURE_LIST_CLASS_BLITTER),
+ MAKE_REGLIST(empty_regs_list, PF, ENGINE_CLASS, GUC_CAPTURE_LIST_CLASS_GSC_OTHER),
+ MAKE_REGLIST(xe_lp_gsc_inst_regs, PF, ENGINE_INSTANCE, GUC_CAPTURE_LIST_CLASS_GSC_OTHER),
+ {}
+};
static const char * const capture_list_type_names[] = {
"Global",
"Class",
@@ -292,7 +317,9 @@ guc_capture_remove_stale_matches_from_list(struct xe_guc_state_capture *gc,
static const struct __guc_mmio_reg_descr_group *
guc_capture_get_device_reglist(struct xe_device *xe)
{
- if (GRAPHICS_VERx100(xe) >= 1255)
+ if (GRAPHICS_VER(xe) >= 35)
+ return xe3p_lists;
+ else if (GRAPHICS_VERx100(xe) >= 1255)
return xe_hpg_lists;
else
return xe_lp_lists;
diff --git a/drivers/gpu/drm/xe/xe_guc_ct.c b/drivers/gpu/drm/xe/xe_guc_ct.c
index 18f6327bf552..e68953ef3a00 100644
--- a/drivers/gpu/drm/xe/xe_guc_ct.c
+++ b/drivers/gpu/drm/xe/xe_guc_ct.c
@@ -25,7 +25,6 @@
#include "xe_gt_printk.h"
#include "xe_gt_sriov_pf_control.h"
#include "xe_gt_sriov_pf_monitor.h"
-#include "xe_gt_sriov_printk.h"
#include "xe_guc.h"
#include "xe_guc_log.h"
#include "xe_guc_relay.h"
@@ -33,6 +32,7 @@
#include "xe_guc_tlb_inval.h"
#include "xe_map.h"
#include "xe_pm.h"
+#include "xe_sriov_vf.h"
#include "xe_trace_guc.h"
static void receive_g2h(struct xe_guc_ct *ct);
@@ -93,8 +93,6 @@ struct g2h_fence {
bool done;
};
-#define make_u64(hi, lo) ((u64)((u64)(u32)(hi) << 32 | (u32)(lo)))
-
static void g2h_fence_init(struct g2h_fence *g2h_fence, u32 *response_buffer)
{
memset(g2h_fence, 0, sizeof(*g2h_fence));
@@ -169,6 +167,7 @@ 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_G2H_BUFFER_SIZE (SZ_128K)
#define G2H_ROOM_BUFFER_SIZE (CTB_G2H_BUFFER_SIZE / 2)
@@ -192,7 +191,7 @@ long xe_guc_ct_queue_proc_time_jiffies(struct xe_guc_ct *ct)
static size_t guc_ct_size(void)
{
- return 2 * CTB_DESC_SIZE + CTB_H2G_BUFFER_SIZE +
+ return CTB_H2G_BUFFER_OFFSET + CTB_H2G_BUFFER_SIZE +
CTB_G2H_BUFFER_SIZE;
}
@@ -333,7 +332,7 @@ static void guc_ct_ctb_h2g_init(struct xe_device *xe, struct guc_ctb *h2g,
h2g->desc = *map;
xe_map_memset(xe, &h2g->desc, 0, 0, sizeof(struct guc_ct_buffer_desc));
- h2g->cmds = IOSYS_MAP_INIT_OFFSET(map, CTB_DESC_SIZE * 2);
+ h2g->cmds = IOSYS_MAP_INIT_OFFSET(map, CTB_H2G_BUFFER_OFFSET);
}
static void guc_ct_ctb_g2h_init(struct xe_device *xe, struct guc_ctb *g2h,
@@ -351,7 +350,7 @@ static void guc_ct_ctb_g2h_init(struct xe_device *xe, struct guc_ctb *g2h,
g2h->desc = IOSYS_MAP_INIT_OFFSET(map, CTB_DESC_SIZE);
xe_map_memset(xe, &g2h->desc, 0, 0, sizeof(struct guc_ct_buffer_desc));
- g2h->cmds = IOSYS_MAP_INIT_OFFSET(map, CTB_DESC_SIZE * 2 +
+ g2h->cmds = IOSYS_MAP_INIT_OFFSET(map, CTB_H2G_BUFFER_OFFSET +
CTB_H2G_BUFFER_SIZE);
}
@@ -362,7 +361,7 @@ static int guc_ct_ctb_h2g_register(struct xe_guc_ct *ct)
int err;
desc_addr = xe_bo_ggtt_addr(ct->bo);
- ctb_addr = xe_bo_ggtt_addr(ct->bo) + CTB_DESC_SIZE * 2;
+ ctb_addr = xe_bo_ggtt_addr(ct->bo) + CTB_H2G_BUFFER_OFFSET;
size = ct->ctbs.h2g.info.size * sizeof(u32);
err = xe_guc_self_cfg64(guc,
@@ -389,7 +388,7 @@ static int guc_ct_ctb_g2h_register(struct xe_guc_ct *ct)
int err;
desc_addr = xe_bo_ggtt_addr(ct->bo) + CTB_DESC_SIZE;
- ctb_addr = xe_bo_ggtt_addr(ct->bo) + CTB_DESC_SIZE * 2 +
+ ctb_addr = xe_bo_ggtt_addr(ct->bo) + CTB_H2G_BUFFER_OFFSET +
CTB_H2G_BUFFER_SIZE;
size = ct->ctbs.g2h.info.size * sizeof(u32);
@@ -503,7 +502,7 @@ static void ct_exit_safe_mode(struct xe_guc_ct *ct)
xe_gt_dbg(ct_to_gt(ct), "GuC CT safe-mode disabled\n");
}
-int xe_guc_ct_enable(struct xe_guc_ct *ct)
+static int __xe_guc_ct_start(struct xe_guc_ct *ct, bool needs_register)
{
struct xe_device *xe = ct_to_xe(ct);
struct xe_gt *gt = ct_to_gt(ct);
@@ -511,21 +510,29 @@ int xe_guc_ct_enable(struct xe_guc_ct *ct)
xe_gt_assert(gt, !xe_guc_ct_enabled(ct));
- xe_map_memset(xe, &ct->bo->vmap, 0, 0, xe_bo_size(ct->bo));
- guc_ct_ctb_h2g_init(xe, &ct->ctbs.h2g, &ct->bo->vmap);
- guc_ct_ctb_g2h_init(xe, &ct->ctbs.g2h, &ct->bo->vmap);
+ if (needs_register) {
+ xe_map_memset(xe, &ct->bo->vmap, 0, 0, xe_bo_size(ct->bo));
+ guc_ct_ctb_h2g_init(xe, &ct->ctbs.h2g, &ct->bo->vmap);
+ guc_ct_ctb_g2h_init(xe, &ct->ctbs.g2h, &ct->bo->vmap);
- err = guc_ct_ctb_h2g_register(ct);
- if (err)
- goto err_out;
+ err = guc_ct_ctb_h2g_register(ct);
+ if (err)
+ goto err_out;
- err = guc_ct_ctb_g2h_register(ct);
- if (err)
- goto err_out;
+ err = guc_ct_ctb_g2h_register(ct);
+ if (err)
+ goto err_out;
- err = guc_ct_control_toggle(ct, true);
- if (err)
- goto err_out;
+ err = guc_ct_control_toggle(ct, true);
+ if (err)
+ goto err_out;
+ } else {
+ ct->ctbs.h2g.info.broken = false;
+ ct->ctbs.g2h.info.broken = false;
+ /* Skip everything in H2G buffer */
+ xe_map_memset(xe, &ct->bo->vmap, CTB_H2G_BUFFER_OFFSET, 0,
+ CTB_H2G_BUFFER_SIZE);
+ }
guc_ct_change_state(ct, XE_GUC_CT_STATE_ENABLED);
@@ -557,6 +564,32 @@ err_out:
return err;
}
+/**
+ * xe_guc_ct_restart() - Restart GuC CT
+ * @ct: the &xe_guc_ct
+ *
+ * Restart GuC CT to an empty state without issuing a CT register MMIO command.
+ *
+ * Return: 0 on success, or a negative errno on failure.
+ */
+int xe_guc_ct_restart(struct xe_guc_ct *ct)
+{
+ return __xe_guc_ct_start(ct, false);
+}
+
+/**
+ * xe_guc_ct_enable() - Enable GuC CT
+ * @ct: the &xe_guc_ct
+ *
+ * Enable GuC CT to an empty state and issue a CT register MMIO command.
+ *
+ * Return: 0 on success, or a negative errno on failure.
+ */
+int xe_guc_ct_enable(struct xe_guc_ct *ct)
+{
+ return __xe_guc_ct_start(ct, true);
+}
+
static void stop_g2h_handler(struct xe_guc_ct *ct)
{
cancel_work_sync(&ct->g2h_worker);
@@ -577,6 +610,16 @@ void xe_guc_ct_disable(struct xe_guc_ct *ct)
}
/**
+ * xe_guc_ct_flush_and_stop - Flush and stop all processing of G2H / H2G
+ * @ct: the &xe_guc_ct
+ */
+void xe_guc_ct_flush_and_stop(struct xe_guc_ct *ct)
+{
+ receive_g2h(ct);
+ xe_guc_ct_stop(ct);
+}
+
+/**
* xe_guc_ct_stop - Set GuC to stopped state
* @ct: the &xe_guc_ct
*
@@ -739,6 +782,28 @@ static u16 next_ct_seqno(struct xe_guc_ct *ct, bool is_g2h_fence)
return seqno;
}
+#define MAKE_ACTION(type, __action) \
+({ \
+ FIELD_PREP(GUC_HXG_MSG_0_TYPE, type) | \
+ FIELD_PREP(GUC_HXG_EVENT_MSG_0_ACTION | \
+ GUC_HXG_EVENT_MSG_0_DATA0, __action); \
+})
+
+static bool vf_action_can_safely_fail(struct xe_device *xe, u32 action)
+{
+ /*
+ * When resuming a VF, we can't reliably track whether context
+ * registration has completed in the GuC state machine. It is harmless
+ * to resend the request, as it will fail silently if GUC_HXG_TYPE_EVENT
+ * is used. Additionally, if there is an H2G protocol issue on a VF,
+ * subsequent H2G messages sent as GUC_HXG_TYPE_FAST_REQUEST will likely
+ * fail.
+ */
+ return IS_SRIOV_VF(xe) && xe_sriov_vf_migration_supported(xe) &&
+ (action == XE_GUC_ACTION_REGISTER_CONTEXT_MULTI_LRC ||
+ action == XE_GUC_ACTION_REGISTER_CONTEXT);
+}
+
#define H2G_CT_HEADERS (GUC_CTB_HDR_LEN + 1) /* one DW CTB header and one DW HxG header */
static int h2g_write(struct xe_guc_ct *ct, const u32 *action, u32 len,
@@ -810,18 +875,14 @@ static int h2g_write(struct xe_guc_ct *ct, const u32 *action, u32 len,
FIELD_PREP(GUC_CTB_MSG_0_NUM_DWORDS, len) |
FIELD_PREP(GUC_CTB_MSG_0_FENCE, ct_fence_value);
if (want_response) {
- cmd[1] =
- FIELD_PREP(GUC_HXG_MSG_0_TYPE, GUC_HXG_TYPE_REQUEST) |
- FIELD_PREP(GUC_HXG_EVENT_MSG_0_ACTION |
- GUC_HXG_EVENT_MSG_0_DATA0, action[0]);
+ cmd[1] = MAKE_ACTION(GUC_HXG_TYPE_REQUEST, action[0]);
+ } else if (vf_action_can_safely_fail(xe, action[0])) {
+ cmd[1] = MAKE_ACTION(GUC_HXG_TYPE_EVENT, action[0]);
} else {
fast_req_track(ct, ct_fence_value,
FIELD_GET(GUC_HXG_EVENT_MSG_0_ACTION, action[0]));
- cmd[1] =
- FIELD_PREP(GUC_HXG_MSG_0_TYPE, GUC_HXG_TYPE_FAST_REQUEST) |
- FIELD_PREP(GUC_HXG_EVENT_MSG_0_ACTION |
- GUC_HXG_EVENT_MSG_0_DATA0, action[0]);
+ cmd[1] = MAKE_ACTION(GUC_HXG_TYPE_FAST_REQUEST, action[0]);
}
/* H2G header in cmd[1] replaces action[0] so: */
@@ -854,7 +915,7 @@ static int __guc_ct_send_locked(struct xe_guc_ct *ct, const u32 *action,
u32 len, u32 g2h_len, u32 num_g2h,
struct g2h_fence *g2h_fence)
{
- struct xe_gt *gt __maybe_unused = ct_to_gt(ct);
+ struct xe_gt *gt = ct_to_gt(ct);
u16 seqno;
int ret;
@@ -875,7 +936,7 @@ static int __guc_ct_send_locked(struct xe_guc_ct *ct, const u32 *action,
goto out;
}
- if (ct->state == XE_GUC_CT_STATE_STOPPED) {
+ if (ct->state == XE_GUC_CT_STATE_STOPPED || xe_gt_recovery_pending(gt)) {
ret = -ECANCELED;
goto out;
}
@@ -930,22 +991,15 @@ static void kick_reset(struct xe_guc_ct *ct)
static int dequeue_one_g2h(struct xe_guc_ct *ct);
-static int guc_ct_send_locked(struct xe_guc_ct *ct, const u32 *action, u32 len,
- u32 g2h_len, u32 num_g2h,
- struct g2h_fence *g2h_fence)
+/*
+ * wait before retry of sending h2g message
+ * Return: true if ready for retry, false if the wait timeouted
+ */
+static bool guc_ct_send_wait_for_retry(struct xe_guc_ct *ct, u32 len,
+ u32 g2h_len, struct g2h_fence *g2h_fence,
+ unsigned int *sleep_period_ms)
{
struct xe_device *xe = ct_to_xe(ct);
- struct xe_gt *gt = ct_to_gt(ct);
- unsigned int sleep_period_ms = 1;
- int ret;
-
- xe_gt_assert(gt, !g2h_len || !g2h_fence);
- lockdep_assert_held(&ct->lock);
- xe_device_assert_mem_access(ct_to_xe(ct));
-
-try_again:
- ret = __guc_ct_send_locked(ct, action, len, g2h_len, num_g2h,
- g2h_fence);
/*
* We wait to try to restore credits for about 1 second before bailing.
@@ -954,24 +1008,22 @@ try_again:
* the case of G2H we process any G2H in the channel, hopefully freeing
* credits as we consume the G2H messages.
*/
- if (unlikely(ret == -EBUSY &&
- !h2g_has_room(ct, len + GUC_CTB_HDR_LEN))) {
+ if (!h2g_has_room(ct, len + GUC_CTB_HDR_LEN)) {
struct guc_ctb *h2g = &ct->ctbs.h2g;
- if (sleep_period_ms == 1024)
- goto broken;
+ if (*sleep_period_ms == 1024)
+ return false;
trace_xe_guc_ct_h2g_flow_control(xe, h2g->info.head, h2g->info.tail,
h2g->info.size,
h2g->info.space,
len + GUC_CTB_HDR_LEN);
- msleep(sleep_period_ms);
- sleep_period_ms <<= 1;
-
- goto try_again;
- } else if (unlikely(ret == -EBUSY)) {
+ msleep(*sleep_period_ms);
+ *sleep_period_ms <<= 1;
+ } else {
struct xe_device *xe = ct_to_xe(ct);
struct guc_ctb *g2h = &ct->ctbs.g2h;
+ int ret;
trace_xe_guc_ct_g2h_flow_control(xe, g2h->info.head,
desc_read(xe, g2h, tail),
@@ -985,7 +1037,7 @@ try_again:
(desc_read(ct_to_xe(ct), (&ct->ctbs.g2h), tail) != ct->ctbs.g2h.info.head)
if (!wait_event_timeout(ct->wq, !ct->g2h_outstanding ||
g2h_avail(ct), HZ))
- goto broken;
+ return false;
#undef g2h_avail
ret = dequeue_one_g2h(ct);
@@ -993,9 +1045,32 @@ try_again:
if (ret != -ECANCELED)
xe_gt_err(ct_to_gt(ct), "CTB receive failed (%pe)",
ERR_PTR(ret));
- goto broken;
+ return false;
}
+ }
+ return true;
+}
+
+static int guc_ct_send_locked(struct xe_guc_ct *ct, const u32 *action, u32 len,
+ u32 g2h_len, u32 num_g2h,
+ struct g2h_fence *g2h_fence)
+{
+ struct xe_gt *gt = ct_to_gt(ct);
+ unsigned int sleep_period_ms = 1;
+ int ret;
+
+ xe_gt_assert(gt, !g2h_len || !g2h_fence);
+ lockdep_assert_held(&ct->lock);
+ xe_device_assert_mem_access(ct_to_xe(ct));
+try_again:
+ ret = __guc_ct_send_locked(ct, action, len, g2h_len, num_g2h,
+ g2h_fence);
+
+ if (unlikely(ret == -EBUSY)) {
+ if (!guc_ct_send_wait_for_retry(ct, len, g2h_len, g2h_fence,
+ &sleep_period_ms))
+ goto broken;
goto try_again;
}
@@ -1337,6 +1412,10 @@ static int parse_g2h_response(struct xe_guc_ct *ct, u32 *msg, u32 len)
fast_req_report(ct, fence);
+ /* FIXME: W/A race in the GuC, will get in firmware soon */
+ if (xe_gt_recovery_pending(gt))
+ return 0;
+
CT_DEAD(ct, NULL, PARSE_G2H_RESPONSE);
return -EPROTO;
@@ -1793,186 +1872,6 @@ static void g2h_worker_func(struct work_struct *w)
receive_g2h(ct);
}
-static void xe_fixup_u64_in_cmds(struct xe_device *xe, struct iosys_map *cmds,
- u32 size, u32 idx, s64 shift)
-{
- u32 hi, lo;
- u64 offset;
-
- lo = xe_map_rd_ring_u32(xe, cmds, idx, size);
- hi = xe_map_rd_ring_u32(xe, cmds, idx + 1, size);
- offset = make_u64(hi, lo);
- offset += shift;
- lo = lower_32_bits(offset);
- hi = upper_32_bits(offset);
- xe_map_wr_ring_u32(xe, cmds, idx, size, lo);
- xe_map_wr_ring_u32(xe, cmds, idx + 1, size, hi);
-}
-
-/*
- * Shift any GGTT addresses within a single message left within CTB from
- * before post-migration recovery.
- * @ct: pointer to CT struct of the target GuC
- * @cmds: iomap buffer containing CT messages
- * @head: start of the target message within the buffer
- * @len: length of the target message
- * @size: size of the commands buffer
- * @shift: the address shift to be added to each GGTT reference
- * Return: true if the message was fixed or needed no fixups, false on failure
- */
-static bool ct_fixup_ggtt_in_message(struct xe_guc_ct *ct,
- struct iosys_map *cmds, u32 head,
- u32 len, u32 size, s64 shift)
-{
- struct xe_gt *gt = ct_to_gt(ct);
- struct xe_device *xe = ct_to_xe(ct);
- u32 msg[GUC_HXG_MSG_MIN_LEN];
- u32 action, i, n;
-
- xe_gt_assert(gt, len >= GUC_HXG_MSG_MIN_LEN);
-
- msg[0] = xe_map_rd_ring_u32(xe, cmds, head, size);
- action = FIELD_GET(GUC_HXG_REQUEST_MSG_0_ACTION, msg[0]);
-
- xe_gt_sriov_dbg_verbose(gt, "fixing H2G %#x\n", action);
-
- switch (action) {
- case XE_GUC_ACTION_REGISTER_CONTEXT:
- if (len != XE_GUC_REGISTER_CONTEXT_MSG_LEN)
- goto err_len;
- xe_fixup_u64_in_cmds(xe, cmds, size, head +
- XE_GUC_REGISTER_CONTEXT_DATA_5_WQ_DESC_ADDR_LOWER,
- shift);
- xe_fixup_u64_in_cmds(xe, cmds, size, head +
- XE_GUC_REGISTER_CONTEXT_DATA_7_WQ_BUF_BASE_LOWER,
- shift);
- xe_fixup_u64_in_cmds(xe, cmds, size, head +
- XE_GUC_REGISTER_CONTEXT_DATA_10_HW_LRC_ADDR, shift);
- break;
- case XE_GUC_ACTION_REGISTER_CONTEXT_MULTI_LRC:
- if (len < XE_GUC_REGISTER_CONTEXT_MULTI_LRC_MSG_MIN_LEN)
- goto err_len;
- n = xe_map_rd_ring_u32(xe, cmds, head +
- XE_GUC_REGISTER_CONTEXT_MULTI_LRC_DATA_10_NUM_CTXS, size);
- if (len != XE_GUC_REGISTER_CONTEXT_MULTI_LRC_MSG_MIN_LEN + 2 * n)
- goto err_len;
- xe_fixup_u64_in_cmds(xe, cmds, size, head +
- XE_GUC_REGISTER_CONTEXT_MULTI_LRC_DATA_5_WQ_DESC_ADDR_LOWER,
- shift);
- xe_fixup_u64_in_cmds(xe, cmds, size, head +
- XE_GUC_REGISTER_CONTEXT_MULTI_LRC_DATA_7_WQ_BUF_BASE_LOWER,
- shift);
- for (i = 0; i < n; i++)
- xe_fixup_u64_in_cmds(xe, cmds, size, head +
- XE_GUC_REGISTER_CONTEXT_MULTI_LRC_DATA_11_HW_LRC_ADDR
- + 2 * i, shift);
- break;
- default:
- break;
- }
- return true;
-
-err_len:
- xe_gt_err(gt, "Skipped G2G %#x message fixups, unexpected length (%u)\n", action, len);
- return false;
-}
-
-/*
- * Apply fixups to the next outgoing CT message within given CTB
- * @ct: the &xe_guc_ct struct instance representing the target GuC
- * @h2g: the &guc_ctb struct instance of the target buffer
- * @shift: shift to be added to all GGTT addresses within the CTB
- * @mhead: pointer to an integer storing message start position; the
- * position is changed to next message before this function return
- * @avail: size of the area available for parsing, that is length
- * of all remaining messages stored within the CTB
- * Return: size of the area available for parsing after one message
- * has been parsed, that is length remaining from the updated mhead
- */
-static int ct_fixup_ggtt_in_buffer(struct xe_guc_ct *ct, struct guc_ctb *h2g,
- s64 shift, u32 *mhead, s32 avail)
-{
- struct xe_gt *gt = ct_to_gt(ct);
- struct xe_device *xe = ct_to_xe(ct);
- u32 msg[GUC_HXG_MSG_MIN_LEN];
- u32 size = h2g->info.size;
- u32 head = *mhead;
- u32 len;
-
- xe_gt_assert(gt, avail >= (s32)GUC_CTB_MSG_MIN_LEN);
-
- /* Read header */
- msg[0] = xe_map_rd_ring_u32(xe, &h2g->cmds, head, size);
- len = FIELD_GET(GUC_CTB_MSG_0_NUM_DWORDS, msg[0]) + GUC_CTB_MSG_MIN_LEN;
-
- if (unlikely(len > (u32)avail)) {
- xe_gt_err(gt, "H2G channel broken on read, avail=%d, len=%d, fixups skipped\n",
- avail, len);
- return 0;
- }
-
- head = (head + GUC_CTB_MSG_MIN_LEN) % size;
- if (!ct_fixup_ggtt_in_message(ct, &h2g->cmds, head, msg_len_to_hxg_len(len), size, shift))
- return 0;
- *mhead = (head + msg_len_to_hxg_len(len)) % size;
-
- return avail - len;
-}
-
-/**
- * xe_guc_ct_fixup_messages_with_ggtt - Fixup any pending H2G CTB messages
- * @ct: pointer to CT struct of the target GuC
- * @ggtt_shift: shift to be added to all GGTT addresses within the CTB
- *
- * Messages in GuC to Host CTB are owned by GuC and any fixups in them
- * are made by GuC. But content of the Host to GuC CTB is owned by the
- * KMD, so fixups to GGTT references in any pending messages need to be
- * applied here.
- * This function updates GGTT offsets in payloads of pending H2G CTB
- * messages (messages which were not consumed by GuC before the VF got
- * paused).
- */
-void xe_guc_ct_fixup_messages_with_ggtt(struct xe_guc_ct *ct, s64 ggtt_shift)
-{
- struct guc_ctb *h2g = &ct->ctbs.h2g;
- struct xe_guc *guc = ct_to_guc(ct);
- struct xe_gt *gt = guc_to_gt(guc);
- u32 head, tail, size;
- s32 avail;
-
- if (unlikely(h2g->info.broken))
- return;
-
- h2g->info.head = desc_read(ct_to_xe(ct), h2g, head);
- head = h2g->info.head;
- tail = READ_ONCE(h2g->info.tail);
- size = h2g->info.size;
-
- if (unlikely(head > size))
- goto corrupted;
-
- if (unlikely(tail >= size))
- goto corrupted;
-
- avail = tail - head;
-
- /* beware of buffer wrap case */
- if (unlikely(avail < 0))
- avail += size;
- xe_gt_dbg(gt, "available %d (%u:%u:%u)\n", avail, head, tail, size);
- xe_gt_assert(gt, avail >= 0);
-
- while (avail > 0)
- avail = ct_fixup_ggtt_in_buffer(ct, h2g, ggtt_shift, &head, avail);
-
- return;
-
-corrupted:
- xe_gt_err(gt, "Corrupted H2G descriptor head=%u tail=%u size=%u, fixups not applied\n",
- head, tail, size);
- h2g->info.broken = true;
-}
-
static struct xe_guc_ct_snapshot *guc_ct_snapshot_alloc(struct xe_guc_ct *ct, bool atomic,
bool want_ctb)
{
diff --git a/drivers/gpu/drm/xe/xe_guc_ct.h b/drivers/gpu/drm/xe/xe_guc_ct.h
index cf41210ab30a..ca1ce2b3c354 100644
--- a/drivers/gpu/drm/xe/xe_guc_ct.h
+++ b/drivers/gpu/drm/xe/xe_guc_ct.h
@@ -15,8 +15,10 @@ int xe_guc_ct_init_noalloc(struct xe_guc_ct *ct);
int xe_guc_ct_init(struct xe_guc_ct *ct);
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_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);
struct xe_guc_ct_snapshot *xe_guc_ct_snapshot_capture(struct xe_guc_ct *ct);
@@ -24,8 +26,6 @@ void xe_guc_ct_snapshot_print(struct xe_guc_ct_snapshot *snapshot, struct drm_pr
void xe_guc_ct_snapshot_free(struct xe_guc_ct_snapshot *snapshot);
void xe_guc_ct_print(struct xe_guc_ct *ct, struct drm_printer *p, bool want_ctb);
-void xe_guc_ct_fixup_messages_with_ggtt(struct xe_guc_ct *ct, s64 ggtt_shift);
-
static inline bool xe_guc_ct_initialized(struct xe_guc_ct *ct)
{
return ct->state != XE_GUC_CT_STATE_NOT_INITIALIZED;
@@ -74,4 +74,13 @@ xe_guc_ct_send_block_no_fail(struct xe_guc_ct *ct, const u32 *action, u32 len)
long xe_guc_ct_queue_proc_time_jiffies(struct xe_guc_ct *ct);
+/**
+ * xe_guc_ct_wake_waiters() - GuC CT wake up waiters
+ * @ct: GuC CT object
+ */
+static inline void xe_guc_ct_wake_waiters(struct xe_guc_ct *ct)
+{
+ wake_up_all(&ct->wq);
+}
+
#endif
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 c30c0e3ccbbb..a3b034e4b205 100644
--- a/drivers/gpu/drm/xe/xe_guc_exec_queue_types.h
+++ b/drivers/gpu/drm/xe/xe_guc_exec_queue_types.h
@@ -51,6 +51,21 @@ struct xe_guc_exec_queue {
wait_queue_head_t suspend_wait;
/** @suspend_pending: a suspend of the exec_queue is pending */
bool suspend_pending;
+ /**
+ * @needs_cleanup: Needs a cleanup message during VF post migration
+ * recovery.
+ */
+ bool needs_cleanup;
+ /**
+ * @needs_suspend: Needs a suspend message during VF post migration
+ * recovery.
+ */
+ bool needs_suspend;
+ /**
+ * @needs_resume: Needs a resume message during VF post migration
+ * recovery.
+ */
+ bool needs_resume;
};
#endif
diff --git a/drivers/gpu/drm/xe/xe_guc_pc.c b/drivers/gpu/drm/xe/xe_guc_pc.c
index 53fdf59524c4..ff22235857f8 100644
--- a/drivers/gpu/drm/xe/xe_guc_pc.c
+++ b/drivers/gpu/drm/xe/xe_guc_pc.c
@@ -7,12 +7,14 @@
#include <linux/cleanup.h>
#include <linux/delay.h>
+#include <linux/iopoll.h>
#include <linux/jiffies.h>
#include <linux/ktime.h>
#include <linux/wait_bit.h>
#include <drm/drm_managed.h>
#include <drm/drm_print.h>
+#include <generated/xe_device_wa_oob.h>
#include <generated/xe_wa_oob.h>
#include "abi/guc_actions_slpc_abi.h"
@@ -130,26 +132,16 @@ static struct iosys_map *pc_to_maps(struct xe_guc_pc *pc)
FIELD_PREP(HOST2GUC_PC_SLPC_REQUEST_MSG_1_EVENT_ARGC, count))
static int wait_for_pc_state(struct xe_guc_pc *pc,
- enum slpc_global_state state,
+ enum slpc_global_state target_state,
int timeout_ms)
{
- int timeout_us = 1000 * timeout_ms;
- int slept, wait = 10;
+ enum slpc_global_state state;
xe_device_assert_mem_access(pc_to_xe(pc));
- for (slept = 0; slept < timeout_us;) {
- if (slpc_shared_data_read(pc, header.global_state) == state)
- return 0;
-
- usleep_range(wait, wait << 1);
- slept += wait;
- wait <<= 1;
- if (slept + wait > timeout_us)
- wait = timeout_us - slept;
- }
-
- return -ETIMEDOUT;
+ return poll_timeout_us(state = slpc_shared_data_read(pc, header.global_state),
+ state == target_state,
+ 20, timeout_ms * USEC_PER_MSEC, false);
}
static int wait_for_flush_complete(struct xe_guc_pc *pc)
@@ -164,24 +156,15 @@ static int wait_for_flush_complete(struct xe_guc_pc *pc)
return 0;
}
-static int wait_for_act_freq_limit(struct xe_guc_pc *pc, u32 freq)
+static int wait_for_act_freq_max_limit(struct xe_guc_pc *pc, u32 max_limit)
{
- int timeout_us = SLPC_ACT_FREQ_TIMEOUT_MS * USEC_PER_MSEC;
- int slept, wait = 10;
-
- for (slept = 0; slept < timeout_us;) {
- if (xe_guc_pc_get_act_freq(pc) <= freq)
- return 0;
-
- usleep_range(wait, wait << 1);
- slept += wait;
- wait <<= 1;
- if (slept + wait > timeout_us)
- wait = timeout_us - slept;
- }
+ u32 freq;
- return -ETIMEDOUT;
+ return poll_timeout_us(freq = xe_guc_pc_get_act_freq(pc),
+ freq <= max_limit,
+ 20, SLPC_ACT_FREQ_TIMEOUT_MS * USEC_PER_MSEC, false);
}
+
static int pc_action_reset(struct xe_guc_pc *pc)
{
struct xe_guc_ct *ct = pc_to_ct(pc);
@@ -904,7 +887,7 @@ static int pc_adjust_freq_bounds(struct xe_guc_pc *pc)
if (pc_get_min_freq(pc) > pc->rp0_freq)
ret = pc_set_min_freq(pc, pc->rp0_freq);
- if (XE_GT_WA(tile->primary_gt, 14022085890))
+ if (XE_DEVICE_WA(tile_to_xe(tile), 14022085890))
ret = pc_set_min_freq(pc, max(BMG_MIN_FREQ, pc_get_min_freq(pc)));
out:
@@ -983,7 +966,7 @@ void xe_guc_pc_apply_flush_freq_limit(struct xe_guc_pc *pc)
* Wait for actual freq to go below the flush cap: even if the previous
* max was below cap, the current one might still be above it
*/
- ret = wait_for_act_freq_limit(pc, BMG_MERT_FLUSH_FREQ_CAP);
+ ret = wait_for_act_freq_max_limit(pc, BMG_MERT_FLUSH_FREQ_CAP);
if (ret)
xe_gt_err_once(gt, "Actual freq did not reduce to %u, %pe\n",
BMG_MERT_FLUSH_FREQ_CAP, ERR_PTR(ret));
diff --git a/drivers/gpu/drm/xe/xe_guc_relay.c b/drivers/gpu/drm/xe/xe_guc_relay.c
index e5dc94f3e618..0c0ff24ba62a 100644
--- a/drivers/gpu/drm/xe/xe_guc_relay.c
+++ b/drivers/gpu/drm/xe/xe_guc_relay.c
@@ -56,9 +56,19 @@ static struct xe_device *relay_to_xe(struct xe_guc_relay *relay)
return gt_to_xe(relay_to_gt(relay));
}
+#define XE_RELAY_DIAG_RATELIMIT_INTERVAL (10 * HZ)
+#define XE_RELAY_DIAG_RATELIMIT_BURST 10
+
+#define relay_ratelimit_printk(relay, _level, fmt...) ({ \
+ typeof(relay) _r = (relay); \
+ if (IS_ENABLED(CONFIG_DRM_XE_DEBUG_SRIOV) || \
+ ___ratelimit(&_r->diag_ratelimit, "xe_guc_relay")) \
+ xe_gt_sriov_##_level(relay_to_gt(_r), "relay: " fmt); \
+})
+
#define relay_assert(relay, condition) xe_gt_assert(relay_to_gt(relay), condition)
-#define relay_notice(relay, msg...) xe_gt_sriov_notice(relay_to_gt(relay), "relay: " msg)
-#define relay_debug(relay, msg...) xe_gt_sriov_dbg_verbose(relay_to_gt(relay), "relay: " msg)
+#define relay_notice(relay, msg...) relay_ratelimit_printk((relay), notice, msg)
+#define relay_debug(relay, msg...) relay_ratelimit_printk((relay), dbg_verbose, msg)
static int relay_get_totalvfs(struct xe_guc_relay *relay)
{
@@ -345,6 +355,9 @@ int xe_guc_relay_init(struct xe_guc_relay *relay)
INIT_WORK(&relay->worker, relays_worker_fn);
INIT_LIST_HEAD(&relay->pending_relays);
INIT_LIST_HEAD(&relay->incoming_actions);
+ ratelimit_state_init(&relay->diag_ratelimit,
+ XE_RELAY_DIAG_RATELIMIT_INTERVAL,
+ XE_RELAY_DIAG_RATELIMIT_BURST);
err = mempool_init_kmalloc_pool(&relay->pool, XE_RELAY_MEMPOOL_MIN_NUM +
relay_get_totalvfs(relay),
diff --git a/drivers/gpu/drm/xe/xe_guc_relay_types.h b/drivers/gpu/drm/xe/xe_guc_relay_types.h
index 5999fcb77e96..20eee10856b2 100644
--- a/drivers/gpu/drm/xe/xe_guc_relay_types.h
+++ b/drivers/gpu/drm/xe/xe_guc_relay_types.h
@@ -7,6 +7,7 @@
#define _XE_GUC_RELAY_TYPES_H_
#include <linux/mempool.h>
+#include <linux/ratelimit_types.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
@@ -31,6 +32,9 @@ struct xe_guc_relay {
/** @last_rid: last Relay-ID used while sending a message. */
u32 last_rid;
+
+ /** @diag_ratelimit: ratelimit state used to throttle diagnostics messages. */
+ struct ratelimit_state diag_ratelimit;
};
#endif
diff --git a/drivers/gpu/drm/xe/xe_guc_submit.c b/drivers/gpu/drm/xe/xe_guc_submit.c
index 94ed8159496f..0ef67d3523a7 100644
--- a/drivers/gpu/drm/xe/xe_guc_submit.c
+++ b/drivers/gpu/drm/xe/xe_guc_submit.c
@@ -70,6 +70,8 @@ exec_queue_to_guc(struct xe_exec_queue *q)
#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)
static bool exec_queue_registered(struct xe_exec_queue *q)
{
@@ -141,6 +143,11 @@ static void set_exec_queue_destroyed(struct xe_exec_queue *q)
atomic_or(EXEC_QUEUE_STATE_DESTROYED, &q->guc->state);
}
+static void clear_exec_queue_destroyed(struct xe_exec_queue *q)
+{
+ atomic_and(~EXEC_QUEUE_STATE_DESTROYED, &q->guc->state);
+}
+
static bool exec_queue_banned(struct xe_exec_queue *q)
{
return atomic_read(&q->guc->state) & EXEC_QUEUE_STATE_BANNED;
@@ -221,6 +228,41 @@ 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;
+}
+
+static void set_exec_queue_pending_resume(struct xe_exec_queue *q)
+{
+ atomic_or(EXEC_QUEUE_STATE_PENDING_RESUME, &q->guc->state);
+}
+
+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)
+{
+ return atomic_read(&q->guc->state) & EXEC_QUEUE_STATE_PENDING_TDR_EXIT;
+}
+
+static void set_exec_queue_pending_tdr_exit(struct xe_exec_queue *q)
+{
+ atomic_or(EXEC_QUEUE_STATE_PENDING_TDR_EXIT, &q->guc->state);
+}
+
+static void clear_exec_queue_pending_tdr_exit(struct xe_exec_queue *q)
+{
+ atomic_and(~EXEC_QUEUE_STATE_PENDING_TDR_EXIT, &q->guc->state);
+}
+
static bool exec_queue_killed_or_banned_or_wedged(struct xe_exec_queue *q)
{
return (atomic_read(&q->guc->state) &
@@ -670,6 +712,11 @@ 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)
+{
+ return xe_gt_recovery_pending(guc_to_gt(guc));
+}
+
static int wq_wait_for_space(struct xe_exec_queue *q, u32 wqi_size)
{
struct xe_guc *guc = exec_queue_to_guc(q);
@@ -679,7 +726,7 @@ static int wq_wait_for_space(struct xe_exec_queue *q, u32 wqi_size)
#define AVAILABLE_SPACE \
CIRC_SPACE(q->guc->wqi_tail, q->guc->wqi_head, WQ_SIZE)
- if (wqi_size > AVAILABLE_SPACE) {
+ 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) {
@@ -736,18 +783,12 @@ static void wq_item_append(struct xe_exec_queue *q)
if (wq_wait_for_space(q, wqi_size))
return;
- xe_gt_assert(guc_to_gt(guc), i == XE_GUC_CONTEXT_WQ_HEADER_DATA_0_TYPE_LEN);
wqi[i++] = FIELD_PREP(WQ_TYPE_MASK, WQ_TYPE_MULTI_LRC) |
FIELD_PREP(WQ_LEN_MASK, len_dw);
- xe_gt_assert(guc_to_gt(guc), i == XE_GUC_CONTEXT_WQ_EL_INFO_DATA_1_CTX_DESC_LOW);
wqi[i++] = xe_lrc_descriptor(q->lrc[0]);
- xe_gt_assert(guc_to_gt(guc), i ==
- XE_GUC_CONTEXT_WQ_EL_INFO_DATA_2_GUCCTX_RINGTAIL_FREEZEPOCS);
wqi[i++] = FIELD_PREP(WQ_GUC_ID_MASK, q->guc->id) |
FIELD_PREP(WQ_RING_TAIL_MASK, q->lrc[0]->ring.tail / sizeof(u64));
- xe_gt_assert(guc_to_gt(guc), i == XE_GUC_CONTEXT_WQ_EL_INFO_DATA_3_WI_FENCE_ID);
wqi[i++] = 0;
- xe_gt_assert(guc_to_gt(guc), i == XE_GUC_CONTEXT_WQ_EL_CHILD_LIST_DATA_4_RINGTAIL);
for (j = 1; j < q->width; ++j) {
struct xe_lrc *lrc = q->lrc[j];
@@ -768,52 +809,8 @@ static void wq_item_append(struct xe_exec_queue *q)
parallel_write(xe, map, wq_desc.tail, q->guc->wqi_tail);
}
-static int wq_items_rebase(struct xe_exec_queue *q)
-{
- struct xe_guc *guc = exec_queue_to_guc(q);
- struct xe_device *xe = guc_to_xe(guc);
- struct iosys_map map = xe_lrc_parallel_map(q->lrc[0]);
- int i = q->guc->wqi_head;
-
- /* the ring starts after a header struct */
- iosys_map_incr(&map, offsetof(struct guc_submit_parallel_scratch, wq[0]));
-
- while ((i % WQ_SIZE) != (q->guc->wqi_tail % WQ_SIZE)) {
- u32 len_dw, type, val;
-
- if (drm_WARN_ON_ONCE(&xe->drm, i < 0 || i > 2 * WQ_SIZE))
- break;
-
- val = xe_map_rd_ring_u32(xe, &map, i / sizeof(u32) +
- XE_GUC_CONTEXT_WQ_HEADER_DATA_0_TYPE_LEN,
- WQ_SIZE / sizeof(u32));
- len_dw = FIELD_GET(WQ_LEN_MASK, val);
- type = FIELD_GET(WQ_TYPE_MASK, val);
-
- if (drm_WARN_ON_ONCE(&xe->drm, len_dw >= WQ_SIZE / sizeof(u32)))
- break;
-
- if (type == WQ_TYPE_MULTI_LRC) {
- val = xe_lrc_descriptor(q->lrc[0]);
- xe_map_wr_ring_u32(xe, &map, i / sizeof(u32) +
- XE_GUC_CONTEXT_WQ_EL_INFO_DATA_1_CTX_DESC_LOW,
- WQ_SIZE / sizeof(u32), val);
- } else if (drm_WARN_ON_ONCE(&xe->drm, type != WQ_TYPE_NOOP)) {
- break;
- }
-
- i += (len_dw + 1) * sizeof(u32);
- }
-
- if ((i % WQ_SIZE) != (q->guc->wqi_tail % WQ_SIZE)) {
- xe_gt_err(q->gt, "Exec queue fixups incomplete - wqi parse failed\n");
- return -EBADMSG;
- }
- return 0;
-}
-
#define RESUME_PENDING ~0x0ull
-static void submit_exec_queue(struct xe_exec_queue *q)
+static void submit_exec_queue(struct xe_exec_queue *q, struct xe_sched_job *job)
{
struct xe_guc *guc = exec_queue_to_guc(q);
struct xe_lrc *lrc = q->lrc[0];
@@ -825,10 +822,13 @@ static void submit_exec_queue(struct xe_exec_queue *q)
xe_gt_assert(guc_to_gt(guc), exec_queue_registered(q));
- if (xe_exec_queue_is_parallel(q))
- wq_item_append(q);
- else
- xe_lrc_set_ring_tail(lrc, lrc->ring.tail);
+ if (!job->skip_emit || job->last_replay) {
+ if (xe_exec_queue_is_parallel(q))
+ wq_item_append(q);
+ else
+ xe_lrc_set_ring_tail(lrc, lrc->ring.tail);
+ job->last_replay = false;
+ }
if (exec_queue_suspended(q) && !xe_exec_queue_is_parallel(q))
return;
@@ -870,54 +870,33 @@ 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);
- struct dma_fence *fence = NULL;
- bool lr = xe_exec_queue_is_lr(q);
+ bool lr = xe_exec_queue_is_lr(q), 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)) ||
exec_queue_banned(q) || exec_queue_suspended(q));
trace_xe_sched_job_run(job);
- if (!exec_queue_killed_or_banned_or_wedged(q) && !xe_sched_job_is_error(job)) {
+ if (!killed_or_banned_or_wedged && !xe_sched_job_is_error(job)) {
if (!exec_queue_registered(q))
register_exec_queue(q, GUC_CONTEXT_NORMAL);
- if (!lr) /* LR jobs are emitted in the exec IOCTL */
+ if (!job->skip_emit)
q->ring_ops->emit_job(job);
- submit_exec_queue(q);
+ submit_exec_queue(q, job);
+ job->skip_emit = false;
}
- if (lr) {
- xe_sched_job_set_error(job, -EOPNOTSUPP);
- dma_fence_put(job->fence); /* Drop ref from xe_sched_job_arm */
- } else {
- fence = job->fence;
- }
-
- return fence;
-}
-
-/**
- * xe_guc_jobs_ring_rebase - Re-emit ring commands of requests pending
- * on all queues under a guc.
- * @guc: the &xe_guc struct instance
- */
-void xe_guc_jobs_ring_rebase(struct xe_guc *guc)
-{
- struct xe_exec_queue *q;
- unsigned long index;
-
/*
- * This routine is used within VF migration recovery. This means
- * using the lock here introduces a restriction: we cannot wait
- * for any GFX HW response while the lock is taken.
+ * 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.
*/
- mutex_lock(&guc->submission_state.lock);
- xa_for_each(&guc->submission_state.exec_queue_lookup, index, q) {
- if (exec_queue_killed_or_banned_or_wedged(q))
- continue;
- xe_exec_queue_jobs_ring_restore(q);
- }
- mutex_unlock(&guc->submission_state.lock);
+ if (killed_or_banned_or_wedged && lr)
+ xe_sched_job_set_error(job, -ECANCELED);
+
+ return job->fence;
}
static void guc_exec_queue_free_job(struct drm_sched_job *drm_job)
@@ -951,15 +930,17 @@ static void disable_scheduling_deregister(struct xe_guc *guc,
ret = wait_event_timeout(guc->ct.wq,
(!exec_queue_pending_enable(q) &&
!exec_queue_pending_disable(q)) ||
- xe_guc_read_stopped(guc),
+ xe_guc_read_stopped(guc) ||
+ vf_recovery(guc),
HZ * 5);
- if (!ret) {
+ if (!ret && !vf_recovery(guc)) {
struct xe_gpu_scheduler *sched = &q->guc->sched;
xe_gt_warn(q->gt, "Pending enable/disable failed to respond\n");
xe_sched_submission_start(sched);
xe_gt_reset_async(q->gt);
- xe_sched_tdr_queue_imm(sched);
+ if (!xe_exec_queue_is_lr(q))
+ xe_sched_tdr_queue_imm(sched);
return;
}
@@ -1051,9 +1032,14 @@ static void xe_guc_exec_queue_lr_cleanup(struct work_struct *w)
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))
@@ -1086,7 +1072,11 @@ static void xe_guc_exec_queue_lr_cleanup(struct work_struct *w)
*/
ret = wait_event_timeout(guc->ct.wq,
!exec_queue_pending_disable(q) ||
- xe_guc_read_stopped(guc), HZ * 5);
+ 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);
@@ -1101,7 +1091,16 @@ static void xe_guc_exec_queue_lr_cleanup(struct work_struct *w)
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)
@@ -1167,12 +1166,14 @@ static void enable_scheduling(struct xe_exec_queue *q)
ret = wait_event_timeout(guc->ct.wq,
!exec_queue_pending_enable(q) ||
- xe_guc_read_stopped(guc), HZ * 5);
- if (!ret || xe_guc_read_stopped(guc)) {
+ xe_guc_read_stopped(guc) ||
+ vf_recovery(guc), HZ * 5);
+ if ((!ret && !vf_recovery(guc)) || xe_guc_read_stopped(guc)) {
xe_gt_warn(guc_to_gt(guc), "Schedule enable failed to respond");
set_exec_queue_banned(q);
xe_gt_reset_async(q->gt);
- xe_sched_tdr_queue_imm(&q->guc->sched);
+ if (!xe_exec_queue_is_lr(q))
+ xe_sched_tdr_queue_imm(&q->guc->sched);
}
}
@@ -1230,13 +1231,16 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job)
int i = 0;
bool wedged = false, skip_timeout_check;
+ xe_gt_assert(guc_to_gt(guc), !xe_exec_queue_is_lr(q));
+
/*
* TDR has fired before free job worker. Common if exec queue
* immediately closed after last fence signaled. Add back to pending
* list so job can be freed and kick scheduler ensuring free job is not
* lost.
*/
- if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &job->fence->flags))
+ if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &job->fence->flags) ||
+ vf_recovery(guc))
return DRM_GPU_SCHED_STAT_NO_HANG;
/* Kill the run_job entry point */
@@ -1288,7 +1292,10 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job)
ret = wait_event_timeout(guc->ct.wq,
(!exec_queue_pending_enable(q) &&
!exec_queue_pending_disable(q)) ||
- xe_guc_read_stopped(guc), HZ * 5);
+ xe_guc_read_stopped(guc) ||
+ vf_recovery(guc), HZ * 5);
+ if (vf_recovery(guc))
+ goto handle_vf_resume;
if (!ret || xe_guc_read_stopped(guc))
goto trigger_reset;
@@ -1313,7 +1320,10 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job)
smp_rmb();
ret = wait_event_timeout(guc->ct.wq,
!exec_queue_pending_disable(q) ||
- xe_guc_read_stopped(guc), HZ * 5);
+ xe_guc_read_stopped(guc) ||
+ vf_recovery(guc), HZ * 5);
+ if (vf_recovery(guc))
+ goto handle_vf_resume;
if (!ret || xe_guc_read_stopped(guc)) {
trigger_reset:
if (!ret)
@@ -1409,6 +1419,7 @@ trigger_reset:
return DRM_GPU_SCHED_STAT_RESET;
sched_enable:
+ set_exec_queue_pending_tdr_exit(q);
enable_scheduling(q);
rearm:
/*
@@ -1417,6 +1428,7 @@ rearm:
* some thought, do this in a follow up.
*/
xe_sched_submission_start(sched);
+handle_vf_resume:
return DRM_GPU_SCHED_STAT_NO_HANG;
}
@@ -1523,11 +1535,24 @@ static void __guc_exec_queue_process_msg_set_sched_props(struct xe_sched_msg *ms
static void __suspend_fence_signal(struct xe_exec_queue *q)
{
+ struct xe_guc *guc = exec_queue_to_guc(q);
+ struct xe_device *xe = guc_to_xe(guc);
+
if (!q->guc->suspend_pending)
return;
WRITE_ONCE(q->guc->suspend_pending, false);
- wake_up(&q->guc->suspend_wait);
+
+ /*
+ * We use a GuC shared wait queue for VFs because the VF resfix start
+ * interrupt must be able to wake all instances of suspend_wait. This
+ * prevents the VF migration worker from being starved during
+ * scheduling.
+ */
+ if (IS_SRIOV_VF(xe))
+ wake_up_all(&guc->ct.wq);
+ else
+ wake_up(&q->guc->suspend_wait);
}
static void suspend_fence_signal(struct xe_exec_queue *q)
@@ -1548,8 +1573,9 @@ static void __guc_exec_queue_process_msg_suspend(struct xe_sched_msg *msg)
if (guc_exec_queue_allowed_to_change_state(q) && !exec_queue_suspended(q) &&
exec_queue_enabled(q)) {
- wait_event(guc->ct.wq, (q->guc->resume_time != RESUME_PENDING ||
- xe_guc_read_stopped(guc)) && !exec_queue_pending_disable(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)));
if (!xe_guc_read_stopped(guc)) {
s64 since_resume_ms =
@@ -1578,6 +1604,7 @@ static void __guc_exec_queue_process_msg_resume(struct xe_sched_msg *msg)
clear_exec_queue_suspended(q);
if (!exec_queue_enabled(q)) {
q->guc->resume_time = RESUME_PENDING;
+ set_exec_queue_pending_resume(q);
enable_scheduling(q);
}
} else {
@@ -1591,6 +1618,7 @@ static void __guc_exec_queue_process_msg_resume(struct xe_sched_msg *msg)
#define RESUME 4
#define OPCODE_MASK 0xf
#define MSG_LOCKED BIT(8)
+#define MSG_HEAD BIT(9)
static void guc_exec_queue_process_msg(struct xe_sched_msg *msg)
{
@@ -1653,7 +1681,7 @@ 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);
err = xe_sched_init(&ge->sched, &drm_sched_ops, &xe_sched_ops,
- NULL, q->lrc[0]->ring.size / MAX_JOB_SIZE_BYTES, 64,
+ NULL, 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)
@@ -1675,7 +1703,7 @@ static int guc_exec_queue_init(struct xe_exec_queue *q)
q->entity = &ge->entity;
- if (xe_guc_read_stopped(guc))
+ if (xe_guc_read_stopped(guc) || vf_recovery(guc))
xe_sched_stop(sched);
mutex_unlock(&guc->submission_state.lock);
@@ -1715,12 +1743,24 @@ static void guc_exec_queue_add_msg(struct xe_exec_queue *q, struct xe_sched_msg
msg->private_data = q;
trace_xe_sched_msg_add(msg);
- if (opcode & MSG_LOCKED)
+ if (opcode & MSG_HEAD)
+ xe_sched_add_msg_head(&q->guc->sched, msg);
+ else if (opcode & MSG_LOCKED)
xe_sched_add_msg_locked(&q->guc->sched, msg);
else
xe_sched_add_msg(&q->guc->sched, msg);
}
+static void guc_exec_queue_try_add_msg_head(struct xe_exec_queue *q,
+ struct xe_sched_msg *msg,
+ u32 opcode)
+{
+ if (!list_empty(&msg->link))
+ return;
+
+ guc_exec_queue_add_msg(q, msg, opcode | MSG_LOCKED | MSG_HEAD);
+}
+
static bool guc_exec_queue_try_add_msg(struct xe_exec_queue *q,
struct xe_sched_msg *msg,
u32 opcode)
@@ -1821,6 +1861,7 @@ static int guc_exec_queue_suspend(struct xe_exec_queue *q)
static int guc_exec_queue_suspend_wait(struct xe_exec_queue *q)
{
struct xe_guc *guc = exec_queue_to_guc(q);
+ struct xe_device *xe = guc_to_xe(guc);
int ret;
/*
@@ -1828,11 +1869,21 @@ static int guc_exec_queue_suspend_wait(struct xe_exec_queue *q)
* suspend_pending upon kill but to be paranoid but races in which
* suspend_pending is set after kill also check kill here.
*/
- ret = wait_event_interruptible_timeout(q->guc->suspend_wait,
- !READ_ONCE(q->guc->suspend_pending) ||
- exec_queue_killed(q) ||
- xe_guc_read_stopped(guc),
- HZ * 5);
+#define WAIT_COND \
+ (!READ_ONCE(q->guc->suspend_pending) || exec_queue_killed(q) || \
+ xe_guc_read_stopped(guc))
+
+retry:
+ if (IS_SRIOV_VF(xe))
+ ret = wait_event_interruptible_timeout(guc->ct.wq, WAIT_COND ||
+ vf_recovery(guc),
+ HZ * 5);
+ else
+ ret = wait_event_interruptible_timeout(q->guc->suspend_wait,
+ WAIT_COND, HZ * 5);
+
+ if (vf_recovery(guc) && !xe_device_wedged((guc_to_xe(guc))))
+ return -EAGAIN;
if (!ret) {
xe_gt_warn(guc_to_gt(guc),
@@ -1840,8 +1891,13 @@ static int guc_exec_queue_suspend_wait(struct xe_exec_queue *q)
q->guc->id);
/* XXX: Trigger GT reset? */
return -ETIME;
+ } else if (IS_SRIOV_VF(xe) && !WAIT_COND) {
+ /* Corner case on RESFIX DONE where vf_recovery() changes */
+ goto retry;
}
+#undef WAIT_COND
+
return ret < 0 ? ret : 0;
}
@@ -1936,47 +1992,13 @@ static void guc_exec_queue_stop(struct xe_guc *guc, struct xe_exec_queue *q)
}
}
-/**
- * xe_guc_submit_reset_block - Disallow reset calls on given GuC.
- * @guc: the &xe_guc struct instance
- */
-int xe_guc_submit_reset_block(struct xe_guc *guc)
-{
- return atomic_fetch_or(1, &guc->submission_state.reset_blocked);
-}
-
-/**
- * xe_guc_submit_reset_unblock - Allow back reset calls on given GuC.
- * @guc: the &xe_guc struct instance
- */
-void xe_guc_submit_reset_unblock(struct xe_guc *guc)
-{
- atomic_set_release(&guc->submission_state.reset_blocked, 0);
- wake_up_all(&guc->ct.wq);
-}
-
-static int guc_submit_reset_is_blocked(struct xe_guc *guc)
-{
- return atomic_read_acquire(&guc->submission_state.reset_blocked);
-}
-
-/* Maximum time of blocking reset */
-#define RESET_BLOCK_PERIOD_MAX (HZ * 5)
-
-/**
- * xe_guc_wait_reset_unblock - Wait until reset blocking flag is lifted, or timeout.
- * @guc: the &xe_guc struct instance
- */
-int xe_guc_wait_reset_unblock(struct xe_guc *guc)
-{
- return wait_event_timeout(guc->ct.wq,
- !guc_submit_reset_is_blocked(guc), RESET_BLOCK_PERIOD_MAX);
-}
-
int xe_guc_submit_reset_prepare(struct xe_guc *guc)
{
int ret;
+ if (xe_gt_WARN_ON(guc_to_gt(guc), vf_recovery(guc)))
+ return 0;
+
if (!guc->submission_state.initialized)
return 0;
@@ -2026,6 +2048,119 @@ void xe_guc_submit_stop(struct xe_guc *guc)
}
+static void guc_exec_queue_revert_pending_state_change(struct xe_guc *guc,
+ struct xe_exec_queue *q)
+{
+ bool pending_enable, pending_disable, pending_resume;
+
+ pending_enable = exec_queue_pending_enable(q);
+ pending_resume = exec_queue_pending_resume(q);
+
+ if (pending_enable && pending_resume) {
+ q->guc->needs_resume = true;
+ xe_gt_dbg(guc_to_gt(guc), "Replay RESUME - guc_id=%d",
+ q->guc->id);
+ }
+
+ if (pending_enable && !pending_resume &&
+ !exec_queue_pending_tdr_exit(q)) {
+ 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);
+ }
+
+ 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);
+ }
+
+ 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);
+ xe_gt_dbg(guc_to_gt(guc), "Replay CLEANUP - guc_id=%d",
+ q->guc->id);
+ }
+
+ pending_disable = exec_queue_pending_disable(q);
+
+ if (pending_disable && exec_queue_suspended(q)) {
+ clear_exec_queue_suspended(q);
+ q->guc->needs_suspend = true;
+ xe_gt_dbg(guc_to_gt(guc), "Replay SUSPEND - guc_id=%d",
+ q->guc->id);
+ }
+
+ if (pending_disable) {
+ 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);
+ }
+
+ q->guc->resume_time = 0;
+}
+
+/*
+ * This function is quite complex but only real way to ensure no state is lost
+ * during VF resume flows. The function scans the queue state, make adjustments
+ * as needed, and queues jobs / messages which replayed upon unpause.
+ */
+static void guc_exec_queue_pause(struct xe_guc *guc, struct xe_exec_queue *q)
+{
+ struct xe_gpu_scheduler *sched = &q->guc->sched;
+ struct xe_sched_job *job;
+ int i;
+
+ lockdep_assert_held(&guc->submission_state.lock);
+
+ /* 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);
+
+ guc_exec_queue_revert_pending_state_change(guc, q);
+
+ if (xe_exec_queue_is_parallel(q)) {
+ struct xe_device *xe = guc_to_xe(guc);
+ struct iosys_map map = xe_lrc_parallel_map(q->lrc[0]);
+
+ /*
+ * NOP existing WQ commands that may contain stale GGTT
+ * addresses. These will be replayed upon unpause. The hardware
+ * seems to get confused if the WQ head/tail pointers are
+ * adjusted.
+ */
+ for (i = 0; i < WQ_SIZE / sizeof(u32); ++i)
+ parallel_write(xe, map, wq[i],
+ FIELD_PREP(WQ_TYPE_MASK, WQ_TYPE_NOOP) |
+ FIELD_PREP(WQ_LEN_MASK, 0));
+ }
+
+ job = xe_sched_first_pending_job(sched);
+ if (job) {
+ /*
+ * Adjust software tail so jobs submitted overwrite previous
+ * position in ring buffer with new GGTT addresses.
+ */
+ for (i = 0; i < q->width; ++i)
+ q->lrc[i]->ring.tail = job->ptrs[i].head;
+ }
+}
+
/**
* xe_guc_submit_pause - Stop further runs of submission tasks on given GuC.
* @guc: the &xe_guc struct instance whose scheduler is to be disabled
@@ -2035,8 +2170,17 @@ void xe_guc_submit_pause(struct xe_guc *guc)
struct xe_exec_queue *q;
unsigned long index;
- xa_for_each(&guc->submission_state.exec_queue_lookup, index, q)
- xe_sched_submission_stop_async(&q->guc->sched);
+ xe_gt_assert(guc_to_gt(guc), vf_recovery(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 */
+ if (q->guc->id != index)
+ continue;
+
+ guc_exec_queue_pause(guc, q);
+ }
+ mutex_unlock(&guc->submission_state.lock);
}
static void guc_exec_queue_start(struct xe_exec_queue *q)
@@ -2044,11 +2188,25 @@ static void guc_exec_queue_start(struct xe_exec_queue *q)
struct xe_gpu_scheduler *sched = &q->guc->sched;
if (!exec_queue_killed_or_banned_or_wedged(q)) {
+ struct xe_sched_job *job = xe_sched_first_pending_job(sched);
int i;
trace_xe_exec_queue_resubmit(q);
- for (i = 0; i < q->width; ++i)
- xe_lrc_set_ring_head(q->lrc[i], q->lrc[i]->ring.tail);
+ if (job) {
+ for (i = 0; i < q->width; ++i) {
+ /*
+ * The GuC context is unregistered at this point
+ * time, adjusting software ring tail ensures
+ * jobs are rewritten in original placement,
+ * adjusting LRC tail ensures the newly loaded
+ * GuC / contexts only view the LRC tail
+ * increasing as jobs are written out.
+ */
+ q->lrc[i]->ring.tail = job->ptrs[i].head;
+ xe_lrc_set_ring_tail(q->lrc[i],
+ xe_lrc_ring_head(q->lrc[i]));
+ }
+ }
xe_sched_resubmit_jobs(sched);
}
@@ -2079,11 +2237,100 @@ int xe_guc_submit_start(struct xe_guc *guc)
return 0;
}
-static void guc_exec_queue_unpause(struct xe_exec_queue *q)
+static void guc_exec_queue_unpause_prepare(struct xe_guc *guc,
+ struct xe_exec_queue *q)
{
struct xe_gpu_scheduler *sched = &q->guc->sched;
+ struct drm_sched_job *s_job;
+ struct xe_sched_job *job = NULL;
+
+ list_for_each_entry(s_job, &sched->base.pending_list, list) {
+ job = to_xe_sched_job(s_job);
+
+ xe_gt_dbg(guc_to_gt(guc), "Replay JOB - guc_id=%d, seqno=%d",
+ q->guc->id, xe_sched_job_seqno(job));
+
+ q->ring_ops->emit_job(job);
+ job->skip_emit = true;
+ }
+ if (job)
+ job->last_replay = true;
+}
+
+/**
+ * xe_guc_submit_unpause_prepare - Prepare unpause submission tasks on given GuC.
+ * @guc: the &xe_guc struct instance whose scheduler is to be prepared for unpause
+ */
+void xe_guc_submit_unpause_prepare(struct xe_guc *guc)
+{
+ struct xe_exec_queue *q;
+ unsigned long index;
+
+ xe_gt_assert(guc_to_gt(guc), vf_recovery(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 */
+ if (q->guc->id != index)
+ continue;
+
+ guc_exec_queue_unpause_prepare(guc, q);
+ }
+ mutex_unlock(&guc->submission_state.lock);
+}
+
+static void guc_exec_queue_replay_pending_state_change(struct xe_exec_queue *q)
+{
+ struct xe_gpu_scheduler *sched = &q->guc->sched;
+ struct xe_sched_msg *msg;
+
+ if (q->guc->needs_cleanup) {
+ msg = q->guc->static_msgs + STATIC_MSG_CLEANUP;
+
+ guc_exec_queue_add_msg(q, msg, CLEANUP);
+ q->guc->needs_cleanup = false;
+ }
+
+ if (q->guc->needs_suspend) {
+ msg = q->guc->static_msgs + STATIC_MSG_SUSPEND;
+
+ xe_sched_msg_lock(sched);
+ guc_exec_queue_try_add_msg_head(q, msg, SUSPEND);
+ xe_sched_msg_unlock(sched);
+
+ q->guc->needs_suspend = false;
+ }
+
+ /*
+ * The resume must be in the message queue before the suspend as it is
+ * not possible for a resume to be issued if a suspend pending is, but
+ * the inverse is possible.
+ */
+ if (q->guc->needs_resume) {
+ msg = q->guc->static_msgs + STATIC_MSG_RESUME;
+
+ xe_sched_msg_lock(sched);
+ guc_exec_queue_try_add_msg_head(q, msg, RESUME);
+ xe_sched_msg_unlock(sched);
+
+ q->guc->needs_resume = false;
+ }
+}
+
+static void guc_exec_queue_unpause(struct xe_guc *guc, struct xe_exec_queue *q)
+{
+ struct xe_gpu_scheduler *sched = &q->guc->sched;
+ bool needs_tdr = exec_queue_killed_or_banned_or_wedged(q);
+
+ lockdep_assert_held(&guc->submission_state.lock);
+
+ xe_sched_resubmit_jobs(sched);
+ guc_exec_queue_replay_pending_state_change(q);
xe_sched_submission_start(sched);
+ if (needs_tdr)
+ xe_guc_exec_queue_trigger_cleanup(q);
+ xe_sched_submission_resume_tdr(sched);
}
/**
@@ -2095,10 +2342,43 @@ void xe_guc_submit_unpause(struct xe_guc *guc)
struct xe_exec_queue *q;
unsigned long index;
- xa_for_each(&guc->submission_state.exec_queue_lookup, index, q)
- guc_exec_queue_unpause(q);
+ 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))
+ continue;
- wake_up_all(&guc->ct.wq);
+ guc_exec_queue_unpause(guc, q);
+ }
+ mutex_unlock(&guc->submission_state.lock);
+}
+
+/**
+ * xe_guc_submit_pause_abort - Abort all paused submission task on given GuC.
+ * @guc: the &xe_guc struct instance whose scheduler is to be aborted
+ */
+void xe_guc_submit_pause_abort(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) {
+ struct xe_gpu_scheduler *sched = &q->guc->sched;
+
+ /* Prevent redundant attempts to stop parallel queues */
+ if (q->guc->id != index)
+ continue;
+
+ xe_sched_submission_start(sched);
+ if (exec_queue_killed_or_banned_or_wedged(q))
+ xe_guc_exec_queue_trigger_cleanup(q);
+ }
+ mutex_unlock(&guc->submission_state.lock);
}
static struct xe_exec_queue *
@@ -2150,6 +2430,8 @@ static void handle_sched_done(struct xe_guc *guc, struct xe_exec_queue *q,
xe_gt_assert(guc_to_gt(guc), exec_queue_pending_enable(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);
@@ -2677,13 +2959,13 @@ int xe_guc_contexts_hwsp_rebase(struct xe_guc *guc, void *scratch)
mutex_lock(&guc->submission_state.lock);
xa_for_each(&guc->submission_state.exec_queue_lookup, index, q) {
+ /* Prevent redundant attempts to stop parallel queues */
+ if (q->guc->id != index)
+ continue;
+
err = xe_exec_queue_contexts_hwsp_rebase(q, scratch);
if (err)
break;
- if (xe_exec_queue_is_parallel(q))
- err = wq_items_rebase(q);
- if (err)
- break;
}
mutex_unlock(&guc->submission_state.lock);
diff --git a/drivers/gpu/drm/xe/xe_guc_submit.h b/drivers/gpu/drm/xe/xe_guc_submit.h
index 78c3f07e31a0..b49a2748ec46 100644
--- a/drivers/gpu/drm/xe/xe_guc_submit.h
+++ b/drivers/gpu/drm/xe/xe_guc_submit.h
@@ -22,9 +22,8 @@ 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);
-int xe_guc_submit_reset_block(struct xe_guc *guc);
-void xe_guc_submit_reset_unblock(struct xe_guc *guc);
-int xe_guc_wait_reset_unblock(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_wedge(struct xe_guc *guc);
int xe_guc_read_stopped(struct xe_guc *guc);
@@ -36,8 +35,6 @@ int xe_guc_exec_queue_memory_cat_error_handler(struct xe_guc *guc, u32 *msg,
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);
-void xe_guc_jobs_ring_rebase(struct xe_guc *guc);
-
struct xe_guc_submit_exec_queue_snapshot *
xe_guc_exec_queue_snapshot_capture(struct xe_exec_queue *q);
void
diff --git a/drivers/gpu/drm/xe/xe_huc.c b/drivers/gpu/drm/xe/xe_huc.c
index 7e43b2dd6a32..0a70c8924582 100644
--- a/drivers/gpu/drm/xe/xe_huc.c
+++ b/drivers/gpu/drm/xe/xe_huc.c
@@ -66,14 +66,18 @@ static int huc_alloc_gsc_pkt(struct xe_huc *huc)
int xe_huc_init(struct xe_huc *huc)
{
struct xe_gt *gt = huc_to_gt(huc);
- struct xe_tile *tile = gt_to_tile(gt);
struct xe_device *xe = gt_to_xe(gt);
int ret;
huc->fw.type = XE_UC_FW_TYPE_HUC;
- /* On platforms with a media GT the HuC is only available there */
- if (tile->media_gt && (gt != tile->media_gt)) {
+ /*
+ * The HuC is only available on the media GT on most platforms. The
+ * exception to that rule are the old Xe1 platforms where there was
+ * no separate GT for media IP, so the HuC was part of the primary
+ * GT. Such platforms have graphics versions 12.55 and earlier.
+ */
+ if (!xe_gt_is_media_type(gt) && GRAPHICS_VERx100(xe) > 1255) {
xe_uc_fw_change_status(&huc->fw, XE_UC_FIRMWARE_NOT_SUPPORTED);
return 0;
}
diff --git a/drivers/gpu/drm/xe/xe_hw_engine.c b/drivers/gpu/drm/xe/xe_hw_engine.c
index 1cf623b4a5bc..6a9e2a4272dd 100644
--- a/drivers/gpu/drm/xe/xe_hw_engine.c
+++ b/drivers/gpu/drm/xe/xe_hw_engine.c
@@ -346,17 +346,26 @@ void xe_hw_engine_enable_ring(struct xe_hw_engine *hwe)
xe_hw_engine_mmio_read32(hwe, RING_MI_MODE(0));
}
-static bool xe_hw_engine_match_fixed_cslice_mode(const struct xe_gt *gt,
+static bool xe_hw_engine_match_fixed_cslice_mode(const struct xe_device *xe,
+ const struct xe_gt *gt,
const struct xe_hw_engine *hwe)
{
+ /*
+ * Xe3p no longer supports load balance mode, so "fixed cslice" mode
+ * is automatic and no RCU_MODE programming is required.
+ */
+ if (GRAPHICS_VER(gt_to_xe(gt)) >= 35)
+ return false;
+
return xe_gt_ccs_mode_enabled(gt) &&
- xe_rtp_match_first_render_or_compute(gt, hwe);
+ xe_rtp_match_first_render_or_compute(xe, gt, hwe);
}
-static bool xe_rtp_cfeg_wmtp_disabled(const struct xe_gt *gt,
+static bool xe_rtp_cfeg_wmtp_disabled(const struct xe_device *xe,
+ const struct xe_gt *gt,
const struct xe_hw_engine *hwe)
{
- if (GRAPHICS_VER(gt_to_xe(gt)) < 20)
+ if (GRAPHICS_VER(xe) < 20)
return false;
if (hwe->class != XE_ENGINE_CLASS_COMPUTE &&
@@ -709,27 +718,52 @@ static void read_media_fuses(struct xe_gt *gt)
}
}
+static u32 infer_svccopy_from_meml3(struct xe_gt *gt)
+{
+ u32 meml3 = REG_FIELD_GET(MEML3_EN_MASK,
+ xe_mmio_read32(&gt->mmio, MIRROR_FUSE3));
+ u32 svccopy_mask = 0;
+
+ /*
+ * Each of the four meml3 bits determines the fusing of two service
+ * copy engines.
+ */
+ for (int i = 0; i < 4; i++)
+ svccopy_mask |= (meml3 & BIT(i)) ? 0b11 << 2 * i : 0;
+
+ return svccopy_mask;
+}
+
+static u32 read_svccopy_fuses(struct xe_gt *gt)
+{
+ return REG_FIELD_GET(FUSE_SERVICE_COPY_ENABLE_MASK,
+ xe_mmio_read32(&gt->mmio, SERVICE_COPY_ENABLE));
+}
+
static void read_copy_fuses(struct xe_gt *gt)
{
struct xe_device *xe = gt_to_xe(gt);
u32 bcs_mask;
- if (GRAPHICS_VERx100(xe) < 1260 || GRAPHICS_VERx100(xe) >= 1270)
- return;
-
xe_force_wake_assert_held(gt_to_fw(gt), XE_FW_GT);
- bcs_mask = xe_mmio_read32(&gt->mmio, MIRROR_FUSE3);
- bcs_mask = REG_FIELD_GET(MEML3_EN_MASK, bcs_mask);
+ if (GRAPHICS_VER(xe) >= 35)
+ bcs_mask = read_svccopy_fuses(gt);
+ else if (GRAPHICS_VERx100(xe) == 1260)
+ bcs_mask = infer_svccopy_from_meml3(gt);
+ else
+ return;
- /* BCS0 is always present; only BCS1-BCS8 may be fused off */
- for (int i = XE_HW_ENGINE_BCS1, j = 0; i <= XE_HW_ENGINE_BCS8; ++i, ++j) {
+ /* Only BCS1-BCS8 may be fused off */
+ bcs_mask <<= XE_HW_ENGINE_BCS1;
+ for (int i = XE_HW_ENGINE_BCS1; i <= XE_HW_ENGINE_BCS8; ++i) {
if (!(gt->info.engine_mask & BIT(i)))
continue;
- if (!(BIT(j / 2) & bcs_mask)) {
+ if (!(bcs_mask & BIT(i))) {
gt->info.engine_mask &= ~BIT(i);
- xe_gt_info(gt, "bcs%u fused off\n", j);
+ xe_gt_info(gt, "bcs%u fused off\n",
+ i - XE_HW_ENGINE_BCS0);
}
}
}
@@ -870,7 +904,7 @@ void xe_hw_engine_handle_irq(struct xe_hw_engine *hwe, u16 intr_vec)
if (hwe->irq_handler)
hwe->irq_handler(hwe, intr_vec);
- if (intr_vec & GT_RENDER_USER_INTERRUPT)
+ if (intr_vec & GT_MI_USER_INTERRUPT)
xe_hw_fence_irq_run(hwe->fence_irq);
}
diff --git a/drivers/gpu/drm/xe/xe_hwmon.c b/drivers/gpu/drm/xe/xe_hwmon.c
index b6790589e623..97879daeefc1 100644
--- a/drivers/gpu/drm/xe/xe_hwmon.c
+++ b/drivers/gpu/drm/xe/xe_hwmon.c
@@ -658,8 +658,6 @@ static umode_t xe_hwmon_attributes_visible(struct kobject *kobj,
struct xe_reg rapl_limit;
struct xe_mmio *mmio = xe_root_tile_mmio(hwmon->xe);
- xe_pm_runtime_get(hwmon->xe);
-
if (hwmon->xe->info.has_mbx_power_limits) {
xe_hwmon_pcode_read_power_limit(hwmon, power_attr, channel, &uval);
} else if (power_attr != PL2_HWMON_ATTR) {
@@ -669,8 +667,6 @@ static umode_t xe_hwmon_attributes_visible(struct kobject *kobj,
}
ret = (uval & PWR_LIM_EN) ? attr->mode : 0;
- xe_pm_runtime_put(hwmon->xe);
-
return ret;
}
@@ -1096,8 +1092,6 @@ xe_hwmon_is_visible(const void *drvdata, enum hwmon_sensor_types type,
struct xe_hwmon *hwmon = (struct xe_hwmon *)drvdata;
int ret;
- xe_pm_runtime_get(hwmon->xe);
-
switch (type) {
case hwmon_temp:
ret = xe_hwmon_temp_is_visible(hwmon, attr, channel);
@@ -1122,8 +1116,6 @@ xe_hwmon_is_visible(const void *drvdata, enum hwmon_sensor_types type,
break;
}
- xe_pm_runtime_put(hwmon->xe);
-
return ret;
}
diff --git a/drivers/gpu/drm/xe/xe_i2c.c b/drivers/gpu/drm/xe/xe_i2c.c
index 48dfcb41fa08..0b5452be0c87 100644
--- a/drivers/gpu/drm/xe/xe_i2c.c
+++ b/drivers/gpu/drm/xe/xe_i2c.c
@@ -160,6 +160,11 @@ bool xe_i2c_present(struct xe_device *xe)
return xe->i2c && xe->i2c->ep.cookie == XE_I2C_EP_COOKIE_DEVICE;
}
+static bool xe_i2c_irq_present(struct xe_device *xe)
+{
+ return xe->i2c && xe->i2c->adapter_irq;
+}
+
/**
* xe_i2c_irq_handler: Handler for I2C interrupts
* @xe: xe device instance
@@ -170,13 +175,33 @@ bool xe_i2c_present(struct xe_device *xe)
*/
void xe_i2c_irq_handler(struct xe_device *xe, u32 master_ctl)
{
- if (!xe->i2c || !xe->i2c->adapter_irq)
+ if (!xe_i2c_irq_present(xe))
return;
if (master_ctl & I2C_IRQ)
generic_handle_irq_safe(xe->i2c->adapter_irq);
}
+void xe_i2c_irq_reset(struct xe_device *xe)
+{
+ struct xe_mmio *mmio = xe_root_tile_mmio(xe);
+
+ if (!xe_i2c_irq_present(xe))
+ return;
+
+ xe_mmio_rmw32(mmio, I2C_BRIDGE_PCICFGCTL, ACPI_INTR_EN, 0);
+}
+
+void xe_i2c_irq_postinstall(struct xe_device *xe)
+{
+ struct xe_mmio *mmio = xe_root_tile_mmio(xe);
+
+ if (!xe_i2c_irq_present(xe))
+ return;
+
+ xe_mmio_rmw32(mmio, I2C_BRIDGE_PCICFGCTL, 0, ACPI_INTR_EN);
+}
+
static int xe_i2c_irq_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hw_irq_num)
{
@@ -334,6 +359,7 @@ int xe_i2c_probe(struct xe_device *xe)
if (ret)
goto err_remove_irq;
+ xe_i2c_irq_postinstall(xe);
return devm_add_action_or_reset(drm_dev, xe_i2c_remove, i2c);
err_remove_irq:
diff --git a/drivers/gpu/drm/xe/xe_i2c.h b/drivers/gpu/drm/xe/xe_i2c.h
index ecd5f10358e2..425d8160835f 100644
--- a/drivers/gpu/drm/xe/xe_i2c.h
+++ b/drivers/gpu/drm/xe/xe_i2c.h
@@ -51,12 +51,16 @@ struct xe_i2c {
int xe_i2c_probe(struct xe_device *xe);
bool xe_i2c_present(struct xe_device *xe);
void xe_i2c_irq_handler(struct xe_device *xe, u32 master_ctl);
+void xe_i2c_irq_postinstall(struct xe_device *xe);
+void xe_i2c_irq_reset(struct xe_device *xe);
void xe_i2c_pm_suspend(struct xe_device *xe);
void xe_i2c_pm_resume(struct xe_device *xe, bool d3cold);
#else
static inline int xe_i2c_probe(struct xe_device *xe) { return 0; }
static inline bool xe_i2c_present(struct xe_device *xe) { return false; }
static inline void xe_i2c_irq_handler(struct xe_device *xe, u32 master_ctl) { }
+static inline void xe_i2c_irq_postinstall(struct xe_device *xe) { }
+static inline void xe_i2c_irq_reset(struct xe_device *xe) { }
static inline void xe_i2c_pm_suspend(struct xe_device *xe) { }
static inline void xe_i2c_pm_resume(struct xe_device *xe, bool d3cold) { }
#endif
diff --git a/drivers/gpu/drm/xe/xe_irq.c b/drivers/gpu/drm/xe/xe_irq.c
index 870edaf69388..e5ed0242f7b1 100644
--- a/drivers/gpu/drm/xe/xe_irq.c
+++ b/drivers/gpu/drm/xe/xe_irq.c
@@ -139,68 +139,112 @@ void xe_irq_enable_hwe(struct xe_gt *gt)
{
struct xe_device *xe = gt_to_xe(gt);
struct xe_mmio *mmio = &gt->mmio;
- u32 ccs_mask, bcs_mask;
- u32 irqs, dmask, smask;
- u32 gsc_mask = 0;
- u32 heci_mask = 0;
+ u32 common_mask, val, gsc_mask = 0, heci_mask = 0,
+ rcs_mask = 0, bcs_mask = 0, vcs_mask = 0, vecs_mask = 0,
+ ccs_mask = 0;
if (xe_device_uses_memirq(xe))
return;
if (xe_device_uc_enabled(xe)) {
- irqs = GT_RENDER_USER_INTERRUPT |
- GT_RENDER_PIPECTL_NOTIFY_INTERRUPT;
+ common_mask = GT_MI_USER_INTERRUPT |
+ GT_FLUSH_COMPLETE_INTERRUPT;
+
+ /* Enable Compute Walker Interrupt for non-MSIX platforms */
+ if (GRAPHICS_VERx100(xe) >= 3511 && !xe_device_has_msix(xe)) {
+ rcs_mask |= GT_COMPUTE_WALKER_INTERRUPT;
+ ccs_mask |= GT_COMPUTE_WALKER_INTERRUPT;
+ }
} else {
- irqs = GT_RENDER_USER_INTERRUPT |
- GT_CS_MASTER_ERROR_INTERRUPT |
- GT_CONTEXT_SWITCH_INTERRUPT |
- GT_WAIT_SEMAPHORE_INTERRUPT;
+ common_mask = GT_MI_USER_INTERRUPT |
+ GT_CS_MASTER_ERROR_INTERRUPT |
+ GT_CONTEXT_SWITCH_INTERRUPT |
+ GT_WAIT_SEMAPHORE_INTERRUPT;
}
- ccs_mask = xe_hw_engine_mask_per_class(gt, XE_ENGINE_CLASS_COMPUTE);
- bcs_mask = xe_hw_engine_mask_per_class(gt, XE_ENGINE_CLASS_COPY);
-
- dmask = irqs << 16 | irqs;
- smask = irqs << 16;
+ rcs_mask |= common_mask;
+ bcs_mask |= common_mask;
+ vcs_mask |= common_mask;
+ vecs_mask |= common_mask;
+ ccs_mask |= common_mask;
if (xe_gt_is_main_type(gt)) {
+ /*
+ * For enabling the interrupts, the information about fused off
+ * engines doesn't matter much, but this also allows to check if
+ * the engine is available architecturally in the platform
+ */
+ u32 ccs_fuse_mask = xe_hw_engine_mask_per_class(gt, XE_ENGINE_CLASS_COMPUTE);
+ u32 bcs_fuse_mask = xe_hw_engine_mask_per_class(gt, XE_ENGINE_CLASS_COPY);
+
/* Enable interrupts for each engine class */
- xe_mmio_write32(mmio, RENDER_COPY_INTR_ENABLE, dmask);
- if (ccs_mask)
- xe_mmio_write32(mmio, CCS_RSVD_INTR_ENABLE, smask);
+ xe_mmio_write32(mmio, RENDER_COPY_INTR_ENABLE,
+ REG_FIELD_PREP(ENGINE1_MASK, rcs_mask) |
+ REG_FIELD_PREP(ENGINE0_MASK, bcs_mask));
+ if (ccs_fuse_mask)
+ xe_mmio_write32(mmio, CCS_RSVD_INTR_ENABLE,
+ REG_FIELD_PREP(ENGINE1_MASK, ccs_mask));
/* Unmask interrupts for each engine instance */
- xe_mmio_write32(mmio, RCS0_RSVD_INTR_MASK, ~smask);
- xe_mmio_write32(mmio, BCS_RSVD_INTR_MASK, ~smask);
- if (bcs_mask & (BIT(1)|BIT(2)))
- xe_mmio_write32(mmio, XEHPC_BCS1_BCS2_INTR_MASK, ~dmask);
- if (bcs_mask & (BIT(3)|BIT(4)))
- xe_mmio_write32(mmio, XEHPC_BCS3_BCS4_INTR_MASK, ~dmask);
- if (bcs_mask & (BIT(5)|BIT(6)))
- xe_mmio_write32(mmio, XEHPC_BCS5_BCS6_INTR_MASK, ~dmask);
- if (bcs_mask & (BIT(7)|BIT(8)))
- xe_mmio_write32(mmio, XEHPC_BCS7_BCS8_INTR_MASK, ~dmask);
- if (ccs_mask & (BIT(0)|BIT(1)))
- xe_mmio_write32(mmio, CCS0_CCS1_INTR_MASK, ~dmask);
- if (ccs_mask & (BIT(2)|BIT(3)))
- xe_mmio_write32(mmio, CCS2_CCS3_INTR_MASK, ~dmask);
+ val = ~REG_FIELD_PREP(ENGINE1_MASK, rcs_mask);
+ xe_mmio_write32(mmio, RCS0_RSVD_INTR_MASK, val);
+ val = ~REG_FIELD_PREP(ENGINE1_MASK, bcs_mask);
+ xe_mmio_write32(mmio, BCS_RSVD_INTR_MASK, val);
+
+ val = ~(REG_FIELD_PREP(ENGINE1_MASK, bcs_mask) |
+ REG_FIELD_PREP(ENGINE0_MASK, bcs_mask));
+ if (bcs_fuse_mask & (BIT(1)|BIT(2)))
+ xe_mmio_write32(mmio, XEHPC_BCS1_BCS2_INTR_MASK, val);
+ if (bcs_fuse_mask & (BIT(3)|BIT(4)))
+ xe_mmio_write32(mmio, XEHPC_BCS3_BCS4_INTR_MASK, val);
+ if (bcs_fuse_mask & (BIT(5)|BIT(6)))
+ xe_mmio_write32(mmio, XEHPC_BCS5_BCS6_INTR_MASK, val);
+ if (bcs_fuse_mask & (BIT(7)|BIT(8)))
+ xe_mmio_write32(mmio, XEHPC_BCS7_BCS8_INTR_MASK, val);
+
+ val = ~(REG_FIELD_PREP(ENGINE1_MASK, ccs_mask) |
+ REG_FIELD_PREP(ENGINE0_MASK, ccs_mask));
+ if (ccs_fuse_mask & (BIT(0)|BIT(1)))
+ xe_mmio_write32(mmio, CCS0_CCS1_INTR_MASK, val);
+ if (ccs_fuse_mask & (BIT(2)|BIT(3)))
+ xe_mmio_write32(mmio, CCS2_CCS3_INTR_MASK, val);
}
if (xe_gt_is_media_type(gt) || MEDIA_VER(xe) < 13) {
+ u32 vcs_fuse_mask = xe_hw_engine_mask_per_class(gt, XE_ENGINE_CLASS_VIDEO_DECODE);
+ u32 vecs_fuse_mask = xe_hw_engine_mask_per_class(gt, XE_ENGINE_CLASS_VIDEO_ENHANCE);
+ u32 other_fuse_mask = xe_hw_engine_mask_per_class(gt, XE_ENGINE_CLASS_OTHER);
+
/* Enable interrupts for each engine class */
- xe_mmio_write32(mmio, VCS_VECS_INTR_ENABLE, dmask);
+ xe_mmio_write32(mmio, VCS_VECS_INTR_ENABLE,
+ REG_FIELD_PREP(ENGINE1_MASK, vcs_mask) |
+ REG_FIELD_PREP(ENGINE0_MASK, vecs_mask));
/* Unmask interrupts for each engine instance */
- xe_mmio_write32(mmio, VCS0_VCS1_INTR_MASK, ~dmask);
- xe_mmio_write32(mmio, VCS2_VCS3_INTR_MASK, ~dmask);
- xe_mmio_write32(mmio, VECS0_VECS1_INTR_MASK, ~dmask);
+ val = ~(REG_FIELD_PREP(ENGINE1_MASK, vcs_mask) |
+ REG_FIELD_PREP(ENGINE0_MASK, vcs_mask));
+ if (vcs_fuse_mask & (BIT(0) | BIT(1)))
+ xe_mmio_write32(mmio, VCS0_VCS1_INTR_MASK, val);
+ if (vcs_fuse_mask & (BIT(2) | BIT(3)))
+ xe_mmio_write32(mmio, VCS2_VCS3_INTR_MASK, val);
+ if (vcs_fuse_mask & (BIT(4) | BIT(5)))
+ xe_mmio_write32(mmio, VCS4_VCS5_INTR_MASK, val);
+ if (vcs_fuse_mask & (BIT(6) | BIT(7)))
+ xe_mmio_write32(mmio, VCS6_VCS7_INTR_MASK, val);
+
+ val = ~(REG_FIELD_PREP(ENGINE1_MASK, vecs_mask) |
+ REG_FIELD_PREP(ENGINE0_MASK, vecs_mask));
+ if (vecs_fuse_mask & (BIT(0) | BIT(1)))
+ xe_mmio_write32(mmio, VECS0_VECS1_INTR_MASK, val);
+ if (vecs_fuse_mask & (BIT(2) | BIT(3)))
+ xe_mmio_write32(mmio, VECS2_VECS3_INTR_MASK, val);
/*
* the heci2 interrupt is enabled via the same register as the
* GSCCS interrupts, but it has its own mask register.
*/
- if (xe_hw_engine_mask_per_class(gt, XE_ENGINE_CLASS_OTHER)) {
- gsc_mask = irqs | GSC_ER_COMPLETE;
+ if (other_fuse_mask) {
+ gsc_mask = common_mask | GSC_ER_COMPLETE;
heci_mask = GSC_IRQ_INTF(1);
} else if (xe->info.has_heci_gscfi) {
gsc_mask = GSC_IRQ_INTF(1);
@@ -494,11 +538,15 @@ static irqreturn_t dg1_irq_handler(int irq, void *arg)
static void gt_irq_reset(struct xe_tile *tile)
{
struct xe_mmio *mmio = &tile->mmio;
-
- u32 ccs_mask = xe_hw_engine_mask_per_class(tile->primary_gt,
- XE_ENGINE_CLASS_COMPUTE);
- u32 bcs_mask = xe_hw_engine_mask_per_class(tile->primary_gt,
- XE_ENGINE_CLASS_COPY);
+ u32 ccs_mask = ~0;
+ u32 bcs_mask = ~0;
+
+ if (tile->primary_gt) {
+ ccs_mask = xe_hw_engine_mask_per_class(tile->primary_gt,
+ XE_ENGINE_CLASS_COMPUTE);
+ bcs_mask = xe_hw_engine_mask_per_class(tile->primary_gt,
+ XE_ENGINE_CLASS_COPY);
+ }
/* Disable RCS, BCS, VCS and VECS class engines. */
xe_mmio_write32(mmio, RENDER_COPY_INTR_ENABLE, 0);
@@ -616,6 +664,7 @@ static void xe_irq_reset(struct xe_device *xe)
tile = xe_device_get_root_tile(xe);
mask_and_disable(tile, GU_MISC_IRQ_OFFSET);
xe_display_irq_reset(xe);
+ xe_i2c_irq_reset(xe);
/*
* The tile's top-level status register should be the last one
@@ -656,7 +705,8 @@ static void xe_irq_postinstall(struct xe_device *xe)
xe_memirq_postinstall(&tile->memirq);
}
- xe_display_irq_postinstall(xe, xe_root_mmio_gt(xe));
+ xe_display_irq_postinstall(xe);
+ xe_i2c_irq_postinstall(xe);
/*
* ASLE backlight operations are reported via GUnit GSE interrupts
diff --git a/drivers/gpu/drm/xe/xe_lmtt.c b/drivers/gpu/drm/xe/xe_lmtt.c
index 62fc5a1a332d..4dc1de482eee 100644
--- a/drivers/gpu/drm/xe/xe_lmtt.c
+++ b/drivers/gpu/drm/xe/xe_lmtt.c
@@ -17,7 +17,7 @@
#include "xe_mmio.h"
#include "xe_res_cursor.h"
#include "xe_sriov.h"
-#include "xe_sriov_printk.h"
+#include "xe_tile_sriov_printk.h"
/**
* DOC: Local Memory Translation Table
@@ -32,7 +32,7 @@
*/
#define lmtt_assert(lmtt, condition) xe_tile_assert(lmtt_to_tile(lmtt), condition)
-#define lmtt_debug(lmtt, msg...) xe_sriov_dbg_verbose(lmtt_to_xe(lmtt), "LMTT: " msg)
+#define lmtt_debug(lmtt, msg...) xe_tile_sriov_dbg_verbose(lmtt_to_tile(lmtt), "LMTT: " msg)
static bool xe_has_multi_level_lmtt(struct xe_device *xe)
{
@@ -267,15 +267,14 @@ static int lmtt_invalidate_hw(struct xe_lmtt *lmtt)
*/
void xe_lmtt_invalidate_hw(struct xe_lmtt *lmtt)
{
- struct xe_device *xe = lmtt_to_xe(lmtt);
int err;
- lmtt_assert(lmtt, IS_SRIOV_PF(xe));
+ lmtt_assert(lmtt, IS_SRIOV_PF(lmtt_to_xe(lmtt)));
err = lmtt_invalidate_hw(lmtt);
if (err)
- xe_sriov_warn(xe, "LMTT%u invalidation failed (%pe)",
- lmtt_to_tile(lmtt)->id, ERR_PTR(err));
+ xe_tile_sriov_err(lmtt_to_tile(lmtt), "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 47e9df775072..b5083c99dd50 100644
--- a/drivers/gpu/drm/xe/xe_lrc.c
+++ b/drivers/gpu/drm/xe/xe_lrc.c
@@ -1214,8 +1214,7 @@ static int setup_bo(struct bo_setup_state *state)
ssize_t remain;
if (state->lrc->bo->vmap.is_iomem) {
- if (!state->buffer)
- return -ENOMEM;
+ xe_gt_assert(state->hwe->gt, state->buffer);
state->ptr = state->buffer;
} else {
state->ptr = state->lrc->bo->vmap.vaddr + state->offset;
@@ -1248,7 +1247,7 @@ fail:
static void finish_bo(struct bo_setup_state *state)
{
- if (!state->buffer)
+ if (!state->lrc->bo->vmap.is_iomem)
return;
xe_map_memcpy_to(gt_to_xe(state->lrc->gt), &state->lrc->bo->vmap,
@@ -1303,8 +1302,11 @@ static int setup_wa_bb(struct xe_lrc *lrc, struct xe_hw_engine *hwe)
u32 *buf = NULL;
int ret;
- if (lrc->bo->vmap.is_iomem)
+ if (lrc->bo->vmap.is_iomem) {
buf = kmalloc(LRC_WA_BB_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+ }
ret = xe_lrc_setup_wa_bb_with_scratch(lrc, hwe, buf);
@@ -1347,8 +1349,11 @@ setup_indirect_ctx(struct xe_lrc *lrc, struct xe_hw_engine *hwe)
if (xe_gt_WARN_ON(lrc->gt, !state.funcs))
return 0;
- if (lrc->bo->vmap.is_iomem)
+ if (lrc->bo->vmap.is_iomem) {
state.buffer = kmalloc(state.max_size, GFP_KERNEL);
+ if (!state.buffer)
+ return -ENOMEM;
+ }
ret = setup_bo(&state);
if (ret) {
@@ -1412,8 +1417,9 @@ static int xe_lrc_init(struct xe_lrc *lrc, struct xe_hw_engine *hwe,
bo_flags = XE_BO_FLAG_VRAM_IF_DGFX(tile) | XE_BO_FLAG_GGTT |
XE_BO_FLAG_GGTT_INVALIDATE;
- if (vm && vm->xef) /* userspace */
- bo_flags |= XE_BO_FLAG_PINNED_LATE_RESTORE;
+
+ if ((vm && vm->xef) || init_flags & XE_LRC_CREATE_USER_CTX) /* userspace */
+ bo_flags |= XE_BO_FLAG_PINNED_LATE_RESTORE | XE_BO_FLAG_FORCE_USER_VRAM;
lrc->bo = xe_bo_create_pin_map_novm(xe, tile,
bo_size,
diff --git a/drivers/gpu/drm/xe/xe_lrc.h b/drivers/gpu/drm/xe/xe_lrc.h
index 188565465779..2fb628da5c43 100644
--- a/drivers/gpu/drm/xe/xe_lrc.h
+++ b/drivers/gpu/drm/xe/xe_lrc.h
@@ -44,8 +44,10 @@ struct xe_lrc_snapshot {
#define LRC_WA_BB_SIZE SZ_4K
-#define XE_LRC_CREATE_RUNALONE 0x1
-#define XE_LRC_CREATE_PXP 0x2
+#define XE_LRC_CREATE_RUNALONE BIT(0)
+#define XE_LRC_CREATE_PXP BIT(1)
+#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 xe_lrc_destroy(struct kref *ref);
@@ -74,6 +76,16 @@ static inline void xe_lrc_put(struct xe_lrc *lrc)
kref_put(&lrc->refcount, xe_lrc_destroy);
}
+/**
+ * xe_lrc_ring_size() - Xe LRC ring size
+ *
+ * Return: Size of LRC ring buffer
+ */
+static inline size_t xe_lrc_ring_size(void)
+{
+ return SZ_16K;
+}
+
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);
diff --git a/drivers/gpu/drm/xe/xe_map.h b/drivers/gpu/drm/xe/xe_map.h
index 8d67f6ba2d95..f62e0c8b67ab 100644
--- a/drivers/gpu/drm/xe/xe_map.h
+++ b/drivers/gpu/drm/xe/xe_map.h
@@ -78,24 +78,6 @@ static inline void xe_map_write32(struct xe_device *xe, struct iosys_map *map,
iosys_map_wr(map__, offset__, type__, val__); \
})
-#define xe_map_rd_array(xe__, map__, index__, type__) \
- xe_map_rd(xe__, map__, (index__) * sizeof(type__), type__)
-
-#define xe_map_wr_array(xe__, map__, index__, type__, val__) \
- xe_map_wr(xe__, map__, (index__) * sizeof(type__), type__, val__)
-
-#define xe_map_rd_array_u32(xe__, map__, index__) \
- xe_map_rd_array(xe__, map__, index__, u32)
-
-#define xe_map_wr_array_u32(xe__, map__, index__, val__) \
- xe_map_wr_array(xe__, map__, index__, u32, val__)
-
-#define xe_map_rd_ring_u32(xe__, map__, index__, size__) \
- xe_map_rd_array_u32(xe__, map__, (index__) % (size__))
-
-#define xe_map_wr_ring_u32(xe__, map__, index__, size__, val__) \
- xe_map_wr_array_u32(xe__, map__, (index__) % (size__), val__)
-
#define xe_map_rd_field(xe__, map__, struct_offset__, struct_type__, field__) ({ \
struct xe_device *__xe = xe__; \
xe_device_assert_mem_access(__xe); \
diff --git a/drivers/gpu/drm/xe/xe_memirq.c b/drivers/gpu/drm/xe/xe_memirq.c
index 49c45ec3e83c..b0c7ce0a5d1e 100644
--- a/drivers/gpu/drm/xe/xe_memirq.c
+++ b/drivers/gpu/drm/xe/xe_memirq.c
@@ -14,16 +14,15 @@
#include "xe_device.h"
#include "xe_device_types.h"
#include "xe_gt.h"
-#include "xe_gt_printk.h"
#include "xe_guc.h"
#include "xe_hw_engine.h"
#include "xe_map.h"
#include "xe_memirq.h"
+#include "xe_tile_printk.h"
#define memirq_assert(m, condition) xe_tile_assert(memirq_to_tile(m), condition)
#define memirq_printk(m, _level, _fmt, ...) \
- drm_##_level(&memirq_to_xe(m)->drm, "MEMIRQ%u: " _fmt, \
- memirq_to_tile(m)->id, ##__VA_ARGS__)
+ xe_tile_##_level(memirq_to_tile(m), "MEMIRQ: " _fmt, ##__VA_ARGS__)
#ifdef CONFIG_DRM_XE_DEBUG_MEMIRQ
#define memirq_debug(m, _fmt, ...) memirq_printk(m, dbg, _fmt, ##__VA_ARGS__)
@@ -398,8 +397,9 @@ void xe_memirq_postinstall(struct xe_memirq *memirq)
memirq_set_enable(memirq, true);
}
-static bool memirq_received(struct xe_memirq *memirq, struct iosys_map *vector,
- u16 offset, const char *name)
+static bool __memirq_received(struct xe_memirq *memirq,
+ struct iosys_map *vector, u16 offset,
+ const char *name, bool clear)
{
u8 value;
@@ -409,19 +409,33 @@ static bool memirq_received(struct xe_memirq *memirq, struct iosys_map *vector,
memirq_err_ratelimited(memirq,
"Unexpected memirq value %#x from %s at %u\n",
value, name, offset);
- iosys_map_wr(vector, offset, u8, 0x00);
+ if (clear)
+ iosys_map_wr(vector, offset, u8, 0x00);
}
return value;
}
+static bool memirq_received_noclear(struct xe_memirq *memirq,
+ struct iosys_map *vector,
+ u16 offset, const char *name)
+{
+ return __memirq_received(memirq, vector, offset, name, false);
+}
+
+static bool memirq_received(struct xe_memirq *memirq, struct iosys_map *vector,
+ u16 offset, const char *name)
+{
+ return __memirq_received(memirq, vector, offset, name, true);
+}
+
static void memirq_dispatch_engine(struct xe_memirq *memirq, struct iosys_map *status,
struct xe_hw_engine *hwe)
{
memirq_debug(memirq, "STATUS %s %*ph\n", hwe->name, 16, status->vaddr);
- if (memirq_received(memirq, status, ilog2(GT_RENDER_USER_INTERRUPT), hwe->name))
- xe_hw_engine_handle_irq(hwe, GT_RENDER_USER_INTERRUPT);
+ if (memirq_received(memirq, status, ilog2(GT_MI_USER_INTERRUPT), hwe->name))
+ xe_hw_engine_handle_irq(hwe, GT_MI_USER_INTERRUPT);
}
static void memirq_dispatch_guc(struct xe_memirq *memirq, struct iosys_map *status,
@@ -434,8 +448,16 @@ static void memirq_dispatch_guc(struct xe_memirq *memirq, struct iosys_map *stat
if (memirq_received(memirq, status, ilog2(GUC_INTR_GUC2HOST), name))
xe_guc_irq_handler(guc, GUC_INTR_GUC2HOST);
- if (memirq_received(memirq, status, ilog2(GUC_INTR_SW_INT_0), name))
+ /*
+ * This is a software interrupt that must be cleared after it's consumed
+ * to avoid race conditions where xe_gt_sriov_vf_recovery_pending()
+ * returns false.
+ */
+ if (memirq_received_noclear(memirq, status, ilog2(GUC_INTR_SW_INT_0),
+ name)) {
xe_guc_irq_handler(guc, GUC_INTR_SW_INT_0);
+ iosys_map_wr(status, ilog2(GUC_INTR_SW_INT_0), u8, 0x00);
+ }
}
/**
@@ -461,6 +483,23 @@ void xe_memirq_hwe_handler(struct xe_memirq *memirq, struct xe_hw_engine *hwe)
}
/**
+ * xe_memirq_guc_sw_int_0_irq_pending() - SW_INT_0 IRQ is pending
+ * @memirq: the &xe_memirq
+ * @guc: the &xe_guc to check for IRQ
+ *
+ * Return: True if SW_INT_0 IRQ is pending on @guc, False otherwise
+ */
+bool xe_memirq_guc_sw_int_0_irq_pending(struct xe_memirq *memirq, struct xe_guc *guc)
+{
+ struct xe_gt *gt = guc_to_gt(guc);
+ u32 offset = xe_gt_is_media_type(gt) ? ilog2(INTR_MGUC) : ilog2(INTR_GUC);
+ struct iosys_map map = IOSYS_MAP_INIT_OFFSET(&memirq->status, offset * SZ_16);
+
+ return memirq_received_noclear(memirq, &map, ilog2(GUC_INTR_SW_INT_0),
+ guc_name(guc));
+}
+
+/**
* xe_memirq_handler - The `Memory Based Interrupts`_ Handler.
* @memirq: the &xe_memirq
*
diff --git a/drivers/gpu/drm/xe/xe_memirq.h b/drivers/gpu/drm/xe/xe_memirq.h
index 06130650e9d6..e25d2234ab87 100644
--- a/drivers/gpu/drm/xe/xe_memirq.h
+++ b/drivers/gpu/drm/xe/xe_memirq.h
@@ -25,4 +25,6 @@ void xe_memirq_handler(struct xe_memirq *memirq);
int xe_memirq_init_guc(struct xe_memirq *memirq, struct xe_guc *guc);
+bool xe_memirq_guc_sw_int_0_irq_pending(struct xe_memirq *memirq, struct xe_guc *guc);
+
#endif
diff --git a/drivers/gpu/drm/xe/xe_migrate.c b/drivers/gpu/drm/xe/xe_migrate.c
index a36ce7dce8cc..3112c966c67d 100644
--- a/drivers/gpu/drm/xe/xe_migrate.c
+++ b/drivers/gpu/drm/xe/xe_migrate.c
@@ -57,6 +57,13 @@ struct xe_migrate {
u64 usm_batch_base_ofs;
/** @cleared_mem_ofs: VM offset of @cleared_bo. */
u64 cleared_mem_ofs;
+ /** @large_page_copy_ofs: VM offset of 2M pages used for large copies */
+ u64 large_page_copy_ofs;
+ /**
+ * @large_page_copy_pdes: BO offset to writeout 2M pages (PDEs) used for
+ * large copies
+ */
+ u64 large_page_copy_pdes;
/**
* @fence: dma-fence representing the last migration job batch.
* Protected by @job_mutex.
@@ -288,6 +295,12 @@ static int xe_migrate_prepare_vm(struct xe_tile *tile, struct xe_migrate *m,
(i + 1) * 8, u64, entry);
}
+ /* Reserve 2M PDEs */
+ level = 1;
+ m->large_page_copy_ofs = NUM_PT_SLOTS << xe_pt_shift(level);
+ m->large_page_copy_pdes = map_ofs + XE_PAGE_SIZE * level +
+ NUM_PT_SLOTS * 8;
+
/* Set up a 1GiB NULL mapping at 255GiB offset. */
level = 2;
xe_map_wr(xe, &bo->vmap, map_ofs + XE_PAGE_SIZE * level + 255 * 8, u64,
@@ -980,15 +993,27 @@ struct xe_lrc *xe_migrate_lrc(struct xe_migrate *migrate)
return migrate->q->lrc[0];
}
-static int emit_flush_invalidate(struct xe_exec_queue *q, u32 *dw, int i,
- u32 flags)
+static u64 migrate_vm_ppgtt_addr_tlb_inval(void)
+{
+ /*
+ * The migrate VM is self-referential so it can modify its own PTEs (see
+ * pte_update_size() or emit_pte() functions). We reserve NUM_KERNEL_PDE
+ * entries for kernel operations (copies, clears, CCS migrate), and
+ * suballocate the rest to user operations (binds/unbinds). With
+ * NUM_KERNEL_PDE = 15, NUM_KERNEL_PDE - 1 is already used for PTE updates,
+ * so assign NUM_KERNEL_PDE - 2 for TLB invalidation.
+ */
+ return (NUM_KERNEL_PDE - 2) * XE_PAGE_SIZE;
+}
+
+static int emit_flush_invalidate(u32 *dw, int i, u32 flags)
{
- struct xe_lrc *lrc = xe_exec_queue_lrc(q);
+ u64 addr = migrate_vm_ppgtt_addr_tlb_inval();
+
dw[i++] = MI_FLUSH_DW | MI_INVALIDATE_TLB | MI_FLUSH_DW_OP_STOREDW |
MI_FLUSH_IMM_DW | flags;
- dw[i++] = lower_32_bits(xe_lrc_start_seqno_ggtt_addr(lrc)) |
- MI_FLUSH_DW_USE_GTT;
- dw[i++] = upper_32_bits(xe_lrc_start_seqno_ggtt_addr(lrc));
+ dw[i++] = lower_32_bits(addr);
+ dw[i++] = upper_32_bits(addr);
dw[i++] = MI_NOOP;
dw[i++] = MI_NOOP;
@@ -1101,11 +1126,11 @@ int xe_migrate_ccs_rw_copy(struct xe_tile *tile, struct xe_exec_queue *q,
emit_pte(m, bb, ccs_pt, false, false, &ccs_it, ccs_size, src);
- bb->len = emit_flush_invalidate(q, bb->cs, bb->len, flush_flags);
+ bb->len = emit_flush_invalidate(bb->cs, bb->len, flush_flags);
flush_flags = xe_migrate_ccs_copy(m, bb, src_L0_ofs, src_is_pltt,
src_L0_ofs, dst_is_pltt,
src_L0, ccs_ofs, true);
- bb->len = emit_flush_invalidate(q, bb->cs, bb->len, flush_flags);
+ bb->len = emit_flush_invalidate(bb->cs, bb->len, flush_flags);
size -= src_L0;
}
@@ -1766,16 +1791,18 @@ static u32 pte_update_cmd_size(u64 size)
static void build_pt_update_batch_sram(struct xe_migrate *m,
struct xe_bb *bb, u32 pt_offset,
struct drm_pagemap_addr *sram_addr,
- u32 size)
+ u32 size, int level)
{
u16 pat_index = tile_to_xe(m->tile)->pat.idx[XE_CACHE_WB];
+ u64 gpu_page_size = 0x1ull << xe_pt_shift(level);
u32 ptes;
int i = 0;
- ptes = DIV_ROUND_UP(size, XE_PAGE_SIZE);
+ ptes = DIV_ROUND_UP(size, gpu_page_size);
while (ptes) {
u32 chunk = min(MAX_PTE_PER_SDI, ptes);
+ chunk = ALIGN_DOWN(chunk, PAGE_SIZE / XE_PAGE_SIZE);
bb->cs[bb->len++] = MI_STORE_DATA_IMM | MI_SDI_NUM_QW(chunk);
bb->cs[bb->len++] = pt_offset;
bb->cs[bb->len++] = 0;
@@ -1784,22 +1811,47 @@ static void build_pt_update_batch_sram(struct xe_migrate *m,
ptes -= chunk;
while (chunk--) {
- u64 addr = sram_addr[i].addr & PAGE_MASK;
+ u64 addr = sram_addr[i].addr & ~(gpu_page_size - 1);
+ u64 pte, orig_addr = addr;
xe_tile_assert(m->tile, sram_addr[i].proto ==
DRM_INTERCONNECT_SYSTEM);
xe_tile_assert(m->tile, addr);
- addr = m->q->vm->pt_ops->pte_encode_addr(m->tile->xe,
- addr, pat_index,
- 0, false, 0);
- bb->cs[bb->len++] = lower_32_bits(addr);
- bb->cs[bb->len++] = upper_32_bits(addr);
- i++;
+again:
+ pte = m->q->vm->pt_ops->pte_encode_addr(m->tile->xe,
+ addr, pat_index,
+ level, false, 0);
+ bb->cs[bb->len++] = lower_32_bits(pte);
+ bb->cs[bb->len++] = upper_32_bits(pte);
+
+ if (gpu_page_size < PAGE_SIZE) {
+ addr += XE_PAGE_SIZE;
+ if (orig_addr + PAGE_SIZE != addr) {
+ chunk--;
+ goto again;
+ }
+ i++;
+ } else {
+ i += gpu_page_size / PAGE_SIZE;
+ }
}
}
}
+static bool xe_migrate_vram_use_pde(struct drm_pagemap_addr *sram_addr,
+ unsigned long size)
+{
+ u32 large_size = (0x1 << xe_pt_shift(1));
+ unsigned long i, incr = large_size / PAGE_SIZE;
+
+ for (i = 0; i < DIV_ROUND_UP(size, PAGE_SIZE); i += incr)
+ if (PAGE_SIZE << sram_addr[i].order != large_size)
+ return false;
+
+ return true;
+}
+
enum xe_migrate_copy_dir {
XE_MIGRATE_COPY_TO_VRAM,
XE_MIGRATE_COPY_TO_SRAM,
@@ -1829,6 +1881,7 @@ static struct dma_fence *xe_migrate_vram(struct xe_migrate *m,
PAGE_SIZE : 4;
int err;
unsigned long i, j;
+ bool use_pde = xe_migrate_vram_use_pde(sram_addr, len + sram_offset);
if (drm_WARN_ON(&xe->drm, (len & XE_CACHELINE_MASK) ||
(sram_offset | vram_addr) & XE_CACHELINE_MASK))
@@ -1853,7 +1906,7 @@ static struct dma_fence *xe_migrate_vram(struct xe_migrate *m,
* struct drm_pagemap_addr. Ensure this is the case even with higher
* orders.
*/
- for (i = 0; i < npages;) {
+ for (i = 0; !use_pde && i < npages;) {
unsigned int order = sram_addr[i].order;
for (j = 1; j < NR_PAGES(order) && i + j < npages; j++)
@@ -1863,16 +1916,26 @@ static struct dma_fence *xe_migrate_vram(struct xe_migrate *m,
i += NR_PAGES(order);
}
- build_pt_update_batch_sram(m, bb, pt_slot * XE_PAGE_SIZE,
- sram_addr, len + sram_offset);
+ if (use_pde)
+ build_pt_update_batch_sram(m, bb, m->large_page_copy_pdes,
+ sram_addr, len + sram_offset, 1);
+ else
+ build_pt_update_batch_sram(m, bb, pt_slot * XE_PAGE_SIZE,
+ sram_addr, len + sram_offset, 0);
if (dir == XE_MIGRATE_COPY_TO_VRAM) {
- src_L0_ofs = xe_migrate_vm_addr(pt_slot, 0) + sram_offset;
+ if (use_pde)
+ src_L0_ofs = m->large_page_copy_ofs + sram_offset;
+ else
+ src_L0_ofs = xe_migrate_vm_addr(pt_slot, 0) + sram_offset;
dst_L0_ofs = xe_migrate_vram_ofs(xe, vram_addr, false);
} else {
src_L0_ofs = xe_migrate_vram_ofs(xe, vram_addr, false);
- dst_L0_ofs = xe_migrate_vm_addr(pt_slot, 0) + sram_offset;
+ if (use_pde)
+ dst_L0_ofs = m->large_page_copy_ofs + sram_offset;
+ else
+ dst_L0_ofs = xe_migrate_vm_addr(pt_slot, 0) + sram_offset;
}
bb->cs[bb->len++] = MI_BATCH_BUFFER_END;
diff --git a/drivers/gpu/drm/xe/xe_mocs.c b/drivers/gpu/drm/xe/xe_mocs.c
index 0c737413fcb6..e8ec4114302e 100644
--- a/drivers/gpu/drm/xe/xe_mocs.c
+++ b/drivers/gpu/drm/xe/xe_mocs.c
@@ -576,6 +576,7 @@ static unsigned int get_mocs_settings(struct xe_device *xe,
memset(info, 0, sizeof(struct xe_mocs_info));
switch (xe->info.platform) {
+ case XE_NOVALAKE_S:
case XE_PANTHERLAKE:
case XE_LUNARLAKE:
case XE_BATTLEMAGE:
@@ -772,12 +773,20 @@ void xe_mocs_init(struct xe_gt *gt)
init_l3cc_table(gt, &table);
}
-void xe_mocs_dump(struct xe_gt *gt, struct drm_printer *p)
+/**
+ * xe_mocs_dump() - Dump MOCS table.
+ * @gt: the &xe_gt with MOCS table
+ * @p: the &drm_printer to dump info to
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+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;
flags = get_mocs_settings(xe, &table);
@@ -785,14 +794,17 @@ void xe_mocs_dump(struct xe_gt *gt, struct drm_printer *p)
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))
+ if (!xe_force_wake_ref_has_domain(fw_ref, domain)) {
+ err = -ETIMEDOUT;
goto err_fw;
+ }
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;
}
#if IS_ENABLED(CONFIG_DRM_XE_KUNIT_TEST)
diff --git a/drivers/gpu/drm/xe/xe_mocs.h b/drivers/gpu/drm/xe/xe_mocs.h
index dc972ffd4d07..f00bbb269829 100644
--- a/drivers/gpu/drm/xe/xe_mocs.h
+++ b/drivers/gpu/drm/xe/xe_mocs.h
@@ -11,12 +11,6 @@ struct xe_gt;
void xe_mocs_init_early(struct xe_gt *gt);
void xe_mocs_init(struct xe_gt *gt);
-
-/**
- * xe_mocs_dump - Dump mocs table
- * @gt: GT structure
- * @p: Printer to dump info to
- */
-void xe_mocs_dump(struct xe_gt *gt, struct drm_printer *p);
+int xe_mocs_dump(struct xe_gt *gt, struct drm_printer *p);
#endif
diff --git a/drivers/gpu/drm/xe/xe_oa.c b/drivers/gpu/drm/xe/xe_oa.c
index a4894eb0d7f3..f901ba52b403 100644
--- a/drivers/gpu/drm/xe/xe_oa.c
+++ b/drivers/gpu/drm/xe/xe_oa.c
@@ -837,7 +837,8 @@ static void xe_oa_disable_metric_set(struct xe_oa_stream *stream)
xe_oa_configure_oa_context(stream, false);
/* Make sure we disable noa to save power. */
- xe_mmio_rmw32(mmio, RPM_CONFIG1, GT_NOA_ENABLE, 0);
+ if (GT_VER(stream->gt) < 35)
+ xe_mmio_rmw32(mmio, RPM_CONFIG1, GT_NOA_ENABLE, 0);
sqcnt1 = SQCNT1_PMON_ENABLE |
(HAS_OA_BPC_REPORTING(stream->oa->xe) ? SQCNT1_OABPC : 0);
diff --git a/drivers/gpu/drm/xe/xe_pat.c b/drivers/gpu/drm/xe/xe_pat.c
index 2e7cb99ae87a..7649b554942a 100644
--- a/drivers/gpu/drm/xe/xe_pat.c
+++ b/drivers/gpu/drm/xe/xe_pat.c
@@ -57,7 +57,7 @@ struct xe_pat_ops {
int n_entries);
void (*program_media)(struct xe_gt *gt, const struct xe_pat_table_entry table[],
int n_entries);
- void (*dump)(struct xe_gt *gt, struct drm_printer *p);
+ int (*dump)(struct xe_gt *gt, struct drm_printer *p);
};
static const struct xe_pat_table_entry xelp_pat_table[] = {
@@ -154,6 +154,41 @@ static const struct xe_pat_table_entry xe2_pat_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 );
+/*
+ * Xe3p_XPC PAT table uses the same layout as Xe2/Xe3, except that there's no
+ * option for compression. Also note that the "L3" and "L4" register fields
+ * actually control L2 and L3 cache respectively on this platform.
+ */
+#define XE3P_XPC_PAT(no_promote, l3clos, l3_policy, l4_policy, __coh_mode) \
+ XE2_PAT(no_promote, 0, l3clos, l3_policy, l4_policy, __coh_mode)
+
+static const struct xe_pat_table_entry xe3p_xpc_pat_ats = XE3P_XPC_PAT( 0, 0, 0, 0, 3 );
+static const struct xe_pat_table_entry xe3p_xpc_pat_pta = XE3P_XPC_PAT( 0, 0, 0, 0, 0 );
+
+static const struct xe_pat_table_entry xe3p_xpc_pat_table[] = {
+ [ 0] = XE3P_XPC_PAT( 0, 0, 0, 0, 0 ),
+ [ 1] = XE3P_XPC_PAT( 0, 0, 0, 0, 2 ),
+ [ 2] = XE3P_XPC_PAT( 0, 0, 0, 0, 3 ),
+ [ 3] = XE3P_XPC_PAT( 0, 0, 3, 3, 0 ),
+ [ 4] = XE3P_XPC_PAT( 0, 0, 3, 3, 2 ),
+ [ 5] = XE3P_XPC_PAT( 0, 0, 3, 0, 0 ),
+ [ 6] = XE3P_XPC_PAT( 0, 0, 3, 0, 2 ),
+ [ 7] = XE3P_XPC_PAT( 0, 0, 3, 0, 3 ),
+ [ 8] = XE3P_XPC_PAT( 0, 0, 0, 3, 0 ),
+ [ 9] = XE3P_XPC_PAT( 0, 0, 0, 3, 2 ),
+ [10] = XE3P_XPC_PAT( 0, 0, 0, 3, 3 ),
+ /* 11..22 are reserved; leave set to all 0's */
+ [23] = XE3P_XPC_PAT( 0, 1, 0, 0, 0 ),
+ [24] = XE3P_XPC_PAT( 0, 1, 0, 0, 2 ),
+ [25] = XE3P_XPC_PAT( 0, 1, 0, 0, 3 ),
+ [26] = XE3P_XPC_PAT( 0, 2, 0, 0, 0 ),
+ [27] = XE3P_XPC_PAT( 0, 2, 0, 0, 2 ),
+ [28] = XE3P_XPC_PAT( 0, 2, 0, 0, 3 ),
+ [29] = XE3P_XPC_PAT( 0, 3, 0, 0, 0 ),
+ [30] = XE3P_XPC_PAT( 0, 3, 0, 0, 2 ),
+ [31] = XE3P_XPC_PAT( 0, 3, 0, 0, 3 ),
+};
+
u16 xe_pat_index_get_coh_mode(struct xe_device *xe, u16 pat_index)
{
WARN_ON(pat_index >= xe->pat.n_entries);
@@ -194,7 +229,7 @@ static void program_pat_mcr(struct xe_gt *gt, const struct xe_pat_table_entry ta
xe_gt_mcr_multicast_write(gt, XE_REG_MCR(_PAT_PTA), xe->pat.pat_pta->value);
}
-static void xelp_dump(struct xe_gt *gt, struct drm_printer *p)
+static int xelp_dump(struct xe_gt *gt, struct drm_printer *p)
{
struct xe_device *xe = gt_to_xe(gt);
unsigned int fw_ref;
@@ -202,7 +237,7 @@ static void xelp_dump(struct xe_gt *gt, struct drm_printer *p)
fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
if (!fw_ref)
- return;
+ return -ETIMEDOUT;
drm_printf(p, "PAT table:\n");
@@ -215,6 +250,7 @@ static void xelp_dump(struct xe_gt *gt, struct drm_printer *p)
}
xe_force_wake_put(gt_to_fw(gt), fw_ref);
+ return 0;
}
static const struct xe_pat_ops xelp_pat_ops = {
@@ -222,7 +258,7 @@ static const struct xe_pat_ops xelp_pat_ops = {
.dump = xelp_dump,
};
-static void xehp_dump(struct xe_gt *gt, struct drm_printer *p)
+static int xehp_dump(struct xe_gt *gt, struct drm_printer *p)
{
struct xe_device *xe = gt_to_xe(gt);
unsigned int fw_ref;
@@ -230,7 +266,7 @@ static void xehp_dump(struct xe_gt *gt, struct drm_printer *p)
fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
if (!fw_ref)
- return;
+ return -ETIMEDOUT;
drm_printf(p, "PAT table:\n");
@@ -245,6 +281,7 @@ static void xehp_dump(struct xe_gt *gt, struct drm_printer *p)
}
xe_force_wake_put(gt_to_fw(gt), fw_ref);
+ return 0;
}
static const struct xe_pat_ops xehp_pat_ops = {
@@ -252,7 +289,7 @@ static const struct xe_pat_ops xehp_pat_ops = {
.dump = xehp_dump,
};
-static void xehpc_dump(struct xe_gt *gt, struct drm_printer *p)
+static int xehpc_dump(struct xe_gt *gt, struct drm_printer *p)
{
struct xe_device *xe = gt_to_xe(gt);
unsigned int fw_ref;
@@ -260,7 +297,7 @@ static void xehpc_dump(struct xe_gt *gt, struct drm_printer *p)
fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
if (!fw_ref)
- return;
+ return -ETIMEDOUT;
drm_printf(p, "PAT table:\n");
@@ -273,6 +310,7 @@ static void xehpc_dump(struct xe_gt *gt, struct drm_printer *p)
}
xe_force_wake_put(gt_to_fw(gt), fw_ref);
+ return 0;
}
static const struct xe_pat_ops xehpc_pat_ops = {
@@ -280,7 +318,7 @@ static const struct xe_pat_ops xehpc_pat_ops = {
.dump = xehpc_dump,
};
-static void xelpg_dump(struct xe_gt *gt, struct drm_printer *p)
+static int xelpg_dump(struct xe_gt *gt, struct drm_printer *p)
{
struct xe_device *xe = gt_to_xe(gt);
unsigned int fw_ref;
@@ -288,7 +326,7 @@ static void xelpg_dump(struct xe_gt *gt, struct drm_printer *p)
fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
if (!fw_ref)
- return;
+ return -ETIMEDOUT;
drm_printf(p, "PAT table:\n");
@@ -306,6 +344,7 @@ static void xelpg_dump(struct xe_gt *gt, struct drm_printer *p)
}
xe_force_wake_put(gt_to_fw(gt), fw_ref);
+ return 0;
}
/*
@@ -318,7 +357,7 @@ static const struct xe_pat_ops xelpg_pat_ops = {
.dump = xelpg_dump,
};
-static void xe2_dump(struct xe_gt *gt, struct drm_printer *p)
+static int xe2_dump(struct xe_gt *gt, struct drm_printer *p)
{
struct xe_device *xe = gt_to_xe(gt);
unsigned int fw_ref;
@@ -327,7 +366,7 @@ static void xe2_dump(struct xe_gt *gt, struct drm_printer *p)
fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
if (!fw_ref)
- return;
+ return -ETIMEDOUT;
drm_printf(p, "PAT table:\n");
@@ -367,6 +406,7 @@ static void xe2_dump(struct xe_gt *gt, struct drm_printer *p)
pat);
xe_force_wake_put(gt_to_fw(gt), fw_ref);
+ return 0;
}
static const struct xe_pat_ops xe2_pat_ops = {
@@ -375,9 +415,68 @@ static const struct xe_pat_ops xe2_pat_ops = {
.dump = xe2_dump,
};
+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;
+
+ fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
+ if (!fw_ref)
+ return -ETIMEDOUT;
+
+ drm_printf(p, "PAT table:\n");
+
+ 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)\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);
+ }
+
+ /*
+ * Also print PTA_MODE, which describes how the hardware accesses
+ * PPGTT entries.
+ */
+ 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);
+
+ xe_force_wake_put(gt_to_fw(gt), fw_ref);
+ return 0;
+}
+
+static const struct xe_pat_ops xe3p_xpc_pat_ops = {
+ .program_graphics = program_pat_mcr,
+ .program_media = program_pat,
+ .dump = xe3p_xpc_dump,
+};
+
void xe_pat_init_early(struct xe_device *xe)
{
- if (GRAPHICS_VER(xe) == 30 || GRAPHICS_VER(xe) == 20) {
+ if (GRAPHICS_VERx100(xe) == 3511) {
+ xe->pat.ops = &xe3p_xpc_pat_ops;
+ xe->pat.table = xe3p_xpc_pat_table;
+ xe->pat.pat_ats = &xe3p_xpc_pat_ats;
+ xe->pat.pat_pta = &xe3p_xpc_pat_pta;
+ xe->pat.n_entries = ARRAY_SIZE(xe3p_xpc_pat_table);
+ xe->pat.idx[XE_CACHE_NONE] = 3;
+ xe->pat.idx[XE_CACHE_WT] = 3; /* N/A (no display); use UC */
+ 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;
xe->pat.pat_ats = &xe2_pat_ats;
@@ -462,12 +561,19 @@ void xe_pat_init(struct xe_gt *gt)
xe->pat.ops->program_graphics(gt, xe->pat.table, xe->pat.n_entries);
}
-void xe_pat_dump(struct xe_gt *gt, struct drm_printer *p)
+/**
+ * xe_pat_dump() - Dump 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(struct xe_gt *gt, struct drm_printer *p)
{
struct xe_device *xe = gt_to_xe(gt);
if (!xe->pat.ops)
- return;
+ return -EOPNOTSUPP;
- xe->pat.ops->dump(gt, p);
+ return xe->pat.ops->dump(gt, p);
}
diff --git a/drivers/gpu/drm/xe/xe_pat.h b/drivers/gpu/drm/xe/xe_pat.h
index fa0dfbe525cd..268c9a899f56 100644
--- a/drivers/gpu/drm/xe/xe_pat.h
+++ b/drivers/gpu/drm/xe/xe_pat.h
@@ -43,12 +43,7 @@ void xe_pat_init_early(struct xe_device *xe);
*/
void xe_pat_init(struct xe_gt *gt);
-/**
- * xe_pat_dump - Dump PAT table
- * @gt: GT structure
- * @p: Printer to dump info to
- */
-void xe_pat_dump(struct xe_gt *gt, struct drm_printer *p);
+int xe_pat_dump(struct xe_gt *gt, struct drm_printer *p);
/**
* xe_pat_index_get_coh_mode - Extract the coherency mode for the given
diff --git a/drivers/gpu/drm/xe/xe_pci.c b/drivers/gpu/drm/xe/xe_pci.c
index 89cc6d32f041..7a3911728720 100644
--- a/drivers/gpu/drm/xe/xe_pci.c
+++ b/drivers/gpu/drm/xe/xe_pci.c
@@ -30,6 +30,7 @@
#include "xe_pci_sriov.h"
#include "xe_pci_types.h"
#include "xe_pm.h"
+#include "xe_printk.h"
#include "xe_sriov.h"
#include "xe_step.h"
#include "xe_survivability_mode.h"
@@ -51,15 +52,10 @@ __diag_ignore_all("-Woverride-init", "Allow field overrides in table");
static const struct xe_graphics_desc graphics_xelp = {
.hw_engine_mask = BIT(XE_HW_ENGINE_RCS0) | BIT(XE_HW_ENGINE_BCS0),
-
- .va_bits = 48,
- .vm_max_level = 3,
};
#define XE_HP_FEATURES \
- .has_range_tlb_inval = true, \
- .va_bits = 48, \
- .vm_max_level = 3
+ .has_range_tlb_inval = true
static const struct xe_graphics_desc graphics_xehpg = {
.hw_engine_mask =
@@ -68,9 +64,6 @@ static const struct xe_graphics_desc graphics_xehpg = {
BIT(XE_HW_ENGINE_CCS2) | BIT(XE_HW_ENGINE_CCS3),
XE_HP_FEATURES,
- .vram_flags = XE_VRAM_FLAGS_NEED64K,
-
- .has_flat_ccs = 1,
};
static const struct xe_graphics_desc graphics_xehpc = {
@@ -84,9 +77,6 @@ static const struct xe_graphics_desc graphics_xehpc = {
BIT(XE_HW_ENGINE_CCS2) | BIT(XE_HW_ENGINE_CCS3),
XE_HP_FEATURES,
- .va_bits = 57,
- .vm_max_level = 4,
- .vram_flags = XE_VRAM_FLAGS_NEED64K,
.has_asid = 1,
.has_atomic_enable_pte_bit = 1,
@@ -104,12 +94,9 @@ static const struct xe_graphics_desc graphics_xelpg = {
#define XE2_GFX_FEATURES \
.has_asid = 1, \
.has_atomic_enable_pte_bit = 1, \
- .has_flat_ccs = 1, \
.has_range_tlb_inval = 1, \
.has_usm = 1, \
.has_64bit_timestamp = 1, \
- .va_bits = 48, \
- .vm_max_level = 4, \
.hw_engine_mask = \
BIT(XE_HW_ENGINE_RCS0) | \
BIT(XE_HW_ENGINE_BCS8) | BIT(XE_HW_ENGINE_BCS0) | \
@@ -119,6 +106,13 @@ static const struct xe_graphics_desc graphics_xe2 = {
XE2_GFX_FEATURES,
};
+static const struct xe_graphics_desc graphics_xe3p_xpc = {
+ XE2_GFX_FEATURES,
+ .hw_engine_mask =
+ GENMASK(XE_HW_ENGINE_BCS8, XE_HW_ENGINE_BCS1) |
+ GENMASK(XE_HW_ENGINE_CCS3, XE_HW_ENGINE_CCS0),
+};
+
static const struct xe_media_desc media_xem = {
.hw_engine_mask =
GENMASK(XE_HW_ENGINE_VCS7, XE_HW_ENGINE_VCS0) |
@@ -149,6 +143,9 @@ static const struct xe_ip graphics_ips[] = {
{ 3000, "Xe3_LPG", &graphics_xe2 },
{ 3001, "Xe3_LPG", &graphics_xe2 },
{ 3003, "Xe3_LPG", &graphics_xe2 },
+ { 3004, "Xe3_LPG", &graphics_xe2 },
+ { 3005, "Xe3_LPG", &graphics_xe2 },
+ { 3511, "Xe3p_XPC", &graphics_xe3p_xpc },
};
/* Pre-GMDID Media IPs */
@@ -162,6 +159,8 @@ static const struct xe_ip media_ips[] = {
{ 2000, "Xe2_LPM", &media_xelpmp },
{ 3000, "Xe3_LPM", &media_xelpmp },
{ 3002, "Xe3_LPM", &media_xelpmp },
+ { 3500, "Xe3p_LPM", &media_xelpmp },
+ { 3503, "Xe3p_HPM", &media_xelpmp },
};
static const struct xe_device_desc tgl_desc = {
@@ -174,6 +173,8 @@ static const struct xe_device_desc tgl_desc = {
.has_sriov = true,
.max_gt_per_tile = 1,
.require_force_probe = true,
+ .va_bits = 48,
+ .vm_max_level = 3,
};
static const struct xe_device_desc rkl_desc = {
@@ -185,6 +186,8 @@ static const struct xe_device_desc rkl_desc = {
.has_llc = true,
.max_gt_per_tile = 1,
.require_force_probe = true,
+ .va_bits = 48,
+ .vm_max_level = 3,
};
static const u16 adls_rpls_ids[] = { INTEL_RPLS_IDS(NOP), 0 };
@@ -203,6 +206,8 @@ static const struct xe_device_desc adl_s_desc = {
{ XE_SUBPLATFORM_ALDERLAKE_S_RPLS, "RPLS", adls_rpls_ids },
{},
},
+ .va_bits = 48,
+ .vm_max_level = 3,
};
static const u16 adlp_rplu_ids[] = { INTEL_RPLU_IDS(NOP), 0 };
@@ -221,6 +226,8 @@ static const struct xe_device_desc adl_p_desc = {
{ XE_SUBPLATFORM_ALDERLAKE_P_RPLU, "RPLU", adlp_rplu_ids },
{},
},
+ .va_bits = 48,
+ .vm_max_level = 3,
};
static const struct xe_device_desc adl_n_desc = {
@@ -233,6 +240,8 @@ static const struct xe_device_desc adl_n_desc = {
.has_sriov = true,
.max_gt_per_tile = 1,
.require_force_probe = true,
+ .va_bits = 48,
+ .vm_max_level = 3,
};
#define DGFX_FEATURES \
@@ -249,6 +258,8 @@ static const struct xe_device_desc dg1_desc = {
.has_heci_gscfi = 1,
.max_gt_per_tile = 1,
.require_force_probe = true,
+ .va_bits = 48,
+ .vm_max_level = 3,
};
static const u16 dg2_g10_ids[] = { INTEL_DG2_G10_IDS(NOP), INTEL_ATS_M150_IDS(NOP), 0 };
@@ -258,6 +269,7 @@ static const u16 dg2_g12_ids[] = { INTEL_DG2_G12_IDS(NOP), 0 };
#define DG2_FEATURES \
DGFX_FEATURES, \
PLATFORM(DG2), \
+ .has_flat_ccs = 1, \
.has_gsc_nvm = 1, \
.has_heci_gscfi = 1, \
.subplatforms = (const struct xe_subplatform_desc[]) { \
@@ -265,7 +277,10 @@ static const u16 dg2_g12_ids[] = { INTEL_DG2_G12_IDS(NOP), 0 };
{ XE_SUBPLATFORM_DG2_G11, "G11", dg2_g11_ids }, \
{ XE_SUBPLATFORM_DG2_G12, "G12", dg2_g12_ids }, \
{ } \
- }
+ }, \
+ .va_bits = 48, \
+ .vm_max_level = 3, \
+ .vram_flags = XE_VRAM_FLAGS_NEED64K
static const struct xe_device_desc ats_m_desc = {
.pre_gmdid_graphics_ip = &graphics_ip_xehpg,
@@ -303,6 +318,9 @@ static const __maybe_unused struct xe_device_desc pvc_desc = {
.max_gt_per_tile = 1,
.max_remote_tiles = 1,
.require_force_probe = true,
+ .va_bits = 57,
+ .vm_max_level = 4,
+ .vram_flags = XE_VRAM_FLAGS_NEED64K,
.has_mbx_power_limits = false,
};
@@ -314,23 +332,31 @@ static const struct xe_device_desc mtl_desc = {
.has_display = true,
.has_pxp = true,
.max_gt_per_tile = 2,
+ .va_bits = 48,
+ .vm_max_level = 4,
};
static const struct xe_device_desc lnl_desc = {
PLATFORM(LUNARLAKE),
.dma_mask_size = 46,
.has_display = true,
+ .has_flat_ccs = 1,
.has_pxp = true,
.max_gt_per_tile = 2,
.needs_scratch = true,
+ .va_bits = 48,
+ .vm_max_level = 4,
};
+static const u16 bmg_g21_ids[] = { INTEL_BMG_G21_IDS(NOP), 0 };
+
static const struct xe_device_desc bmg_desc = {
DGFX_FEATURES,
PLATFORM(BATTLEMAGE),
.dma_mask_size = 46,
.has_display = true,
.has_fan_control = true,
+ .has_flat_ccs = 1,
.has_mbx_power_limits = true,
.has_gsc_nvm = 1,
.has_heci_cscfi = 1,
@@ -338,15 +364,36 @@ static const struct xe_device_desc bmg_desc = {
.has_sriov = true,
.max_gt_per_tile = 2,
.needs_scratch = true,
+ .subplatforms = (const struct xe_subplatform_desc[]) {
+ { XE_SUBPLATFORM_BATTLEMAGE_G21, "G21", bmg_g21_ids },
+ { }
+ },
+ .va_bits = 48,
+ .vm_max_level = 4,
};
static const struct xe_device_desc ptl_desc = {
PLATFORM(PANTHERLAKE),
.dma_mask_size = 46,
.has_display = true,
+ .has_flat_ccs = 1,
.has_sriov = true,
.max_gt_per_tile = 2,
.needs_scratch = true,
+ .needs_shared_vf_gt_wq = true,
+ .va_bits = 48,
+ .vm_max_level = 4,
+};
+
+static const struct xe_device_desc nvls_desc = {
+ PLATFORM(NOVALAKE_S),
+ .dma_mask_size = 46,
+ .has_display = true,
+ .has_flat_ccs = 1,
+ .max_gt_per_tile = 2,
+ .require_force_probe = true,
+ .va_bits = 48,
+ .vm_max_level = 4,
};
#undef PLATFORM
@@ -376,6 +423,7 @@ static const struct pci_device_id pciidlist[] = {
INTEL_BMG_IDS(INTEL_VGA_DEVICE, &bmg_desc),
INTEL_PTL_IDS(INTEL_VGA_DEVICE, &ptl_desc),
INTEL_WCL_IDS(INTEL_VGA_DEVICE, &ptl_desc),
+ INTEL_NVLS_IDS(INTEL_VGA_DEVICE, &nvls_desc),
{ }
};
MODULE_DEVICE_TABLE(pci, pciidlist);
@@ -448,7 +496,7 @@ enum xe_gmdid_type {
GMDID_MEDIA
};
-static void read_gmdid(struct xe_device *xe, enum xe_gmdid_type type, u32 *ver, u32 *revid)
+static int read_gmdid(struct xe_device *xe, enum xe_gmdid_type type, u32 *ver, u32 *revid)
{
struct xe_mmio *mmio = xe_root_tile_mmio(xe);
struct xe_reg gmdid_reg = GMD_ID;
@@ -457,22 +505,24 @@ static void read_gmdid(struct xe_device *xe, enum xe_gmdid_type type, u32 *ver,
KUNIT_STATIC_STUB_REDIRECT(read_gmdid, xe, type, ver, revid);
if (IS_SRIOV_VF(xe)) {
- struct xe_gt *gt = xe_root_mmio_gt(xe);
-
/*
* To get the value of the GMDID register, VFs must obtain it
* from the GuC using MMIO communication.
*
- * Note that at this point the xe_gt is not fully uninitialized
- * and only basic access to MMIO registers is possible. To use
- * our existing GuC communication functions we must perform at
- * least basic xe_gt and xe_guc initialization.
- *
- * Since to obtain the value of GMDID_MEDIA we need to use the
- * media GuC, temporarily tweak the gt type.
+ * Note that at this point the GTs are not initialized and only
+ * tile-level access to MMIO registers is possible. To use our
+ * existing GuC communication functions we must create a dummy
+ * GT structure and perform at least basic xe_gt and xe_guc
+ * initialization.
*/
- xe_gt_assert(gt, gt->info.type == XE_GT_TYPE_UNINITIALIZED);
+ struct xe_gt *gt __free(kfree) = NULL;
+ int err;
+ gt = kzalloc(sizeof(*gt), GFP_KERNEL);
+ if (!gt)
+ return -ENOMEM;
+
+ gt->tile = &xe->tiles[0];
if (type == GMDID_MEDIA) {
gt->info.id = 1;
gt->info.type = XE_GT_TYPE_MEDIA;
@@ -484,15 +534,11 @@ static void read_gmdid(struct xe_device *xe, enum xe_gmdid_type type, u32 *ver,
xe_gt_mmio_init(gt);
xe_guc_comm_init_early(&gt->uc.guc);
- /* Don't bother with GMDID if failed to negotiate the GuC ABI */
- val = xe_gt_sriov_vf_bootstrap(gt) ? 0 : xe_gt_sriov_vf_gmdid(gt);
+ err = xe_gt_sriov_vf_bootstrap(gt);
+ if (err)
+ return err;
- /*
- * Only undo xe_gt.info here, the remaining changes made above
- * will be overwritten as part of the regular initialization.
- */
- gt->info.id = 0;
- gt->info.type = XE_GT_TYPE_UNINITIALIZED;
+ val = xe_gt_sriov_vf_gmdid(gt);
} else {
/*
* GMD_ID is a GT register, but at this point in the driver
@@ -510,6 +556,8 @@ static void read_gmdid(struct xe_device *xe, enum xe_gmdid_type type, u32 *ver,
*ver = REG_FIELD_GET(GMD_ID_ARCH_MASK, val) * 100 + REG_FIELD_GET(GMD_ID_RELEASE_MASK, val);
*revid = REG_FIELD_GET(GMD_ID_REVID, val);
+
+ return 0;
}
static const struct xe_ip *find_graphics_ip(unsigned int verx100)
@@ -536,18 +584,21 @@ static const struct xe_ip *find_media_ip(unsigned int verx100)
* Read IP version from hardware and select graphics/media IP descriptors
* based on the result.
*/
-static void handle_gmdid(struct xe_device *xe,
- const struct xe_ip **graphics_ip,
- const struct xe_ip **media_ip,
- u32 *graphics_revid,
- u32 *media_revid)
+static int handle_gmdid(struct xe_device *xe,
+ const struct xe_ip **graphics_ip,
+ const struct xe_ip **media_ip,
+ u32 *graphics_revid,
+ u32 *media_revid)
{
u32 ver;
+ int ret;
*graphics_ip = NULL;
*media_ip = NULL;
- read_gmdid(xe, GMDID_GRAPHICS, &ver, graphics_revid);
+ ret = read_gmdid(xe, GMDID_GRAPHICS, &ver, graphics_revid);
+ if (ret)
+ return ret;
*graphics_ip = find_graphics_ip(ver);
if (!*graphics_ip) {
@@ -555,16 +606,21 @@ static void handle_gmdid(struct xe_device *xe,
ver / 100, ver % 100);
}
- read_gmdid(xe, GMDID_MEDIA, &ver, media_revid);
+ ret = read_gmdid(xe, GMDID_MEDIA, &ver, media_revid);
+ if (ret)
+ return ret;
+
/* Media may legitimately be fused off / not present */
if (ver == 0)
- return;
+ return 0;
*media_ip = find_media_ip(ver);
if (!*media_ip) {
drm_err(&xe->drm, "Hardware reports unknown media version %u.%02u\n",
ver / 100, ver % 100);
}
+
+ return 0;
}
/*
@@ -583,8 +639,14 @@ static int xe_info_init_early(struct xe_device *xe,
subplatform_desc->subplatform : XE_SUBPLATFORM_NONE;
xe->info.dma_mask_size = desc->dma_mask_size;
+ xe->info.va_bits = desc->va_bits;
+ xe->info.vm_max_level = desc->vm_max_level;
+ xe->info.vram_flags = desc->vram_flags;
+
xe->info.is_dgfx = desc->is_dgfx;
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_gsc_nvm = desc->has_gsc_nvm;
xe->info.has_heci_gscfi = desc->has_heci_gscfi;
@@ -592,11 +654,13 @@ static int xe_info_init_early(struct xe_device *xe,
xe->info.has_late_bind = desc->has_late_bind;
xe->info.has_llc = desc->has_llc;
xe->info.has_pxp = desc->has_pxp;
- xe->info.has_sriov = desc->has_sriov;
+ xe->info.has_sriov = xe_configfs_primary_gt_allowed(to_pci_dev(xe->drm.dev)) &&
+ desc->has_sriov;
xe->info.skip_guc_pc = desc->skip_guc_pc;
xe->info.skip_mtcfg = desc->skip_mtcfg;
xe->info.skip_pcode = desc->skip_pcode;
xe->info.needs_scratch = desc->needs_scratch;
+ xe->info.needs_shared_vf_gt_wq = desc->needs_shared_vf_gt_wq;
xe->info.probe_display = IS_ENABLED(CONFIG_DRM_XE_DISPLAY) &&
xe_modparam.probe_display &&
@@ -652,6 +716,63 @@ static void xe_info_probe_tile_count(struct xe_device *xe)
}
}
+static struct xe_gt *alloc_primary_gt(struct xe_tile *tile,
+ const struct xe_graphics_desc *graphics_desc,
+ const struct xe_media_desc *media_desc)
+{
+ struct xe_device *xe = tile_to_xe(tile);
+ struct xe_gt *gt;
+
+ if (!xe_configfs_primary_gt_allowed(to_pci_dev(xe->drm.dev))) {
+ xe_info(xe, "Primary GT disabled via configfs\n");
+ return NULL;
+ }
+
+ gt = xe_gt_alloc(tile);
+ if (IS_ERR(gt))
+ return gt;
+
+ 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.engine_mask = graphics_desc->hw_engine_mask;
+
+ /*
+ * Before media version 13, the media IP was part of the primary GT
+ * so we need to add the media engines to the primary GT's engine list.
+ */
+ if (MEDIA_VER(xe) < 13 && media_desc)
+ gt->info.engine_mask |= media_desc->hw_engine_mask;
+
+ return gt;
+}
+
+static struct xe_gt *alloc_media_gt(struct xe_tile *tile,
+ const struct xe_media_desc *media_desc)
+{
+ struct xe_device *xe = tile_to_xe(tile);
+ struct xe_gt *gt;
+
+ if (!xe_configfs_media_gt_allowed(to_pci_dev(xe->drm.dev))) {
+ xe_info(xe, "Media GT disabled via configfs\n");
+ return NULL;
+ }
+
+ if (MEDIA_VER(xe) < 13 || !media_desc)
+ return NULL;
+
+ gt = xe_gt_alloc(tile);
+ if (IS_ERR(gt))
+ return gt;
+
+ gt->info.type = XE_GT_TYPE_MEDIA;
+ gt->info.id = tile->id * xe->info.max_gt_per_tile + 1;
+ gt->info.has_indirect_ring_state = media_desc->has_indirect_ring_state;
+ gt->info.engine_mask = media_desc->hw_engine_mask;
+
+ return gt;
+}
+
/*
* Initialize device info content that does require knowledge about
* graphics / media IP version.
@@ -668,6 +789,7 @@ static int xe_info_init(struct xe_device *xe,
const struct xe_media_desc *media_desc;
struct xe_tile *tile;
struct xe_gt *gt;
+ int ret;
u8 id;
/*
@@ -683,8 +805,11 @@ static int xe_info_init(struct xe_device *xe,
xe->info.step = xe_step_pre_gmdid_get(xe);
} else {
xe_assert(xe, !desc->pre_gmdid_media_ip);
- handle_gmdid(xe, &graphics_ip, &media_ip,
- &graphics_gmdid_revid, &media_gmdid_revid);
+ ret = handle_gmdid(xe, &graphics_ip, &media_ip,
+ &graphics_gmdid_revid, &media_gmdid_revid);
+ if (ret)
+ return ret;
+
xe->info.step = xe_step_gmdid_get(xe,
graphics_gmdid_revid,
media_gmdid_revid);
@@ -711,17 +836,11 @@ static int xe_info_init(struct xe_device *xe,
media_desc = NULL;
}
- xe->info.vram_flags = graphics_desc->vram_flags;
- xe->info.va_bits = graphics_desc->va_bits;
- xe->info.vm_max_level = graphics_desc->vm_max_level;
xe->info.has_asid = graphics_desc->has_asid;
xe->info.has_atomic_enable_pte_bit = graphics_desc->has_atomic_enable_pte_bit;
if (xe->info.platform != XE_PVC)
xe->info.has_device_atomics_on_smem = 1;
- /* Runtime detection may change this later */
- xe->info.has_flat_ccs = graphics_desc->has_flat_ccs;
-
xe->info.has_range_tlb_inval = graphics_desc->has_range_tlb_inval;
xe->info.has_usm = graphics_desc->has_usm;
xe->info.has_64bit_timestamp = graphics_desc->has_64bit_timestamp;
@@ -736,44 +855,33 @@ static int xe_info_init(struct xe_device *xe,
return err;
}
- /*
- * All platforms have at least one primary GT. Any platform with media
- * version 13 or higher has an additional dedicated media GT. And
- * depending on the graphics IP there may be additional "remote tiles."
- * All of these together determine the overall GT count.
- */
+ /* Allocate any GT and VRAM structures necessary for the platform. */
for_each_tile(tile, xe, id) {
int err;
- gt = tile->primary_gt;
- 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.engine_mask = graphics_desc->hw_engine_mask;
-
err = xe_tile_alloc_vram(tile);
if (err)
return err;
- if (MEDIA_VER(xe) < 13 && media_desc)
- gt->info.engine_mask |= media_desc->hw_engine_mask;
-
- if (MEDIA_VER(xe) < 13 || !media_desc)
- continue;
+ tile->primary_gt = alloc_primary_gt(tile, graphics_desc, media_desc);
+ if (IS_ERR(tile->primary_gt))
+ return PTR_ERR(tile->primary_gt);
/*
- * Allocate and setup media GT for platforms with standalone
- * media.
+ * It's not currently possible to probe a device with the
+ * primary GT disabled. With some work, this may be future in
+ * the possible for igpu platforms (although probably not for
+ * dgpu's since access to the primary GT's BCS engines is
+ * required for VRAM management).
*/
- tile->media_gt = xe_gt_alloc(tile);
+ if (!tile->primary_gt) {
+ drm_err(&xe->drm, "Cannot probe device with without a primary GT\n");
+ return -ENODEV;
+ }
+
+ tile->media_gt = alloc_media_gt(tile, media_desc);
if (IS_ERR(tile->media_gt))
return PTR_ERR(tile->media_gt);
-
- gt = tile->media_gt;
- gt->info.type = XE_GT_TYPE_MEDIA;
- gt->info.id = tile->id * xe->info.max_gt_per_tile + 1;
- gt->info.has_indirect_ring_state = media_desc->has_indirect_ring_state;
- gt->info.engine_mask = media_desc->hw_engine_mask;
}
/*
diff --git a/drivers/gpu/drm/xe/xe_pci_sriov.c b/drivers/gpu/drm/xe/xe_pci_sriov.c
index af05db07162e..735f51effc7a 100644
--- a/drivers/gpu/drm/xe/xe_pci_sriov.c
+++ b/drivers/gpu/drm/xe/xe_pci_sriov.c
@@ -17,56 +17,17 @@
#include "xe_pm.h"
#include "xe_sriov.h"
#include "xe_sriov_pf.h"
+#include "xe_sriov_pf_control.h"
#include "xe_sriov_pf_helpers.h"
+#include "xe_sriov_pf_provision.h"
#include "xe_sriov_printk.h"
-static int pf_needs_provisioning(struct xe_gt *gt, unsigned int num_vfs)
-{
- unsigned int n;
-
- for (n = 1; n <= num_vfs; n++)
- if (!xe_gt_sriov_pf_config_is_empty(gt, n))
- return false;
-
- return true;
-}
-
-static int pf_provision_vfs(struct xe_device *xe, unsigned int num_vfs)
-{
- struct xe_gt *gt;
- unsigned int id;
- int result = 0, err;
-
- for_each_gt(gt, xe, id) {
- if (!pf_needs_provisioning(gt, num_vfs))
- continue;
- err = xe_gt_sriov_pf_config_set_fair(gt, VFID(1), num_vfs);
- result = result ?: err;
- }
-
- return result;
-}
-
-static void pf_unprovision_vfs(struct xe_device *xe, unsigned int num_vfs)
-{
- struct xe_gt *gt;
- unsigned int id;
- unsigned int n;
-
- for_each_gt(gt, xe, id)
- for (n = 1; n <= num_vfs; n++)
- xe_gt_sriov_pf_config_release(gt, n, true);
-}
-
static void pf_reset_vfs(struct xe_device *xe, unsigned int num_vfs)
{
- struct xe_gt *gt;
- unsigned int id;
unsigned int n;
- for_each_gt(gt, xe, id)
- for (n = 1; n <= num_vfs; n++)
- xe_gt_sriov_pf_control_trigger_flr(gt, n);
+ for (n = 1; n <= num_vfs; n++)
+ xe_sriov_pf_control_reset_vf(xe, n);
}
static struct pci_dev *xe_pci_pf_get_vf_dev(struct xe_device *xe, unsigned int vf_id)
@@ -170,7 +131,7 @@ static int pf_enable_vfs(struct xe_device *xe, int num_vfs)
*/
xe_pm_runtime_get_noresume(xe);
- err = pf_provision_vfs(xe, num_vfs);
+ err = xe_sriov_pf_provision_vfs(xe, num_vfs);
if (err < 0)
goto failed;
@@ -194,7 +155,7 @@ static int pf_enable_vfs(struct xe_device *xe, int num_vfs)
return num_vfs;
failed:
- pf_unprovision_vfs(xe, num_vfs);
+ xe_sriov_pf_unprovision_vfs(xe, num_vfs);
xe_pm_runtime_put(xe);
out:
xe_sriov_notice(xe, "Failed to enable %u VF%s (%pe)\n",
@@ -220,7 +181,7 @@ static int pf_disable_vfs(struct xe_device *xe)
pf_reset_vfs(xe, num_vfs);
- pf_unprovision_vfs(xe, num_vfs);
+ xe_sriov_pf_unprovision_vfs(xe, num_vfs);
/* not needed anymore - see pf_enable_vfs() */
xe_pm_runtime_put(xe);
diff --git a/drivers/gpu/drm/xe/xe_pci_types.h b/drivers/gpu/drm/xe/xe_pci_types.h
index 9b9766a3baa3..a4451bdc79fb 100644
--- a/drivers/gpu/drm/xe/xe_pci_types.h
+++ b/drivers/gpu/drm/xe/xe_pci_types.h
@@ -30,12 +30,16 @@ struct xe_device_desc {
u8 dma_mask_size;
u8 max_remote_tiles:2;
u8 max_gt_per_tile:2;
+ u8 va_bits;
+ u8 vm_max_level;
+ u8 vram_flags;
u8 require_force_probe:1;
u8 is_dgfx: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;
@@ -48,18 +52,14 @@ struct xe_device_desc {
u8 skip_guc_pc:1;
u8 skip_mtcfg:1;
u8 skip_pcode:1;
+ u8 needs_shared_vf_gt_wq:1;
};
struct xe_graphics_desc {
- u8 va_bits;
- u8 vm_max_level;
- u8 vram_flags;
-
u64 hw_engine_mask; /* hardware engines provided by graphics IP */
u8 has_asid:1;
u8 has_atomic_enable_pte_bit:1;
- u8 has_flat_ccs:1;
u8 has_indirect_ring_state:1;
u8 has_range_tlb_inval:1;
u8 has_usm:1;
diff --git a/drivers/gpu/drm/xe/xe_platform_types.h b/drivers/gpu/drm/xe/xe_platform_types.h
index d08574c4cdb8..78286285c249 100644
--- a/drivers/gpu/drm/xe/xe_platform_types.h
+++ b/drivers/gpu/drm/xe/xe_platform_types.h
@@ -24,6 +24,7 @@ enum xe_platform {
XE_LUNARLAKE,
XE_BATTLEMAGE,
XE_PANTHERLAKE,
+ XE_NOVALAKE_S,
};
enum xe_subplatform {
@@ -34,6 +35,7 @@ enum xe_subplatform {
XE_SUBPLATFORM_DG2_G10,
XE_SUBPLATFORM_DG2_G11,
XE_SUBPLATFORM_DG2_G12,
+ XE_SUBPLATFORM_BATTLEMAGE_G21,
};
#endif
diff --git a/drivers/gpu/drm/xe/xe_pm.c b/drivers/gpu/drm/xe/xe_pm.c
index 2c5a44377994..53507e09f7bc 100644
--- a/drivers/gpu/drm/xe/xe_pm.c
+++ b/drivers/gpu/drm/xe/xe_pm.c
@@ -83,8 +83,58 @@ static struct lockdep_map xe_pm_runtime_d3cold_map = {
static struct lockdep_map xe_pm_runtime_nod3cold_map = {
.name = "xe_rpm_nod3cold_map"
};
+
+static struct lockdep_map xe_pm_block_lockdep_map = {
+ .name = "xe_pm_block_map",
+};
#endif
+static void xe_pm_block_begin_signalling(void)
+{
+ lock_acquire_shared_recursive(&xe_pm_block_lockdep_map, 0, 1, NULL, _RET_IP_);
+}
+
+static void xe_pm_block_end_signalling(void)
+{
+ lock_release(&xe_pm_block_lockdep_map, _RET_IP_);
+}
+
+/**
+ * xe_pm_might_block_on_suspend() - Annotate that the code might block on suspend
+ *
+ * Annotation to use where the code might block or sieze to make
+ * progress pending resume completion.
+ */
+void xe_pm_might_block_on_suspend(void)
+{
+ lock_map_acquire(&xe_pm_block_lockdep_map);
+ lock_map_release(&xe_pm_block_lockdep_map);
+}
+
+/**
+ * xe_pm_might_block_on_suspend() - Block pending suspend.
+ * @xe: The xe device about to be suspended.
+ *
+ * Block if the pm notifier has start evicting bos, to avoid
+ * racing and validating those bos back. The function is
+ * annotated to ensure no locks are held that are also grabbed
+ * in the pm notifier or the device suspend / resume.
+ * This is intended to be used by freezable tasks only.
+ * (Not freezable workqueues), with the intention that the function
+ * returns %-ERESTARTSYS when tasks are frozen during suspend,
+ * and allows the task to freeze. The caller must be able to
+ * handle the %-ERESTARTSYS.
+ *
+ * Return: %0 on success, %-ERESTARTSYS on signal pending or
+ * if freezing requested.
+ */
+int xe_pm_block_on_suspend(struct xe_device *xe)
+{
+ xe_pm_might_block_on_suspend();
+
+ return wait_for_completion_interruptible(&xe->pm_block);
+}
+
/**
* xe_rpm_reclaim_safe() - Whether runtime resume can be done from reclaim context
* @xe: The xe device.
@@ -124,6 +174,7 @@ int xe_pm_suspend(struct xe_device *xe)
int err;
drm_dbg(&xe->drm, "Suspending device\n");
+ xe_pm_block_begin_signalling();
trace_xe_pm_suspend(xe, __builtin_return_address(0));
err = xe_pxp_pm_suspend(xe->pxp);
@@ -155,6 +206,8 @@ int xe_pm_suspend(struct xe_device *xe)
xe_i2c_pm_suspend(xe);
drm_dbg(&xe->drm, "Device suspended\n");
+ xe_pm_block_end_signalling();
+
return 0;
err_display:
@@ -162,6 +215,7 @@ err_display:
xe_pxp_pm_resume(xe->pxp);
err:
drm_dbg(&xe->drm, "Device suspend failed %d\n", err);
+ xe_pm_block_end_signalling();
return err;
}
@@ -178,6 +232,7 @@ int xe_pm_resume(struct xe_device *xe)
u8 id;
int err;
+ xe_pm_block_begin_signalling();
drm_dbg(&xe->drm, "Resuming device\n");
trace_xe_pm_resume(xe, __builtin_return_address(0));
@@ -222,9 +277,11 @@ int xe_pm_resume(struct xe_device *xe)
xe_late_bind_fw_load(&xe->late_bind);
drm_dbg(&xe->drm, "Device resumed\n");
+ xe_pm_block_end_signalling();
return 0;
err:
drm_dbg(&xe->drm, "Device resume failed %d\n", err);
+ xe_pm_block_end_signalling();
return err;
}
@@ -329,9 +386,16 @@ static int xe_pm_notifier_callback(struct notifier_block *nb,
switch (action) {
case PM_HIBERNATION_PREPARE:
case PM_SUSPEND_PREPARE:
+ {
+ struct xe_validation_ctx ctx;
+
reinit_completion(&xe->pm_block);
+ xe_pm_block_begin_signalling();
xe_pm_runtime_get(xe);
+ (void)xe_validation_ctx_init(&ctx, &xe->val, NULL,
+ (struct xe_val_flags) {.exclusive = true});
err = xe_bo_evict_all_user(xe);
+ xe_validation_ctx_fini(&ctx);
if (err)
drm_dbg(&xe->drm, "Notifier evict user failed (%d)\n", err);
@@ -343,7 +407,9 @@ static int xe_pm_notifier_callback(struct notifier_block *nb,
* avoid a runtime suspend interfering with evicted objects or backup
* allocations.
*/
+ xe_pm_block_end_signalling();
break;
+ }
case PM_POST_HIBERNATION:
case PM_POST_SUSPEND:
complete_all(&xe->pm_block);
diff --git a/drivers/gpu/drm/xe/xe_pm.h b/drivers/gpu/drm/xe/xe_pm.h
index 59678b310e55..f7f89a18b6fc 100644
--- a/drivers/gpu/drm/xe/xe_pm.h
+++ b/drivers/gpu/drm/xe/xe_pm.h
@@ -33,6 +33,8 @@ int xe_pm_set_vram_threshold(struct xe_device *xe, u32 threshold);
void xe_pm_d3cold_allowed_toggle(struct xe_device *xe);
bool xe_rpm_reclaim_safe(const struct xe_device *xe);
struct task_struct *xe_pm_read_callback_task(struct xe_device *xe);
+int xe_pm_block_on_suspend(struct xe_device *xe);
+void xe_pm_might_block_on_suspend(void);
int xe_pm_module_init(void);
#endif
diff --git a/drivers/gpu/drm/xe/xe_pmu.c b/drivers/gpu/drm/xe/xe_pmu.c
index cab51d826345..c63335eb69e5 100644
--- a/drivers/gpu/drm/xe/xe_pmu.c
+++ b/drivers/gpu/drm/xe/xe_pmu.c
@@ -497,7 +497,12 @@ static const struct attribute_group *pmu_events_attr_update[] = {
static void set_supported_events(struct xe_pmu *pmu)
{
struct xe_device *xe = container_of(pmu, typeof(*xe), pmu);
- struct xe_gt *gt = xe_device_get_gt(xe, 0);
+ struct xe_gt *gt;
+ int id;
+
+ /* If there are no GTs, don't support any GT-related events */
+ if (xe->info.gt_count == 0)
+ return;
if (!xe->info.skip_guc_pc) {
pmu->supported_events |= BIT_ULL(XE_PMU_EVENT_GT_C6_RESIDENCY);
@@ -505,6 +510,10 @@ static void set_supported_events(struct xe_pmu *pmu)
pmu->supported_events |= BIT_ULL(XE_PMU_EVENT_GT_REQUESTED_FREQUENCY);
}
+ /* Find the first available GT to query engine event capabilities */
+ for_each_gt(gt, xe, id)
+ break;
+
if (xe_guc_engine_activity_supported(&gt->uc.guc)) {
pmu->supported_events |= BIT_ULL(XE_PMU_EVENT_ENGINE_ACTIVE_TICKS);
pmu->supported_events |= BIT_ULL(XE_PMU_EVENT_ENGINE_TOTAL_TICKS);
diff --git a/drivers/gpu/drm/xe/xe_preempt_fence.c b/drivers/gpu/drm/xe/xe_preempt_fence.c
index 83fbeea5aa20..7f587ca3947d 100644
--- a/drivers/gpu/drm/xe/xe_preempt_fence.c
+++ b/drivers/gpu/drm/xe/xe_preempt_fence.c
@@ -8,6 +8,8 @@
#include <linux/slab.h>
#include "xe_exec_queue.h"
+#include "xe_gt_printk.h"
+#include "xe_guc_exec_queue_types.h"
#include "xe_vm.h"
static void preempt_fence_work_func(struct work_struct *w)
@@ -22,6 +24,15 @@ static void preempt_fence_work_func(struct work_struct *w)
} else if (!q->ops->reset_status(q)) {
int err = q->ops->suspend_wait(q);
+ if (err == -EAGAIN) {
+ xe_gt_dbg(q->gt, "PREEMPT FENCE RETRY guc_id=%d",
+ q->guc->id);
+ queue_work(q->vm->xe->preempt_fence_wq,
+ &pfence->preempt_work);
+ dma_fence_end_signalling(cookie);
+ return;
+ }
+
if (err)
dma_fence_set_error(&pfence->base, err);
} else {
diff --git a/drivers/gpu/drm/xe/xe_psmi.c b/drivers/gpu/drm/xe/xe_psmi.c
index 45d142191d60..6a54e38b81ba 100644
--- a/drivers/gpu/drm/xe/xe_psmi.c
+++ b/drivers/gpu/drm/xe/xe_psmi.c
@@ -70,8 +70,8 @@ static struct xe_bo *psmi_alloc_object(struct xe_device *xe,
{
struct xe_tile *tile;
- if (!id || !bo_size)
- return NULL;
+ xe_assert(xe, id);
+ xe_assert(xe, bo_size);
tile = &xe->tiles[id - 1];
diff --git a/drivers/gpu/drm/xe/xe_pt.c b/drivers/gpu/drm/xe/xe_pt.c
index a1c88f9a6c76..d22fd1ccc0ba 100644
--- a/drivers/gpu/drm/xe/xe_pt.c
+++ b/drivers/gpu/drm/xe/xe_pt.c
@@ -122,7 +122,7 @@ struct xe_pt *xe_pt_create(struct xe_vm *vm, struct xe_tile *tile,
XE_BO_FLAG_IGNORE_MIN_PAGE_SIZE |
XE_BO_FLAG_NO_RESV_EVICT | XE_BO_FLAG_PAGETABLE;
if (vm->xef) /* userspace */
- bo_flags |= XE_BO_FLAG_PINNED_LATE_RESTORE;
+ bo_flags |= XE_BO_FLAG_PINNED_LATE_RESTORE | XE_BO_FLAG_FORCE_USER_VRAM;
pt->level = level;
@@ -2022,7 +2022,7 @@ static int op_prepare(struct xe_vm *vm,
case DRM_GPUVA_OP_MAP:
if ((!op->map.immediate && xe_vm_in_fault_mode(vm) &&
!op->map.invalidate_on_bind) ||
- op->map.is_cpu_addr_mirror)
+ (op->map.vma_flags & XE_VMA_SYSTEM_ALLOCATOR))
break;
err = bind_op_prepare(vm, tile, pt_update_ops, op->map.vma,
@@ -2252,7 +2252,7 @@ static void op_commit(struct xe_vm *vm,
switch (op->base.op) {
case DRM_GPUVA_OP_MAP:
if ((!op->map.immediate && xe_vm_in_fault_mode(vm)) ||
- op->map.is_cpu_addr_mirror)
+ (op->map.vma_flags & XE_VMA_SYSTEM_ALLOCATOR))
break;
bind_op_commit(vm, tile, pt_update_ops, op->map.vma, fence,
diff --git a/drivers/gpu/drm/xe/xe_query.c b/drivers/gpu/drm/xe/xe_query.c
index 2e9ff33ed2fe..1c0915e2cc16 100644
--- a/drivers/gpu/drm/xe/xe_query.c
+++ b/drivers/gpu/drm/xe/xe_query.c
@@ -436,7 +436,7 @@ static int query_hwconfig(struct xe_device *xe,
struct drm_xe_device_query *query)
{
struct xe_gt *gt = xe_root_mmio_gt(xe);
- size_t size = xe_guc_hwconfig_size(&gt->uc.guc);
+ size_t size = gt ? xe_guc_hwconfig_size(&gt->uc.guc) : 0;
void __user *query_ptr = u64_to_user_ptr(query->data);
void *hwconfig;
diff --git a/drivers/gpu/drm/xe/xe_reg_whitelist.c b/drivers/gpu/drm/xe/xe_reg_whitelist.c
index 23f6c81d9994..690bc327a363 100644
--- a/drivers/gpu/drm/xe/xe_reg_whitelist.c
+++ b/drivers/gpu/drm/xe/xe_reg_whitelist.c
@@ -19,7 +19,8 @@
#undef XE_REG_MCR
#define XE_REG_MCR(...) XE_REG(__VA_ARGS__, .mcr = 1)
-static bool match_not_render(const struct xe_gt *gt,
+static bool match_not_render(const struct xe_device *xe,
+ const struct xe_gt *gt,
const struct xe_hw_engine *hwe)
{
return hwe->class != XE_ENGINE_CLASS_RENDER;
diff --git a/drivers/gpu/drm/xe/xe_ring_ops.c b/drivers/gpu/drm/xe/xe_ring_ops.c
index d71837773d6c..ac0c6dcffe15 100644
--- a/drivers/gpu/drm/xe/xe_ring_ops.c
+++ b/drivers/gpu/drm/xe/xe_ring_ops.c
@@ -245,12 +245,14 @@ static int emit_copy_timestamp(struct xe_lrc *lrc, u32 *dw, int i)
/* for engines that don't require any special HW handling (no EUs, no aux inval, etc) */
static void __emit_job_gen12_simple(struct xe_sched_job *job, struct xe_lrc *lrc,
- u64 batch_addr, u32 seqno)
+ u64 batch_addr, u32 *head, u32 seqno)
{
u32 dw[MAX_JOB_SIZE_DW], i = 0;
u32 ppgtt_flag = get_ppgtt_flag(job);
struct xe_gt *gt = job->q->gt;
+ *head = lrc->ring.tail;
+
i = emit_copy_timestamp(lrc, dw, i);
if (job->ring_ops_flush_tlb) {
@@ -296,7 +298,7 @@ static bool has_aux_ccs(struct xe_device *xe)
}
static void __emit_job_gen12_video(struct xe_sched_job *job, struct xe_lrc *lrc,
- u64 batch_addr, u32 seqno)
+ u64 batch_addr, u32 *head, u32 seqno)
{
u32 dw[MAX_JOB_SIZE_DW], i = 0;
u32 ppgtt_flag = get_ppgtt_flag(job);
@@ -304,6 +306,8 @@ static void __emit_job_gen12_video(struct xe_sched_job *job, struct xe_lrc *lrc,
struct xe_device *xe = gt_to_xe(gt);
bool decode = job->q->class == XE_ENGINE_CLASS_VIDEO_DECODE;
+ *head = lrc->ring.tail;
+
i = emit_copy_timestamp(lrc, dw, i);
dw[i++] = preparser_disable(true);
@@ -346,7 +350,8 @@ static void __emit_job_gen12_video(struct xe_sched_job *job, struct xe_lrc *lrc,
static void __emit_job_gen12_render_compute(struct xe_sched_job *job,
struct xe_lrc *lrc,
- u64 batch_addr, u32 seqno)
+ u64 batch_addr, u32 *head,
+ u32 seqno)
{
u32 dw[MAX_JOB_SIZE_DW], i = 0;
u32 ppgtt_flag = get_ppgtt_flag(job);
@@ -355,6 +360,8 @@ static void __emit_job_gen12_render_compute(struct xe_sched_job *job,
bool lacks_render = !(gt->info.engine_mask & XE_HW_ENGINE_RCS_MASK);
u32 mask_flags = 0;
+ *head = lrc->ring.tail;
+
i = emit_copy_timestamp(lrc, dw, i);
dw[i++] = preparser_disable(true);
@@ -396,11 +403,14 @@ static void __emit_job_gen12_render_compute(struct xe_sched_job *job,
}
static void emit_migration_job_gen12(struct xe_sched_job *job,
- struct xe_lrc *lrc, u32 seqno)
+ struct xe_lrc *lrc, u32 *head,
+ u32 seqno)
{
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_store_imm_ggtt(saddr, seqno, dw, i);
@@ -434,6 +444,7 @@ static void emit_job_gen12_gsc(struct xe_sched_job *job)
__emit_job_gen12_simple(job, job->q->lrc[0],
job->ptrs[0].batch_addr,
+ &job->ptrs[0].head,
xe_sched_job_lrc_seqno(job));
}
@@ -443,6 +454,7 @@ static void emit_job_gen12_copy(struct xe_sched_job *job)
if (xe_sched_job_is_migration(job->q)) {
emit_migration_job_gen12(job, job->q->lrc[0],
+ &job->ptrs[0].head,
xe_sched_job_lrc_seqno(job));
return;
}
@@ -450,6 +462,7 @@ static void emit_job_gen12_copy(struct xe_sched_job *job)
for (i = 0; i < job->q->width; ++i)
__emit_job_gen12_simple(job, job->q->lrc[i],
job->ptrs[i].batch_addr,
+ &job->ptrs[i].head,
xe_sched_job_lrc_seqno(job));
}
@@ -461,6 +474,7 @@ static void emit_job_gen12_video(struct xe_sched_job *job)
for (i = 0; i < job->q->width; ++i)
__emit_job_gen12_video(job, job->q->lrc[i],
job->ptrs[i].batch_addr,
+ &job->ptrs[i].head,
xe_sched_job_lrc_seqno(job));
}
@@ -471,6 +485,7 @@ static void emit_job_gen12_render_compute(struct xe_sched_job *job)
for (i = 0; i < job->q->width; ++i)
__emit_job_gen12_render_compute(job, job->q->lrc[i],
job->ptrs[i].batch_addr,
+ &job->ptrs[i].head,
xe_sched_job_lrc_seqno(job));
}
diff --git a/drivers/gpu/drm/xe/xe_rtp.c b/drivers/gpu/drm/xe/xe_rtp.c
index b5f430d59f80..ed509b1c8cfc 100644
--- a/drivers/gpu/drm/xe/xe_rtp.c
+++ b/drivers/gpu/drm/xe/xe_rtp.c
@@ -133,10 +133,7 @@ static bool rule_matches(const struct xe_device *xe,
match = hwe->class != r->engine_class;
break;
case XE_RTP_MATCH_FUNC:
- if (drm_WARN_ON(&xe->drm, !gt))
- return false;
-
- match = r->match_func(gt, hwe);
+ match = r->match_func(xe, gt, hwe);
break;
default:
drm_warn(&xe->drm, "Invalid RTP match %u\n",
@@ -343,13 +340,15 @@ void xe_rtp_process(struct xe_rtp_process_ctx *ctx,
}
EXPORT_SYMBOL_IF_KUNIT(xe_rtp_process);
-bool xe_rtp_match_even_instance(const struct xe_gt *gt,
+bool xe_rtp_match_even_instance(const struct xe_device *xe,
+ const struct xe_gt *gt,
const struct xe_hw_engine *hwe)
{
return hwe->instance % 2 == 0;
}
-bool xe_rtp_match_first_render_or_compute(const struct xe_gt *gt,
+bool xe_rtp_match_first_render_or_compute(const struct xe_device *xe,
+ const struct xe_gt *gt,
const struct xe_hw_engine *hwe)
{
u64 render_compute_mask = gt->info.engine_mask &
@@ -359,20 +358,30 @@ bool xe_rtp_match_first_render_or_compute(const struct xe_gt *gt,
hwe->engine_id == __ffs(render_compute_mask);
}
-bool xe_rtp_match_not_sriov_vf(const struct xe_gt *gt,
+bool xe_rtp_match_not_sriov_vf(const struct xe_device *xe,
+ const struct xe_gt *gt,
const struct xe_hw_engine *hwe)
{
- return !IS_SRIOV_VF(gt_to_xe(gt));
+ return !IS_SRIOV_VF(xe);
}
-bool xe_rtp_match_psmi_enabled(const struct xe_gt *gt,
+bool xe_rtp_match_psmi_enabled(const struct xe_device *xe,
+ const struct xe_gt *gt,
const struct xe_hw_engine *hwe)
{
- return xe_configfs_get_psmi_enabled(to_pci_dev(gt_to_xe(gt)->drm.dev));
+ return xe_configfs_get_psmi_enabled(to_pci_dev(xe->drm.dev));
}
-bool xe_rtp_match_gt_has_discontiguous_dss_groups(const struct xe_gt *gt,
+bool xe_rtp_match_gt_has_discontiguous_dss_groups(const struct xe_device *xe,
+ const struct xe_gt *gt,
const struct xe_hw_engine *hwe)
{
return xe_gt_has_discontiguous_dss_groups(gt);
}
+
+bool xe_rtp_match_has_flat_ccs(const struct xe_device *xe,
+ const struct xe_gt *gt,
+ const struct xe_hw_engine *hwe)
+{
+ return xe->info.has_flat_ccs;
+}
diff --git a/drivers/gpu/drm/xe/xe_rtp.h b/drivers/gpu/drm/xe/xe_rtp.h
index ac12ddf6cde6..ba5f940c0a96 100644
--- a/drivers/gpu/drm/xe/xe_rtp.h
+++ b/drivers/gpu/drm/xe/xe_rtp.h
@@ -440,18 +440,21 @@ void xe_rtp_process(struct xe_rtp_process_ctx *ctx,
/**
* xe_rtp_match_even_instance - Match if engine instance is even
+ * @xe: Device structure
* @gt: GT structure
* @hwe: Engine instance
*
* Returns: true if engine instance is even, false otherwise
*/
-bool xe_rtp_match_even_instance(const struct xe_gt *gt,
+bool xe_rtp_match_even_instance(const struct xe_device *xe,
+ const struct xe_gt *gt,
const struct xe_hw_engine *hwe);
/*
* xe_rtp_match_first_render_or_compute - Match if it's first render or compute
* engine in the GT
*
+ * @xe: Device structure
* @gt: GT structure
* @hwe: Engine instance
*
@@ -463,24 +466,41 @@ bool xe_rtp_match_even_instance(const struct xe_gt *gt,
* Returns: true if engine id is the first to match the render reset domain,
* false otherwise.
*/
-bool xe_rtp_match_first_render_or_compute(const struct xe_gt *gt,
+bool xe_rtp_match_first_render_or_compute(const struct xe_device *xe,
+ const struct xe_gt *gt,
const struct xe_hw_engine *hwe);
/*
* xe_rtp_match_not_sriov_vf - Match when not on SR-IOV VF device
*
+ * @xe: Device structure
* @gt: GT structure
* @hwe: Engine instance
*
* Returns: true if device is not VF, false otherwise.
*/
-bool xe_rtp_match_not_sriov_vf(const struct xe_gt *gt,
+bool xe_rtp_match_not_sriov_vf(const struct xe_device *xe,
+ const struct xe_gt *gt,
const struct xe_hw_engine *hwe);
-bool xe_rtp_match_psmi_enabled(const struct xe_gt *gt,
+bool xe_rtp_match_psmi_enabled(const struct xe_device *xe,
+ const struct xe_gt *gt,
const struct xe_hw_engine *hwe);
-bool xe_rtp_match_gt_has_discontiguous_dss_groups(const struct xe_gt *gt,
+bool xe_rtp_match_gt_has_discontiguous_dss_groups(const struct xe_device *xe,
+ const struct xe_gt *gt,
const struct xe_hw_engine *hwe);
+/**
+ * xe_rtp_match_has_flat_ccs - Match when platform has FlatCCS compression
+ * @xe: Device structure
+ * @gt: GT structure
+ * @hwe: Engine instance
+ *
+ * Returns: true if platform has FlatCCS compression, false otherwise
+ */
+bool xe_rtp_match_has_flat_ccs(const struct xe_device *xe,
+ const struct xe_gt *gt,
+ const struct xe_hw_engine *hwe);
+
#endif
diff --git a/drivers/gpu/drm/xe/xe_rtp_types.h b/drivers/gpu/drm/xe/xe_rtp_types.h
index f4cf30e298cf..6ba7f226c227 100644
--- a/drivers/gpu/drm/xe/xe_rtp_types.h
+++ b/drivers/gpu/drm/xe/xe_rtp_types.h
@@ -10,6 +10,7 @@
#include "regs/xe_reg_defs.h"
+struct xe_device;
struct xe_hw_engine;
struct xe_gt;
@@ -86,7 +87,8 @@ struct xe_rtp_rule {
u8 engine_class;
};
/* MATCH_FUNC */
- bool (*match_func)(const struct xe_gt *gt,
+ bool (*match_func)(const struct xe_device *xe,
+ const struct xe_gt *gt,
const struct xe_hw_engine *hwe);
};
};
diff --git a/drivers/gpu/drm/xe/xe_sched_job_types.h b/drivers/gpu/drm/xe/xe_sched_job_types.h
index dbf260dded8d..13e7a12b03ad 100644
--- a/drivers/gpu/drm/xe/xe_sched_job_types.h
+++ b/drivers/gpu/drm/xe/xe_sched_job_types.h
@@ -24,6 +24,11 @@ struct xe_job_ptrs {
struct dma_fence_chain *chain_fence;
/** @batch_addr: Batch buffer address. */
u64 batch_addr;
+ /**
+ * @head: The tail pointer of the LRC (so head pointer of job) when the
+ * job was submitted
+ */
+ u32 head;
};
/**
@@ -58,6 +63,10 @@ struct xe_sched_job {
bool ring_ops_flush_tlb;
/** @ggtt: mapped in ggtt. */
bool ggtt;
+ /** @skip_emit: skip emitting the job */
+ bool skip_emit;
+ /** @last_replay: last job being replayed */
+ bool last_replay;
/** @ptrs: per instance pointers. */
struct xe_job_ptrs ptrs[];
};
diff --git a/drivers/gpu/drm/xe/xe_sriov.c b/drivers/gpu/drm/xe/xe_sriov.c
index 7d2d6de2aabf..ea411944609b 100644
--- a/drivers/gpu/drm/xe/xe_sriov.c
+++ b/drivers/gpu/drm/xe/xe_sriov.c
@@ -167,6 +167,8 @@ const char *xe_sriov_function_name(unsigned int n, char *buf, size_t size)
*/
int xe_sriov_init_late(struct xe_device *xe)
{
+ if (IS_SRIOV_PF(xe))
+ return xe_sriov_pf_init_late(xe);
if (IS_SRIOV_VF(xe))
return xe_sriov_vf_init_late(xe);
diff --git a/drivers/gpu/drm/xe/xe_sriov_pf.c b/drivers/gpu/drm/xe/xe_sriov_pf.c
index 27ddf3cc80e9..bc1ab9ee31d9 100644
--- a/drivers/gpu/drm/xe/xe_sriov_pf.c
+++ b/drivers/gpu/drm/xe/xe_sriov_pf.c
@@ -8,6 +8,7 @@
#include <drm/drm_managed.h>
#include "xe_assert.h"
+#include "xe_configfs.h"
#include "xe_device.h"
#include "xe_gt_sriov_pf.h"
#include "xe_module.h"
@@ -19,6 +20,8 @@
static unsigned int wanted_max_vfs(struct xe_device *xe)
{
+ if (IS_ENABLED(CONFIG_CONFIGFS_FS))
+ return xe_configfs_get_max_vfs(to_pci_dev(xe->drm.dev));
return xe_modparam.max_vfs;
}
@@ -104,6 +107,31 @@ int xe_sriov_pf_init_early(struct xe_device *xe)
}
/**
+ * xe_sriov_pf_init_late() - Late initialization of the SR-IOV PF.
+ * @xe: the &xe_device to initialize
+ *
+ * This function can only be called on PF.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_sriov_pf_init_late(struct xe_device *xe)
+{
+ struct xe_gt *gt;
+ unsigned int id;
+ int err;
+
+ xe_assert(xe, IS_SRIOV_PF(xe));
+
+ for_each_gt(gt, xe, id) {
+ err = xe_gt_sriov_pf_init(gt);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+/**
* xe_sriov_pf_wait_ready() - Wait until PF is ready to operate.
* @xe: the &xe_device to test
*
@@ -146,45 +174,3 @@ void xe_sriov_pf_print_vfs_summary(struct xe_device *xe, struct drm_printer *p)
drm_printf(p, "supported: %u\n", xe->sriov.pf.driver_max_vfs);
drm_printf(p, "enabled: %u\n", pci_num_vf(pdev));
}
-
-static int simple_show(struct seq_file *m, void *data)
-{
- struct drm_printer p = drm_seq_file_printer(m);
- struct drm_info_node *node = m->private;
- struct dentry *parent = node->dent->d_parent;
- struct xe_device *xe = parent->d_inode->i_private;
- void (*print)(struct xe_device *, struct drm_printer *) = node->info_ent->data;
-
- print(xe, &p);
- return 0;
-}
-
-static const struct drm_info_list debugfs_list[] = {
- { .name = "vfs", .show = simple_show, .data = xe_sriov_pf_print_vfs_summary },
- { .name = "versions", .show = simple_show, .data = xe_sriov_pf_service_print_versions },
-};
-
-/**
- * xe_sriov_pf_debugfs_register - Register PF debugfs attributes.
- * @xe: the &xe_device
- * @root: the root &dentry
- *
- * Prepare debugfs attributes exposed by the PF.
- */
-void xe_sriov_pf_debugfs_register(struct xe_device *xe, struct dentry *root)
-{
- struct drm_minor *minor = xe->drm.primary;
- struct dentry *parent;
-
- /*
- * /sys/kernel/debug/dri/0/
- * ├── pf
- * │   ├── ...
- */
- parent = debugfs_create_dir("pf", root);
- if (IS_ERR(parent))
- return;
- parent->d_inode->i_private = xe;
-
- drm_debugfs_create_files(debugfs_list, ARRAY_SIZE(debugfs_list), parent, minor);
-}
diff --git a/drivers/gpu/drm/xe/xe_sriov_pf.h b/drivers/gpu/drm/xe/xe_sriov_pf.h
index e3b34f8f5e04..cba3fde9581f 100644
--- a/drivers/gpu/drm/xe/xe_sriov_pf.h
+++ b/drivers/gpu/drm/xe/xe_sriov_pf.h
@@ -15,23 +15,13 @@ struct xe_device;
#ifdef CONFIG_PCI_IOV
bool xe_sriov_pf_readiness(struct xe_device *xe);
int xe_sriov_pf_init_early(struct xe_device *xe);
+int xe_sriov_pf_init_late(struct xe_device *xe);
int xe_sriov_pf_wait_ready(struct xe_device *xe);
-void xe_sriov_pf_debugfs_register(struct xe_device *xe, struct dentry *root);
void xe_sriov_pf_print_vfs_summary(struct xe_device *xe, struct drm_printer *p);
#else
-static inline bool xe_sriov_pf_readiness(struct xe_device *xe)
-{
- return false;
-}
-
-static inline int xe_sriov_pf_init_early(struct xe_device *xe)
-{
- return 0;
-}
-
-static inline void xe_sriov_pf_debugfs_register(struct xe_device *xe, struct dentry *root)
-{
-}
+static inline bool xe_sriov_pf_readiness(struct xe_device *xe) { return false; }
+static inline int xe_sriov_pf_init_early(struct xe_device *xe) { return 0; }
+static inline int xe_sriov_pf_init_late(struct xe_device *xe) { return 0; }
#endif
#endif
diff --git a/drivers/gpu/drm/xe/xe_sriov_pf_control.c b/drivers/gpu/drm/xe/xe_sriov_pf_control.c
new file mode 100644
index 000000000000..416d00a03fbb
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_sriov_pf_control.c
@@ -0,0 +1,151 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#include "xe_device.h"
+#include "xe_gt_sriov_pf_control.h"
+#include "xe_sriov_pf_control.h"
+#include "xe_sriov_printk.h"
+
+/**
+ * xe_sriov_pf_control_pause_vf() - Pause a VF on all GTs.
+ * @xe: the &xe_device
+ * @vfid: the VF identifier (can't be 0 == PFID)
+ *
+ * This function is for PF only.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_sriov_pf_control_pause_vf(struct xe_device *xe, unsigned int vfid)
+{
+ struct xe_gt *gt;
+ unsigned int id;
+ int result = 0;
+ int err;
+
+ for_each_gt(gt, xe, id) {
+ err = xe_gt_sriov_pf_control_pause_vf(gt, vfid);
+ result = result ? -EUCLEAN : err;
+ }
+
+ if (result)
+ return result;
+
+ xe_sriov_info(xe, "VF%u paused!\n", vfid);
+ return 0;
+}
+
+/**
+ * xe_sriov_pf_control_resume_vf() - Resume a VF on all GTs.
+ * @xe: the &xe_device
+ * @vfid: the VF identifier
+ *
+ * This function is for PF only.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_sriov_pf_control_resume_vf(struct xe_device *xe, unsigned int vfid)
+{
+ struct xe_gt *gt;
+ unsigned int id;
+ int result = 0;
+ int err;
+
+ for_each_gt(gt, xe, id) {
+ err = xe_gt_sriov_pf_control_resume_vf(gt, vfid);
+ result = result ? -EUCLEAN : err;
+ }
+
+ if (result)
+ return result;
+
+ xe_sriov_info(xe, "VF%u resumed!\n", vfid);
+ return 0;
+}
+
+/**
+ * xe_sriov_pf_control_stop_vf - Stop a VF on all GTs.
+ * @xe: the &xe_device
+ * @vfid: the VF identifier
+ *
+ * This function is for PF only.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_sriov_pf_control_stop_vf(struct xe_device *xe, unsigned int vfid)
+{
+ struct xe_gt *gt;
+ unsigned int id;
+ int result = 0;
+ int err;
+
+ for_each_gt(gt, xe, id) {
+ err = xe_gt_sriov_pf_control_stop_vf(gt, vfid);
+ result = result ? -EUCLEAN : err;
+ }
+
+ if (result)
+ return result;
+
+ xe_sriov_info(xe, "VF%u stopped!\n", vfid);
+ return 0;
+}
+
+/**
+ * xe_sriov_pf_control_reset_vf() - Perform a VF reset (FLR).
+ * @xe: the &xe_device
+ * @vfid: the VF identifier
+ *
+ * This function is for PF only.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_sriov_pf_control_reset_vf(struct xe_device *xe, unsigned int vfid)
+{
+ struct xe_gt *gt;
+ unsigned int id;
+ int result = 0;
+ int err;
+
+ for_each_gt(gt, xe, id) {
+ err = xe_gt_sriov_pf_control_trigger_flr(gt, vfid);
+ result = result ? -EUCLEAN : err;
+ }
+
+ for_each_gt(gt, xe, id) {
+ err = xe_gt_sriov_pf_control_wait_flr(gt, vfid);
+ result = result ? -EUCLEAN : err;
+ }
+
+ return result;
+}
+
+/**
+ * xe_sriov_pf_control_sync_flr() - Synchronize a VF FLR between all GTs.
+ * @xe: the &xe_device
+ * @vfid: the VF identifier
+ *
+ * This function is for PF only.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_sriov_pf_control_sync_flr(struct xe_device *xe, unsigned int vfid)
+{
+ struct xe_gt *gt;
+ unsigned int id;
+ int ret;
+
+ for_each_gt(gt, xe, id) {
+ ret = xe_gt_sriov_pf_control_sync_flr(gt, vfid, false);
+ if (ret < 0)
+ return ret;
+ }
+ for_each_gt(gt, xe, id) {
+ ret = xe_gt_sriov_pf_control_sync_flr(gt, vfid, true);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/xe/xe_sriov_pf_control.h b/drivers/gpu/drm/xe/xe_sriov_pf_control.h
new file mode 100644
index 000000000000..2d52d0ac1b28
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_sriov_pf_control.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#ifndef _XE_SRIOV_PF_CONTROL_H_
+#define _XE_SRIOV_PF_CONTROL_H_
+
+struct xe_device;
+
+int xe_sriov_pf_control_pause_vf(struct xe_device *xe, unsigned int vfid);
+int xe_sriov_pf_control_resume_vf(struct xe_device *xe, unsigned int vfid);
+int xe_sriov_pf_control_stop_vf(struct xe_device *xe, unsigned int vfid);
+int xe_sriov_pf_control_reset_vf(struct xe_device *xe, unsigned int vfid);
+int xe_sriov_pf_control_sync_flr(struct xe_device *xe, unsigned int vfid);
+
+#endif
diff --git a/drivers/gpu/drm/xe/xe_sriov_pf_debugfs.c b/drivers/gpu/drm/xe/xe_sriov_pf_debugfs.c
new file mode 100644
index 000000000000..a81aa05c5532
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_sriov_pf_debugfs.c
@@ -0,0 +1,264 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#include <linux/debugfs.h>
+#include <drm/drm_debugfs.h>
+
+#include "xe_device.h"
+#include "xe_device_types.h"
+#include "xe_pm.h"
+#include "xe_sriov_pf.h"
+#include "xe_sriov_pf_control.h"
+#include "xe_sriov_pf_debugfs.h"
+#include "xe_sriov_pf_helpers.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"
+
+/*
+ * /sys/kernel/debug/dri/BDF/
+ * ├── sriov # d_inode->i_private = (xe_device*)
+ * │ ├── pf # d_inode->i_private = (xe_device*)
+ * │ ├── vf1 # d_inode->i_private = VFID(1)
+ * : :
+ * │ ├── vfN # d_inode->i_private = VFID(N)
+ */
+
+static void *extract_priv(struct dentry *d)
+{
+ return d->d_inode->i_private;
+}
+
+static struct xe_device *extract_xe(struct dentry *d)
+{
+ return extract_priv(d->d_parent);
+}
+
+static unsigned int extract_vfid(struct dentry *d)
+{
+ void *p = extract_priv(d);
+
+ return p == extract_xe(d) ? PFID : (uintptr_t)p;
+}
+
+/*
+ * /sys/kernel/debug/dri/BDF/
+ * ├── sriov
+ * │ ├── restore_auto_provisioning
+ * │ :
+ * │ ├── pf/
+ * │ ├── vf1
+ * │ │ ├── ...
+ */
+
+static ssize_t from_file_write_to_xe_call(struct file *file, const char __user *userbuf,
+ size_t count, loff_t *ppos,
+ int (*call)(struct xe_device *))
+{
+ struct dentry *dent = file_dentry(file);
+ struct xe_device *xe = extract_xe(dent);
+ bool yes;
+ int ret;
+
+ if (*ppos)
+ return -EINVAL;
+ ret = kstrtobool_from_user(userbuf, count, &yes);
+ if (ret < 0)
+ return ret;
+ if (yes) {
+ xe_pm_runtime_get(xe);
+ ret = call(xe);
+ xe_pm_runtime_put(xe);
+ }
+ if (ret < 0)
+ return ret;
+ return count;
+}
+
+#define DEFINE_SRIOV_ATTRIBUTE(OP) \
+static int OP##_show(struct seq_file *s, void *unused) \
+{ \
+ return 0; \
+} \
+static ssize_t OP##_write(struct file *file, const char __user *userbuf, \
+ size_t count, loff_t *ppos) \
+{ \
+ return from_file_write_to_xe_call(file, userbuf, count, ppos, \
+ xe_sriov_pf_##OP); \
+} \
+DEFINE_SHOW_STORE_ATTRIBUTE(OP)
+
+static inline int xe_sriov_pf_restore_auto_provisioning(struct xe_device *xe)
+{
+ return xe_sriov_pf_provision_set_mode(xe, XE_SRIOV_PROVISIONING_MODE_AUTO);
+}
+
+DEFINE_SRIOV_ATTRIBUTE(restore_auto_provisioning);
+
+static void pf_populate_root(struct xe_device *xe, struct dentry *dent)
+{
+ debugfs_create_file("restore_auto_provisioning", 0200, dent, xe,
+ &restore_auto_provisioning_fops);
+}
+
+static int simple_show(struct seq_file *m, void *data)
+{
+ struct drm_printer p = drm_seq_file_printer(m);
+ struct drm_info_node *node = m->private;
+ struct dentry *parent = node->dent->d_parent;
+ struct xe_device *xe = parent->d_inode->i_private;
+ void (*print)(struct xe_device *, struct drm_printer *) = node->info_ent->data;
+
+ print(xe, &p);
+ return 0;
+}
+
+static const struct drm_info_list debugfs_list[] = {
+ { .name = "vfs", .show = simple_show, .data = xe_sriov_pf_print_vfs_summary },
+ { .name = "versions", .show = simple_show, .data = xe_sriov_pf_service_print_versions },
+};
+
+static void pf_populate_pf(struct xe_device *xe, struct dentry *pfdent)
+{
+ struct drm_minor *minor = xe->drm.primary;
+
+ drm_debugfs_create_files(debugfs_list, ARRAY_SIZE(debugfs_list), pfdent, minor);
+}
+
+/*
+ * /sys/kernel/debug/dri/BDF/
+ * ├── sriov
+ * │ ├── vf1
+ * │ │ ├── pause
+ * │ │ ├── reset
+ * │ │ ├── resume
+ * │ │ ├── stop
+ * │ │ :
+ * │ ├── vf2
+ * │ │ ├── ...
+ */
+
+static ssize_t from_file_write_to_vf_call(struct file *file, const char __user *userbuf,
+ size_t count, loff_t *ppos,
+ int (*call)(struct xe_device *, unsigned int))
+{
+ struct dentry *dent = file_dentry(file)->d_parent;
+ struct xe_device *xe = extract_xe(dent);
+ unsigned int vfid = extract_vfid(dent);
+ bool yes;
+ int ret;
+
+ if (*ppos)
+ return -EINVAL;
+ ret = kstrtobool_from_user(userbuf, count, &yes);
+ if (ret < 0)
+ return ret;
+ if (yes) {
+ xe_pm_runtime_get(xe);
+ ret = call(xe, vfid);
+ xe_pm_runtime_put(xe);
+ }
+ if (ret < 0)
+ return ret;
+ return count;
+}
+
+#define DEFINE_VF_CONTROL_ATTRIBUTE(OP) \
+static int OP##_show(struct seq_file *s, void *unused) \
+{ \
+ return 0; \
+} \
+static ssize_t OP##_write(struct file *file, const char __user *userbuf, \
+ size_t count, loff_t *ppos) \
+{ \
+ return from_file_write_to_vf_call(file, userbuf, count, ppos, \
+ xe_sriov_pf_control_##OP); \
+} \
+DEFINE_SHOW_STORE_ATTRIBUTE(OP)
+
+DEFINE_VF_CONTROL_ATTRIBUTE(pause_vf);
+DEFINE_VF_CONTROL_ATTRIBUTE(resume_vf);
+DEFINE_VF_CONTROL_ATTRIBUTE(stop_vf);
+DEFINE_VF_CONTROL_ATTRIBUTE(reset_vf);
+
+static void pf_populate_vf(struct xe_device *xe, struct dentry *vfdent)
+{
+ debugfs_create_file("pause", 0200, vfdent, xe, &pause_vf_fops);
+ debugfs_create_file("resume", 0200, vfdent, xe, &resume_vf_fops);
+ debugfs_create_file("stop", 0200, vfdent, xe, &stop_vf_fops);
+ debugfs_create_file("reset", 0200, vfdent, xe, &reset_vf_fops);
+}
+
+static void pf_populate_with_tiles(struct xe_device *xe, struct dentry *dent, unsigned int vfid)
+{
+ struct xe_tile *tile;
+ unsigned int id;
+
+ for_each_tile(tile, xe, id)
+ xe_tile_sriov_pf_debugfs_populate(tile, dent, vfid);
+}
+
+/**
+ * xe_sriov_pf_debugfs_register - Register PF debugfs attributes.
+ * @xe: the &xe_device
+ * @root: the root &dentry
+ *
+ * Create separate directory that will contain all SR-IOV related files,
+ * organized per each SR-IOV function (PF, VF1, VF2, ..., VFn).
+ */
+void xe_sriov_pf_debugfs_register(struct xe_device *xe, struct dentry *root)
+{
+ int totalvfs = xe_sriov_pf_get_totalvfs(xe);
+ struct dentry *pfdent;
+ struct dentry *vfdent;
+ struct dentry *dent;
+ char vfname[16]; /* should be more than enough for "vf%u\0" and VFID(UINT_MAX) */
+ unsigned int n;
+
+ /*
+ * /sys/kernel/debug/dri/BDF/
+ * ├── sriov # d_inode->i_private = (xe_device*)
+ * │ ├── ...
+ */
+ dent = debugfs_create_dir("sriov", root);
+ if (IS_ERR(dent))
+ return;
+ dent->d_inode->i_private = xe;
+
+ pf_populate_root(xe, dent);
+
+ /*
+ * /sys/kernel/debug/dri/BDF/
+ * ├── sriov # d_inode->i_private = (xe_device*)
+ * │ ├── pf # d_inode->i_private = (xe_device*)
+ * │ │ ├── ...
+ */
+ pfdent = debugfs_create_dir("pf", dent);
+ if (IS_ERR(pfdent))
+ return;
+ pfdent->d_inode->i_private = xe;
+
+ pf_populate_pf(xe, pfdent);
+ pf_populate_with_tiles(xe, pfdent, PFID);
+
+ /*
+ * /sys/kernel/debug/dri/BDF/
+ * ├── sriov # d_inode->i_private = (xe_device*)
+ * │ ├── vf1 # d_inode->i_private = VFID(1)
+ * │ ├── vf2 # d_inode->i_private = VFID(2)
+ * │ ├── ...
+ */
+ for (n = 1; n <= totalvfs; n++) {
+ snprintf(vfname, sizeof(vfname), "vf%u", VFID(n));
+ vfdent = debugfs_create_dir(vfname, dent);
+ if (IS_ERR(vfdent))
+ return;
+ vfdent->d_inode->i_private = (void *)(uintptr_t)VFID(n);
+
+ pf_populate_vf(xe, vfdent);
+ pf_populate_with_tiles(xe, vfdent, VFID(n));
+ }
+}
diff --git a/drivers/gpu/drm/xe/xe_sriov_pf_debugfs.h b/drivers/gpu/drm/xe/xe_sriov_pf_debugfs.h
new file mode 100644
index 000000000000..93db13585b82
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_sriov_pf_debugfs.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#ifndef _XE_SRIOV_PF_DEBUGFS_H_
+#define _XE_SRIOV_PF_DEBUGFS_H_
+
+struct dentry;
+struct xe_device;
+
+#ifdef CONFIG_PCI_IOV
+void xe_sriov_pf_debugfs_register(struct xe_device *xe, struct dentry *root);
+#else
+static inline void xe_sriov_pf_debugfs_register(struct xe_device *xe, struct dentry *root) { }
+#endif
+
+#endif
diff --git a/drivers/gpu/drm/xe/xe_sriov_pf_helpers.h b/drivers/gpu/drm/xe/xe_sriov_pf_helpers.h
index dd1df950b021..4a4340fb633a 100644
--- a/drivers/gpu/drm/xe/xe_sriov_pf_helpers.h
+++ b/drivers/gpu/drm/xe/xe_sriov_pf_helpers.h
@@ -37,6 +37,17 @@ static inline int xe_sriov_pf_get_totalvfs(struct xe_device *xe)
return xe->sriov.pf.driver_max_vfs;
}
+/**
+ * xe_sriov_pf_num_vfs() - Number of enabled VFs on the PF.
+ * @xe: the PF &xe_device
+ *
+ * Return: Number of enabled VFs on the PF.
+ */
+static inline unsigned int xe_sriov_pf_num_vfs(const struct xe_device *xe)
+{
+ return pci_num_vf(to_pci_dev(xe->drm.dev));
+}
+
static inline struct mutex *xe_sriov_pf_master_mutex(struct xe_device *xe)
{
xe_assert(xe, IS_SRIOV_PF(xe));
diff --git a/drivers/gpu/drm/xe/xe_sriov_pf_provision.c b/drivers/gpu/drm/xe/xe_sriov_pf_provision.c
new file mode 100644
index 000000000000..663fb0c045e9
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_sriov_pf_provision.c
@@ -0,0 +1,154 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#include "xe_assert.h"
+#include "xe_device.h"
+#include "xe_gt_sriov_pf_config.h"
+#include "xe_sriov.h"
+#include "xe_sriov_pf_helpers.h"
+#include "xe_sriov_pf_provision.h"
+#include "xe_sriov_pf_provision_types.h"
+#include "xe_sriov_printk.h"
+
+static const char *mode_to_string(enum xe_sriov_provisioning_mode mode)
+{
+ switch (mode) {
+ case XE_SRIOV_PROVISIONING_MODE_AUTO:
+ return "auto";
+ case XE_SRIOV_PROVISIONING_MODE_CUSTOM:
+ return "custom";
+ default:
+ return "<invalid>";
+ }
+}
+
+static bool pf_auto_provisioning_mode(struct xe_device *xe)
+{
+ xe_assert(xe, IS_SRIOV_PF(xe));
+
+ return xe->sriov.pf.provision.mode == XE_SRIOV_PROVISIONING_MODE_AUTO;
+}
+
+static bool pf_needs_provisioning(struct xe_gt *gt, unsigned int num_vfs)
+{
+ unsigned int n;
+
+ for (n = 1; n <= num_vfs; n++)
+ if (!xe_gt_sriov_pf_config_is_empty(gt, n))
+ return false;
+
+ return true;
+}
+
+static int pf_provision_vfs(struct xe_device *xe, unsigned int num_vfs)
+{
+ struct xe_gt *gt;
+ unsigned int id;
+ int result = 0;
+ int err;
+
+ for_each_gt(gt, xe, id) {
+ if (!pf_needs_provisioning(gt, num_vfs))
+ return -EUCLEAN;
+ err = xe_gt_sriov_pf_config_set_fair(gt, VFID(1), num_vfs);
+ result = result ?: err;
+ }
+
+ return result;
+}
+
+static void pf_unprovision_vfs(struct xe_device *xe, unsigned int num_vfs)
+{
+ struct xe_gt *gt;
+ unsigned int id;
+ unsigned int n;
+
+ for_each_gt(gt, xe, id)
+ for (n = 1; n <= num_vfs; n++)
+ xe_gt_sriov_pf_config_release(gt, n, true);
+}
+
+static void pf_unprovision_all_vfs(struct xe_device *xe)
+{
+ pf_unprovision_vfs(xe, xe_sriov_pf_get_totalvfs(xe));
+}
+
+/**
+ * xe_sriov_pf_provision_vfs() - Provision VFs in auto-mode.
+ * @xe: the PF &xe_device
+ * @num_vfs: the number of VFs to auto-provision
+ *
+ * This function can only be called on PF.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_sriov_pf_provision_vfs(struct xe_device *xe, unsigned int num_vfs)
+{
+ xe_assert(xe, IS_SRIOV_PF(xe));
+
+ if (!pf_auto_provisioning_mode(xe))
+ return 0;
+
+ return pf_provision_vfs(xe, num_vfs);
+}
+
+/**
+ * xe_sriov_pf_unprovision_vfs() - Unprovision VFs in auto-mode.
+ * @xe: the PF &xe_device
+ * @num_vfs: the number of VFs to unprovision
+ *
+ * This function can only be called on PF.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_sriov_pf_unprovision_vfs(struct xe_device *xe, unsigned int num_vfs)
+{
+ xe_assert(xe, IS_SRIOV_PF(xe));
+
+ if (!pf_auto_provisioning_mode(xe))
+ return 0;
+
+ pf_unprovision_vfs(xe, num_vfs);
+ return 0;
+}
+
+/**
+ * xe_sriov_pf_provision_set_mode() - Change VFs provision mode.
+ * @xe: the PF &xe_device
+ * @mode: the new VFs provisioning mode
+ *
+ * When changing from AUTO to CUSTOM mode, any already allocated VFs resources
+ * will remain allocated and will not be released upon VFs disabling.
+ *
+ * When changing back to AUTO mode, if VFs are not enabled, already allocated
+ * VFs resources will be immediately released. If VFs are still enabled, such
+ * mode change is rejected.
+ *
+ * This function can only be called on PF.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_sriov_pf_provision_set_mode(struct xe_device *xe, enum xe_sriov_provisioning_mode mode)
+{
+ xe_assert(xe, IS_SRIOV_PF(xe));
+
+ if (mode == xe->sriov.pf.provision.mode)
+ return 0;
+
+ if (mode == XE_SRIOV_PROVISIONING_MODE_AUTO) {
+ if (xe_sriov_pf_num_vfs(xe)) {
+ xe_sriov_dbg(xe, "can't restore %s: VFs must be disabled!\n",
+ mode_to_string(mode));
+ return -EBUSY;
+ }
+ pf_unprovision_all_vfs(xe);
+ }
+
+ xe_sriov_dbg(xe, "mode %s changed to %s by %ps\n",
+ mode_to_string(xe->sriov.pf.provision.mode),
+ mode_to_string(mode), __builtin_return_address(0));
+ xe->sriov.pf.provision.mode = mode;
+ return 0;
+}
diff --git a/drivers/gpu/drm/xe/xe_sriov_pf_provision.h b/drivers/gpu/drm/xe/xe_sriov_pf_provision.h
new file mode 100644
index 000000000000..cf3657a32e90
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_sriov_pf_provision.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#ifndef _XE_SRIOV_PF_PROVISION_H_
+#define _XE_SRIOV_PF_PROVISION_H_
+
+#include "xe_sriov_pf_provision_types.h"
+
+struct xe_device;
+
+int xe_sriov_pf_provision_vfs(struct xe_device *xe, unsigned int num_vfs);
+int xe_sriov_pf_unprovision_vfs(struct xe_device *xe, unsigned int num_vfs);
+
+int xe_sriov_pf_provision_set_mode(struct xe_device *xe, enum xe_sriov_provisioning_mode mode);
+
+/**
+ * xe_sriov_pf_provision_set_custom_mode() - Change VFs provision mode to custom.
+ * @xe: the PF &xe_device
+ *
+ * This function can only be called on PF.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+static inline int xe_sriov_pf_provision_set_custom_mode(struct xe_device *xe)
+{
+ return xe_sriov_pf_provision_set_mode(xe, XE_SRIOV_PROVISIONING_MODE_CUSTOM);
+}
+
+#endif
diff --git a/drivers/gpu/drm/xe/xe_sriov_pf_provision_types.h b/drivers/gpu/drm/xe/xe_sriov_pf_provision_types.h
new file mode 100644
index 000000000000..a847b8a4c4da
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_sriov_pf_provision_types.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#ifndef _XE_SRIOV_PF_PROVISION_TYPES_H_
+#define _XE_SRIOV_PF_PROVISION_TYPES_H_
+
+#include <linux/build_bug.h>
+
+/**
+ * enum xe_sriov_provisioning_mode - SR-IOV provisioning mode.
+ *
+ * @XE_SRIOV_PROVISIONING_MODE_AUTO: VFs are provisioned during VFs enabling.
+ * Any allocated resources to the VFs will be
+ * automatically released when disabling VFs.
+ * This is a default mode.
+ * @XE_SRIOV_PROVISIONING_MODE_CUSTOM: Explicit VFs provisioning using uABI interfaces.
+ * VFs resources remains allocated regardless if
+ * VFs are enabled or not.
+ */
+enum xe_sriov_provisioning_mode {
+ XE_SRIOV_PROVISIONING_MODE_AUTO,
+ XE_SRIOV_PROVISIONING_MODE_CUSTOM,
+};
+static_assert(XE_SRIOV_PROVISIONING_MODE_AUTO == 0);
+
+/**
+ * struct xe_sriov_pf_provision - Data used by the PF provisioning.
+ */
+struct xe_sriov_pf_provision {
+ /** @mode: selected provisioning mode. */
+ enum xe_sriov_provisioning_mode mode;
+};
+
+#endif
diff --git a/drivers/gpu/drm/xe/xe_sriov_pf_types.h b/drivers/gpu/drm/xe/xe_sriov_pf_types.h
index 956a88f9f213..c753cd59aed2 100644
--- a/drivers/gpu/drm/xe/xe_sriov_pf_types.h
+++ b/drivers/gpu/drm/xe/xe_sriov_pf_types.h
@@ -9,6 +9,7 @@
#include <linux/mutex.h>
#include <linux/types.h>
+#include "xe_sriov_pf_provision_types.h"
#include "xe_sriov_pf_service_types.h"
/**
@@ -35,6 +36,9 @@ struct xe_device_pf {
/** @master_lock: protects all VFs configurations across GTs */
struct mutex master_lock;
+ /** @provision: device level provisioning data. */
+ struct xe_sriov_pf_provision provision;
+
/** @service: device level service data. */
struct xe_sriov_pf_service service;
diff --git a/drivers/gpu/drm/xe/xe_sriov_printk.h b/drivers/gpu/drm/xe/xe_sriov_printk.h
index 117e1d541692..4c6b5c3d2190 100644
--- a/drivers/gpu/drm/xe/xe_sriov_printk.h
+++ b/drivers/gpu/drm/xe/xe_sriov_printk.h
@@ -1,22 +1,22 @@
/* SPDX-License-Identifier: MIT */
/*
- * Copyright © 2023 Intel Corporation
+ * Copyright © 2023-2025 Intel Corporation
*/
#ifndef _XE_SRIOV_PRINTK_H_
#define _XE_SRIOV_PRINTK_H_
-#include <drm/drm_print.h>
-
-#include "xe_device_types.h"
-#include "xe_sriov_types.h"
+#include "xe_printk.h"
#define xe_sriov_printk_prefix(xe) \
((xe)->sriov.__mode == XE_SRIOV_MODE_PF ? "PF: " : \
(xe)->sriov.__mode == XE_SRIOV_MODE_VF ? "VF: " : "")
+#define __XE_SRIOV_PRINTK_FMT(_xe, _fmt, _args...) \
+ "%s" _fmt, xe_sriov_printk_prefix(_xe), ##_args
+
#define xe_sriov_printk(xe, _level, fmt, ...) \
- drm_##_level(&(xe)->drm, "%s" fmt, xe_sriov_printk_prefix(xe), ##__VA_ARGS__)
+ xe_##_level((xe), __XE_SRIOV_PRINTK_FMT((xe), fmt, ##__VA_ARGS__))
#define xe_sriov_err(xe, fmt, ...) \
xe_sriov_printk((xe), err, fmt, ##__VA_ARGS__)
diff --git a/drivers/gpu/drm/xe/xe_sriov_vf.c b/drivers/gpu/drm/xe/xe_sriov_vf.c
index cdd9f8e78b2a..911d5720917b 100644
--- a/drivers/gpu/drm/xe/xe_sriov_vf.c
+++ b/drivers/gpu/drm/xe/xe_sriov_vf.c
@@ -6,22 +6,12 @@
#include <drm/drm_debugfs.h>
#include <drm/drm_managed.h>
-#include "xe_assert.h"
-#include "xe_device.h"
#include "xe_gt.h"
-#include "xe_gt_sriov_printk.h"
#include "xe_gt_sriov_vf.h"
#include "xe_guc.h"
-#include "xe_guc_ct.h"
-#include "xe_guc_submit.h"
-#include "xe_irq.h"
-#include "xe_lrc.h"
-#include "xe_pm.h"
-#include "xe_sriov.h"
#include "xe_sriov_printk.h"
#include "xe_sriov_vf.h"
#include "xe_sriov_vf_ccs.h"
-#include "xe_tile_sriov_vf.h"
/**
* DOC: VF restore procedure in PF KMD and VF KMD
@@ -159,8 +149,6 @@ static void vf_disable_migration(struct xe_device *xe, const char *fmt, ...)
xe->sriov.vf.migration.enabled = false;
}
-static void migration_worker_func(struct work_struct *w);
-
static void vf_migration_init_early(struct xe_device *xe)
{
/*
@@ -185,8 +173,6 @@ static void vf_migration_init_early(struct xe_device *xe)
guc_version.major, guc_version.minor);
}
- INIT_WORK(&xe->sriov.vf.migration.worker, migration_worker_func);
-
xe->sriov.vf.migration.enabled = true;
xe_sriov_dbg(xe, "migration support enabled\n");
}
@@ -201,235 +187,6 @@ void xe_sriov_vf_init_early(struct xe_device *xe)
}
/**
- * vf_post_migration_shutdown - Stop the driver activities after VF migration.
- * @xe: the &xe_device struct instance
- *
- * After this VM is migrated and assigned to a new VF, it is running on a new
- * hardware, and therefore many hardware-dependent states and related structures
- * require fixups. Without fixups, the hardware cannot do any work, and therefore
- * all GPU pipelines are stalled.
- * Stop some of kernel activities to make the fixup process faster.
- */
-static void vf_post_migration_shutdown(struct xe_device *xe)
-{
- struct xe_gt *gt;
- unsigned int id;
- int ret = 0;
-
- for_each_gt(gt, xe, id) {
- xe_guc_submit_pause(&gt->uc.guc);
- ret |= xe_guc_submit_reset_block(&gt->uc.guc);
- }
-
- if (ret)
- drm_info(&xe->drm, "migration recovery encountered ongoing reset\n");
-}
-
-/**
- * vf_post_migration_kickstart - Re-start the driver activities under new hardware.
- * @xe: the &xe_device struct instance
- *
- * After we have finished with all post-migration fixups, restart the driver
- * activities to continue feeding the GPU with workloads.
- */
-static void vf_post_migration_kickstart(struct xe_device *xe)
-{
- struct xe_gt *gt;
- unsigned int id;
-
- /*
- * 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(xe);
-
- for_each_gt(gt, xe, id) {
- xe_guc_submit_reset_unblock(&gt->uc.guc);
- xe_guc_submit_unpause(&gt->uc.guc);
- }
-}
-
-static bool gt_vf_post_migration_needed(struct xe_gt *gt)
-{
- return test_bit(gt->info.id, &gt_to_xe(gt)->sriov.vf.migration.gt_flags);
-}
-
-/*
- * Notify GuCs marked in flags about resource fixups apply finished.
- * @xe: the &xe_device struct instance
- * @gt_flags: flags marking to which GTs the notification shall be sent
- */
-static int vf_post_migration_notify_resfix_done(struct xe_device *xe, unsigned long gt_flags)
-{
- struct xe_gt *gt;
- unsigned int id;
- int err = 0;
-
- for_each_gt(gt, xe, id) {
- if (!test_bit(id, &gt_flags))
- continue;
- /* skip asking GuC for RESFIX exit if new recovery request arrived */
- if (gt_vf_post_migration_needed(gt))
- continue;
- err = xe_gt_sriov_vf_notify_resfix_done(gt);
- if (err)
- break;
- clear_bit(id, &gt_flags);
- }
-
- if (gt_flags && !err)
- drm_dbg(&xe->drm, "another recovery imminent, skipped some notifications\n");
- return err;
-}
-
-static int vf_get_next_migrated_gt_id(struct xe_device *xe)
-{
- struct xe_gt *gt;
- unsigned int id;
-
- for_each_gt(gt, xe, id) {
- if (test_and_clear_bit(id, &xe->sriov.vf.migration.gt_flags))
- return id;
- }
- return -1;
-}
-
-static size_t post_migration_scratch_size(struct xe_device *xe)
-{
- return max(xe_lrc_reg_size(xe), LRC_WA_BB_SIZE);
-}
-
-/**
- * Perform post-migration fixups on a single GT.
- *
- * After migration, GuC needs to be re-queried for VF configuration to check
- * if it matches previous provisioning. Most of VF provisioning shall be the
- * same, except GGTT range, since GGTT is not virtualized per-VF. If GGTT
- * range has changed, we have to perform fixups - shift all GGTT references
- * used anywhere within the driver. After the fixups in this function succeed,
- * it is allowed to ask the GuC bound to this GT to continue normal operation.
- *
- * Returns: 0 if the operation completed successfully, or a negative error
- * code otherwise.
- */
-static int gt_vf_post_migration_fixups(struct xe_gt *gt)
-{
- s64 shift;
- void *buf;
- int err;
-
- buf = kmalloc(post_migration_scratch_size(gt_to_xe(gt)), GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- err = xe_gt_sriov_vf_query_config(gt);
- if (err)
- goto out;
-
- shift = xe_gt_sriov_vf_ggtt_shift(gt);
- if (shift) {
- xe_tile_sriov_vf_fixup_ggtt_nodes(gt_to_tile(gt), shift);
- xe_gt_sriov_vf_default_lrcs_hwsp_rebase(gt);
- err = xe_guc_contexts_hwsp_rebase(&gt->uc.guc, buf);
- if (err)
- goto out;
- xe_guc_jobs_ring_rebase(&gt->uc.guc);
- xe_guc_ct_fixup_messages_with_ggtt(&gt->uc.guc.ct, shift);
- }
-
-out:
- kfree(buf);
- return err;
-}
-
-static void vf_post_migration_recovery(struct xe_device *xe)
-{
- unsigned long fixed_gts = 0;
- int id, err;
-
- drm_dbg(&xe->drm, "migration recovery in progress\n");
- xe_pm_runtime_get(xe);
- vf_post_migration_shutdown(xe);
-
- if (!xe_sriov_vf_migration_supported(xe)) {
- xe_sriov_err(xe, "migration is not supported\n");
- err = -ENOTRECOVERABLE;
- goto fail;
- }
-
- while (id = vf_get_next_migrated_gt_id(xe), id >= 0) {
- struct xe_gt *gt = xe_device_get_gt(xe, id);
-
- err = gt_vf_post_migration_fixups(gt);
- if (err)
- goto fail;
-
- set_bit(id, &fixed_gts);
- }
-
- vf_post_migration_kickstart(xe);
- err = vf_post_migration_notify_resfix_done(xe, fixed_gts);
- if (err)
- goto fail;
-
- xe_pm_runtime_put(xe);
- drm_notice(&xe->drm, "migration recovery ended\n");
- return;
-fail:
- xe_pm_runtime_put(xe);
- drm_err(&xe->drm, "migration recovery failed (%pe)\n", ERR_PTR(err));
- xe_device_declare_wedged(xe);
-}
-
-static void migration_worker_func(struct work_struct *w)
-{
- struct xe_device *xe = container_of(w, struct xe_device,
- sriov.vf.migration.worker);
-
- vf_post_migration_recovery(xe);
-}
-
-/*
- * Check if post-restore recovery is coming on any of GTs.
- * @xe: the &xe_device struct instance
- *
- * Return: True if migration recovery worker will soon be running. Any worker currently
- * executing does not affect the result.
- */
-static bool vf_ready_to_recovery_on_any_gts(struct xe_device *xe)
-{
- struct xe_gt *gt;
- unsigned int id;
-
- for_each_gt(gt, xe, id) {
- if (test_bit(id, &xe->sriov.vf.migration.gt_flags))
- return true;
- }
- return false;
-}
-
-/**
- * xe_sriov_vf_start_migration_recovery - Start VF migration recovery.
- * @xe: the &xe_device to start recovery on
- *
- * This function shall be called only by VF.
- */
-void xe_sriov_vf_start_migration_recovery(struct xe_device *xe)
-{
- bool started;
-
- xe_assert(xe, IS_SRIOV_VF(xe));
-
- if (!vf_ready_to_recovery_on_any_gts(xe))
- return;
-
- started = queue_work(xe->sriov.wq, &xe->sriov.vf.migration.worker);
- drm_info(&xe->drm, "VF migration recovery %s\n", started ?
- "scheduled" : "already in progress");
-}
-
-/**
* xe_sriov_vf_init_late() - SR-IOV VF late initialization functions.
* @xe: the &xe_device to initialize
*
diff --git a/drivers/gpu/drm/xe/xe_sriov_vf.h b/drivers/gpu/drm/xe/xe_sriov_vf.h
index 9e752105ec2a..4df95266b261 100644
--- a/drivers/gpu/drm/xe/xe_sriov_vf.h
+++ b/drivers/gpu/drm/xe/xe_sriov_vf.h
@@ -13,7 +13,6 @@ struct xe_device;
void xe_sriov_vf_init_early(struct xe_device *xe);
int xe_sriov_vf_init_late(struct xe_device *xe);
-void xe_sriov_vf_start_migration_recovery(struct xe_device *xe);
bool xe_sriov_vf_migration_supported(struct xe_device *xe);
void xe_sriov_vf_debugfs_register(struct xe_device *xe, struct dentry *root);
diff --git a/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c b/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c
index 8dec616c37c9..790249801364 100644
--- a/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c
+++ b/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c
@@ -175,6 +175,15 @@ static void ccs_rw_update_ring(struct xe_sriov_vf_ccs_ctx *ctx)
struct xe_lrc *lrc = xe_exec_queue_lrc(ctx->mig_q);
u32 dw[10], i = 0;
+ /*
+ * XXX: Save/restore fixes — for some reason, the GuC only accepts the
+ * save/restore context if the LRC head pointer is zero. This is evident
+ * from repeated VF migrations failing when the LRC head pointer is
+ * non-zero.
+ */
+ lrc->ring.tail = 0;
+ xe_lrc_set_ring_head(lrc, 0);
+
dw[i++] = MI_ARB_ON_OFF | MI_ARB_ENABLE;
dw[i++] = MI_BATCH_BUFFER_START | XE_INSTR_NUM_DW(3);
dw[i++] = lower_32_bits(addr);
@@ -186,6 +195,25 @@ static void ccs_rw_update_ring(struct xe_sriov_vf_ccs_ctx *ctx)
xe_lrc_set_ring_tail(lrc, lrc->ring.tail);
}
+/**
+ * xe_sriov_vf_ccs_rebase - Rebase GGTT addresses for CCS save / restore
+ * @xe: the &xe_device.
+ */
+void xe_sriov_vf_ccs_rebase(struct xe_device *xe)
+{
+ enum xe_sriov_vf_ccs_rw_ctxs ctx_id;
+
+ if (!IS_VF_CCS_READY(xe))
+ return;
+
+ for_each_ccs_rw_ctx(ctx_id) {
+ struct xe_sriov_vf_ccs_ctx *ctx =
+ &xe->sriov.vf.ccs.contexts[ctx_id];
+
+ ccs_rw_update_ring(ctx);
+ }
+}
+
static int register_save_restore_context(struct xe_sriov_vf_ccs_ctx *ctx)
{
int ctx_type;
diff --git a/drivers/gpu/drm/xe/xe_sriov_vf_ccs.h b/drivers/gpu/drm/xe/xe_sriov_vf_ccs.h
index 0745c0ff0228..f8ca6efce9ee 100644
--- a/drivers/gpu/drm/xe/xe_sriov_vf_ccs.h
+++ b/drivers/gpu/drm/xe/xe_sriov_vf_ccs.h
@@ -18,6 +18,7 @@ int xe_sriov_vf_ccs_init(struct xe_device *xe);
int xe_sriov_vf_ccs_attach_bo(struct xe_bo *bo);
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);
static inline bool xe_sriov_vf_ccs_ready(struct xe_device *xe)
diff --git a/drivers/gpu/drm/xe/xe_sriov_vf_types.h b/drivers/gpu/drm/xe/xe_sriov_vf_types.h
index 426cc5841958..6a0fd0f5463e 100644
--- a/drivers/gpu/drm/xe/xe_sriov_vf_types.h
+++ b/drivers/gpu/drm/xe/xe_sriov_vf_types.h
@@ -33,10 +33,6 @@ struct xe_device_vf {
/** @migration: VF Migration state data */
struct {
- /** @migration.worker: VF migration recovery worker */
- struct work_struct worker;
- /** @migration.gt_flags: Per-GT request flags for VF migration recovery */
- unsigned long gt_flags;
/**
* @migration.enabled: flag indicating if migration support
* was enabled or not due to missing prerequisites
diff --git a/drivers/gpu/drm/xe/xe_svm.c b/drivers/gpu/drm/xe/xe_svm.c
index da2a412f80c0..129e7818565c 100644
--- a/drivers/gpu/drm/xe/xe_svm.c
+++ b/drivers/gpu/drm/xe/xe_svm.c
@@ -302,6 +302,11 @@ static int xe_svm_range_set_default_attr(struct xe_vm *vm, u64 range_start, u64
if (!vma)
return -EINVAL;
+ if (!(vma->gpuva.flags & XE_VMA_MADV_AUTORESET)) {
+ drm_dbg(&vm->xe->drm, "Skipping madvise reset for vma.\n");
+ return 0;
+ }
+
if (xe_vma_has_default_mem_attrs(vma))
return 0;
diff --git a/drivers/gpu/drm/xe/xe_tile.c b/drivers/gpu/drm/xe/xe_tile.c
index d49ba3401963..4f4f9a5c43af 100644
--- a/drivers/gpu/drm/xe/xe_tile.c
+++ b/drivers/gpu/drm/xe/xe_tile.c
@@ -19,9 +19,9 @@
#include "xe_tile.h"
#include "xe_tile_sysfs.h"
#include "xe_ttm_vram_mgr.h"
-#include "xe_wa.h"
#include "xe_vram.h"
#include "xe_vram_types.h"
+#include "xe_wa.h"
/**
* DOC: Multi-tile Design
@@ -124,6 +124,14 @@ int xe_tile_alloc_vram(struct xe_tile *tile)
return -ENOMEM;
tile->mem.vram = vram;
+ /*
+ * If the kernel_vram is not already allocated,
+ * it means that tile has common VRAM region for
+ * kernel and user space.
+ */
+ if (!tile->mem.kernel_vram)
+ tile->mem.kernel_vram = tile->mem.vram;
+
return 0;
}
@@ -149,10 +157,6 @@ int xe_tile_init_early(struct xe_tile *tile, struct xe_device *xe, u8 id)
if (err)
return err;
- tile->primary_gt = xe_gt_alloc(tile);
- if (IS_ERR(tile->primary_gt))
- return PTR_ERR(tile->primary_gt);
-
xe_pcode_init(tile);
return 0;
diff --git a/drivers/gpu/drm/xe/xe_tile_debugfs.c b/drivers/gpu/drm/xe/xe_tile_debugfs.c
index 5523874cba7b..fff242a5ae56 100644
--- a/drivers/gpu/drm/xe/xe_tile_debugfs.c
+++ b/drivers/gpu/drm/xe/xe_tile_debugfs.c
@@ -6,6 +6,7 @@
#include <linux/debugfs.h>
#include <drm/drm_debugfs.h>
+#include "xe_ggtt.h"
#include "xe_pm.h"
#include "xe_sa.h"
#include "xe_tile_debugfs.h"
@@ -16,7 +17,7 @@ static struct xe_tile *node_to_tile(struct drm_info_node *node)
}
/**
- * tile_debugfs_simple_show - A show callback for struct drm_info_list
+ * xe_tile_debugfs_simple_show() - A show callback for struct drm_info_list
* @m: the &seq_file
* @data: data used by the drm debugfs helpers
*
@@ -57,7 +58,7 @@ static struct xe_tile *node_to_tile(struct drm_info_node *node)
*
* Return: 0 on success or a negative error code on failure.
*/
-static int tile_debugfs_simple_show(struct seq_file *m, void *data)
+int xe_tile_debugfs_simple_show(struct seq_file *m, void *data)
{
struct drm_printer p = drm_seq_file_printer(m);
struct drm_info_node *node = m->private;
@@ -68,7 +69,7 @@ static int tile_debugfs_simple_show(struct seq_file *m, void *data)
}
/**
- * tile_debugfs_show_with_rpm - A show callback for struct drm_info_list
+ * xe_tile_debugfs_show_with_rpm() - A show callback for struct drm_info_list
* @m: the &seq_file
* @data: data used by the drm debugfs helpers
*
@@ -76,7 +77,7 @@ static int tile_debugfs_simple_show(struct seq_file *m, void *data)
*
* Return: 0 on success or a negative error code on failure.
*/
-static int tile_debugfs_show_with_rpm(struct seq_file *m, void *data)
+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);
@@ -84,12 +85,17 @@ static int tile_debugfs_show_with_rpm(struct seq_file *m, void *data)
int ret;
xe_pm_runtime_get(xe);
- ret = tile_debugfs_simple_show(m, data);
+ ret = xe_tile_debugfs_simple_show(m, data);
xe_pm_runtime_put(xe);
return ret;
}
+static int ggtt(struct xe_tile *tile, struct drm_printer *p)
+{
+ return xe_ggtt_dump(tile->mem.ggtt, p);
+}
+
static int sa_info(struct xe_tile *tile, struct drm_printer *p)
{
drm_suballoc_dump_debug_info(&tile->mem.kernel_bb_pool->base, p,
@@ -100,7 +106,8 @@ static int sa_info(struct xe_tile *tile, struct drm_printer *p)
/* only for debugfs files which can be safely used on the VF */
static const struct drm_info_list vf_safe_debugfs_list[] = {
- { "sa_info", .show = tile_debugfs_show_with_rpm, .data = sa_info },
+ { "ggtt", .show = xe_tile_debugfs_show_with_rpm, .data = ggtt },
+ { "sa_info", .show = xe_tile_debugfs_show_with_rpm, .data = sa_info },
};
/**
diff --git a/drivers/gpu/drm/xe/xe_tile_debugfs.h b/drivers/gpu/drm/xe/xe_tile_debugfs.h
index 0e5f724de37f..4429c22542f4 100644
--- a/drivers/gpu/drm/xe/xe_tile_debugfs.h
+++ b/drivers/gpu/drm/xe/xe_tile_debugfs.h
@@ -6,8 +6,11 @@
#ifndef _XE_TILE_DEBUGFS_H_
#define _XE_TILE_DEBUGFS_H_
+struct seq_file;
struct xe_tile;
void xe_tile_debugfs_register(struct xe_tile *tile);
+int xe_tile_debugfs_simple_show(struct seq_file *m, void *data);
+int xe_tile_debugfs_show_with_rpm(struct seq_file *m, void *data);
#endif
diff --git a/drivers/gpu/drm/xe/xe_tile_sriov_pf_debugfs.c b/drivers/gpu/drm/xe/xe_tile_sriov_pf_debugfs.c
new file mode 100644
index 000000000000..f3f478f14ff5
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_tile_sriov_pf_debugfs.c
@@ -0,0 +1,253 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#include <linux/debugfs.h>
+#include <drm/drm_debugfs.h>
+
+#include "xe_device.h"
+#include "xe_device_types.h"
+#include "xe_gt_sriov_pf_config.h"
+#include "xe_gt_sriov_pf_debugfs.h"
+#include "xe_pm.h"
+#include "xe_tile_debugfs.h"
+#include "xe_tile_sriov_pf_debugfs.h"
+#include "xe_sriov.h"
+#include "xe_sriov_pf.h"
+#include "xe_sriov_pf_provision.h"
+
+/*
+ * /sys/kernel/debug/dri/BDF/
+ * ├── sriov # d_inode->i_private = (xe_device*)
+ * │ ├── pf # d_inode->i_private = (xe_device*)
+ * │ │ ├── tile0 # d_inode->i_private = (xe_tile*)
+ * │ │ ├── tile1
+ * │ │ : :
+ * │ ├── vf1 # d_inode->i_private = VFID(1)
+ * │ │ ├── tile0 # d_inode->i_private = (xe_tile*)
+ * │ │ ├── tile1
+ * │ │ : :
+ * │ ├── vfN # d_inode->i_private = VFID(N)
+ * │ │ ├── tile0 # d_inode->i_private = (xe_tile*)
+ * │ │ ├── tile1
+ * : : : :
+ */
+
+static void *extract_priv(struct dentry *d)
+{
+ return d->d_inode->i_private;
+}
+
+__maybe_unused
+static struct xe_tile *extract_tile(struct dentry *d)
+{
+ return extract_priv(d);
+}
+
+static struct xe_device *extract_xe(struct dentry *d)
+{
+ return extract_priv(d->d_parent->d_parent);
+}
+
+__maybe_unused
+static unsigned int extract_vfid(struct dentry *d)
+{
+ void *pp = extract_priv(d->d_parent);
+
+ return pp == extract_xe(d) ? PFID : (uintptr_t)pp;
+}
+
+/*
+ * /sys/kernel/debug/dri/BDF/
+ * ├── sriov
+ * : ├── pf
+ * : ├── tile0
+ * : ├── ggtt_available
+ * ├── ggtt_provisioned
+ */
+
+static int pf_config_print_available_ggtt(struct xe_tile *tile, struct drm_printer *p)
+{
+ return xe_gt_sriov_pf_config_print_available_ggtt(tile->primary_gt, p);
+}
+
+static int pf_config_print_ggtt(struct xe_tile *tile, struct drm_printer *p)
+{
+ return xe_gt_sriov_pf_config_print_ggtt(tile->primary_gt, p);
+}
+
+static const struct drm_info_list pf_ggtt_info[] = {
+ {
+ "ggtt_available",
+ .show = xe_tile_debugfs_simple_show,
+ .data = pf_config_print_available_ggtt,
+ },
+ {
+ "ggtt_provisioned",
+ .show = xe_tile_debugfs_simple_show,
+ .data = pf_config_print_ggtt,
+ },
+};
+
+/*
+ * /sys/kernel/debug/dri/BDF/
+ * ├── sriov
+ * : ├── pf
+ * : ├── tile0
+ * : ├── vram_provisioned
+ */
+
+static int pf_config_print_vram(struct xe_tile *tile, struct drm_printer *p)
+{
+ return xe_gt_sriov_pf_config_print_lmem(tile->primary_gt, p);
+}
+
+static const struct drm_info_list pf_vram_info[] = {
+ {
+ "vram_provisioned",
+ .show = xe_tile_debugfs_simple_show,
+ .data = pf_config_print_vram,
+ },
+};
+
+/*
+ * /sys/kernel/debug/dri/BDF/
+ * ├── sriov
+ * │ ├── pf
+ * │ │ ├── tile0
+ * │ │ │ ├── ggtt_spare
+ * │ │ │ ├── vram_spare
+ * │ │ ├── tile1
+ * │ │ : :
+ * │ ├── vf1
+ * │ : ├── tile0
+ * │ │ ├── ggtt_quota
+ * │ │ ├── vram_quota
+ * │ ├── tile1
+ * │ : :
+ */
+
+#define DEFINE_SRIOV_TILE_CONFIG_DEBUGFS_ATTRIBUTE(NAME, CONFIG, TYPE, FORMAT) \
+ \
+static int NAME##_set(void *data, u64 val) \
+{ \
+ struct xe_tile *tile = extract_tile(data); \
+ unsigned int vfid = extract_vfid(data); \
+ struct xe_gt *gt = tile->primary_gt; \
+ struct xe_device *xe = tile->xe; \
+ int err; \
+ \
+ if (val > (TYPE)~0ull) \
+ return -EOVERFLOW; \
+ \
+ xe_pm_runtime_get(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; \
+} \
+ \
+static int NAME##_get(void *data, u64 *val) \
+{ \
+ struct xe_tile *tile = extract_tile(data); \
+ unsigned int vfid = extract_vfid(data); \
+ struct xe_gt *gt = tile->primary_gt; \
+ \
+ *val = xe_gt_sriov_pf_config_get_##CONFIG(gt, vfid); \
+ return 0; \
+} \
+ \
+DEFINE_DEBUGFS_ATTRIBUTE(NAME##_fops, NAME##_get, NAME##_set, FORMAT)
+
+DEFINE_SRIOV_TILE_CONFIG_DEBUGFS_ATTRIBUTE(ggtt, ggtt, u64, "%llu\n");
+DEFINE_SRIOV_TILE_CONFIG_DEBUGFS_ATTRIBUTE(vram, lmem, u64, "%llu\n");
+
+static void pf_add_config_attrs(struct xe_tile *tile, struct dentry *dent, unsigned int vfid)
+{
+ struct xe_device *xe = tile->xe;
+
+ xe_tile_assert(tile, tile == extract_tile(dent));
+ xe_tile_assert(tile, vfid == extract_vfid(dent));
+
+ debugfs_create_file_unsafe(vfid ? "ggtt_quota" : "ggtt_spare",
+ 0644, dent, dent, &ggtt_fops);
+ if (IS_DGFX(xe))
+ debugfs_create_file_unsafe(vfid ? "vram_quota" : "vram_spare",
+ xe_device_has_lmtt(xe) ? 0644 : 0444,
+ dent, dent, &vram_fops);
+}
+
+static void pf_populate_tile(struct xe_tile *tile, struct dentry *dent, unsigned int vfid)
+{
+ struct xe_device *xe = tile->xe;
+ struct drm_minor *minor = xe->drm.primary;
+ struct xe_gt *gt;
+ unsigned int id;
+
+ pf_add_config_attrs(tile, dent, vfid);
+
+ if (!vfid) {
+ drm_debugfs_create_files(pf_ggtt_info,
+ ARRAY_SIZE(pf_ggtt_info),
+ dent, minor);
+ if (IS_DGFX(xe))
+ drm_debugfs_create_files(pf_vram_info,
+ ARRAY_SIZE(pf_vram_info),
+ dent, minor);
+ }
+
+ for_each_gt_on_tile(gt, tile, id)
+ xe_gt_sriov_pf_debugfs_populate(gt, dent, vfid);
+}
+
+/**
+ * xe_tile_sriov_pf_debugfs_populate() - Populate SR-IOV debugfs tree with tile files.
+ * @tile: the &xe_tile to register
+ * @parent: the parent &dentry that represents the SR-IOV @vfid function
+ * @vfid: the VF identifier
+ *
+ * Add to the @parent directory new debugfs directory that will represent a @tile and
+ * populate it with files that are related to the SR-IOV @vfid function.
+ *
+ * This function can only be called on PF.
+ */
+void xe_tile_sriov_pf_debugfs_populate(struct xe_tile *tile, struct dentry *parent,
+ unsigned int vfid)
+{
+ struct xe_device *xe = tile->xe;
+ struct dentry *dent;
+ char name[10]; /* should be enough up to "tile%u\0" for 2^16 - 1 */
+
+ xe_tile_assert(tile, IS_SRIOV_PF(xe));
+ xe_tile_assert(tile, extract_priv(parent->d_parent) == xe);
+ xe_tile_assert(tile, extract_priv(parent) == tile->xe ||
+ (uintptr_t)extract_priv(parent) == vfid);
+
+ /*
+ * /sys/kernel/debug/dri/BDF/
+ * ├── sriov
+ * │ ├── pf # parent, d_inode->i_private = (xe_device*)
+ * │ │ ├── tile0 # d_inode->i_private = (xe_tile*)
+ * │ │ ├── tile1
+ * │ │ : :
+ * │ ├── vf1 # parent, d_inode->i_private = VFID(1)
+ * │ │ ├── tile0 # d_inode->i_private = (xe_tile*)
+ * │ │ ├── tile1
+ * : : : :
+ */
+ snprintf(name, sizeof(name), "tile%u", tile->id);
+ dent = debugfs_create_dir(name, parent);
+ if (IS_ERR(dent))
+ return;
+ dent->d_inode->i_private = tile;
+
+ xe_tile_assert(tile, extract_tile(dent) == tile);
+ xe_tile_assert(tile, extract_vfid(dent) == vfid);
+ xe_tile_assert(tile, extract_xe(dent) == xe);
+
+ pf_populate_tile(tile, dent, vfid);
+}
diff --git a/drivers/gpu/drm/xe/xe_tile_sriov_pf_debugfs.h b/drivers/gpu/drm/xe/xe_tile_sriov_pf_debugfs.h
new file mode 100644
index 000000000000..55d179c44634
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_tile_sriov_pf_debugfs.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#ifndef _XE_TILE_SRIOV_PF_DEBUGFS_H_
+#define _XE_TILE_SRIOV_PF_DEBUGFS_H_
+
+struct dentry;
+struct xe_tile;
+
+void xe_tile_sriov_pf_debugfs_populate(struct xe_tile *tile, struct dentry *parent,
+ unsigned int vfid);
+
+#endif
diff --git a/drivers/gpu/drm/xe/xe_tile_sriov_printk.h b/drivers/gpu/drm/xe/xe_tile_sriov_printk.h
new file mode 100644
index 000000000000..68323512872c
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_tile_sriov_printk.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#ifndef _XE_TILE_SRIOV_PRINTK_H_
+#define _XE_TILE_SRIOV_PRINTK_H_
+
+#include "xe_tile_printk.h"
+#include "xe_sriov_printk.h"
+
+#define __XE_TILE_SRIOV_PRINTK_FMT(_tile, _fmt, ...) \
+ __XE_TILE_PRINTK_FMT((_tile), _fmt, ##__VA_ARGS__)
+
+#define xe_tile_sriov_printk(_tile, _level, _fmt, ...) \
+ xe_sriov_##_level((_tile)->xe, __XE_TILE_SRIOV_PRINTK_FMT((_tile), _fmt, ##__VA_ARGS__))
+
+#define xe_tile_sriov_err(_tile, _fmt, ...) \
+ xe_tile_sriov_printk(_tile, err, _fmt, ##__VA_ARGS__)
+
+#define xe_tile_sriov_notice(_tile, _fmt, ...) \
+ xe_tile_sriov_printk(_tile, notice, _fmt, ##__VA_ARGS__)
+
+#define xe_tile_sriov_info(_tile, _fmt, ...) \
+ xe_tile_sriov_printk(_tile, info, _fmt, ##__VA_ARGS__)
+
+#define xe_tile_sriov_dbg(_tile, _fmt, ...) \
+ xe_tile_sriov_printk(_tile, dbg, _fmt, ##__VA_ARGS__)
+
+#define xe_tile_sriov_dbg_verbose(_tile, _fmt, ...) \
+ xe_tile_sriov_printk(_tile, dbg_verbose, _fmt, ##__VA_ARGS__)
+
+#endif
diff --git a/drivers/gpu/drm/xe/xe_tile_sriov_vf.c b/drivers/gpu/drm/xe/xe_tile_sriov_vf.c
index f221dbed16f0..c9bac2cfdd04 100644
--- a/drivers/gpu/drm/xe/xe_tile_sriov_vf.c
+++ b/drivers/gpu/drm/xe/xe_tile_sriov_vf.c
@@ -9,7 +9,6 @@
#include "xe_assert.h"
#include "xe_ggtt.h"
-#include "xe_gt_sriov_vf.h"
#include "xe_sriov.h"
#include "xe_sriov_printk.h"
#include "xe_tile_sriov_vf.h"
@@ -40,10 +39,10 @@ static int vf_init_ggtt_balloons(struct xe_tile *tile)
*
* Return: 0 on success or a negative error code on failure.
*/
-int xe_tile_sriov_vf_balloon_ggtt_locked(struct xe_tile *tile)
+static int xe_tile_sriov_vf_balloon_ggtt_locked(struct xe_tile *tile)
{
- u64 ggtt_base = xe_gt_sriov_vf_ggtt_base(tile->primary_gt);
- u64 ggtt_size = xe_gt_sriov_vf_ggtt(tile->primary_gt);
+ u64 ggtt_base = tile->sriov.vf.self_config.ggtt_base;
+ u64 ggtt_size = tile->sriov.vf.self_config.ggtt_size;
struct xe_device *xe = tile_to_xe(tile);
u64 wopcm = xe_wopcm_size(xe);
u64 start, end;
@@ -232,7 +231,7 @@ int xe_tile_sriov_vf_prepare_ggtt(struct xe_tile *tile)
*/
/**
- * xe_tile_sriov_vf_fixup_ggtt_nodes - Shift GGTT allocations to match assigned range.
+ * xe_tile_sriov_vf_fixup_ggtt_nodes_locked - Shift GGTT allocations to match assigned range.
* @tile: the &xe_tile struct instance
* @shift: the shift value
*
@@ -240,15 +239,112 @@ int xe_tile_sriov_vf_prepare_ggtt(struct xe_tile *tile)
* within the global space. This range might have changed during migration,
* which requires all memory addresses pointing to GGTT to be shifted.
*/
-void xe_tile_sriov_vf_fixup_ggtt_nodes(struct xe_tile *tile, s64 shift)
+void xe_tile_sriov_vf_fixup_ggtt_nodes_locked(struct xe_tile *tile, s64 shift)
{
struct xe_ggtt *ggtt = tile->mem.ggtt;
- mutex_lock(&ggtt->lock);
+ lockdep_assert_held(&ggtt->lock);
xe_tile_sriov_vf_deballoon_ggtt_locked(tile);
xe_ggtt_shift_nodes_locked(ggtt, shift);
xe_tile_sriov_vf_balloon_ggtt_locked(tile);
+}
- mutex_unlock(&ggtt->lock);
+/**
+ * xe_tile_sriov_vf_lmem - VF LMEM configuration.
+ * @tile: the &xe_tile
+ *
+ * This function is for VF use only.
+ *
+ * Return: size of the LMEM assigned to VF.
+ */
+u64 xe_tile_sriov_vf_lmem(struct xe_tile *tile)
+{
+ struct xe_tile_sriov_vf_selfconfig *config = &tile->sriov.vf.self_config;
+
+ xe_tile_assert(tile, IS_SRIOV_VF(tile_to_xe(tile)));
+
+ return config->lmem_size;
+}
+
+/**
+ * xe_tile_sriov_vf_lmem_store - Store VF LMEM configuration
+ * @tile: the &xe_tile
+ * @lmem_size: VF LMEM size to store
+ *
+ * This function is for VF use only.
+ */
+void xe_tile_sriov_vf_lmem_store(struct xe_tile *tile, u64 lmem_size)
+{
+ struct xe_tile_sriov_vf_selfconfig *config = &tile->sriov.vf.self_config;
+
+ xe_tile_assert(tile, IS_SRIOV_VF(tile_to_xe(tile)));
+
+ config->lmem_size = lmem_size;
+}
+
+/**
+ * xe_tile_sriov_vf_ggtt - VF GGTT configuration.
+ * @tile: the &xe_tile
+ *
+ * This function is for VF use only.
+ *
+ * Return: size of the GGTT assigned to VF.
+ */
+u64 xe_tile_sriov_vf_ggtt(struct xe_tile *tile)
+{
+ struct xe_tile_sriov_vf_selfconfig *config = &tile->sriov.vf.self_config;
+
+ xe_tile_assert(tile, IS_SRIOV_VF(tile_to_xe(tile)));
+
+ return config->ggtt_size;
+}
+
+/**
+ * xe_tile_sriov_vf_ggtt_store - Store VF GGTT configuration
+ * @tile: the &xe_tile
+ * @ggtt_size: VF GGTT size to store
+ *
+ * This function is for VF use only.
+ */
+void xe_tile_sriov_vf_ggtt_store(struct xe_tile *tile, u64 ggtt_size)
+{
+ struct xe_tile_sriov_vf_selfconfig *config = &tile->sriov.vf.self_config;
+
+ xe_tile_assert(tile, IS_SRIOV_VF(tile_to_xe(tile)));
+
+ config->ggtt_size = ggtt_size;
+}
+
+/**
+ * xe_tile_sriov_vf_ggtt_base - VF GGTT base configuration.
+ * @tile: the &xe_tile
+ *
+ * This function is for VF use only.
+ *
+ * Return: base of the GGTT assigned to VF.
+ */
+u64 xe_tile_sriov_vf_ggtt_base(struct xe_tile *tile)
+{
+ struct xe_tile_sriov_vf_selfconfig *config = &tile->sriov.vf.self_config;
+
+ xe_tile_assert(tile, IS_SRIOV_VF(tile_to_xe(tile)));
+
+ return config->ggtt_base;
+}
+
+/**
+ * xe_tile_sriov_vf_ggtt_base_store - Store VF GGTT base configuration
+ * @tile: the &xe_tile
+ * @ggtt_base: VF GGTT base to store
+ *
+ * This function is for VF use only.
+ */
+void xe_tile_sriov_vf_ggtt_base_store(struct xe_tile *tile, u64 ggtt_base)
+{
+ struct xe_tile_sriov_vf_selfconfig *config = &tile->sriov.vf.self_config;
+
+ xe_tile_assert(tile, IS_SRIOV_VF(tile_to_xe(tile)));
+
+ config->ggtt_base = ggtt_base;
}
diff --git a/drivers/gpu/drm/xe/xe_tile_sriov_vf.h b/drivers/gpu/drm/xe/xe_tile_sriov_vf.h
index 93eb043171e8..749f41504883 100644
--- a/drivers/gpu/drm/xe/xe_tile_sriov_vf.h
+++ b/drivers/gpu/drm/xe/xe_tile_sriov_vf.h
@@ -11,8 +11,13 @@
struct xe_tile;
int xe_tile_sriov_vf_prepare_ggtt(struct xe_tile *tile);
-int xe_tile_sriov_vf_balloon_ggtt_locked(struct xe_tile *tile);
void xe_tile_sriov_vf_deballoon_ggtt_locked(struct xe_tile *tile);
-void xe_tile_sriov_vf_fixup_ggtt_nodes(struct xe_tile *tile, s64 shift);
+void xe_tile_sriov_vf_fixup_ggtt_nodes_locked(struct xe_tile *tile, s64 shift);
+u64 xe_tile_sriov_vf_ggtt(struct xe_tile *tile);
+void xe_tile_sriov_vf_ggtt_store(struct xe_tile *tile, u64 ggtt_size);
+u64 xe_tile_sriov_vf_ggtt_base(struct xe_tile *tile);
+void xe_tile_sriov_vf_ggtt_base_store(struct xe_tile *tile, u64 ggtt_size);
+u64 xe_tile_sriov_vf_lmem(struct xe_tile *tile);
+void xe_tile_sriov_vf_lmem_store(struct xe_tile *tile, u64 lmem_size);
#endif
diff --git a/drivers/gpu/drm/xe/xe_tile_sriov_vf_types.h b/drivers/gpu/drm/xe/xe_tile_sriov_vf_types.h
new file mode 100644
index 000000000000..4807ca51614c
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_tile_sriov_vf_types.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#ifndef _XE_TILE_SRIOV_VF_TYPES_H_
+#define _XE_TILE_SRIOV_VF_TYPES_H_
+
+#include <linux/types.h>
+
+/**
+ * struct xe_tile_sriov_vf_selfconfig - VF configuration data.
+ */
+struct xe_tile_sriov_vf_selfconfig {
+ /** @ggtt_base: assigned base offset of the GGTT region. */
+ u64 ggtt_base;
+ /** @ggtt_size: assigned size of the GGTT region. */
+ u64 ggtt_size;
+ /** @lmem_size: assigned size of the LMEM. */
+ u64 lmem_size;
+};
+
+#endif
diff --git a/drivers/gpu/drm/xe/xe_ttm_stolen_mgr.c b/drivers/gpu/drm/xe/xe_ttm_stolen_mgr.c
index dc588255674d..e368b2a36bac 100644
--- a/drivers/gpu/drm/xe/xe_ttm_stolen_mgr.c
+++ b/drivers/gpu/drm/xe/xe_ttm_stolen_mgr.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
/*
* Copyright © 2021-2023 Intel Corporation
- * Copyright (C) 2021-2002 Red Hat
+ * Copyright (C) 2021-2022 Red Hat
*/
#include <drm/drm_managed.h>
@@ -24,8 +24,8 @@
#include "xe_sriov.h"
#include "xe_ttm_stolen_mgr.h"
#include "xe_ttm_vram_mgr.h"
-#include "xe_wa.h"
#include "xe_vram.h"
+#include "xe_wa.h"
struct xe_ttm_stolen_mgr {
struct xe_ttm_vram_mgr base;
@@ -81,7 +81,7 @@ static u32 get_wopcm_size(struct xe_device *xe)
return wopcm_size;
}
-static s64 detect_bar2_dgfx(struct xe_device *xe, struct xe_ttm_stolen_mgr *mgr)
+static u64 detect_bar2_dgfx(struct xe_device *xe, struct xe_ttm_stolen_mgr *mgr)
{
struct xe_vram_region *tile_vram = xe_device_get_root_tile(xe)->mem.vram;
resource_size_t tile_io_start = xe_vram_region_io_start(tile_vram);
@@ -105,6 +105,8 @@ static s64 detect_bar2_dgfx(struct xe_device *xe, struct xe_ttm_stolen_mgr *mgr)
return 0;
stolen_size = tile_size - mgr->stolen_base;
+
+ xe_assert(xe, stolen_size > wopcm_size);
stolen_size -= wopcm_size;
/* Verify usage fits in the actual resource available */
diff --git a/drivers/gpu/drm/xe/xe_ttm_sys_mgr.c b/drivers/gpu/drm/xe/xe_ttm_sys_mgr.c
index d38b91872da3..3e404eb8d098 100644
--- a/drivers/gpu/drm/xe/xe_ttm_sys_mgr.c
+++ b/drivers/gpu/drm/xe/xe_ttm_sys_mgr.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
/*
* Copyright © 2021-2022 Intel Corporation
- * Copyright (C) 2021-2002 Red Hat
+ * Copyright (C) 2021-2022 Red Hat
*/
#include "xe_ttm_sys_mgr.h"
@@ -85,7 +85,7 @@ static const struct ttm_resource_manager_func xe_ttm_sys_mgr_func = {
.debug = xe_ttm_sys_mgr_debug
};
-static void ttm_sys_mgr_fini(struct drm_device *drm, void *arg)
+static void xe_ttm_sys_mgr_fini(struct drm_device *drm, void *arg)
{
struct xe_device *xe = (struct xe_device *)arg;
struct ttm_resource_manager *man = &xe->mem.sys_mgr;
@@ -116,5 +116,5 @@ int xe_ttm_sys_mgr_init(struct xe_device *xe)
ttm_resource_manager_init(man, &xe->ttm, gtt_size >> PAGE_SHIFT);
ttm_set_driver_manager(&xe->ttm, XE_PL_TT, man);
ttm_resource_manager_set_used(man, true);
- return drmm_add_action_or_reset(&xe->drm, ttm_sys_mgr_fini, xe);
+ return drmm_add_action_or_reset(&xe->drm, xe_ttm_sys_mgr_fini, xe);
}
diff --git a/drivers/gpu/drm/xe/xe_ttm_vram_mgr.c b/drivers/gpu/drm/xe/xe_ttm_vram_mgr.c
index 9175b4a2214b..9f70802fce92 100644
--- a/drivers/gpu/drm/xe/xe_ttm_vram_mgr.c
+++ b/drivers/gpu/drm/xe/xe_ttm_vram_mgr.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
/*
* Copyright © 2021-2022 Intel Corporation
- * Copyright (C) 2021-2002 Red Hat
+ * Copyright (C) 2021-2022 Red Hat
*/
#include <drm/drm_managed.h>
@@ -284,7 +284,7 @@ static const struct ttm_resource_manager_func xe_ttm_vram_mgr_func = {
.debug = xe_ttm_vram_mgr_debug
};
-static void ttm_vram_mgr_fini(struct drm_device *dev, void *arg)
+static void xe_ttm_vram_mgr_fini(struct drm_device *dev, void *arg)
{
struct xe_device *xe = to_xe_device(dev);
struct xe_ttm_vram_mgr *mgr = arg;
@@ -335,7 +335,7 @@ int __xe_ttm_vram_mgr_init(struct xe_device *xe, struct xe_ttm_vram_mgr *mgr,
ttm_set_driver_manager(&xe->ttm, mem_type, &mgr->manager);
ttm_resource_manager_set_used(&mgr->manager, true);
- return drmm_add_action_or_reset(&xe->drm, ttm_vram_mgr_fini, mgr);
+ return drmm_add_action_or_reset(&xe->drm, xe_ttm_vram_mgr_fini, mgr);
}
/**
diff --git a/drivers/gpu/drm/xe/xe_tuning.c b/drivers/gpu/drm/xe/xe_tuning.c
index a524170a04d0..7c140d8cb1e0 100644
--- a/drivers/gpu/drm/xe/xe_tuning.c
+++ b/drivers/gpu/drm/xe/xe_tuning.c
@@ -40,7 +40,8 @@ static const struct xe_rtp_entry_sr gt_tunings[] = {
REG_FIELD_PREP(L3_PWM_TIMER_INIT_VAL_MASK, 0x7f)))
},
{ XE_RTP_NAME("Tuning: Compression Overfetch"),
- XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, XE_RTP_END_VERSION_UNDEFINED)),
+ XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, XE_RTP_END_VERSION_UNDEFINED),
+ FUNC(xe_rtp_match_has_flat_ccs)),
XE_RTP_ACTIONS(CLR(CCCHKNREG1, ENCOMPPERFFIX),
SET(CCCHKNREG1, L3CMPCTRL))
},
@@ -58,12 +59,14 @@ static const struct xe_rtp_entry_sr gt_tunings[] = {
XE_RTP_ACTIONS(SET(XE2LPM_L3SQCREG3, COMPPWOVERFETCHEN))
},
{ XE_RTP_NAME("Tuning: L2 Overfetch Compressible Only"),
- XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, XE_RTP_END_VERSION_UNDEFINED)),
+ XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, XE_RTP_END_VERSION_UNDEFINED),
+ FUNC(xe_rtp_match_has_flat_ccs)),
XE_RTP_ACTIONS(SET(L3SQCREG2,
COMPMEMRD256BOVRFETCHEN))
},
{ XE_RTP_NAME("Tuning: L2 Overfetch Compressible Only - media"),
- XE_RTP_RULES(MEDIA_VERSION_RANGE(2000, XE_RTP_END_VERSION_UNDEFINED)),
+ XE_RTP_RULES(MEDIA_VERSION_RANGE(2000, XE_RTP_END_VERSION_UNDEFINED),
+ FUNC(xe_rtp_match_has_flat_ccs)),
XE_RTP_ACTIONS(SET(XE2LPM_L3SQCREG2,
COMPMEMRD256BOVRFETCHEN))
},
@@ -214,7 +217,14 @@ void xe_tuning_process_lrc(struct xe_hw_engine *hwe)
xe_rtp_process_to_sr(&ctx, lrc_tunings, ARRAY_SIZE(lrc_tunings), &hwe->reg_lrc);
}
-void xe_tuning_dump(struct xe_gt *gt, struct drm_printer *p)
+/**
+ * xe_tuning_dump() - Dump GT tuning info into a drm printer.
+ * @gt: the &xe_gt
+ * @p: the &drm_printer
+ *
+ * Return: always 0.
+ */
+int xe_tuning_dump(struct xe_gt *gt, struct drm_printer *p)
{
size_t idx;
@@ -222,11 +232,15 @@ void xe_tuning_dump(struct xe_gt *gt, struct drm_printer *p)
for_each_set_bit(idx, gt->tuning_active.gt, ARRAY_SIZE(gt_tunings))
drm_printf_indent(p, 1, "%s\n", gt_tunings[idx].name);
- drm_printf(p, "\nEngine Tunings\n");
+ drm_puts(p, "\n");
+ drm_printf(p, "Engine Tunings\n");
for_each_set_bit(idx, gt->tuning_active.engine, ARRAY_SIZE(engine_tunings))
drm_printf_indent(p, 1, "%s\n", engine_tunings[idx].name);
- drm_printf(p, "\nLRC Tunings\n");
+ drm_puts(p, "\n");
+ drm_printf(p, "LRC Tunings\n");
for_each_set_bit(idx, gt->tuning_active.lrc, ARRAY_SIZE(lrc_tunings))
drm_printf_indent(p, 1, "%s\n", lrc_tunings[idx].name);
+
+ return 0;
}
diff --git a/drivers/gpu/drm/xe/xe_tuning.h b/drivers/gpu/drm/xe/xe_tuning.h
index dd0d3ccc9c65..c1cc5927fda7 100644
--- a/drivers/gpu/drm/xe/xe_tuning.h
+++ b/drivers/gpu/drm/xe/xe_tuning.h
@@ -14,6 +14,6 @@ int xe_tuning_init(struct xe_gt *gt);
void xe_tuning_process_gt(struct xe_gt *gt);
void xe_tuning_process_engine(struct xe_hw_engine *hwe);
void xe_tuning_process_lrc(struct xe_hw_engine *hwe);
-void xe_tuning_dump(struct xe_gt *gt, struct drm_printer *p);
+int xe_tuning_dump(struct xe_gt *gt, struct drm_printer *p);
#endif
diff --git a/drivers/gpu/drm/xe/xe_userptr.c b/drivers/gpu/drm/xe/xe_userptr.c
index f16e92cd8090..0d9130b1958a 100644
--- a/drivers/gpu/drm/xe/xe_userptr.c
+++ b/drivers/gpu/drm/xe/xe_userptr.c
@@ -3,6 +3,7 @@
* Copyright © 2025 Intel Corporation
*/
+#include "xe_svm.h"
#include "xe_userptr.h"
#include <linux/mm.h>
@@ -54,7 +55,8 @@ 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 = NULL,
+ .device_private_page_owner = xe_svm_devm_owner(xe),
+ .allow_mixed = true,
};
lockdep_assert_held(&vm->lock);
diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c
index f602b874e054..10d77666a425 100644
--- a/drivers/gpu/drm/xe/xe_vm.c
+++ b/drivers/gpu/drm/xe/xe_vm.c
@@ -35,6 +35,7 @@
#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"
#include "xe_tile.h"
@@ -111,12 +112,22 @@ static int alloc_preempt_fences(struct xe_vm *vm, struct list_head *list,
static int wait_for_existing_preempt_fences(struct xe_vm *vm)
{
struct xe_exec_queue *q;
+ bool vf_migration = IS_SRIOV_VF(vm->xe) &&
+ xe_sriov_vf_migration_supported(vm->xe);
+ signed long wait_time = vf_migration ? HZ / 5 : MAX_SCHEDULE_TIMEOUT;
xe_vm_assert_held(vm);
list_for_each_entry(q, &vm->preempt.exec_queues, lr.link) {
if (q->lr.pfence) {
- long timeout = dma_fence_wait(q->lr.pfence, false);
+ long timeout;
+
+ timeout = dma_fence_wait_timeout(q->lr.pfence, false,
+ wait_time);
+ if (!timeout) {
+ xe_assert(vm->xe, vf_migration);
+ return -EAGAIN;
+ }
/* Only -ETIME on fence indicates VM needs to be killed */
if (timeout < 0 || q->lr.pfence->error == -ETIME)
@@ -466,6 +477,8 @@ static void preempt_rebind_work_func(struct work_struct *w)
retry:
if (!try_wait_for_completion(&vm->xe->pm_block) && vm_suspend_rebind_worker(vm)) {
up_write(&vm->lock);
+ /* We don't actually block but don't make progress. */
+ xe_pm_might_block_on_suspend();
return;
}
@@ -539,6 +552,19 @@ out_unlock:
out_unlock_outer:
if (err == -EAGAIN) {
trace_xe_vm_rebind_worker_retry(vm);
+
+ /*
+ * We can't block in workers on a VF which supports migration
+ * given this can block the VF post-migration workers from
+ * getting scheduled.
+ */
+ if (IS_SRIOV_VF(vm->xe) &&
+ xe_sriov_vf_migration_supported(vm->xe)) {
+ up_write(&vm->lock);
+ xe_vm_queue_rebind_worker(vm);
+ return;
+ }
+
goto retry;
}
@@ -616,6 +642,13 @@ static void xe_vma_ops_incr_pt_update_ops(struct xe_vma_ops *vops, u8 tile_mask,
vops->pt_update_ops[i].num_ops += inc_val;
}
+#define XE_VMA_CREATE_MASK ( \
+ XE_VMA_READ_ONLY | \
+ XE_VMA_DUMPABLE | \
+ XE_VMA_SYSTEM_ALLOCATOR | \
+ DRM_GPUVA_SPARSE | \
+ XE_VMA_MADV_AUTORESET)
+
static void xe_vm_populate_rebind(struct xe_vma_op *op, struct xe_vma *vma,
u8 tile_mask)
{
@@ -628,8 +661,7 @@ static void xe_vm_populate_rebind(struct xe_vma_op *op, struct xe_vma *vma,
op->base.map.gem.offset = vma->gpuva.gem.offset;
op->map.vma = vma;
op->map.immediate = true;
- op->map.dumpable = vma->gpuva.flags & XE_VMA_DUMPABLE;
- op->map.is_null = xe_vma_is_null(vma);
+ op->map.vma_flags = vma->gpuva.flags & XE_VMA_CREATE_MASK;
}
static int xe_vm_ops_add_rebind(struct xe_vma_ops *vops, struct xe_vma *vma,
@@ -932,11 +964,6 @@ static void xe_vma_free(struct xe_vma *vma)
kfree(vma);
}
-#define VMA_CREATE_FLAG_READ_ONLY BIT(0)
-#define VMA_CREATE_FLAG_IS_NULL BIT(1)
-#define VMA_CREATE_FLAG_DUMPABLE BIT(2)
-#define VMA_CREATE_FLAG_IS_SYSTEM_ALLOCATOR BIT(3)
-
static struct xe_vma *xe_vma_create(struct xe_vm *vm,
struct xe_bo *bo,
u64 bo_offset_or_userptr,
@@ -947,11 +974,8 @@ static struct xe_vma *xe_vma_create(struct xe_vm *vm,
struct xe_vma *vma;
struct xe_tile *tile;
u8 id;
- bool read_only = (flags & VMA_CREATE_FLAG_READ_ONLY);
- bool is_null = (flags & VMA_CREATE_FLAG_IS_NULL);
- bool dumpable = (flags & VMA_CREATE_FLAG_DUMPABLE);
- bool is_cpu_addr_mirror =
- (flags & VMA_CREATE_FLAG_IS_SYSTEM_ALLOCATOR);
+ bool is_null = (flags & DRM_GPUVA_SPARSE);
+ bool is_cpu_addr_mirror = (flags & XE_VMA_SYSTEM_ALLOCATOR);
xe_assert(vm->xe, start < end);
xe_assert(vm->xe, end < vm->size);
@@ -972,10 +996,6 @@ static struct xe_vma *xe_vma_create(struct xe_vm *vm,
if (!vma)
return ERR_PTR(-ENOMEM);
- if (is_cpu_addr_mirror)
- vma->gpuva.flags |= XE_VMA_SYSTEM_ALLOCATOR;
- if (is_null)
- vma->gpuva.flags |= DRM_GPUVA_SPARSE;
if (bo)
vma->gpuva.gem.obj = &bo->ttm.base;
}
@@ -986,10 +1006,7 @@ static struct xe_vma *xe_vma_create(struct xe_vm *vm,
vma->gpuva.vm = &vm->gpuvm;
vma->gpuva.va.addr = start;
vma->gpuva.va.range = end - start + 1;
- if (read_only)
- vma->gpuva.flags |= XE_VMA_READ_ONLY;
- if (dumpable)
- vma->gpuva.flags |= XE_VMA_DUMPABLE;
+ vma->gpuva.flags = flags;
for_each_tile(tile, vm->xe, id)
vma->tile_mask |= 0x1 << id;
@@ -1884,6 +1901,7 @@ int xe_vm_create_ioctl(struct drm_device *dev, void *data,
struct xe_device *xe = to_xe_device(dev);
struct xe_file *xef = to_xe_file(file);
struct drm_xe_vm_create *args = data;
+ struct xe_gt *wa_gt = xe_root_mmio_gt(xe);
struct xe_vm *vm;
u32 id;
int err;
@@ -1892,7 +1910,7 @@ int xe_vm_create_ioctl(struct drm_device *dev, void *data,
if (XE_IOCTL_DBG(xe, args->extensions))
return -EINVAL;
- if (XE_GT_WA(xe_root_mmio_gt(xe), 14016763929))
+ if (wa_gt && XE_GT_WA(wa_gt, 22014953428))
args->flags |= DRM_XE_VM_CREATE_FLAG_SCRATCH_PAGE;
if (XE_IOCTL_DBG(xe, args->flags & DRM_XE_VM_CREATE_FLAG_FAULT_MODE &&
@@ -2272,12 +2290,16 @@ vm_bind_ioctl_ops_create(struct xe_vm *vm, struct xe_vma_ops *vops,
if (__op->op == DRM_GPUVA_OP_MAP) {
op->map.immediate =
flags & DRM_XE_VM_BIND_FLAG_IMMEDIATE;
- op->map.read_only =
- flags & DRM_XE_VM_BIND_FLAG_READONLY;
- op->map.is_null = flags & DRM_XE_VM_BIND_FLAG_NULL;
- op->map.is_cpu_addr_mirror = flags &
- DRM_XE_VM_BIND_FLAG_CPU_ADDR_MIRROR;
- op->map.dumpable = flags & DRM_XE_VM_BIND_FLAG_DUMPABLE;
+ if (flags & DRM_XE_VM_BIND_FLAG_READONLY)
+ op->map.vma_flags |= XE_VMA_READ_ONLY;
+ if (flags & DRM_XE_VM_BIND_FLAG_NULL)
+ op->map.vma_flags |= DRM_GPUVA_SPARSE;
+ if (flags & DRM_XE_VM_BIND_FLAG_CPU_ADDR_MIRROR)
+ op->map.vma_flags |= XE_VMA_SYSTEM_ALLOCATOR;
+ if (flags & DRM_XE_VM_BIND_FLAG_DUMPABLE)
+ op->map.vma_flags |= XE_VMA_DUMPABLE;
+ if (flags & DRM_XE_VM_BIND_FLAG_MADVISE_AUTORESET)
+ op->map.vma_flags |= XE_VMA_MADV_AUTORESET;
op->map.pat_index = pat_index;
op->map.invalidate_on_bind =
__xe_vm_needs_clear_scratch_pages(vm, flags);
@@ -2590,14 +2612,7 @@ static int vm_bind_ioctl_ops_parse(struct xe_vm *vm, struct drm_gpuva_ops *ops,
.pat_index = op->map.pat_index,
};
- flags |= op->map.read_only ?
- VMA_CREATE_FLAG_READ_ONLY : 0;
- flags |= op->map.is_null ?
- VMA_CREATE_FLAG_IS_NULL : 0;
- flags |= op->map.dumpable ?
- VMA_CREATE_FLAG_DUMPABLE : 0;
- flags |= op->map.is_cpu_addr_mirror ?
- VMA_CREATE_FLAG_IS_SYSTEM_ALLOCATOR : 0;
+ flags |= op->map.vma_flags & XE_VMA_CREATE_MASK;
vma = new_vma(vm, &op->base.map, &default_attr,
flags);
@@ -2606,7 +2621,7 @@ static int vm_bind_ioctl_ops_parse(struct xe_vm *vm, struct drm_gpuva_ops *ops,
op->map.vma = vma;
if (((op->map.immediate || !xe_vm_in_fault_mode(vm)) &&
- !op->map.is_cpu_addr_mirror) ||
+ !(op->map.vma_flags & XE_VMA_SYSTEM_ALLOCATOR)) ||
op->map.invalidate_on_bind)
xe_vma_ops_incr_pt_update_ops(vops,
op->tile_mask, 1);
@@ -2637,18 +2652,7 @@ static int vm_bind_ioctl_ops_parse(struct xe_vm *vm, struct drm_gpuva_ops *ops,
op->remap.start = xe_vma_start(old);
op->remap.range = xe_vma_size(old);
- flags |= op->base.remap.unmap->va->flags &
- XE_VMA_READ_ONLY ?
- VMA_CREATE_FLAG_READ_ONLY : 0;
- flags |= op->base.remap.unmap->va->flags &
- DRM_GPUVA_SPARSE ?
- VMA_CREATE_FLAG_IS_NULL : 0;
- flags |= op->base.remap.unmap->va->flags &
- XE_VMA_DUMPABLE ?
- VMA_CREATE_FLAG_DUMPABLE : 0;
- flags |= xe_vma_is_cpu_addr_mirror(old) ?
- VMA_CREATE_FLAG_IS_SYSTEM_ALLOCATOR : 0;
-
+ flags |= op->base.remap.unmap->va->flags & XE_VMA_CREATE_MASK;
if (op->base.remap.prev) {
vma = new_vma(vm, op->base.remap.prev,
&old->attr, flags);
@@ -3279,7 +3283,8 @@ ALLOW_ERROR_INJECTION(vm_bind_ioctl_ops_execute, ERRNO);
DRM_XE_VM_BIND_FLAG_NULL | \
DRM_XE_VM_BIND_FLAG_DUMPABLE | \
DRM_XE_VM_BIND_FLAG_CHECK_PXP | \
- DRM_XE_VM_BIND_FLAG_CPU_ADDR_MIRROR)
+ DRM_XE_VM_BIND_FLAG_CPU_ADDR_MIRROR | \
+ DRM_XE_VM_BIND_FLAG_MADVISE_AUTORESET)
#ifdef TEST_VM_OPS_ERROR
#define SUPPORTED_FLAGS (SUPPORTED_FLAGS_STUB | FORCE_OP_ERROR)
@@ -3394,7 +3399,9 @@ static int vm_bind_ioctl_check_args(struct xe_device *xe, struct xe_vm *vm,
XE_IOCTL_DBG(xe, (prefetch_region != DRM_XE_CONSULT_MEM_ADVISE_PREF_LOC &&
!(BIT(prefetch_region) & xe->info.mem_region_mask))) ||
XE_IOCTL_DBG(xe, obj &&
- op == DRM_XE_VM_BIND_OP_UNMAP)) {
+ op == DRM_XE_VM_BIND_OP_UNMAP) ||
+ XE_IOCTL_DBG(xe, (flags & DRM_XE_VM_BIND_FLAG_MADVISE_AUTORESET) &&
+ (!is_cpu_addr_mirror || op != DRM_XE_VM_BIND_OP_MAP))) {
err = -EINVAL;
goto free_bind_ops;
}
@@ -4212,7 +4219,7 @@ static int xe_vm_alloc_vma(struct xe_vm *vm,
struct xe_vma_ops vops;
struct drm_gpuva_ops *ops = NULL;
struct drm_gpuva_op *__op;
- bool is_cpu_addr_mirror = false;
+ unsigned int vma_flags = 0;
bool remap_op = false;
struct xe_vma_mem_attr tmp_attr;
u16 default_pat;
@@ -4242,15 +4249,17 @@ static int xe_vm_alloc_vma(struct xe_vm *vm,
vma = gpuva_to_vma(op->base.unmap.va);
XE_WARN_ON(!xe_vma_has_default_mem_attrs(vma));
default_pat = vma->attr.default_pat_index;
+ vma_flags = vma->gpuva.flags;
}
if (__op->op == DRM_GPUVA_OP_REMAP) {
vma = gpuva_to_vma(op->base.remap.unmap->va);
default_pat = vma->attr.default_pat_index;
+ vma_flags = vma->gpuva.flags;
}
if (__op->op == DRM_GPUVA_OP_MAP) {
- op->map.is_cpu_addr_mirror = true;
+ op->map.vma_flags |= vma_flags & XE_VMA_CREATE_MASK;
op->map.pat_index = default_pat;
}
} else {
@@ -4259,11 +4268,7 @@ static int xe_vm_alloc_vma(struct xe_vm *vm,
xe_assert(vm->xe, !remap_op);
xe_assert(vm->xe, xe_vma_has_no_bo(vma));
remap_op = true;
-
- if (xe_vma_is_cpu_addr_mirror(vma))
- is_cpu_addr_mirror = true;
- else
- is_cpu_addr_mirror = false;
+ vma_flags = vma->gpuva.flags;
}
if (__op->op == DRM_GPUVA_OP_MAP) {
@@ -4272,10 +4277,10 @@ static int xe_vm_alloc_vma(struct xe_vm *vm,
/*
* In case of madvise ops DRM_GPUVA_OP_MAP is
* always after DRM_GPUVA_OP_REMAP, so ensure
- * we assign op->map.is_cpu_addr_mirror true
- * if REMAP is for xe_vma_is_cpu_addr_mirror vma
+ * to propagate the flags from the vma we're
+ * unmapping.
*/
- op->map.is_cpu_addr_mirror = is_cpu_addr_mirror;
+ op->map.vma_flags |= vma_flags & XE_VMA_CREATE_MASK;
}
}
print_op(vm->xe, __op);
diff --git a/drivers/gpu/drm/xe/xe_vm_types.h b/drivers/gpu/drm/xe/xe_vm_types.h
index 413353e1c225..d6e2a0fdd4b3 100644
--- a/drivers/gpu/drm/xe/xe_vm_types.h
+++ b/drivers/gpu/drm/xe/xe_vm_types.h
@@ -46,6 +46,7 @@ struct xe_vm_pgtable_update_op;
#define XE_VMA_PTE_COMPACT (DRM_GPUVA_USERBITS << 7)
#define XE_VMA_DUMPABLE (DRM_GPUVA_USERBITS << 8)
#define XE_VMA_SYSTEM_ALLOCATOR (DRM_GPUVA_USERBITS << 9)
+#define XE_VMA_MADV_AUTORESET (DRM_GPUVA_USERBITS << 10)
/**
* struct xe_vma_mem_attr - memory attributes associated with vma
@@ -345,17 +346,10 @@ struct xe_vm {
struct xe_vma_op_map {
/** @vma: VMA to map */
struct xe_vma *vma;
+ unsigned int vma_flags;
/** @immediate: Immediate bind */
bool immediate;
/** @read_only: Read only */
- bool read_only;
- /** @is_null: is NULL binding */
- bool is_null;
- /** @is_cpu_addr_mirror: is CPU address mirror binding */
- bool is_cpu_addr_mirror;
- /** @dumpable: whether BO is dumped on GPU hang */
- bool dumpable;
- /** @invalidate: invalidate the VMA before bind */
bool invalidate_on_bind;
/** @pat_index: The pat index to use for this operation. */
u16 pat_index;
diff --git a/drivers/gpu/drm/xe/xe_vram.c b/drivers/gpu/drm/xe/xe_vram.c
index 652df7a5f4f6..b62a96f8ef9e 100644
--- a/drivers/gpu/drm/xe/xe_vram.c
+++ b/drivers/gpu/drm/xe/xe_vram.c
@@ -13,13 +13,14 @@
#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_gt_sriov_vf.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"
@@ -255,9 +256,9 @@ static int tile_vram_size(struct xe_tile *tile, u64 *vram_size,
offset = 0;
for_each_tile(t, xe, id)
for_each_if(t->id < tile->id)
- offset += xe_gt_sriov_vf_lmem(t->primary_gt);
+ offset += xe_tile_sriov_vf_lmem(t);
- *tile_size = xe_gt_sriov_vf_lmem(gt);
+ *tile_size = xe_tile_sriov_vf_lmem(tile);
*vram_size = *tile_size;
*tile_offset = offset;
@@ -301,8 +302,11 @@ static void vram_fini(void *arg)
xe->mem.vram->mapping = NULL;
- for_each_tile(tile, xe, id)
+ for_each_tile(tile, xe, id) {
tile->mem.vram->mapping = NULL;
+ if (tile->mem.kernel_vram)
+ tile->mem.kernel_vram->mapping = NULL;
+ }
}
struct xe_vram_region *xe_vram_region_alloc(struct xe_device *xe, u8 id, u32 placement)
diff --git a/drivers/gpu/drm/xe/xe_wa.c b/drivers/gpu/drm/xe/xe_wa.c
index cd03891654a1..b6dcd9827354 100644
--- a/drivers/gpu/drm/xe/xe_wa.c
+++ b/drivers/gpu/drm/xe/xe_wa.c
@@ -684,7 +684,7 @@ static const struct xe_rtp_entry_sr engine_was[] = {
},
{ XE_RTP_NAME("13012615864"),
XE_RTP_RULES(GRAPHICS_VERSION_RANGE(3000, 3001), OR,
- GRAPHICS_VERSION(3003),
+ GRAPHICS_VERSION_RANGE(3003, 3005),
FUNC(xe_rtp_match_first_render_or_compute)),
XE_RTP_ACTIONS(SET(TDL_TSL_CHICKEN, RES_CHK_SPR_DIS))
},
@@ -695,7 +695,7 @@ static const struct xe_rtp_entry_sr engine_was[] = {
XE_RTP_ACTION_FLAG(ENGINE_BASE)))
},
{ XE_RTP_NAME("14021402888"),
- XE_RTP_RULES(GRAPHICS_VERSION(3003), FUNC(xe_rtp_match_first_render_or_compute)),
+ XE_RTP_RULES(GRAPHICS_VERSION_RANGE(3003, 3005), FUNC(xe_rtp_match_first_render_or_compute)),
XE_RTP_ACTIONS(SET(HALF_SLICE_CHICKEN7, CLEAR_OPTIMIZATION_DISABLE))
},
{ XE_RTP_NAME("18041344222"),
@@ -913,7 +913,7 @@ static const struct xe_rtp_entry_sr lrc_was[] = {
DIS_AUTOSTRIP))
},
{ XE_RTP_NAME("22021007897"),
- XE_RTP_RULES(GRAPHICS_VERSION_RANGE(3000, 3003), ENGINE_CLASS(RENDER)),
+ XE_RTP_RULES(GRAPHICS_VERSION_RANGE(3000, 3005), ENGINE_CLASS(RENDER)),
XE_RTP_ACTIONS(SET(COMMON_SLICE_CHICKEN4, SBE_PUSH_CONSTANT_BEHIND_FIX_ENABLE))
},
};
@@ -1086,7 +1086,14 @@ void xe_wa_device_dump(struct xe_device *xe, struct drm_printer *p)
drm_printf_indent(p, 1, "%s\n", device_oob_was[idx].name);
}
-void xe_wa_dump(struct xe_gt *gt, struct drm_printer *p)
+/**
+ * xe_wa_gt_dump() - Dump GT workarounds into a drm printer.
+ * @gt: the &xe_gt
+ * @p: the &drm_printer
+ *
+ * Return: always 0.
+ */
+int xe_wa_gt_dump(struct xe_gt *gt, struct drm_printer *p)
{
size_t idx;
@@ -1094,18 +1101,22 @@ void xe_wa_dump(struct xe_gt *gt, struct drm_printer *p)
for_each_set_bit(idx, gt->wa_active.gt, ARRAY_SIZE(gt_was))
drm_printf_indent(p, 1, "%s\n", gt_was[idx].name);
- drm_printf(p, "\nEngine Workarounds\n");
+ drm_puts(p, "\n");
+ drm_printf(p, "Engine Workarounds\n");
for_each_set_bit(idx, gt->wa_active.engine, ARRAY_SIZE(engine_was))
drm_printf_indent(p, 1, "%s\n", engine_was[idx].name);
- drm_printf(p, "\nLRC Workarounds\n");
+ drm_puts(p, "\n");
+ drm_printf(p, "LRC Workarounds\n");
for_each_set_bit(idx, gt->wa_active.lrc, ARRAY_SIZE(lrc_was))
drm_printf_indent(p, 1, "%s\n", lrc_was[idx].name);
- drm_printf(p, "\nOOB Workarounds\n");
+ drm_puts(p, "\n");
+ drm_printf(p, "OOB Workarounds\n");
for_each_set_bit(idx, gt->wa_active.oob, ARRAY_SIZE(oob_was))
if (oob_was[idx].name)
drm_printf_indent(p, 1, "%s\n", oob_was[idx].name);
+ return 0;
}
/*
@@ -1127,6 +1138,6 @@ void xe_wa_apply_tile_workarounds(struct xe_tile *tile)
if (IS_SRIOV_VF(tile->xe))
return;
- if (XE_GT_WA(tile->primary_gt, 22010954014))
+ if (XE_DEVICE_WA(tile->xe, 22010954014))
xe_mmio_rmw32(mmio, XEHP_CLOCK_GATE_DIS, 0, SGSI_SIDECLK_DIS);
}
diff --git a/drivers/gpu/drm/xe/xe_wa.h b/drivers/gpu/drm/xe/xe_wa.h
index 6a869b2de643..8fd6a5af0910 100644
--- a/drivers/gpu/drm/xe/xe_wa.h
+++ b/drivers/gpu/drm/xe/xe_wa.h
@@ -22,7 +22,7 @@ void xe_wa_process_engine(struct xe_hw_engine *hwe);
void xe_wa_process_lrc(struct xe_hw_engine *hwe);
void xe_wa_apply_tile_workarounds(struct xe_tile *tile);
void xe_wa_device_dump(struct xe_device *xe, struct drm_printer *p);
-void xe_wa_dump(struct xe_gt *gt, struct drm_printer *p);
+int xe_wa_gt_dump(struct xe_gt *gt, struct drm_printer *p);
/**
* XE_GT_WA - Out-of-band GT workarounds, to be queried and called as needed.
diff --git a/drivers/gpu/drm/xe/xe_wa_oob.rules b/drivers/gpu/drm/xe/xe_wa_oob.rules
index f3a6d5d239ce..fb38eb3d6e9a 100644
--- a/drivers/gpu/drm/xe/xe_wa_oob.rules
+++ b/drivers/gpu/drm/xe/xe_wa_oob.rules
@@ -11,10 +11,9 @@
18020744125 PLATFORM(PVC)
1509372804 PLATFORM(PVC), GRAPHICS_STEP(A0, C0)
1409600907 GRAPHICS_VERSION_RANGE(1200, 1250)
-14016763929 SUBPLATFORM(DG2, G10)
+22014953428 SUBPLATFORM(DG2, G10)
SUBPLATFORM(DG2, G12)
16017236439 PLATFORM(PVC)
-22010954014 PLATFORM(DG2)
14019821291 MEDIA_VERSION_RANGE(1300, 2000)
14015076503 MEDIA_VERSION(1300)
16020292621 GRAPHICS_VERSION(2004), GRAPHICS_STEP(A0, B0)
@@ -34,18 +33,18 @@
13011645652 GRAPHICS_VERSION(2004)
GRAPHICS_VERSION_RANGE(3000, 3001)
GRAPHICS_VERSION(3003)
+ GRAPHICS_VERSION_RANGE(3004, 3005)
14022293748 GRAPHICS_VERSION_RANGE(2001, 2002)
GRAPHICS_VERSION(2004)
- GRAPHICS_VERSION_RANGE(3000, 3001)
- GRAPHICS_VERSION(3003)
+ GRAPHICS_VERSION_RANGE(3000, 3005)
22019794406 GRAPHICS_VERSION_RANGE(2001, 2002)
GRAPHICS_VERSION(2004)
GRAPHICS_VERSION_RANGE(3000, 3001)
GRAPHICS_VERSION(3003)
+ GRAPHICS_VERSION_RANGE(3004, 3005)
22019338487 MEDIA_VERSION(2000)
GRAPHICS_VERSION(2001), FUNC(xe_rtp_match_not_sriov_vf)
MEDIA_VERSION(3000), MEDIA_STEP(A0, B0), FUNC(xe_rtp_match_not_sriov_vf)
-22019338487_display PLATFORM(LUNARLAKE)
16023588340 GRAPHICS_VERSION(2001), FUNC(xe_rtp_match_not_sriov_vf)
14019789679 GRAPHICS_VERSION(1255)
GRAPHICS_VERSION_RANGE(1270, 2004)
@@ -63,11 +62,11 @@
16023105232 GRAPHICS_VERSION_RANGE(2001, 3001)
MEDIA_VERSION_RANGE(1301, 3000)
MEDIA_VERSION(3002)
- GRAPHICS_VERSION(3003)
+ GRAPHICS_VERSION_RANGE(3003, 3005)
16026508708 GRAPHICS_VERSION_RANGE(1200, 3001)
MEDIA_VERSION_RANGE(1300, 3000)
MEDIA_VERSION(3002)
- GRAPHICS_VERSION(3003)
+ GRAPHICS_VERSION_RANGE(3003, 3005)
14020001231 GRAPHICS_VERSION_RANGE(2001,2004), FUNC(xe_rtp_match_psmi_enabled)
MEDIA_VERSION(2000), FUNC(xe_rtp_match_psmi_enabled)
MEDIA_VERSION(3000), FUNC(xe_rtp_match_psmi_enabled)
@@ -75,9 +74,5 @@
16023683509 MEDIA_VERSION(2000), FUNC(xe_rtp_match_psmi_enabled)
MEDIA_VERSION(3000), MEDIA_STEP(A0, B0), FUNC(xe_rtp_match_psmi_enabled)
-# SoC workaround - currently applies to all platforms with the following
-# primary GT GMDID
-14022085890 GRAPHICS_VERSION(2001)
-
15015404425_disable PLATFORM(PANTHERLAKE), MEDIA_STEP(B0, FOREVER)
16026007364 MEDIA_VERSION(3000)
diff --git a/include/drm/drm_gpusvm.h b/include/drm/drm_gpusvm.h
index b92faa9a26b2..632e100e6efb 100644
--- a/include/drm/drm_gpusvm.h
+++ b/include/drm/drm_gpusvm.h
@@ -235,6 +235,9 @@ struct drm_gpusvm {
* @read_only: operating on read-only memory
* @devmem_possible: possible to use device memory
* @devmem_only: use only device memory
+ * @allow_mixed: Allow mixed mappings in get pages. Mixing between system and
+ * single dpagemap is supported, mixing between multiple dpagemap
+ * is unsupported.
*
* Context that is DRM GPUSVM is operating in (i.e. user arguments).
*/
@@ -246,6 +249,7 @@ struct drm_gpusvm_ctx {
unsigned int read_only :1;
unsigned int devmem_possible :1;
unsigned int devmem_only :1;
+ unsigned int allow_mixed :1;
};
int drm_gpusvm_init(struct drm_gpusvm *gpusvm,
diff --git a/include/drm/intel/pciids.h b/include/drm/intel/pciids.h
index 69d4ae92d822..13c592e1a28c 100644
--- a/include/drm/intel/pciids.h
+++ b/include/drm/intel/pciids.h
@@ -849,7 +849,7 @@
MACRO__(0x64B0, ## __VA_ARGS__)
/* BMG */
-#define INTEL_BMG_IDS(MACRO__, ...) \
+#define INTEL_BMG_G21_IDS(MACRO__, ...) \
MACRO__(0xE202, ## __VA_ARGS__), \
MACRO__(0xE209, ## __VA_ARGS__), \
MACRO__(0xE20B, ## __VA_ARGS__), \
@@ -858,7 +858,10 @@
MACRO__(0xE210, ## __VA_ARGS__), \
MACRO__(0xE211, ## __VA_ARGS__), \
MACRO__(0xE212, ## __VA_ARGS__), \
- MACRO__(0xE216, ## __VA_ARGS__), \
+ MACRO__(0xE216, ## __VA_ARGS__)
+
+#define INTEL_BMG_IDS(MACRO__, ...) \
+ INTEL_BMG_G21_IDS(MACRO__, __VA_ARGS__), \
MACRO__(0xE220, ## __VA_ARGS__), \
MACRO__(0xE221, ## __VA_ARGS__), \
MACRO__(0xE222, ## __VA_ARGS__), \
@@ -884,4 +887,13 @@
MACRO__(0xFD80, ## __VA_ARGS__), \
MACRO__(0xFD81, ## __VA_ARGS__)
+/* NVL-S */
+#define INTEL_NVLS_IDS(MACRO__, ...) \
+ MACRO__(0xD740, ## __VA_ARGS__), \
+ MACRO__(0xD741, ## __VA_ARGS__), \
+ MACRO__(0xD742, ## __VA_ARGS__), \
+ MACRO__(0xD743, ## __VA_ARGS__), \
+ MACRO__(0xD744, ## __VA_ARGS__), \
+ MACRO__(0xD745, ## __VA_ARGS__)
+
#endif /* __PCIIDS_H__ */
diff --git a/include/uapi/drm/xe_drm.h b/include/uapi/drm/xe_drm.h
index 40ff19f52a8d..47853659a705 100644
--- a/include/uapi/drm/xe_drm.h
+++ b/include/uapi/drm/xe_drm.h
@@ -771,7 +771,11 @@ struct drm_xe_device_query {
* until the object is either bound to a virtual memory region via
* VM_BIND or accessed by the CPU. As a result, no backing memory is
* reserved at the time of GEM object creation.
- * - %DRM_XE_GEM_CREATE_FLAG_SCANOUT
+ * - %DRM_XE_GEM_CREATE_FLAG_SCANOUT - Indicates that the GEM object is
+ * intended for scanout via the display engine. When set, kernel ensures
+ * that the allocation is placed in a memory region compatible with the
+ * display engine requirements. This may impose restrictions on tiling,
+ * alignment, and memory placement to guarantee proper display functionality.
* - %DRM_XE_GEM_CREATE_FLAG_NEEDS_VISIBLE_VRAM - When using VRAM as a
* possible placement, ensure that the corresponding VRAM allocation
* will always use the CPU accessible part of VRAM. This is important
@@ -1013,6 +1017,20 @@ struct drm_xe_vm_destroy {
* valid on VMs with DRM_XE_VM_CREATE_FLAG_FAULT_MODE set. The CPU address
* mirror flag are only valid for DRM_XE_VM_BIND_OP_MAP operations, the BO
* handle MBZ, and the BO offset MBZ.
+ * - %DRM_XE_VM_BIND_FLAG_MADVISE_AUTORESET - Can be used in combination with
+ * %DRM_XE_VM_BIND_FLAG_CPU_ADDR_MIRROR to reset madvises when the underlying
+ * CPU address space range is unmapped (typically with munmap(2) or brk(2)).
+ * The madvise values set with &DRM_IOCTL_XE_MADVISE are reset to the values
+ * that were present immediately after the &DRM_IOCTL_XE_VM_BIND.
+ * The reset GPU virtual address range is the intersection of the range bound
+ * using &DRM_IOCTL_XE_VM_BIND and the virtual CPU address space range
+ * unmapped.
+ * This functionality is present to mimic the behaviour of CPU address space
+ * madvises set using madvise(2), which are typically reset on unmap.
+ * Note: free(3) may or may not call munmap(2) and/or brk(2), and may thus
+ * not invoke autoreset. Neither will stack variables going out of scope.
+ * Therefore it's recommended to always explicitly reset the madvises when
+ * freeing the memory backing a region used in a &DRM_IOCTL_XE_MADVISE call.
*
* The @prefetch_mem_region_instance for %DRM_XE_VM_BIND_OP_PREFETCH can also be:
* - %DRM_XE_CONSULT_MEM_ADVISE_PREF_LOC, which ensures prefetching occurs in
@@ -1119,6 +1137,7 @@ struct drm_xe_vm_bind_op {
#define DRM_XE_VM_BIND_FLAG_DUMPABLE (1 << 3)
#define DRM_XE_VM_BIND_FLAG_CHECK_PXP (1 << 4)
#define DRM_XE_VM_BIND_FLAG_CPU_ADDR_MIRROR (1 << 5)
+#define DRM_XE_VM_BIND_FLAG_MADVISE_AUTORESET (1 << 6)
/** @flags: Bind flags */
__u32 flags;