* [PATCH v13 0/5] drm/xe/guc: Add GuC based register capture for error capture
@ 2024-07-19 20:29 Zhanjun Dong
2024-07-19 20:29 ` [PATCH v13 1/5] drm/xe/guc: Prepare GuC register list and update ADS size " Zhanjun Dong
` (7 more replies)
0 siblings, 8 replies; 9+ messages in thread
From: Zhanjun Dong @ 2024-07-19 20:29 UTC (permalink / raw)
To: intel-xe; +Cc: Zhanjun Dong, Alan Previn, Michal Wajdeczko
Port GuC based register capture for error capture from i915 to Xe.
There are 3 parts inside:
. Prepare for capture registers
There is a bo create at guc ads init time, that is very early
and engi ne map is not ready, make it hard to calculate the
capture buffer size, new function created for worst case size
caluation. Other than that, this part basically follows the i915
design.
. Process capture notification message
Basically follows i915 design
. Sysfs command process.
Xe switched to devcoredump, adopted command line process with
captured node list.
Signed-off-by: Zhanjun Dong <zhanjun.dong@intel.com>
Cc: Alan Previn <alan.previn.teres.alexis@intel.com>
Cc: Michal Wajdeczko <michal.wajdeczko@intel.com>
Changes from prior revs:
v13:- Move guc_mmio_reg structure define to guc_capture_abi.h
Remove duplicated crash/debug/capture unit check
Remove unnecessary guc_capture_data_extracted
Update u32 align check in guc_capture_log_remove_bytes
v12:- Rewrite guc log size init from runtime to compile time implementation.
Change log buffer flush to file from structure bitfield to genmask.
Change the capture log data copy from u32 copy to size copy
There are 3 types of engine class refrenced in this series, hw engine
class, GuC class and GuC capture class, update function parameter type
to enum for easy to read.
Update macro names to follow GuC interface specification.
v11:- Fixed a bug of missing captured check on register snapshot pre-capture
Fixed kernel-doc warnings
v10:- Resync with updated job timed out follow
Add pre-capture by read from hw engine if GuC capture data is not
ready, the pre-captured data will be refereshed if GuC capture is
ready at later time.
Add xe_guc_capture_is_ready_for to check if GuC capture is ready
for a job.
Re-orgnize some header files to xe abi folder
Reduce some meesage level from warn/info to debug
Remove duplicated enum of GuC log type.
v9:- Merged snapshot register list into capture register lists
Optimized devcoredump timing to take snapshot after guc reset
Add global and engine class registers into capture list
Fixed bug of incorrect matching guc class id with guc capture class id
v8:- Reorgnize the order of patches
Change the capture size check from worst min size to worst size
Replace the kernel alloc with drm managed alloc
Replace the memcpy with xe_map_memcpy_from
Free GuC capture outlist as part of xe_devcoredump_free
v7:- Kconfig CONFIG_DRM_XE_CAPTURE_ERROR removed
v6:- Change hardcoded register snapshot fill to follow mapping tables
When capture is empty, take snapshot from engine
v5:- Split dss helper code out as an standalone patch
Remove old platform registers definition.
Split register map table to 32 and 64bit each
v4:- Move register map table to xe_hw_engine.c
v3:- Remove condition compilation in code
v2:- Split into multiple chunks
Zhanjun Dong (5):
drm/xe/guc: Prepare GuC register list and update ADS size for error
capture
drm/xe/guc: Add XE_LP steered register lists
drm/xe/guc: Add capture size check in GuC log buffer
drm/xe/guc: Extract GuC error capture lists
drm/xe/guc: Plumb GuC-capture into dev coredump
drivers/gpu/drm/xe/Makefile | 1 +
drivers/gpu/drm/xe/abi/guc_actions_abi.h | 8 +
drivers/gpu/drm/xe/abi/guc_capture_abi.h | 186 +++
drivers/gpu/drm/xe/abi/guc_log_abi.h | 72 +
drivers/gpu/drm/xe/regs/xe_gt_regs.h | 2 +
drivers/gpu/drm/xe/xe_devcoredump.c | 15 +-
drivers/gpu/drm/xe/xe_devcoredump_types.h | 2 +
drivers/gpu/drm/xe/xe_guc.c | 5 +
drivers/gpu/drm/xe/xe_guc.h | 5 +
drivers/gpu/drm/xe/xe_guc_ads.c | 151 +-
drivers/gpu/drm/xe/xe_guc_ads_types.h | 2 +
drivers/gpu/drm/xe/xe_guc_capture.c | 1630 +++++++++++++++++++++
drivers/gpu/drm/xe/xe_guc_capture.h | 68 +
drivers/gpu/drm/xe/xe_guc_capture_types.h | 59 +
drivers/gpu/drm/xe/xe_guc_ct.c | 2 +
drivers/gpu/drm/xe/xe_guc_fwif.h | 26 +-
drivers/gpu/drm/xe/xe_guc_log.c | 141 +-
drivers/gpu/drm/xe/xe_guc_log.h | 12 +-
drivers/gpu/drm/xe/xe_guc_log_types.h | 15 +
drivers/gpu/drm/xe/xe_guc_submit.c | 86 +-
drivers/gpu/drm/xe/xe_guc_submit.h | 2 +
drivers/gpu/drm/xe/xe_guc_types.h | 2 +
drivers/gpu/drm/xe/xe_hw_engine.c | 240 ++-
drivers/gpu/drm/xe/xe_hw_engine.h | 2 +
drivers/gpu/drm/xe/xe_hw_engine_types.h | 180 ++-
drivers/gpu/drm/xe/xe_lrc.h | 1 +
26 files changed, 2712 insertions(+), 203 deletions(-)
create mode 100644 drivers/gpu/drm/xe/abi/guc_capture_abi.h
create mode 100644 drivers/gpu/drm/xe/abi/guc_log_abi.h
create mode 100644 drivers/gpu/drm/xe/xe_guc_capture.c
create mode 100644 drivers/gpu/drm/xe/xe_guc_capture.h
create mode 100644 drivers/gpu/drm/xe/xe_guc_capture_types.h
--
2.34.1
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v13 1/5] drm/xe/guc: Prepare GuC register list and update ADS size for error capture
2024-07-19 20:29 [PATCH v13 0/5] drm/xe/guc: Add GuC based register capture for error capture Zhanjun Dong
@ 2024-07-19 20:29 ` Zhanjun Dong
2024-07-19 20:29 ` [PATCH v13 2/5] drm/xe/guc: Add XE_LP steered register lists Zhanjun Dong
` (6 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Zhanjun Dong @ 2024-07-19 20:29 UTC (permalink / raw)
To: intel-xe; +Cc: Zhanjun Dong, Alan Previn
Add referenced registers defines and list of registers.
Update GuC ADS size allocation to include space for
the lists of error state capture register descriptors.
Then, populate GuC ADS with the lists of registers we want
GuC to report back to host on engine reset events. This list
should include global, engine-class and engine-instance
registers for every engine-class type on the current hardware.
Ensure we allocate a persistent store for the register lists
that are populated into ADS so that we don't need to allocate
memory during GT resets when GuC is reloaded and ADS population
happens again.
Signed-off-by: Zhanjun Dong <zhanjun.dong@intel.com>
Reviewed-by: Alan Previn <alan.previn.teres.alexis@intel.com> #rev11
---
drivers/gpu/drm/xe/Makefile | 1 +
drivers/gpu/drm/xe/abi/guc_capture_abi.h | 186 +++++++++
drivers/gpu/drm/xe/xe_guc.c | 5 +
drivers/gpu/drm/xe/xe_guc.h | 5 +
drivers/gpu/drm/xe/xe_guc_ads.c | 151 ++++++-
drivers/gpu/drm/xe/xe_guc_ads_types.h | 2 +
drivers/gpu/drm/xe/xe_guc_capture.c | 454 ++++++++++++++++++++++
drivers/gpu/drm/xe/xe_guc_capture.h | 48 +++
drivers/gpu/drm/xe/xe_guc_capture_types.h | 55 +++
drivers/gpu/drm/xe/xe_guc_fwif.h | 26 +-
drivers/gpu/drm/xe/xe_guc_types.h | 2 +
11 files changed, 898 insertions(+), 37 deletions(-)
create mode 100644 drivers/gpu/drm/xe/abi/guc_capture_abi.h
create mode 100644 drivers/gpu/drm/xe/xe_guc_capture.c
create mode 100644 drivers/gpu/drm/xe/xe_guc_capture.h
create mode 100644 drivers/gpu/drm/xe/xe_guc_capture_types.h
diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile
index 1ff9602a52f6..2d8f8acaf846 100644
--- a/drivers/gpu/drm/xe/Makefile
+++ b/drivers/gpu/drm/xe/Makefile
@@ -57,6 +57,7 @@ xe-y += xe_bb.o \
xe_gt_topology.o \
xe_guc.o \
xe_guc_ads.o \
+ xe_guc_capture.o \
xe_guc_ct.o \
xe_guc_db_mgr.o \
xe_guc_debugfs.o \
diff --git a/drivers/gpu/drm/xe/abi/guc_capture_abi.h b/drivers/gpu/drm/xe/abi/guc_capture_abi.h
new file mode 100644
index 000000000000..e7898edc6236
--- /dev/null
+++ b/drivers/gpu/drm/xe/abi/guc_capture_abi.h
@@ -0,0 +1,186 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2024 Intel Corporation
+ */
+
+#ifndef _ABI_GUC_CAPTURE_ABI_H
+#define _ABI_GUC_CAPTURE_ABI_H
+
+#include <linux/types.h>
+
+/* Capture List Index */
+enum guc_capture_list_index_type {
+ GUC_CAPTURE_LIST_INDEX_PF = 0,
+ GUC_CAPTURE_LIST_INDEX_VF = 1,
+};
+
+#define GUC_CAPTURE_LIST_INDEX_MAX (GUC_CAPTURE_LIST_INDEX_VF + 1)
+
+/* Register-types of GuC capture register lists */
+enum guc_state_capture_type {
+ GUC_STATE_CAPTURE_TYPE_GLOBAL = 0,
+ GUC_STATE_CAPTURE_TYPE_ENGINE_CLASS,
+ GUC_STATE_CAPTURE_TYPE_ENGINE_INSTANCE
+};
+
+#define GUC_STATE_CAPTURE_TYPE_MAX (GUC_STATE_CAPTURE_TYPE_ENGINE_INSTANCE + 1)
+
+/* Class indecies for capture_class and capture_instance arrays */
+enum guc_capture_list_class_type {
+ GUC_CAPTURE_LIST_CLASS_RENDER_COMPUTE = 0,
+ GUC_CAPTURE_LIST_CLASS_VIDEO = 1,
+ GUC_CAPTURE_LIST_CLASS_VIDEOENHANCE = 2,
+ GUC_CAPTURE_LIST_CLASS_BLITTER = 3,
+ GUC_CAPTURE_LIST_CLASS_GSC_OTHER = 4,
+};
+
+#define GUC_CAPTURE_LIST_CLASS_MAX (GUC_CAPTURE_LIST_CLASS_GSC_OTHER + 1)
+
+/**
+ * struct guc_mmio_reg - GuC MMIO reg state struct
+ *
+ * GuC MMIO reg state struct
+ */
+struct guc_mmio_reg {
+ /** @offset: MMIO Offset - filled in by Host */
+ u32 offset;
+ /** @value: MMIO Value - Used by Firmware to store value */
+ u32 value;
+ /** @flags: Flags for accessing the MMIO */
+ u32 flags;
+ /** @mask: Value of a mask to apply if mask with value is set */
+ u32 mask;
+#define GUC_REGSET_MASKED BIT(0)
+#define GUC_REGSET_STEERING_NEEDED BIT(1)
+#define GUC_REGSET_MASKED_WITH_VALUE BIT(2)
+#define GUC_REGSET_RESTORE_ONLY BIT(3)
+#define GUC_REGSET_STEERING_GROUP GENMASK(16, 12)
+#define GUC_REGSET_STEERING_INSTANCE GENMASK(23, 20)
+} __packed;
+
+/**
+ * struct guc_mmio_reg_set - GuC register sets
+ *
+ * GuC register sets
+ */
+struct guc_mmio_reg_set {
+ /** @address: register address */
+ u32 address;
+ /** @count: register count */
+ u16 count;
+ /** @reserved: reserved */
+ u16 reserved;
+} __packed;
+
+/**
+ * struct guc_debug_capture_list_header - Debug capture list header.
+ *
+ * Debug capture list header.
+ */
+struct guc_debug_capture_list_header {
+ /** @info: contains number of MMIO descriptors in the capture list. */
+ u32 info;
+#define GUC_CAPTURELISTHDR_NUMDESCR GENMASK(15, 0)
+} __packed;
+
+/**
+ * struct guc_debug_capture_list - Debug capture list
+ *
+ * As part of ADS registration, these header structures (followed by
+ * an array of 'struct guc_mmio_reg' entries) are used to register with
+ * GuC microkernel the list of registers we want it to dump out prior
+ * to a engine reset.
+ */
+struct guc_debug_capture_list {
+ /** @header: Debug capture list header. */
+ struct guc_debug_capture_list_header header;
+ /** @regs: MMIO descriptors in the capture list. */
+ struct guc_mmio_reg regs[];
+} __packed;
+
+/**
+ * struct guc_state_capture_header_t - State capture header.
+ *
+ * Prior to resetting engines that have hung or faulted, GuC microkernel
+ * reports the engine error-state (register values that was read) by
+ * logging them into the shared GuC log buffer using these hierarchy
+ * of structures.
+ */
+struct guc_state_capture_header_t {
+ /**
+ * @owner: VFID
+ * BR[ 7: 0] MBZ when SRIOV is disabled. When SRIOV is enabled
+ * VFID is an integer in range [0, 63] where 0 means the state capture
+ * is corresponding to the PF and an integer N in range [1, 63] means
+ * the state capture is for VF N.
+ */
+ u32 owner;
+#define GUC_STATE_CAPTURE_HEADER_VFID GENMASK(7, 0)
+ /** @info: Engine class/instance and capture type info */
+ u32 info;
+#define GUC_STATE_CAPTURE_HEADER_CAPTURE_TYPE GENMASK(3, 0) /* see guc_state_capture_type */
+#define GUC_STATE_CAPTURE_HEADER_ENGINE_CLASS GENMASK(7, 4) /* see guc_capture_list_class_type */
+#define GUC_STATE_CAPTURE_HEADER_ENGINE_INSTANCE GENMASK(11, 8)
+ /**
+ * @lrca: logical ring context address.
+ * if type-instance, LRCA (address) that hung, else set to ~0
+ */
+ u32 lrca;
+ /**
+ * @guc_id: context_index.
+ * if type-instance, context index of hung context, else set to ~0
+ */
+ u32 guc_id;
+ /** @num_mmio_entries: Number of captured MMIO entries. */
+ u32 num_mmio_entries;
+#define GUC_STATE_CAPTURE_HEADER_NUM_MMIO_ENTRIES GENMASK(9, 0)
+} __packed;
+
+/**
+ * struct guc_state_capture_t - State capture.
+ *
+ * State capture
+ */
+struct guc_state_capture_t {
+ /** @header: State capture header. */
+ struct guc_state_capture_header_t header;
+ /** @mmio_entries: Array of captured guc_mmio_reg entries. */
+ struct guc_mmio_reg mmio_entries[];
+} __packed;
+
+/* State Capture Group Type */
+enum guc_state_capture_group_type {
+ GUC_STATE_CAPTURE_GROUP_TYPE_FULL = 0,
+ GUC_STATE_CAPTURE_GROUP_TYPE_PARTIAL
+};
+
+#define GUC_STATE_CAPTURE_GROUP_TYPE_MAX (GUC_STATE_CAPTURE_GROUP_TYPE_PARTIAL + 1)
+
+/**
+ * struct guc_state_capture_group_header_t - State capture group header
+ *
+ * State capture group header.
+ */
+struct guc_state_capture_group_header_t {
+ /** @owner: VFID */
+ u32 owner;
+#define GUC_STATE_CAPTURE_GROUP_HEADER_VFID GENMASK(7, 0)
+ /** @info: Engine class/instance and capture type info */
+ u32 info;
+#define GUC_STATE_CAPTURE_GROUP_HEADER_NUM_CAPTURES GENMASK(7, 0)
+#define GUC_STATE_CAPTURE_GROUP_HEADER_CAPTURE_GROUP_TYPE GENMASK(15, 8)
+} __packed;
+
+/**
+ * struct guc_state_capture_group_t - State capture group.
+ *
+ * this is the top level structure where an error-capture dump starts
+ */
+struct guc_state_capture_group_t {
+ /** @grp_header: State capture group header. */
+ struct guc_state_capture_group_header_t grp_header;
+ /** @capture_entries: Array of state captures */
+ struct guc_state_capture_t capture_entries[];
+} __packed;
+
+#endif
diff --git a/drivers/gpu/drm/xe/xe_guc.c b/drivers/gpu/drm/xe/xe_guc.c
index de0fe9e65746..39a569f92a43 100644
--- a/drivers/gpu/drm/xe/xe_guc.c
+++ b/drivers/gpu/drm/xe/xe_guc.c
@@ -22,6 +22,7 @@
#include "xe_gt_sriov_vf.h"
#include "xe_gt_throttle.h"
#include "xe_guc_ads.h"
+#include "xe_guc_capture.h"
#include "xe_guc_ct.h"
#include "xe_guc_db_mgr.h"
#include "xe_guc_hwconfig.h"
@@ -338,6 +339,10 @@ int xe_guc_init(struct xe_guc *guc)
if (ret)
goto out;
+ ret = xe_guc_capture_init(guc);
+ if (ret)
+ goto out;
+
ret = xe_guc_ads_init(&guc->ads);
if (ret)
goto out;
diff --git a/drivers/gpu/drm/xe/xe_guc.h b/drivers/gpu/drm/xe/xe_guc.h
index e0bbf98f849d..905a8ecdb879 100644
--- a/drivers/gpu/drm/xe/xe_guc.h
+++ b/drivers/gpu/drm/xe/xe_guc.h
@@ -70,4 +70,9 @@ static inline struct xe_device *guc_to_xe(struct xe_guc *guc)
return gt_to_xe(guc_to_gt(guc));
}
+static inline struct drm_device *guc_to_drm(struct xe_guc *guc)
+{
+ return &guc_to_xe(guc)->drm;
+}
+
#endif
diff --git a/drivers/gpu/drm/xe/xe_guc_ads.c b/drivers/gpu/drm/xe/xe_guc_ads.c
index 1c60b685dbc6..533eb5285942 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_gt_ccs_mode.h"
#include "xe_gt_printk.h"
#include "xe_guc.h"
+#include "xe_guc_capture.h"
#include "xe_guc_ct.h"
#include "xe_hw_engine.h"
#include "xe_lrc.h"
@@ -148,8 +149,7 @@ static u32 guc_ads_waklv_size(struct xe_guc_ads *ads)
static size_t guc_ads_capture_size(struct xe_guc_ads *ads)
{
- /* FIXME: Allocate a proper capture list */
- return PAGE_ALIGN(PAGE_SIZE);
+ return PAGE_ALIGN(ads->capture_size);
}
static size_t guc_ads_um_queues_size(struct xe_guc_ads *ads)
@@ -398,6 +398,7 @@ int xe_guc_ads_init(struct xe_guc_ads *ads)
struct xe_bo *bo;
ads->golden_lrc_size = calculate_golden_lrc_size(ads);
+ ads->capture_size = xe_guc_capture_ads_input_worst_size(ads_to_guc(ads));
ads->regset_size = calculate_regset_size(gt);
ads->ads_waklv_size = calculate_waklv_size(ads);
@@ -417,9 +418,9 @@ int xe_guc_ads_init(struct xe_guc_ads *ads)
* xe_guc_ads_init_post_hwconfig - initialize ADS post hwconfig load
* @ads: Additional data structures object
*
- * Recalcuate golden_lrc_size & regset_size as the number hardware engines may
- * have changed after the hwconfig was loaded. Also verify the new sizes fit in
- * the already allocated ADS buffer object.
+ * Recalculate golden_lrc_size, capture_size and regset_size as the number
+ * hardware engines may have changed after the hwconfig was loaded. Also verify
+ * the new sizes fit in the already allocated ADS buffer object.
*
* Return: 0 on success, negative error code on error.
*/
@@ -431,6 +432,8 @@ int xe_guc_ads_init_post_hwconfig(struct xe_guc_ads *ads)
xe_gt_assert(gt, ads->bo);
ads->golden_lrc_size = calculate_golden_lrc_size(ads);
+ /* Calculate Capture size with worst size */
+ ads->capture_size = xe_guc_capture_ads_input_worst_size(ads_to_guc(ads));
ads->regset_size = calculate_regset_size(gt);
xe_gt_assert(gt, ads->golden_lrc_size +
@@ -530,20 +533,142 @@ static void guc_mapping_table_init(struct xe_gt *gt,
}
}
-static void guc_capture_list_init(struct xe_guc_ads *ads)
+static u32 guc_get_capture_engine_mask(struct xe_gt *gt, struct iosys_map *info_map,
+ enum guc_capture_list_class_type capture_class)
{
+ struct xe_device *xe = gt_to_xe(gt);
+ u32 mask;
+
+ switch (capture_class) {
+ case GUC_CAPTURE_LIST_CLASS_RENDER_COMPUTE:
+ mask = info_map_read(xe, info_map, engine_enabled_masks[GUC_RENDER_CLASS]);
+ mask |= info_map_read(xe, info_map, engine_enabled_masks[GUC_COMPUTE_CLASS]);
+ break;
+ case GUC_CAPTURE_LIST_CLASS_VIDEO:
+ mask = info_map_read(xe, info_map, engine_enabled_masks[GUC_VIDEO_CLASS]);
+ break;
+ case GUC_CAPTURE_LIST_CLASS_VIDEOENHANCE:
+ mask = info_map_read(xe, info_map, engine_enabled_masks[GUC_VIDEOENHANCE_CLASS]);
+ break;
+ case GUC_CAPTURE_LIST_CLASS_BLITTER:
+ mask = info_map_read(xe, info_map, engine_enabled_masks[GUC_BLITTER_CLASS]);
+ break;
+ case GUC_CAPTURE_LIST_CLASS_GSC_OTHER:
+ mask = info_map_read(xe, info_map, engine_enabled_masks[GUC_GSC_OTHER_CLASS]);
+ break;
+ default:
+ mask = 0;
+ }
+
+ return mask;
+}
+
+static inline bool get_capture_list(struct xe_guc_ads *ads, struct xe_guc *guc, struct xe_gt *gt,
+ int owner, int type, int class, u32 *total_size, size_t *size,
+ void **pptr)
+{
+ *size = 0;
+
+ if (!xe_guc_capture_getlistsize(guc, owner, type, class, size)) {
+ if (*total_size + *size > ads->capture_size)
+ xe_gt_dbg(gt, "Capture size overflow :%zu vs %d\n",
+ *total_size + *size, ads->capture_size);
+ else if (!xe_guc_capture_getlist(guc, owner, type, class, pptr))
+ return false;
+ }
+
+ return true;
+}
+
+static int guc_capture_prep_lists(struct xe_guc_ads *ads)
+{
+ struct xe_guc *guc = ads_to_guc(ads);
+ struct xe_gt *gt = ads_to_gt(ads);
+ u32 ads_ggtt, capture_offset, null_ggtt, total_size = 0;
+ struct iosys_map info_map;
+ size_t size = 0;
+ void *ptr;
int i, j;
- u32 addr = xe_bo_ggtt_addr(ads->bo) + guc_ads_capture_offset(ads);
- /* FIXME: Populate a proper capture list */
+ capture_offset = guc_ads_capture_offset(ads);
+ ads_ggtt = xe_bo_ggtt_addr(ads->bo);
+ info_map = IOSYS_MAP_INIT_OFFSET(ads_to_map(ads),
+ offsetof(struct __guc_ads_blob, system_info));
+
+ /* first, set aside the first page for a capture_list with zero descriptors */
+ total_size = PAGE_SIZE;
+ if (!xe_guc_capture_getnullheader(guc, &ptr, &size))
+ xe_map_memcpy_to(ads_to_xe(ads), ads_to_map(ads), capture_offset, ptr, size);
+
+ null_ggtt = ads_ggtt + capture_offset;
+ capture_offset += PAGE_SIZE;
+
+ /*
+ * Populate capture list : at this point adps is already allocated and
+ * mapped to worst case size
+ */
for (i = 0; i < GUC_CAPTURE_LIST_INDEX_MAX; i++) {
- for (j = 0; j < GUC_MAX_ENGINE_CLASSES; j++) {
- ads_blob_write(ads, ads.capture_instance[i][j], addr);
- ads_blob_write(ads, ads.capture_class[i][j], addr);
+ bool write_empty_list;
+
+ for (j = 0; j < GUC_CAPTURE_LIST_CLASS_MAX; j++) {
+ u32 engine_mask = guc_get_capture_engine_mask(gt, &info_map, j);
+ /* null list if we dont have said engine or list */
+ if (!engine_mask) {
+ ads_blob_write(ads, ads.capture_class[i][j], null_ggtt);
+ ads_blob_write(ads, ads.capture_instance[i][j], null_ggtt);
+ continue;
+ }
+
+ /* engine exists: start with engine-class registers */
+ write_empty_list = get_capture_list(ads, guc, gt, i,
+ GUC_STATE_CAPTURE_TYPE_ENGINE_CLASS,
+ j, &total_size, &size, &ptr);
+ if (!write_empty_list) {
+ ads_blob_write(ads, ads.capture_class[i][j],
+ ads_ggtt + capture_offset);
+ xe_map_memcpy_to(ads_to_xe(ads), ads_to_map(ads), capture_offset,
+ ptr, size);
+ total_size += size;
+ capture_offset += size;
+ } else {
+ ads_blob_write(ads, ads.capture_class[i][j], null_ggtt);
+ }
+
+ /* engine exists: next, engine-instance registers */
+ write_empty_list = get_capture_list(ads, guc, gt, i,
+ GUC_STATE_CAPTURE_TYPE_ENGINE_INSTANCE,
+ j, &total_size, &size, &ptr);
+ if (!write_empty_list) {
+ ads_blob_write(ads, ads.capture_instance[i][j],
+ ads_ggtt + capture_offset);
+ xe_map_memcpy_to(ads_to_xe(ads), ads_to_map(ads), capture_offset,
+ ptr, size);
+ total_size += size;
+ capture_offset += size;
+ } else {
+ ads_blob_write(ads, ads.capture_instance[i][j], null_ggtt);
+ }
}
- ads_blob_write(ads, ads.capture_global[i], addr);
+ /* global registers is last in our PF/VF loops */
+ write_empty_list = get_capture_list(ads, guc, gt, i,
+ GUC_STATE_CAPTURE_TYPE_GLOBAL,
+ 0, &total_size, &size, &ptr);
+ if (!write_empty_list) {
+ ads_blob_write(ads, ads.capture_global[i], ads_ggtt + capture_offset);
+ xe_map_memcpy_to(ads_to_xe(ads), ads_to_map(ads), capture_offset, ptr,
+ size);
+ total_size += size;
+ capture_offset += size;
+ } else {
+ ads_blob_write(ads, ads.capture_global[i], null_ggtt);
+ }
}
+
+ if (ads->capture_size != PAGE_ALIGN(total_size))
+ xe_gt_dbg(gt, "ADS capture alloc size changed from %d to %d\n",
+ ads->capture_size, PAGE_ALIGN(total_size));
+ return PAGE_ALIGN(total_size);
}
static void guc_mmio_regset_write_one(struct xe_guc_ads *ads,
@@ -732,7 +857,7 @@ void xe_guc_ads_populate(struct xe_guc_ads *ads)
guc_mmio_reg_state_init(ads);
guc_prep_golden_lrc_null(ads);
guc_mapping_table_init(gt, &info_map);
- guc_capture_list_init(ads);
+ guc_capture_prep_lists(ads);
guc_doorbell_init(ads);
guc_waklv_init(ads);
diff --git a/drivers/gpu/drm/xe/xe_guc_ads_types.h b/drivers/gpu/drm/xe/xe_guc_ads_types.h
index 2de5decfe0fd..70c132458ac3 100644
--- a/drivers/gpu/drm/xe/xe_guc_ads_types.h
+++ b/drivers/gpu/drm/xe/xe_guc_ads_types.h
@@ -22,6 +22,8 @@ struct xe_guc_ads {
u32 regset_size;
/** @ads_waklv_size: total waklv size supported by platform */
u32 ads_waklv_size;
+ /** @capture_size: size of register set passed to GuC for capture */
+ u32 capture_size;
};
#endif
diff --git a/drivers/gpu/drm/xe/xe_guc_capture.c b/drivers/gpu/drm/xe/xe_guc_capture.c
new file mode 100644
index 000000000000..f520c5fe0363
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_guc_capture.c
@@ -0,0 +1,454 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2021-2024 Intel Corporation
+ */
+
+#include <linux/types.h>
+
+#include <drm/drm_managed.h>
+#include <drm/drm_print.h>
+
+#include "abi/guc_actions_abi.h"
+#include "abi/guc_capture_abi.h"
+#include "regs/xe_engine_regs.h"
+#include "regs/xe_gt_regs.h"
+#include "regs/xe_guc_regs.h"
+#include "regs/xe_regs.h"
+
+#include "xe_bo.h"
+#include "xe_device.h"
+#include "xe_exec_queue_types.h"
+#include "xe_gt.h"
+#include "xe_gt_mcr.h"
+#include "xe_gt_printk.h"
+#include "xe_guc.h"
+#include "xe_guc_capture.h"
+#include "xe_guc_capture_types.h"
+#include "xe_guc_ct.h"
+#include "xe_guc_log.h"
+#include "xe_guc_submit.h"
+#include "xe_hw_engine_types.h"
+#include "xe_macros.h"
+#include "xe_map.h"
+
+/*
+ * Define all device tables of GuC error capture register lists
+ * NOTE: For engine-registers, GuC only needs the register offsets
+ * from the engine-mmio-base
+ */
+#define COMMON_XELP_BASE_GLOBAL \
+ { FORCEWAKE_GT, 0, 0}
+
+#define COMMON_BASE_ENGINE_INSTANCE \
+ { RING_ESR(0), 0, 0}, \
+ { RING_EMR(0), 0, 0}, \
+ { RING_EIR(0), 0, 0}, \
+ { RING_EXECLIST_STATUS_HI(0), 0, 0}, \
+ { RING_EXECLIST_STATUS_LO(0), 0, 0}, \
+ { RING_DMA_FADD(0), 0, 0}, \
+ { RING_DMA_FADD_UDW(0), 0, 0}, \
+ { RING_IPEHR(0), 0, 0}, \
+ { RING_BBADDR(0), 0, 0}, \
+ { RING_BBADDR_UDW(0), 0, 0}, \
+ { RING_ACTHD(0), 0, 0}, \
+ { RING_ACTHD_UDW(0), 0, 0}, \
+ { RING_START(0), 0, 0}, \
+ { RING_HEAD(0), 0, 0}, \
+ { RING_TAIL(0), 0, 0}, \
+ { RING_CTL(0), 0, 0}, \
+ { RING_MI_MODE(0), 0, 0}, \
+ { RING_HWS_PGA(0), 0, 0}, \
+ { RING_MODE(0), 0, 0}
+
+/* XE_LP Global */
+static const struct __guc_mmio_reg_descr xe_lp_global_regs[] = {
+ COMMON_XELP_BASE_GLOBAL,
+};
+
+/* Render / Compute Per-Engine-Instance */
+static const struct __guc_mmio_reg_descr xe_rc_inst_regs[] = {
+ COMMON_BASE_ENGINE_INSTANCE,
+};
+
+/* Media Decode/Encode Per-Engine-Instance */
+static const struct __guc_mmio_reg_descr xe_vd_inst_regs[] = {
+ COMMON_BASE_ENGINE_INSTANCE,
+};
+
+/* Video Enhancement Per-Engine-Instance */
+static const struct __guc_mmio_reg_descr xe_vec_inst_regs[] = {
+ COMMON_BASE_ENGINE_INSTANCE,
+};
+
+/* Blitter Per-Engine-Instance */
+static const struct __guc_mmio_reg_descr xe_blt_inst_regs[] = {
+ COMMON_BASE_ENGINE_INSTANCE,
+};
+
+/* XE_LP - GSC Per-Engine-Instance */
+static const struct __guc_mmio_reg_descr xe_lp_gsc_inst_regs[] = {
+ COMMON_BASE_ENGINE_INSTANCE,
+};
+
+/*
+ * Empty list to prevent warnings about unknown class/instance types
+ * as not all class/instance types have entries on all platforms.
+ */
+static const struct __guc_mmio_reg_descr empty_regs_list[] = {
+};
+
+#define TO_GCAP_DEF_OWNER(x) (GUC_CAPTURE_LIST_INDEX_##x)
+#define TO_GCAP_DEF_TYPE(x) (GUC_STATE_CAPTURE_TYPE_##x)
+#define MAKE_REGLIST(regslist, regsowner, regstype, class) \
+ { \
+ regslist, \
+ ARRAY_SIZE(regslist), \
+ TO_GCAP_DEF_OWNER(regsowner), \
+ TO_GCAP_DEF_TYPE(regstype), \
+ class, \
+ }
+
+/* List of lists */
+static const struct __guc_mmio_reg_descr_group xe_lp_lists[] = {
+ MAKE_REGLIST(xe_lp_global_regs, PF, GLOBAL, 0),
+ MAKE_REGLIST(empty_regs_list, PF, ENGINE_CLASS, GUC_CAPTURE_LIST_CLASS_RENDER_COMPUTE),
+ MAKE_REGLIST(xe_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(empty_regs_list, 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",
+ "Instance",
+};
+
+static const char * const capture_engine_class_names[] = {
+ "Render/Compute",
+ "Video",
+ "VideoEnhance",
+ "Blitter",
+ "GSC-Other",
+};
+
+struct __guc_capture_ads_cache {
+ bool is_valid;
+ void *ptr;
+ size_t size;
+ int status;
+};
+
+struct xe_guc_state_capture {
+ const struct __guc_mmio_reg_descr_group *reglists;
+ struct __guc_capture_ads_cache ads_cache[GUC_CAPTURE_LIST_INDEX_MAX]
+ [GUC_STATE_CAPTURE_TYPE_MAX]
+ [GUC_CAPTURE_LIST_CLASS_MAX];
+ void *ads_null_cache;
+};
+
+static const struct __guc_mmio_reg_descr_group *
+guc_capture_get_one_list(const struct __guc_mmio_reg_descr_group *reglists,
+ u32 owner, u32 type, enum guc_capture_list_class_type capture_class)
+{
+ int i;
+
+ if (!reglists)
+ return NULL;
+
+ for (i = 0; reglists[i].list; ++i) {
+ if (reglists[i].owner == owner && reglists[i].type == type &&
+ (reglists[i].engine == capture_class ||
+ reglists[i].type == GUC_STATE_CAPTURE_TYPE_GLOBAL))
+ return ®lists[i];
+ }
+
+ return NULL;
+}
+
+static const struct __guc_mmio_reg_descr_group *
+guc_capture_get_device_reglist(struct xe_guc *guc)
+{
+ return xe_lp_lists;
+}
+
+static int
+guc_capture_list_init(struct xe_guc *guc, u32 owner, u32 type,
+ enum guc_capture_list_class_type capture_class, struct guc_mmio_reg *ptr,
+ u16 num_entries)
+{
+ u32 i = 0;
+ const struct __guc_mmio_reg_descr_group *reglists = guc->capture->reglists;
+ const struct __guc_mmio_reg_descr_group *match;
+
+ if (!reglists)
+ return -ENODEV;
+
+ match = guc_capture_get_one_list(reglists, owner, type, capture_class);
+ if (!match)
+ return -ENODATA;
+
+ for (i = 0; i < num_entries && i < match->num_regs; ++i) {
+ ptr[i].offset = match->list[i].reg.addr;
+ ptr[i].value = 0xDEADF00D;
+ ptr[i].flags = match->list[i].flags;
+ ptr[i].mask = match->list[i].mask;
+ }
+
+ if (i < num_entries)
+ xe_gt_dbg(guc_to_gt(guc), "Got short capture reglist init: %d out %d.\n", i,
+ num_entries);
+
+ return 0;
+}
+
+static int
+guc_cap_list_num_regs(struct xe_guc_state_capture *gc, u32 owner, u32 type,
+ enum guc_capture_list_class_type capture_class)
+{
+ const struct __guc_mmio_reg_descr_group *match;
+
+ match = guc_capture_get_one_list(gc->reglists, owner, type, capture_class);
+ if (!match)
+ return 0;
+
+ return match->num_regs;
+}
+
+static int
+guc_capture_getlistsize(struct xe_guc *guc, u32 owner, u32 type,
+ enum guc_capture_list_class_type capture_class,
+ size_t *size, bool is_purpose_est)
+{
+ struct xe_guc_state_capture *gc = guc->capture;
+ struct xe_gt *gt = guc_to_gt(guc);
+ struct __guc_capture_ads_cache *cache;
+ int num_regs;
+
+ xe_gt_assert(gt, type < GUC_STATE_CAPTURE_TYPE_MAX);
+ xe_gt_assert(gt, capture_class < GUC_CAPTURE_LIST_CLASS_MAX);
+
+ cache = &gc->ads_cache[owner][type][capture_class];
+ if (!gc->reglists) {
+ xe_gt_warn(gt, "No capture reglist for this device\n");
+ return -ENODEV;
+ }
+
+ if (cache->is_valid) {
+ *size = cache->size;
+ return cache->status;
+ }
+
+ if (!is_purpose_est && owner == GUC_CAPTURE_LIST_INDEX_PF &&
+ !guc_capture_get_one_list(gc->reglists, owner, type, capture_class)) {
+ if (type == GUC_STATE_CAPTURE_TYPE_GLOBAL)
+ xe_gt_warn(gt, "Missing capture reglist: global!\n");
+ else
+ xe_gt_warn(gt, "Missing capture reglist: %s(%u):%s(%u)!\n",
+ capture_list_type_names[type], type,
+ capture_engine_class_names[capture_class], capture_class);
+ return -ENODEV;
+ }
+
+ num_regs = guc_cap_list_num_regs(gc, owner, type, capture_class);
+ /* intentional empty lists can exist depending on hw config */
+ if (!num_regs)
+ return -ENODATA;
+
+ if (size)
+ *size = PAGE_ALIGN((sizeof(struct guc_debug_capture_list)) +
+ (num_regs * sizeof(struct guc_mmio_reg)));
+
+ return 0;
+}
+
+/**
+ * xe_guc_capture_getlistsize - Get list size for owner/type/class combination
+ * @guc: The GuC object
+ * @owner: PF/VF owner
+ * @type: GuC capture register type
+ * @capture_class: GuC capture engine class id
+ * @size: Point to the size
+ *
+ * This function will get the list for the owner/type/class combination, and
+ * return the page aligned list size.
+ *
+ * Returns: 0 on success or a negative error code on failure.
+ */
+int
+xe_guc_capture_getlistsize(struct xe_guc *guc, u32 owner, u32 type,
+ enum guc_capture_list_class_type capture_class, size_t *size)
+{
+ return guc_capture_getlistsize(guc, owner, type, capture_class, size, false);
+}
+
+/**
+ * xe_guc_capture_getlist - Get register capture list for owner/type/class
+ * combination
+ * @guc: The GuC object
+ * @owner: PF/VF owner
+ * @type: GuC capture register type
+ * @capture_class: GuC capture engine class id
+ * @outptr: Point to cached register capture list
+ *
+ * This function will get the register capture list for the owner/type/class
+ * combination.
+ *
+ * Returns: 0 on success or a negative error code on failure.
+ */
+int
+xe_guc_capture_getlist(struct xe_guc *guc, u32 owner, u32 type,
+ enum guc_capture_list_class_type capture_class, void **outptr)
+{
+ struct xe_guc_state_capture *gc = guc->capture;
+ struct __guc_capture_ads_cache *cache = &gc->ads_cache[owner][type][capture_class];
+ struct guc_debug_capture_list *listnode;
+ int ret, num_regs;
+ u8 *caplist, *tmp;
+ size_t size = 0;
+
+ if (!gc->reglists)
+ return -ENODEV;
+
+ if (cache->is_valid) {
+ *outptr = cache->ptr;
+ return cache->status;
+ }
+
+ ret = xe_guc_capture_getlistsize(guc, owner, type, capture_class, &size);
+ if (ret) {
+ cache->is_valid = true;
+ cache->ptr = NULL;
+ cache->size = 0;
+ cache->status = ret;
+ return ret;
+ }
+
+ caplist = drmm_kzalloc(guc_to_drm(guc), size, GFP_KERNEL);
+ if (!caplist)
+ return -ENOMEM;
+
+ /* populate capture list header */
+ tmp = caplist;
+ num_regs = guc_cap_list_num_regs(guc->capture, owner, type, capture_class);
+ listnode = (struct guc_debug_capture_list *)tmp;
+ listnode->header.info = FIELD_PREP(GUC_CAPTURELISTHDR_NUMDESCR, (u32)num_regs);
+
+ /* populate list of register descriptor */
+ tmp += sizeof(struct guc_debug_capture_list);
+ guc_capture_list_init(guc, owner, type, capture_class,
+ (struct guc_mmio_reg *)tmp, num_regs);
+
+ /* cache this list */
+ cache->is_valid = true;
+ cache->ptr = caplist;
+ cache->size = size;
+ cache->status = 0;
+
+ *outptr = caplist;
+
+ return 0;
+}
+
+/**
+ * xe_guc_capture_getnullheader - Get a null list for register capture
+ * @guc: The GuC object
+ * @outptr: Point to cached register capture list
+ * @size: Point to the size
+ *
+ * This function will alloc for a null list for register capture.
+ *
+ * Returns: 0 on success or a negative error code on failure.
+ */
+int
+xe_guc_capture_getnullheader(struct xe_guc *guc, void **outptr, size_t *size)
+{
+ struct xe_guc_state_capture *gc = guc->capture;
+ int tmp = sizeof(u32) * 4;
+ void *null_header;
+
+ if (gc->ads_null_cache) {
+ *outptr = gc->ads_null_cache;
+ *size = tmp;
+ return 0;
+ }
+
+ null_header = drmm_kzalloc(guc_to_drm(guc), tmp, GFP_KERNEL);
+ if (!null_header)
+ return -ENOMEM;
+
+ gc->ads_null_cache = null_header;
+ *outptr = null_header;
+ *size = tmp;
+
+ return 0;
+}
+
+/**
+ * xe_guc_capture_ads_input_worst_size - Calculate the worst size for GuC register capture
+ * @guc: point to xe_guc structure
+ *
+ * Calculate the worst size for GuC register capture by including all possible engines classes.
+ *
+ * Returns: Calculated size
+ */
+size_t xe_guc_capture_ads_input_worst_size(struct xe_guc *guc)
+{
+ size_t total_size, class_size, instance_size, global_size;
+ int i, j;
+
+ /*
+ * This function calculates the worst case register lists size by
+ * including all possible engines classes. It is called during the
+ * first of a two-phase GuC (and ADS-population) initialization
+ * sequence, that is, during the pre-hwconfig phase before we have
+ * the exact engine fusing info.
+ */
+ total_size = PAGE_SIZE; /* Pad a page in front for empty lists */
+ for (i = 0; i < GUC_CAPTURE_LIST_INDEX_MAX; i++) {
+ for (j = 0; j < GUC_CAPTURE_LIST_CLASS_MAX; j++) {
+ if (xe_guc_capture_getlistsize(guc, i,
+ GUC_STATE_CAPTURE_TYPE_ENGINE_CLASS,
+ j, &class_size) < 0)
+ class_size = 0;
+ if (xe_guc_capture_getlistsize(guc, i,
+ GUC_STATE_CAPTURE_TYPE_ENGINE_INSTANCE,
+ j, &instance_size) < 0)
+ instance_size = 0;
+ total_size += class_size + instance_size;
+ }
+ if (xe_guc_capture_getlistsize(guc, i,
+ GUC_STATE_CAPTURE_TYPE_GLOBAL,
+ 0, &global_size) < 0)
+ global_size = 0;
+ total_size += global_size;
+ }
+
+ return PAGE_ALIGN(total_size);
+}
+
+/**
+ * xe_guc_capture_init - Init for GuC register capture
+ * @guc: The GuC object
+ *
+ * Init for GuC register capture, alloc memory for capture data structure.
+ *
+ * Returns: 0 if success.
+ -ENOMEM if out of memory
+ */
+int xe_guc_capture_init(struct xe_guc *guc)
+{
+ guc->capture = drmm_kzalloc(guc_to_drm(guc), sizeof(*guc->capture), GFP_KERNEL);
+ if (!guc->capture)
+ return -ENOMEM;
+
+ guc->capture->reglists = guc_capture_get_device_reglist(guc);
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/xe/xe_guc_capture.h b/drivers/gpu/drm/xe/xe_guc_capture.h
new file mode 100644
index 000000000000..55c1cedcb923
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_guc_capture.h
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2021-2024 Intel Corporation
+ */
+
+#ifndef _XE_GUC_CAPTURE_H
+#define _XE_GUC_CAPTURE_H
+
+#include <linux/types.h>
+#include "abi/guc_capture_abi.h"
+#include "xe_guc.h"
+#include "xe_guc_fwif.h"
+
+struct xe_guc;
+
+static inline enum guc_capture_list_class_type xe_guc_class_to_capture_class(u16 class)
+{
+ switch (class) {
+ case GUC_RENDER_CLASS:
+ case GUC_COMPUTE_CLASS:
+ return GUC_CAPTURE_LIST_CLASS_RENDER_COMPUTE;
+ case GUC_GSC_OTHER_CLASS:
+ return GUC_CAPTURE_LIST_CLASS_GSC_OTHER;
+ case GUC_VIDEO_CLASS:
+ case GUC_VIDEOENHANCE_CLASS:
+ case GUC_BLITTER_CLASS:
+ return class;
+ default:
+ XE_WARN_ON(class);
+ return GUC_CAPTURE_LIST_CLASS_MAX;
+ }
+}
+
+static inline enum guc_capture_list_class_type
+xe_engine_class_to_guc_capture_class(enum xe_engine_class class)
+{
+ return xe_guc_class_to_capture_class(xe_engine_class_to_guc_class(class));
+}
+
+int xe_guc_capture_getlist(struct xe_guc *guc, u32 owner, u32 type,
+ enum guc_capture_list_class_type capture_class, void **outptr);
+int xe_guc_capture_getlistsize(struct xe_guc *guc, u32 owner, u32 type,
+ enum guc_capture_list_class_type capture_class, size_t *size);
+int xe_guc_capture_getnullheader(struct xe_guc *guc, void **outptr, size_t *size);
+size_t xe_guc_capture_ads_input_worst_size(struct xe_guc *guc);
+int xe_guc_capture_init(struct xe_guc *guc);
+
+#endif
diff --git a/drivers/gpu/drm/xe/xe_guc_capture_types.h b/drivers/gpu/drm/xe/xe_guc_capture_types.h
new file mode 100644
index 000000000000..839148c9fa0f
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_guc_capture_types.h
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2021-2024 Intel Corporation
+ */
+
+#ifndef _XE_GUC_CAPTURE_TYPES_H
+#define _XE_GUC_CAPTURE_TYPES_H
+
+#include <linux/types.h>
+#include "regs/xe_reg_defs.h"
+
+struct xe_guc;
+
+/**
+ * struct __guc_mmio_reg_descr - GuC mmio register descriptor
+ *
+ * xe_guc_capture module uses these structures to define a register
+ * (offsets, names, flags,...) that are used at the ADS regisration
+ * time as well as during runtime processing and reporting of error-
+ * capture states generated by GuC just prior to engine reset events.
+ */
+struct __guc_mmio_reg_descr {
+ /** @reg: the register */
+ struct xe_reg reg;
+ /** @flags: Flags for the register */
+ u32 flags;
+ /** @mask: The mask to apply */
+ u32 mask;
+ /** @regname: Name of the register */
+ const char *regname;
+};
+
+/**
+ * struct __guc_mmio_reg_descr_group - The group of register descriptor
+ *
+ * xe_guc_capture module uses these structures to maintain static
+ * tables (per unique platform) that consists of lists of registers
+ * (offsets, names, flags,...) that are used at the ADS regisration
+ * time as well as during runtime processing and reporting of error-
+ * capture states generated by GuC just prior to engine reset events.
+ */
+struct __guc_mmio_reg_descr_group {
+ /** @list: The register list */
+ const struct __guc_mmio_reg_descr *list;
+ /** @num_regs: Count of registers in the list */
+ u32 num_regs;
+ /** @owner: PF/VF owner */
+ u32 owner; /* see enum guc_capture_list_index_type */
+ /** @type: GuC capture register type */
+ u32 type; /* see enum guc_state_capture_type */
+ /** @engine: The engine class */
+ u32 engine; /* see enum guc_capture_list_class_type */
+};
+
+#endif
diff --git a/drivers/gpu/drm/xe/xe_guc_fwif.h b/drivers/gpu/drm/xe/xe_guc_fwif.h
index 19ee71aeaf17..01e3ab590c3a 100644
--- a/drivers/gpu/drm/xe/xe_guc_fwif.h
+++ b/drivers/gpu/drm/xe/xe_guc_fwif.h
@@ -8,7 +8,9 @@
#include <linux/bits.h>
+#include "abi/guc_capture_abi.h"
#include "abi/guc_klvs_abi.h"
+#include "xe_hw_engine_types.h"
#define G2H_LEN_DW_SCHED_CONTEXT_MODE_SET 4
#define G2H_LEN_DW_DEREGISTER_CONTEXT 3
@@ -157,24 +159,6 @@ struct guc_policies {
u32 reserved[4];
} __packed;
-/* GuC MMIO reg state struct */
-struct guc_mmio_reg {
- u32 offset;
- u32 value;
- u32 flags;
- u32 mask;
-#define GUC_REGSET_MASKED BIT(0)
-#define GUC_REGSET_MASKED_WITH_VALUE BIT(2)
-#define GUC_REGSET_RESTORE_ONLY BIT(3)
-} __packed;
-
-/* GuC register sets */
-struct guc_mmio_reg_set {
- u32 address;
- u16 count;
- u16 reserved;
-} __packed;
-
/* Generic GT SysInfo data types */
#define GUC_GENERIC_GT_SYSINFO_SLICE_ENABLED 0
#define GUC_GENERIC_GT_SYSINFO_VDBOX_SFC_SUPPORT_MASK 1
@@ -188,12 +172,6 @@ struct guc_gt_system_info {
u32 generic_gt_sysinfo[GUC_GENERIC_GT_SYSINFO_MAX];
} __packed;
-enum {
- GUC_CAPTURE_LIST_INDEX_PF = 0,
- GUC_CAPTURE_LIST_INDEX_VF = 1,
- GUC_CAPTURE_LIST_INDEX_MAX = 2,
-};
-
/* GuC Additional Data Struct */
struct guc_ads {
struct guc_mmio_reg_set reg_state_list[GUC_MAX_ENGINE_CLASSES][GUC_MAX_INSTANCES_PER_CLASS];
diff --git a/drivers/gpu/drm/xe/xe_guc_types.h b/drivers/gpu/drm/xe/xe_guc_types.h
index 546ac6350a31..6d7d0976b7af 100644
--- a/drivers/gpu/drm/xe/xe_guc_types.h
+++ b/drivers/gpu/drm/xe/xe_guc_types.h
@@ -58,6 +58,8 @@ struct xe_guc {
struct xe_guc_ads ads;
/** @ct: GuC ct */
struct xe_guc_ct ct;
+ /** @capture: the error-state-capture module's data and objects */
+ struct xe_guc_state_capture *capture;
/** @pc: GuC Power Conservation */
struct xe_guc_pc pc;
/** @dbm: GuC Doorbell Manager */
--
2.34.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v13 2/5] drm/xe/guc: Add XE_LP steered register lists
2024-07-19 20:29 [PATCH v13 0/5] drm/xe/guc: Add GuC based register capture for error capture Zhanjun Dong
2024-07-19 20:29 ` [PATCH v13 1/5] drm/xe/guc: Prepare GuC register list and update ADS size " Zhanjun Dong
@ 2024-07-19 20:29 ` Zhanjun Dong
2024-07-19 20:29 ` [PATCH v13 3/5] drm/xe/guc: Add capture size check in GuC log buffer Zhanjun Dong
` (5 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Zhanjun Dong @ 2024-07-19 20:29 UTC (permalink / raw)
To: intel-xe; +Cc: Zhanjun Dong, Alan Previn
Add the ability for runtime allocation and freeing of
steered register list extentions that depend on the
detected HW config fuses.
Signed-off-by: Zhanjun Dong <zhanjun.dong@intel.com>
Reviewed-by: Alan Previn <alan.previn.teres.alexis@intel.com>
---
drivers/gpu/drm/xe/xe_guc_capture.c | 159 +++++++++++++++++++++-
drivers/gpu/drm/xe/xe_guc_capture_types.h | 2 +
2 files changed, 159 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/xe/xe_guc_capture.c b/drivers/gpu/drm/xe/xe_guc_capture.c
index f520c5fe0363..bf11cd2aaf25 100644
--- a/drivers/gpu/drm/xe/xe_guc_capture.c
+++ b/drivers/gpu/drm/xe/xe_guc_capture.c
@@ -106,6 +106,7 @@ static const struct __guc_mmio_reg_descr empty_regs_list[] = {
TO_GCAP_DEF_OWNER(regsowner), \
TO_GCAP_DEF_TYPE(regstype), \
class, \
+ NULL, \
}
/* List of lists */
@@ -147,6 +148,11 @@ struct __guc_capture_ads_cache {
struct xe_guc_state_capture {
const struct __guc_mmio_reg_descr_group *reglists;
+ /**
+ * NOTE: steered registers have multiple instances depending on the HW configuration
+ * (slices or dual-sub-slices) and thus depends on HW fuses discovered
+ */
+ struct __guc_mmio_reg_descr_group *extlists;
struct __guc_capture_ads_cache ads_cache[GUC_CAPTURE_LIST_INDEX_MAX]
[GUC_STATE_CAPTURE_TYPE_MAX]
[GUC_CAPTURE_LIST_CLASS_MAX];
@@ -172,9 +178,137 @@ guc_capture_get_one_list(const struct __guc_mmio_reg_descr_group *reglists,
return NULL;
}
+static struct __guc_mmio_reg_descr_group *
+guc_capture_get_one_ext_list(struct __guc_mmio_reg_descr_group *reglists,
+ u32 owner, u32 type, u32 id)
+{
+ int i;
+
+ if (!reglists)
+ return NULL;
+
+ for (i = 0; reglists[i].extlist; ++i) {
+ if (reglists[i].owner == owner && reglists[i].type == type &&
+ (reglists[i].engine == id || reglists[i].type == GUC_STATE_CAPTURE_TYPE_GLOBAL))
+ return ®lists[i];
+ }
+
+ return NULL;
+}
+
+struct __ext_steer_reg {
+ const char *name;
+ struct xe_reg_mcr reg;
+};
+
+static const struct __ext_steer_reg xe_extregs[] = {
+ {"SAMPLER_INSTDONE", SAMPLER_INSTDONE},
+ {"ROW_INSTDONE", ROW_INSTDONE}
+};
+
+static const struct __ext_steer_reg xehpg_extregs[] = {
+ {"XEHPG_INSTDONE_GEOM_SVGUNIT", XEHPG_INSTDONE_GEOM_SVGUNIT}
+};
+
+static void __fill_ext_reg(struct __guc_mmio_reg_descr *ext,
+ const struct __ext_steer_reg *extlist,
+ int slice_id, int subslice_id)
+{
+ ext->reg = XE_REG(extlist->reg.__reg.addr);
+ ext->flags = FIELD_PREP(GUC_REGSET_STEERING_GROUP, slice_id);
+ ext->flags |= FIELD_PREP(GUC_REGSET_STEERING_INSTANCE, subslice_id);
+}
+
+static int
+__alloc_ext_regs(struct drm_device *drm, struct __guc_mmio_reg_descr_group *newlist,
+ const struct __guc_mmio_reg_descr_group *rootlist, int num_regs)
+{
+ struct __guc_mmio_reg_descr *list;
+
+ list = drmm_kzalloc(drm, num_regs * sizeof(struct __guc_mmio_reg_descr), GFP_KERNEL);
+ if (!list)
+ return -ENOMEM;
+
+ newlist->extlist = list;
+ newlist->num_regs = num_regs;
+ newlist->owner = rootlist->owner;
+ newlist->engine = rootlist->engine;
+ newlist->type = rootlist->type;
+
+ return 0;
+}
+
+static void
+guc_capture_alloc_steered_lists(struct xe_guc *guc, const struct __guc_mmio_reg_descr_group *lists)
+{
+ struct xe_gt *gt = guc_to_gt(guc);
+ u16 slice, subslice;
+ int iter, i, num_steer_regs, num_tot_regs = 0;
+ const struct __guc_mmio_reg_descr_group *list;
+ struct __guc_mmio_reg_descr_group *extlists;
+ struct __guc_mmio_reg_descr *extarray;
+ bool has_xehpg_extregs;
+ struct drm_device *drm = >_to_xe(gt)->drm;
+
+ /* steered registers currently only exist for the render-class */
+ list = guc_capture_get_one_list(lists, GUC_CAPTURE_LIST_INDEX_PF,
+ GUC_STATE_CAPTURE_TYPE_ENGINE_CLASS,
+ GUC_CAPTURE_LIST_CLASS_RENDER_COMPUTE);
+ /* skip if extlists was previously allocated */
+ if (!list || guc->capture->extlists)
+ return;
+
+ has_xehpg_extregs = GRAPHICS_VERx100(gt_to_xe(gt)) >= 1255;
+
+ num_steer_regs = ARRAY_SIZE(xe_extregs);
+ if (has_xehpg_extregs)
+ num_steer_regs += ARRAY_SIZE(xehpg_extregs);
+
+ num_tot_regs = num_steer_regs * bitmap_weight(gt->fuse_topo.g_dss_mask,
+ sizeof(gt->fuse_topo.g_dss_mask) * 8);
+ if (!num_tot_regs)
+ return;
+
+ /* allocate an extra for an end marker */
+ extlists = drmm_kzalloc(drm, 2 * sizeof(struct __guc_mmio_reg_descr_group), GFP_KERNEL);
+ if (!extlists)
+ return;
+
+ if (__alloc_ext_regs(drm, &extlists[0], list, num_tot_regs)) {
+ drmm_kfree(drm, extlists);
+ return;
+ }
+
+ extarray = extlists[0].extlist;
+ for_each_dss_steering(iter, gt, slice, subslice) {
+ for (i = 0; i < ARRAY_SIZE(xe_extregs); ++i) {
+ __fill_ext_reg(extarray, &xe_extregs[i], slice, subslice);
+ ++extarray;
+ }
+
+ if (has_xehpg_extregs) {
+ for (i = 0; i < ARRAY_SIZE(xehpg_extregs); ++i) {
+ __fill_ext_reg(extarray, &xehpg_extregs[i], slice, subslice);
+ ++extarray;
+ }
+ }
+ }
+
+ xe_gt_dbg(guc_to_gt(guc), "capture found %d ext-regs.\n", num_tot_regs);
+ guc->capture->extlists = extlists;
+}
+
static const struct __guc_mmio_reg_descr_group *
guc_capture_get_device_reglist(struct xe_guc *guc)
{
+ /*
+ * For certain engine classes, there are slice and subslice
+ * level registers requiring steering. We allocate and populate
+ * these at init time based on hw config add it as an extension
+ * list at the end of the pre-populated render list.
+ */
+ guc_capture_alloc_steered_lists(guc, xe_lp_lists);
+
return xe_lp_lists;
}
@@ -183,9 +317,11 @@ guc_capture_list_init(struct xe_guc *guc, u32 owner, u32 type,
enum guc_capture_list_class_type capture_class, struct guc_mmio_reg *ptr,
u16 num_entries)
{
- u32 i = 0;
+ u32 i = 0, j = 0;
const struct __guc_mmio_reg_descr_group *reglists = guc->capture->reglists;
+ struct __guc_mmio_reg_descr_group *extlists = guc->capture->extlists;
const struct __guc_mmio_reg_descr_group *match;
+ struct __guc_mmio_reg_descr_group *matchext;
if (!reglists)
return -ENODEV;
@@ -201,6 +337,17 @@ guc_capture_list_init(struct xe_guc *guc, u32 owner, u32 type,
ptr[i].mask = match->list[i].mask;
}
+ matchext = guc_capture_get_one_ext_list(extlists, owner, type, capture_class);
+ if (matchext) {
+ for (i = match->num_regs, j = 0; i < num_entries &&
+ i < (match->num_regs + matchext->num_regs) &&
+ j < matchext->num_regs; ++i, ++j) {
+ ptr[i].offset = matchext->extlist[j].reg.addr;
+ ptr[i].value = 0xDEADF00D;
+ ptr[i].flags = matchext->extlist[j].flags;
+ ptr[i].mask = matchext->extlist[j].mask;
+ }
+ }
if (i < num_entries)
xe_gt_dbg(guc_to_gt(guc), "Got short capture reglist init: %d out %d.\n", i,
num_entries);
@@ -213,12 +360,20 @@ guc_cap_list_num_regs(struct xe_guc_state_capture *gc, u32 owner, u32 type,
enum guc_capture_list_class_type capture_class)
{
const struct __guc_mmio_reg_descr_group *match;
+ struct __guc_mmio_reg_descr_group *matchext;
+ int num_regs;
match = guc_capture_get_one_list(gc->reglists, owner, type, capture_class);
if (!match)
return 0;
- return match->num_regs;
+ num_regs = match->num_regs;
+
+ matchext = guc_capture_get_one_ext_list(gc->extlists, owner, type, capture_class);
+ if (matchext)
+ num_regs += matchext->num_regs;
+
+ return num_regs;
}
static int
diff --git a/drivers/gpu/drm/xe/xe_guc_capture_types.h b/drivers/gpu/drm/xe/xe_guc_capture_types.h
index 839148c9fa0f..63cb4d7cf518 100644
--- a/drivers/gpu/drm/xe/xe_guc_capture_types.h
+++ b/drivers/gpu/drm/xe/xe_guc_capture_types.h
@@ -50,6 +50,8 @@ struct __guc_mmio_reg_descr_group {
u32 type; /* see enum guc_state_capture_type */
/** @engine: The engine class */
u32 engine; /* see enum guc_capture_list_class_type */
+ /** @extlist: the list for steered registers */
+ struct __guc_mmio_reg_descr *extlist;
};
#endif
--
2.34.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v13 3/5] drm/xe/guc: Add capture size check in GuC log buffer
2024-07-19 20:29 [PATCH v13 0/5] drm/xe/guc: Add GuC based register capture for error capture Zhanjun Dong
2024-07-19 20:29 ` [PATCH v13 1/5] drm/xe/guc: Prepare GuC register list and update ADS size " Zhanjun Dong
2024-07-19 20:29 ` [PATCH v13 2/5] drm/xe/guc: Add XE_LP steered register lists Zhanjun Dong
@ 2024-07-19 20:29 ` Zhanjun Dong
2024-07-19 20:29 ` [PATCH v13 4/5] drm/xe/guc: Extract GuC error capture lists Zhanjun Dong
` (4 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Zhanjun Dong @ 2024-07-19 20:29 UTC (permalink / raw)
To: intel-xe; +Cc: Zhanjun Dong
The capture-nodes is included in GuC log buffer, add the size check
for capture region in the whole GuC log buffer.
Add capture output size check before allocating the shared buffer.
Signed-off-by: Zhanjun Dong <zhanjun.dong@intel.com>
---
drivers/gpu/drm/xe/abi/guc_log_abi.h | 20 ++++
drivers/gpu/drm/xe/xe_guc_capture.c | 83 ++++++++++++++-
drivers/gpu/drm/xe/xe_guc_log.c | 141 +++++++++++++++++++++++++-
drivers/gpu/drm/xe/xe_guc_log.h | 12 ++-
drivers/gpu/drm/xe/xe_guc_log_types.h | 15 +++
5 files changed, 268 insertions(+), 3 deletions(-)
create mode 100644 drivers/gpu/drm/xe/abi/guc_log_abi.h
diff --git a/drivers/gpu/drm/xe/abi/guc_log_abi.h b/drivers/gpu/drm/xe/abi/guc_log_abi.h
new file mode 100644
index 000000000000..10db4ffaa17f
--- /dev/null
+++ b/drivers/gpu/drm/xe/abi/guc_log_abi.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2024 Intel Corporation
+ */
+
+#ifndef _ABI_GUC_LOG_ABI_H
+#define _ABI_GUC_LOG_ABI_H
+
+#include <linux/types.h>
+
+/* GuC logging buffer types */
+enum guc_log_buffer_type {
+ GUC_LOG_BUFFER_CRASH_DUMP,
+ GUC_LOG_BUFFER_DEBUG,
+ GUC_LOG_BUFFER_CAPTURE,
+};
+
+#define GUC_LOG_BUFFER_TYPE_MAX 3
+
+#endif
diff --git a/drivers/gpu/drm/xe/xe_guc_capture.c b/drivers/gpu/drm/xe/xe_guc_capture.c
index bf11cd2aaf25..72abba8f7bf8 100644
--- a/drivers/gpu/drm/xe/xe_guc_capture.c
+++ b/drivers/gpu/drm/xe/xe_guc_capture.c
@@ -22,6 +22,7 @@
#include "xe_gt_mcr.h"
#include "xe_gt_printk.h"
#include "xe_guc.h"
+#include "xe_guc_ads.h"
#include "xe_guc_capture.h"
#include "xe_guc_capture_types.h"
#include "xe_guc_ct.h"
@@ -588,7 +589,86 @@ size_t xe_guc_capture_ads_input_worst_size(struct xe_guc *guc)
return PAGE_ALIGN(total_size);
}
-/**
+static int guc_capture_output_size_est(struct xe_guc *guc)
+{
+ struct xe_gt *gt = guc_to_gt(guc);
+ struct xe_hw_engine *hwe;
+ enum xe_hw_engine_id id;
+
+ int capture_size = 0;
+ size_t tmp = 0;
+
+ if (!guc->capture)
+ return -ENODEV;
+
+ /*
+ * If every single engine-instance suffered a failure in quick succession but
+ * were all unrelated, then a burst of multiple error-capture events would dump
+ * registers for every one engine instance, one at a time. In this case, GuC
+ * would even dump the global-registers repeatedly.
+ *
+ * For each engine instance, there would be 1 x guc_state_capture_group_t output
+ * followed by 3 x guc_state_capture_t lists. The latter is how the register
+ * dumps are split across different register types (where the '3' are global vs class
+ * vs instance).
+ */
+ for_each_hw_engine(hwe, gt, id) {
+ enum guc_capture_list_class_type capture_class;
+
+ capture_class = xe_engine_class_to_guc_capture_class(hwe->class);
+ capture_size += sizeof(struct guc_state_capture_group_header_t) +
+ (3 * sizeof(struct guc_state_capture_header_t));
+
+ if (!guc_capture_getlistsize(guc, 0, GUC_STATE_CAPTURE_TYPE_GLOBAL,
+ 0, &tmp, true))
+ capture_size += tmp;
+ if (!guc_capture_getlistsize(guc, 0, GUC_STATE_CAPTURE_TYPE_ENGINE_CLASS,
+ capture_class, &tmp, true))
+ capture_size += tmp;
+ if (!guc_capture_getlistsize(guc, 0, GUC_STATE_CAPTURE_TYPE_ENGINE_INSTANCE,
+ capture_class, &tmp, true))
+ capture_size += tmp;
+ }
+
+ return capture_size;
+}
+
+/*
+ * Add on a 3x multiplier to allow for multiple back-to-back captures occurring
+ * before the Xe can read the data out and process it
+ */
+#define GUC_CAPTURE_OVERBUFFER_MULTIPLIER 3
+
+static void check_guc_capture_size(struct xe_guc *guc)
+{
+ int capture_size = guc_capture_output_size_est(guc);
+ int spare_size = capture_size * GUC_CAPTURE_OVERBUFFER_MULTIPLIER;
+ u32 buffer_size = xe_guc_log_section_size_capture(&guc->log);
+
+ /*
+ * NOTE: capture_size is much smaller than the capture region
+ * allocation (DG2: <80K vs 1MB).
+ * Additionally, its based on space needed to fit all engines getting
+ * reset at once within the same G2H handler task slot. This is very
+ * unlikely. However, if GuC really does run out of space for whatever
+ * reason, we will see an separate warning message when processing the
+ * G2H event capture-notification, search for:
+ * xe_guc_STATE_CAPTURE_EVENT_STATUS_NOSPACE.
+ */
+ if (capture_size < 0)
+ xe_gt_dbg(guc_to_gt(guc),
+ "Failed to calculate error state capture buffer minimum size: %d!\n",
+ capture_size);
+ if (capture_size > buffer_size)
+ xe_gt_dbg(guc_to_gt(guc), "Error state capture buffer maybe small: %d < %d\n",
+ buffer_size, capture_size);
+ else if (spare_size > buffer_size)
+ xe_gt_dbg(guc_to_gt(guc),
+ "Error state capture buffer lacks spare size: %d < %d (min = %d)\n",
+ buffer_size, spare_size, capture_size);
+}
+
+/*
* xe_guc_capture_init - Init for GuC register capture
* @guc: The GuC object
*
@@ -605,5 +685,6 @@ int xe_guc_capture_init(struct xe_guc *guc)
guc->capture->reglists = guc_capture_get_device_reglist(guc);
+ check_guc_capture_size(guc);
return 0;
}
diff --git a/drivers/gpu/drm/xe/xe_guc_log.c b/drivers/gpu/drm/xe/xe_guc_log.c
index a37ee3419428..23512870503c 100644
--- a/drivers/gpu/drm/xe/xe_guc_log.c
+++ b/drivers/gpu/drm/xe/xe_guc_log.c
@@ -9,9 +9,47 @@
#include "xe_bo.h"
#include "xe_gt.h"
+#include "xe_gt_printk.h"
+#include "xe_guc.h"
#include "xe_map.h"
#include "xe_module.h"
+#define GUC_LOG_DEFAULT_CRASH_BUFFER_SIZE CRASH_BUFFER_SIZE
+#define GUC_LOG_DEFAULT_DEBUG_BUFFER_SIZE DEBUG_BUFFER_SIZE
+#define GUC_LOG_DEFAULT_CAPTURE_BUFFER_SIZE CAPTURE_BUFFER_SIZE
+
+#define GUC_LOG_CALC_UNIT(size) (((size) % SZ_1M) == 0 ? SZ_1M : SZ_4K)
+#define GUC_LOG_CALC_FLAG(size) (((size) % SZ_1M) == 0 ? GUC_LOG_LOG_ALLOC_UNITS : 0)
+
+/* Size is +1 unit */
+#define GUC_LOG_CALC_COUNT(size) ((size) / GUC_LOG_CALC_UNIT(size) - 1)
+
+static const struct xe_log_sizes_type log_sizes[GUC_LOG_BUFFER_TYPE_MAX] = {
+ {
+ GUC_LOG_DEFAULT_CRASH_BUFFER_SIZE,
+ GUC_LOG_CALC_UNIT(GUC_LOG_DEFAULT_CRASH_BUFFER_SIZE),
+ GUC_LOG_CALC_COUNT(GUC_LOG_DEFAULT_CRASH_BUFFER_SIZE),
+ GUC_LOG_CALC_FLAG(GUC_LOG_DEFAULT_CRASH_BUFFER_SIZE)
+ },
+ {
+ GUC_LOG_DEFAULT_DEBUG_BUFFER_SIZE,
+ GUC_LOG_CALC_UNIT(GUC_LOG_DEFAULT_DEBUG_BUFFER_SIZE),
+ GUC_LOG_CALC_COUNT(GUC_LOG_DEFAULT_DEBUG_BUFFER_SIZE),
+ GUC_LOG_CALC_FLAG(GUC_LOG_DEFAULT_DEBUG_BUFFER_SIZE)
+ },
+ {
+ GUC_LOG_DEFAULT_CAPTURE_BUFFER_SIZE,
+ GUC_LOG_CALC_UNIT(GUC_LOG_DEFAULT_CAPTURE_BUFFER_SIZE),
+ GUC_LOG_CALC_COUNT(GUC_LOG_DEFAULT_CAPTURE_BUFFER_SIZE),
+ GUC_LOG_CALC_FLAG(GUC_LOG_DEFAULT_CAPTURE_BUFFER_SIZE)
+ }
+};
+
+static inline struct xe_guc *log_to_guc(struct xe_guc_log *log)
+{
+ return container_of(log, struct xe_guc, log);
+}
+
static struct xe_gt *
log_to_gt(struct xe_guc_log *log)
{
@@ -80,7 +118,8 @@ void xe_guc_log_print(struct xe_guc_log *log, struct drm_printer *p)
int xe_guc_log_init(struct xe_guc_log *log)
{
struct xe_device *xe = log_to_xe(log);
- struct xe_tile *tile = gt_to_tile(log_to_gt(log));
+ struct xe_gt *gt = log_to_gt(log);
+ struct xe_tile *tile = gt_to_tile(gt);
struct xe_bo *bo;
bo = xe_managed_bo_create_pin_map(xe, tile, guc_log_size(),
@@ -96,3 +135,103 @@ int xe_guc_log_init(struct xe_guc_log *log)
return 0;
}
+
+static u32 xe_guc_log_section_size_crash(struct xe_guc_log *log)
+{
+ return log_sizes[GUC_LOG_BUFFER_CRASH_DUMP].bytes;
+}
+
+static u32 xe_guc_log_section_size_debug(struct xe_guc_log *log)
+{
+ return log_sizes[GUC_LOG_BUFFER_DEBUG].bytes;
+}
+
+/**
+ * xe_guc_log_section_size_capture - Get capture buffer size in log sections.
+ * @log: The log object.
+ *
+ * This function will return the capture buffer size in log sections.
+ *
+ * Return: capture buffer size.
+ */
+u32 xe_guc_log_section_size_capture(struct xe_guc_log *log)
+{
+ return log_sizes[GUC_LOG_BUFFER_CAPTURE].bytes;
+}
+
+/**
+ * xe_guc_check_log_buf_overflow - Check if log buffer overflowed
+ * @log: The log object.
+ * @type: The log buffer type
+ * @full_cnt: The count of buffer full
+ *
+ * This function will check count of buffer full against previous, mismatch
+ * indicate overflowed.
+ * Update the sampled_overflow counter, if the 4 bit counter overflowed, add
+ * up 16 to correct the value.
+ *
+ * Return: True if overflowed.
+ */
+bool xe_guc_check_log_buf_overflow(struct xe_guc_log *log, enum guc_log_buffer_type type,
+ unsigned int full_cnt)
+{
+ unsigned int prev_full_cnt = log->stats[type].sampled_overflow;
+ bool overflow = false;
+
+ if (full_cnt != prev_full_cnt) {
+ overflow = true;
+
+ log->stats[type].overflow = full_cnt;
+ log->stats[type].sampled_overflow += full_cnt - prev_full_cnt;
+
+ if (full_cnt < prev_full_cnt) {
+ /* buffer_full_cnt is a 4 bit counter */
+ log->stats[type].sampled_overflow += 16;
+ }
+ xe_gt_notice(log_to_gt(log), "log buffer overflow\n");
+ }
+
+ return overflow;
+}
+
+/**
+ * xe_guc_get_log_buffer_size - Get log buffer size for a type.
+ * @log: The log object.
+ * @type: The log buffer type
+ *
+ * Return: buffer size.
+ */
+u32 xe_guc_get_log_buffer_size(struct xe_guc_log *log, enum guc_log_buffer_type type)
+{
+ switch (type) {
+ case GUC_LOG_BUFFER_CRASH_DUMP:
+ return xe_guc_log_section_size_crash(log);
+ case GUC_LOG_BUFFER_DEBUG:
+ return xe_guc_log_section_size_debug(log);
+ case GUC_LOG_BUFFER_CAPTURE:
+ return xe_guc_log_section_size_capture(log);
+ }
+ return 0;
+}
+
+/**
+ * xe_guc_get_log_buffer_offset - Get offset in log buffer for a type.
+ * @log: The log object.
+ * @type: The log buffer type
+ *
+ * This function will return the offset in the log buffer for a type.
+ * Return: buffer offset.
+ */
+u32 xe_guc_get_log_buffer_offset(struct xe_guc_log *log, enum guc_log_buffer_type type)
+{
+ enum guc_log_buffer_type i;
+ u32 offset = PAGE_SIZE;/* for the log_buffer_states */
+
+ for (i = GUC_LOG_BUFFER_CRASH_DUMP; i < GUC_LOG_BUFFER_TYPE_MAX; ++i) {
+ if (i == type)
+ break;
+ offset += xe_guc_get_log_buffer_size(log, i);
+ }
+
+ return offset;
+}
diff --git a/drivers/gpu/drm/xe/xe_guc_log.h b/drivers/gpu/drm/xe/xe_guc_log.h
index 2d25ab28b4b3..1d3b123cebd3 100644
--- a/drivers/gpu/drm/xe/xe_guc_log.h
+++ b/drivers/gpu/drm/xe/xe_guc_log.h
@@ -7,6 +7,7 @@
#define _XE_GUC_LOG_H_
#include "xe_guc_log_types.h"
+#include "xe_guc_types.h"
struct drm_printer;
@@ -17,7 +18,7 @@ struct drm_printer;
#else
#define CRASH_BUFFER_SIZE SZ_8K
#define DEBUG_BUFFER_SIZE SZ_64K
-#define CAPTURE_BUFFER_SIZE SZ_16K
+#define CAPTURE_BUFFER_SIZE SZ_1M
#endif
/*
* While we're using plain log level in i915, GuC controls are much more...
@@ -45,4 +46,13 @@ xe_guc_log_get_level(struct xe_guc_log *log)
return log->level;
}
+u32 xe_guc_log_section_size_capture(struct xe_guc_log *log);
+
+bool xe_guc_check_log_buf_overflow(struct xe_guc_log *log,
+ enum guc_log_buffer_type type,
+ unsigned int full_cnt);
+
+u32 xe_guc_get_log_buffer_size(struct xe_guc_log *log, enum guc_log_buffer_type type);
+u32 xe_guc_get_log_buffer_offset(struct xe_guc_log *log, enum guc_log_buffer_type type);
+
#endif
diff --git a/drivers/gpu/drm/xe/xe_guc_log_types.h b/drivers/gpu/drm/xe/xe_guc_log_types.h
index 125080d138a7..3da7d5262601 100644
--- a/drivers/gpu/drm/xe/xe_guc_log_types.h
+++ b/drivers/gpu/drm/xe/xe_guc_log_types.h
@@ -7,9 +7,18 @@
#define _XE_GUC_LOG_TYPES_H_
#include <linux/types.h>
+#include "abi/guc_log_abi.h"
struct xe_bo;
+/** @sizes: Allocation settings */
+struct xe_log_sizes_type {
+ u32 bytes; /* Size in bytes */
+ u32 units; /* GuC API units - 1MB or 4KB */
+ u32 count; /* Number of API units */
+ u32 flag; /* GuC API units flag */
+};
+
/**
* struct xe_guc_log - GuC log
*/
@@ -18,6 +27,12 @@ struct xe_guc_log {
u32 level;
/** @bo: XE BO for GuC log */
struct xe_bo *bo;
+ /** @stats: logging related stats */
+ struct {
+ u32 sampled_overflow;
+ u32 overflow;
+ u32 flush;
+ } stats[GUC_LOG_BUFFER_TYPE_MAX];
};
#endif
--
2.34.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v13 4/5] drm/xe/guc: Extract GuC error capture lists
2024-07-19 20:29 [PATCH v13 0/5] drm/xe/guc: Add GuC based register capture for error capture Zhanjun Dong
` (2 preceding siblings ...)
2024-07-19 20:29 ` [PATCH v13 3/5] drm/xe/guc: Add capture size check in GuC log buffer Zhanjun Dong
@ 2024-07-19 20:29 ` Zhanjun Dong
2024-07-19 20:29 ` [PATCH v13 5/5] drm/xe/guc: Plumb GuC-capture into dev coredump Zhanjun Dong
` (3 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Zhanjun Dong @ 2024-07-19 20:29 UTC (permalink / raw)
To: intel-xe; +Cc: Zhanjun Dong
Upon the G2H Notify-Err-Capture event, parse through the
GuC Log Buffer (error-capture-subregion) and generate one or
more capture-nodes. A single node represents a single "engine-
instance-capture-dump" and contains at least 3 register lists:
global, engine-class and engine-instance. An internal link
list is maintained to store one or more nodes.
Because the link-list node generation happen before the call
to devcoredump, duplicate global and engine-class register
lists for each engine-instance register dump if we find
dependent-engine resets in a engine-capture-group.
To avoid dynamically allocate the output nodes during gt reset,
pre-allocate a fixed number of empty nodes up front (at the
time of ADS registration) that we can consume from or return to
an internal cached list of nodes.
Signed-off-by: Zhanjun Dong <zhanjun.dong@intel.com>
---
drivers/gpu/drm/xe/abi/guc_actions_abi.h | 8 +
drivers/gpu/drm/xe/abi/guc_log_abi.h | 52 ++
drivers/gpu/drm/xe/xe_guc_capture.c | 711 ++++++++++++++++++++++-
drivers/gpu/drm/xe/xe_guc_capture.h | 1 +
drivers/gpu/drm/xe/xe_guc_ct.c | 2 +
drivers/gpu/drm/xe/xe_guc_submit.c | 67 ++-
drivers/gpu/drm/xe/xe_guc_submit.h | 2 +
7 files changed, 825 insertions(+), 18 deletions(-)
diff --git a/drivers/gpu/drm/xe/abi/guc_actions_abi.h b/drivers/gpu/drm/xe/abi/guc_actions_abi.h
index 43ad4652c2b2..b54fe40fc5a9 100644
--- a/drivers/gpu/drm/xe/abi/guc_actions_abi.h
+++ b/drivers/gpu/drm/xe/abi/guc_actions_abi.h
@@ -176,6 +176,14 @@ enum xe_guc_sleep_state_status {
#define GUC_LOG_CONTROL_VERBOSITY_MASK (0xF << GUC_LOG_CONTROL_VERBOSITY_SHIFT)
#define GUC_LOG_CONTROL_DEFAULT_LOGGING (1 << 8)
+enum xe_guc_state_capture_event_status {
+ XE_GUC_STATE_CAPTURE_EVENT_STATUS_SUCCESS = 0x0,
+ XE_GUC_STATE_CAPTURE_EVENT_STATUS_NOSPACE = 0x1,
+};
+
+#define XE_GUC_STATE_CAPTURE_EVENT_STATUS_MASK 0x000000FF
+#define XE_GUC_ACTION_STATE_CAPTURE_NOTIFICATION_DATA_LEN 1
+
#define XE_GUC_TLB_INVAL_TYPE_SHIFT 0
#define XE_GUC_TLB_INVAL_MODE_SHIFT 8
/* Flush PPC or SMRO caches along with TLB invalidation request */
diff --git a/drivers/gpu/drm/xe/abi/guc_log_abi.h b/drivers/gpu/drm/xe/abi/guc_log_abi.h
index 10db4ffaa17f..4043564eb819 100644
--- a/drivers/gpu/drm/xe/abi/guc_log_abi.h
+++ b/drivers/gpu/drm/xe/abi/guc_log_abi.h
@@ -17,4 +17,56 @@ enum guc_log_buffer_type {
#define GUC_LOG_BUFFER_TYPE_MAX 3
+/**
+ * struct guc_log_buffer_state - GuC log buffer state
+ *
+ * Below state structure is used for coordination of retrieval of GuC firmware
+ * logs. Separate state is maintained for each log buffer type.
+ * read_ptr points to the location where Xe read last in log buffer and
+ * is read only for GuC firmware. write_ptr is incremented by GuC with number
+ * of bytes written for each log entry and is read only for Xe.
+ * When any type of log buffer becomes half full, GuC sends a flush interrupt.
+ * GuC firmware expects that while it is writing to 2nd half of the buffer,
+ * first half would get consumed by Host and then get a flush completed
+ * acknowledgment from Host, so that it does not end up doing any overwrite
+ * causing loss of logs. So when buffer gets half filled & Xe has requested
+ * for interrupt, GuC will set flush_to_file field, set the sampled_write_ptr
+ * to the value of write_ptr and raise the interrupt.
+ * On receiving the interrupt Xe should read the buffer, clear flush_to_file
+ * field and also update read_ptr with the value of sample_write_ptr, before
+ * sending an acknowledgment to GuC. marker & version fields are for internal
+ * usage of GuC and opaque to Xe. buffer_full_cnt field is incremented every
+ * time GuC detects the log buffer overflow.
+ */
+struct guc_log_buffer_state {
+ /** @marker: buffer state start marker */
+ u32 marker[2];
+ /** @read_ptr: the Last Byte Offset Location */
+ u32 read_ptr;
+ /** @write_ptr: the Byte Offset Location */
+ u32 write_ptr;
+ /** @size: Log buffer size */
+ u32 size;
+ /**
+ * @sampled_write_ptr: Log buffer write pointer
+ * This is written by GuC to the byte offset of the next free entry in
+ * the buffer on log buffer half full or state capture notification
+ */
+ u32 sampled_write_ptr;
+ /**
+ * @wrap_offset: wraparound offset
+ * This is the byte offset of location 1 byte after last valid guc log
+ * event entry written by Guc firmware before there was a wraparound.
+ * This field is updated by guc firmware and should be used by Host
+ * when copying buffer contents to file.
+ */
+ u32 wrap_offset;
+ /** @flags: Flush to file flag and buffer full count */
+ u32 flags;
+#define GUC_LOG_BUFFER_STATE_FLUSH_TO_FILE GENMASK(0, 0)
+#define GUC_LOG_BUFFER_STATE_BUFFER_FULL_CNT GENMASK(4, 1)
+ /** @version: The Guc-Log-Entry format version */
+ u32 version;
+} __packed;
+
#endif
diff --git a/drivers/gpu/drm/xe/xe_guc_capture.c b/drivers/gpu/drm/xe/xe_guc_capture.c
index 72abba8f7bf8..d4a63fbe303d 100644
--- a/drivers/gpu/drm/xe/xe_guc_capture.c
+++ b/drivers/gpu/drm/xe/xe_guc_capture.c
@@ -10,6 +10,7 @@
#include "abi/guc_actions_abi.h"
#include "abi/guc_capture_abi.h"
+#include "abi/guc_log_abi.h"
#include "regs/xe_engine_regs.h"
#include "regs/xe_gt_regs.h"
#include "regs/xe_guc_regs.h"
@@ -32,6 +33,51 @@
#include "xe_macros.h"
#include "xe_map.h"
+/*
+ * struct __guc_capture_bufstate
+ *
+ * Book-keeping structure used to track read and write pointers
+ * as we extract error capture data from the GuC-log-buffer's
+ * error-capture region as a stream of dwords.
+ */
+struct __guc_capture_bufstate {
+ u32 size;
+ u32 data_offset;
+ u32 rd;
+ u32 wr;
+};
+
+/*
+ * struct __guc_capture_parsed_output - extracted error capture node
+ *
+ * A single unit of extracted error-capture output data grouped together
+ * at an engine-instance level. We keep these nodes in a linked list.
+ * See cachelist and outlist below.
+ */
+struct __guc_capture_parsed_output {
+ /*
+ * A single set of 3 capture lists: a global-list
+ * an engine-class-list and an engine-instance list.
+ * outlist in __guc_capture_parsed_output will keep
+ * a linked list of these nodes that will eventually
+ * be detached from outlist and attached into to
+ * xe_codedump in response to a context reset
+ */
+ struct list_head link;
+ bool is_partial;
+ u32 eng_class;
+ u32 eng_inst;
+ u32 guc_id;
+ u32 lrca;
+ struct gcap_reg_list_info {
+ u32 vfid;
+ u32 num_regs;
+ struct guc_mmio_reg *regs;
+ } reginfo[GUC_STATE_CAPTURE_TYPE_MAX];
+#define GCAP_PARSED_REGLIST_INDEX_GLOBAL BIT(GUC_STATE_CAPTURE_TYPE_GLOBAL)
+#define GCAP_PARSED_REGLIST_INDEX_ENGCLASS BIT(GUC_STATE_CAPTURE_TYPE_ENGINE_CLASS)
+};
+
/*
* Define all device tables of GuC error capture register lists
* NOTE: For engine-registers, GuC only needs the register offsets
@@ -158,8 +204,16 @@ struct xe_guc_state_capture {
[GUC_STATE_CAPTURE_TYPE_MAX]
[GUC_CAPTURE_LIST_CLASS_MAX];
void *ads_null_cache;
+ struct list_head cachelist;
+#define PREALLOC_NODES_MAX_COUNT (3 * GUC_MAX_ENGINE_CLASSES * GUC_MAX_INSTANCES_PER_CLASS)
+#define PREALLOC_NODES_DEFAULT_NUMREGS 64
+
+ int max_mmio_per_node;
+ struct list_head outlist;
};
+static void guc_capture_create_prealloc_nodes(struct xe_guc *guc);
+
static const struct __guc_mmio_reg_descr_group *
guc_capture_get_one_list(const struct __guc_mmio_reg_descr_group *reglists,
u32 owner, u32 type, enum guc_capture_list_class_type capture_class)
@@ -477,6 +531,12 @@ xe_guc_capture_getlist(struct xe_guc *guc, u32 owner, u32 type,
return cache->status;
}
+ /*
+ * ADS population of input registers is a good
+ * time to pre-allocate cachelist output nodes
+ */
+ guc_capture_create_prealloc_nodes(guc);
+
ret = xe_guc_capture_getlistsize(guc, owner, type, capture_class, &size);
if (ret) {
cache->is_valid = true;
@@ -668,6 +728,653 @@ static void check_guc_capture_size(struct xe_guc *guc)
buffer_size, spare_size, capture_size);
}
+static void
+guc_capture_add_node_to_list(struct __guc_capture_parsed_output *node,
+ struct list_head *list)
+{
+ list_add_tail(&node->link, list);
+}
+
+static void
+guc_capture_add_node_to_outlist(struct xe_guc_state_capture *gc,
+ struct __guc_capture_parsed_output *node)
+{
+ guc_capture_add_node_to_list(node, &gc->outlist);
+}
+
+static void
+guc_capture_add_node_to_cachelist(struct xe_guc_state_capture *gc,
+ struct __guc_capture_parsed_output *node)
+{
+ guc_capture_add_node_to_list(node, &gc->cachelist);
+}
+
+static void
+guc_capture_init_node(struct xe_guc *guc, struct __guc_capture_parsed_output *node)
+{
+ struct guc_mmio_reg *tmp[GUC_STATE_CAPTURE_TYPE_MAX];
+ int i;
+
+ for (i = 0; i < GUC_STATE_CAPTURE_TYPE_MAX; ++i) {
+ tmp[i] = node->reginfo[i].regs;
+ memset(tmp[i], 0, sizeof(struct guc_mmio_reg) *
+ guc->capture->max_mmio_per_node);
+ }
+ memset(node, 0, sizeof(*node));
+ for (i = 0; i < GUC_STATE_CAPTURE_TYPE_MAX; ++i)
+ node->reginfo[i].regs = tmp[i];
+
+ INIT_LIST_HEAD(&node->link);
+}
+
+/**
+ * DOC: Init, G2H-event and reporting flows for GuC-error-capture
+ *
+ * KMD Init time flows:
+ * --------------------
+ * --> alloc A: GuC input capture regs lists (registered to GuC via ADS).
+ * xe_guc_ads acquires the register lists by calling
+ * xe_guc_capture_list_size and xe_guc_capture_list_get 'n' times,
+ * where n = 1 for global-reg-list +
+ * num_engine_classes for class-reg-list +
+ * num_engine_classes for instance-reg-list
+ * (since all instances of the same engine-class type
+ * have an identical engine-instance register-list).
+ * ADS module also calls separately for PF vs VF.
+ *
+ * --> alloc B: GuC output capture buf (registered via guc_init_params(log_param))
+ * Size = #define CAPTURE_BUFFER_SIZE (warns if on too-small)
+ * Note2: 'x 3' to hold multiple capture groups
+ *
+ * GUC Runtime notify capture:
+ * --------------------------
+ * --> G2H STATE_CAPTURE_NOTIFICATION
+ * L--> xe_guc_capture_process
+ * L--> Loop through B (head..tail) and for each engine instance's
+ * err-state-captured register-list we find, we alloc 'C':
+ * --> alloc C: A capture-output-node structure that includes misc capture info along
+ * with 3 register list dumps (global, engine-class and engine-instance)
+ * This node is created from a pre-allocated list of blank nodes in
+ * guc->capture->cachelist and populated with the error-capture
+ * data from GuC and then it's added into guc->capture->outlist linked
+ * list. This list is used for matchup and printout by xe_devcoredump_read
+ * and xe_hw_engine_snapshot_print, (when user invokes the devcoredump sysfs).
+ *
+ * GUC --> notify context reset:
+ * -----------------------------
+ * --> guc_exec_queue_timedout_job
+ * L--> xe_devcoredump
+ * L--> devcoredump_snapshot(..IS_GUC_CAPTURE)
+ * --> xe_hw_engine_snapshot_capture(..IS_GUC_CAPTURE)
+ * L--> xe_hw_engine_snapshot_from_capture is where
+ * detach C from internal linked list and add it into
+ * xe_hw_engine_snapshot struct (if the context and
+ * engine of the event notification matches a node
+ * in the link list).
+ *
+ * User Sysfs / Debugfs
+ * --------------------
+ * --> xe_devcoredump_read->
+ * L--> xxx_snapshot_print
+ * L--> xe_hw_engine_snapshot_print
+ * register lists values of the xe_hw_engine_snapshot
+ * saved from xe_hw_engine_snapshot_capture.
+ *
+ */
+
+static int guc_capture_buf_cnt(struct __guc_capture_bufstate *buf)
+{
+ if (buf->wr >= buf->rd)
+ return (buf->wr - buf->rd);
+ return (buf->size - buf->rd) + buf->wr;
+}
+
+static int guc_capture_buf_cnt_to_end(struct __guc_capture_bufstate *buf)
+{
+ if (buf->rd > buf->wr)
+ return (buf->size - buf->rd);
+ return (buf->wr - buf->rd);
+}
+
+/*
+ * GuC's error-capture output is a ring buffer populated in a byte-stream fashion:
+ *
+ * The GuC Log buffer region for error-capture is managed like a ring buffer.
+ * The GuC firmware dumps error capture logs into this ring in a byte-stream flow.
+ * Additionally, as per the current and foreseeable future, all packed error-
+ * capture output structures are dword aligned.
+ *
+ * That said, if the GuC firmware is in the midst of writing a structure that is larger
+ * than one dword but the tail end of the err-capture buffer-region has lesser space left,
+ * we would need to extract that structure one dword at a time straddled across the end,
+ * onto the start of the ring.
+ *
+ * Below function, guc_capture_log_remove_bytes is a helper for that. All callers of this
+ * function would typically do a straight-up memcpy from the ring contents and will only
+ * call this helper if their structure-extraction is straddling across the end of the
+ * ring. GuC firmware does not add any padding. The reason for the no-padding is to ease
+ * scalability for future expansion of output data types without requiring a redesign
+ * of the flow controls.
+ */
+static int
+guc_capture_log_remove_bytes(struct xe_guc *guc, struct __guc_capture_bufstate *buf,
+ void *out, int bytes_needed)
+{
+ #define GUC_CAPTURE_LOG_BUF_COPY_RETRY_MAX 3
+ int fill_size = 0, tries = GUC_CAPTURE_LOG_BUF_COPY_RETRY_MAX;
+ int copy_size, avail;
+
+ xe_assert(guc_to_xe(guc), bytes_needed % sizeof(u32) == 0);
+
+ if (bytes_needed > guc_capture_buf_cnt(buf))
+ return -1;
+
+ while (bytes_needed > 0 && tries--) {
+ int misaligned;
+
+ avail = guc_capture_buf_cnt_to_end(buf);
+ misaligned = avail % sizeof(u32);
+ /* wrap if at end */
+ if (!avail) {
+ /* output stream clipped */
+ if (!buf->rd)
+ return fill_size;
+ buf->rd = 0;
+ continue;
+ }
+
+ /* Only copy to u32 aligned data */
+ copy_size = avail < bytes_needed ? avail - misaligned : bytes_needed;
+ xe_map_memcpy_from(guc_to_xe(guc), out, &guc->log.bo->vmap,
+ buf->data_offset + buf->rd, copy_size);
+ buf->rd += copy_size;
+ fill_size += copy_size;
+ bytes_needed -= copy_size;
+
+ if (misaligned)
+ xe_gt_dbg(guc_to_gt(guc),
+ "Bytes extraction not dword aligned, skipping.\n");
+ }
+
+ return fill_size;
+}
+
+static int
+guc_capture_log_get_group_hdr(struct xe_guc *guc, struct __guc_capture_bufstate *buf,
+ struct guc_state_capture_group_header_t *ghdr)
+{
+ int fullsize = sizeof(struct guc_state_capture_group_header_t);
+
+ if (guc_capture_log_remove_bytes(guc, buf, ghdr, fullsize) != fullsize)
+ return -1;
+ return 0;
+}
+
+static int
+guc_capture_log_get_data_hdr(struct xe_guc *guc, struct __guc_capture_bufstate *buf,
+ struct guc_state_capture_header_t *hdr)
+{
+ int fullsize = sizeof(struct guc_state_capture_header_t);
+
+ if (guc_capture_log_remove_bytes(guc, buf, hdr, fullsize) != fullsize)
+ return -1;
+ return 0;
+}
+
+static int
+guc_capture_log_get_register(struct xe_guc *guc, struct __guc_capture_bufstate *buf,
+ struct guc_mmio_reg *reg)
+{
+ int fullsize = sizeof(struct guc_mmio_reg);
+
+ if (guc_capture_log_remove_bytes(guc, buf, reg, fullsize) != fullsize)
+ return -1;
+ return 0;
+}
+
+static struct __guc_capture_parsed_output *
+guc_capture_get_prealloc_node(struct xe_guc *guc)
+{
+ struct __guc_capture_parsed_output *found = NULL;
+
+ if (!list_empty(&guc->capture->cachelist)) {
+ struct __guc_capture_parsed_output *n, *ntmp;
+
+ /* get first avail node from the cache list */
+ list_for_each_entry_safe(n, ntmp, &guc->capture->cachelist, link) {
+ found = n;
+ break;
+ }
+ } else {
+ struct __guc_capture_parsed_output *n, *ntmp;
+
+ /* traverse down and steal back the oldest node already allocated */
+ list_for_each_entry_safe(n, ntmp, &guc->capture->outlist, link) {
+ found = n;
+ }
+ }
+ if (found) {
+ list_del(&found->link);
+ guc_capture_init_node(guc, found);
+ }
+
+ return found;
+}
+
+static struct __guc_capture_parsed_output *
+guc_capture_clone_node(struct xe_guc *guc, struct __guc_capture_parsed_output *original,
+ u32 keep_reglist_mask)
+{
+ struct __guc_capture_parsed_output *new;
+ int i;
+
+ new = guc_capture_get_prealloc_node(guc);
+ if (!new)
+ return NULL;
+ if (!original)
+ return new;
+
+ new->is_partial = original->is_partial;
+
+ /* copy reg-lists that we want to clone */
+ for (i = 0; i < GUC_STATE_CAPTURE_TYPE_MAX; ++i) {
+ if (keep_reglist_mask & BIT(i)) {
+ XE_WARN_ON(original->reginfo[i].num_regs >
+ guc->capture->max_mmio_per_node);
+
+ memcpy(new->reginfo[i].regs, original->reginfo[i].regs,
+ original->reginfo[i].num_regs * sizeof(struct guc_mmio_reg));
+
+ new->reginfo[i].num_regs = original->reginfo[i].num_regs;
+ new->reginfo[i].vfid = original->reginfo[i].vfid;
+
+ if (i == GUC_STATE_CAPTURE_TYPE_ENGINE_CLASS) {
+ new->eng_class = original->eng_class;
+ } else if (i == GUC_STATE_CAPTURE_TYPE_ENGINE_INSTANCE) {
+ new->eng_inst = original->eng_inst;
+ new->guc_id = original->guc_id;
+ new->lrca = original->lrca;
+ }
+ }
+ }
+
+ return new;
+}
+
+static int
+guc_capture_extract_reglists(struct xe_guc *guc, struct __guc_capture_bufstate *buf)
+{
+ struct xe_gt *gt = guc_to_gt(guc);
+ struct guc_state_capture_group_header_t ghdr = {0};
+ struct guc_state_capture_header_t hdr = {0};
+ struct __guc_capture_parsed_output *node = NULL;
+ struct guc_mmio_reg *regs = NULL;
+ int i, numlists, numregs, ret = 0;
+ enum guc_state_capture_type datatype;
+ struct guc_mmio_reg tmp;
+ bool is_partial = false;
+
+ i = guc_capture_buf_cnt(buf);
+ if (!i)
+ return -ENODATA;
+
+ if (i % sizeof(u32)) {
+ xe_gt_warn(gt, "Got mis-aligned register capture entries\n");
+ ret = -EIO;
+ goto bailout;
+ }
+
+ /* first get the capture group header */
+ if (guc_capture_log_get_group_hdr(guc, buf, &ghdr)) {
+ ret = -EIO;
+ goto bailout;
+ }
+ /*
+ * we would typically expect a layout as below where n would be expected to be
+ * anywhere between 3 to n where n > 3 if we are seeing multiple dependent engine
+ * instances being reset together.
+ * ____________________________________________
+ * | Capture Group |
+ * | ________________________________________ |
+ * | | Capture Group Header: | |
+ * | | - num_captures = 5 | |
+ * | |______________________________________| |
+ * | ________________________________________ |
+ * | | Capture1: | |
+ * | | Hdr: GLOBAL, numregs=a | |
+ * | | ____________________________________ | |
+ * | | | Reglist | | |
+ * | | | - reg1, reg2, ... rega | | |
+ * | | |__________________________________| | |
+ * | |______________________________________| |
+ * | ________________________________________ |
+ * | | Capture2: | |
+ * | | Hdr: CLASS=RENDER/COMPUTE, numregs=b| |
+ * | | ____________________________________ | |
+ * | | | Reglist | | |
+ * | | | - reg1, reg2, ... regb | | |
+ * | | |__________________________________| | |
+ * | |______________________________________| |
+ * | ________________________________________ |
+ * | | Capture3: | |
+ * | | Hdr: INSTANCE=RCS, numregs=c | |
+ * | | ____________________________________ | |
+ * | | | Reglist | | |
+ * | | | - reg1, reg2, ... regc | | |
+ * | | |__________________________________| | |
+ * | |______________________________________| |
+ * | ________________________________________ |
+ * | | Capture4: | |
+ * | | Hdr: CLASS=RENDER/COMPUTE, numregs=d| |
+ * | | ____________________________________ | |
+ * | | | Reglist | | |
+ * | | | - reg1, reg2, ... regd | | |
+ * | | |__________________________________| | |
+ * | |______________________________________| |
+ * | ________________________________________ |
+ * | | Capture5: | |
+ * | | Hdr: INSTANCE=CCS0, numregs=e | |
+ * | | ____________________________________ | |
+ * | | | Reglist | | |
+ * | | | - reg1, reg2, ... rege | | |
+ * | | |__________________________________| | |
+ * | |______________________________________| |
+ * |__________________________________________|
+ */
+ is_partial = FIELD_GET(GUC_STATE_CAPTURE_HEADER_CAPTURE_TYPE, ghdr.info);
+ numlists = FIELD_GET(GUC_STATE_CAPTURE_GROUP_HEADER_NUM_CAPTURES, ghdr.info);
+
+ while (numlists--) {
+ if (guc_capture_log_get_data_hdr(guc, buf, &hdr)) {
+ ret = -EIO;
+ break;
+ }
+
+ datatype = FIELD_GET(GUC_STATE_CAPTURE_HEADER_CAPTURE_TYPE, hdr.info);
+ if (datatype > GUC_STATE_CAPTURE_TYPE_ENGINE_INSTANCE) {
+ /* unknown capture type - skip over to next capture set */
+ numregs = FIELD_GET(GUC_STATE_CAPTURE_HEADER_NUM_MMIO_ENTRIES,
+ hdr.num_mmio_entries);
+ while (numregs--) {
+ if (guc_capture_log_get_register(guc, buf, &tmp)) {
+ ret = -EIO;
+ break;
+ }
+ }
+ continue;
+ } else if (node) {
+ /*
+ * Based on the current capture type and what we have so far,
+ * decide if we should add the current node into the internal
+ * linked list for match-up when xe_devcoredump calls later
+ * (and alloc a blank node for the next set of reglists)
+ * or continue with the same node or clone the current node
+ * but only retain the global or class registers (such as the
+ * case of dependent engine resets).
+ */
+ if (datatype == GUC_STATE_CAPTURE_TYPE_GLOBAL) {
+ guc_capture_add_node_to_outlist(guc->capture, node);
+ node = NULL;
+ } else if (datatype == GUC_STATE_CAPTURE_TYPE_ENGINE_CLASS &&
+ node->reginfo[GUC_STATE_CAPTURE_TYPE_ENGINE_CLASS].num_regs) {
+ /* Add to list, clone node and duplicate global list */
+ guc_capture_add_node_to_outlist(guc->capture, node);
+ node = guc_capture_clone_node(guc, node,
+ GCAP_PARSED_REGLIST_INDEX_GLOBAL);
+ } else if (datatype == GUC_STATE_CAPTURE_TYPE_ENGINE_INSTANCE &&
+ node->reginfo[GUC_STATE_CAPTURE_TYPE_ENGINE_INSTANCE].num_regs) {
+ /* Add to list, clone node and duplicate global + class lists */
+ guc_capture_add_node_to_outlist(guc->capture, node);
+ node = guc_capture_clone_node(guc, node,
+ (GCAP_PARSED_REGLIST_INDEX_GLOBAL |
+ GCAP_PARSED_REGLIST_INDEX_ENGCLASS));
+ }
+ }
+
+ if (!node) {
+ node = guc_capture_get_prealloc_node(guc);
+ if (!node) {
+ ret = -ENOMEM;
+ break;
+ }
+ if (datatype != GUC_STATE_CAPTURE_TYPE_GLOBAL)
+ xe_gt_dbg(gt, "Register capture missing global dump: %08x!\n",
+ datatype);
+ }
+ node->is_partial = is_partial;
+ node->reginfo[datatype].vfid = FIELD_GET(GUC_STATE_CAPTURE_HEADER_VFID, hdr.owner);
+
+ switch (datatype) {
+ case GUC_STATE_CAPTURE_TYPE_ENGINE_INSTANCE:
+ node->eng_class = FIELD_GET(GUC_STATE_CAPTURE_HEADER_ENGINE_CLASS,
+ hdr.info);
+ node->eng_inst = FIELD_GET(GUC_STATE_CAPTURE_HEADER_ENGINE_INSTANCE,
+ hdr.info);
+ node->lrca = hdr.lrca;
+ node->guc_id = hdr.guc_id;
+ break;
+ case GUC_STATE_CAPTURE_TYPE_ENGINE_CLASS:
+ node->eng_class = FIELD_GET(GUC_STATE_CAPTURE_HEADER_ENGINE_CLASS,
+ hdr.info);
+ break;
+ default:
+ break;
+ }
+
+ numregs = FIELD_GET(GUC_STATE_CAPTURE_HEADER_NUM_MMIO_ENTRIES,
+ hdr.num_mmio_entries);
+ if (numregs > guc->capture->max_mmio_per_node) {
+ xe_gt_dbg(gt, "Register capture list extraction clipped by prealloc!\n");
+ numregs = guc->capture->max_mmio_per_node;
+ }
+ node->reginfo[datatype].num_regs = numregs;
+ regs = node->reginfo[datatype].regs;
+ i = 0;
+ while (numregs--) {
+ if (guc_capture_log_get_register(guc, buf, ®s[i++])) {
+ ret = -EIO;
+ break;
+ }
+ }
+ }
+
+bailout:
+ if (node) {
+ /* If we have data, add to linked list for match-up when xe_devcoredump calls */
+ for (i = GUC_STATE_CAPTURE_TYPE_GLOBAL; i < GUC_STATE_CAPTURE_TYPE_MAX; ++i) {
+ if (node->reginfo[i].regs) {
+ guc_capture_add_node_to_outlist(guc->capture, node);
+ node = NULL;
+ break;
+ }
+ }
+ if (node) /* else return it back to cache list */
+ guc_capture_add_node_to_cachelist(guc->capture, node);
+ }
+ return ret;
+}
+
+static int __guc_capture_flushlog_complete(struct xe_guc *guc)
+{
+ u32 action[] = {
+ XE_GUC_ACTION_LOG_BUFFER_FILE_FLUSH_COMPLETE,
+ GUC_LOG_BUFFER_CAPTURE
+ };
+
+ return xe_guc_ct_send_g2h_handler(&guc->ct, action, ARRAY_SIZE(action));
+}
+
+static void __guc_capture_process_output(struct xe_guc *guc)
+{
+ unsigned int buffer_size, read_offset, write_offset, full_count;
+ struct xe_uc *uc = container_of(guc, typeof(*uc), guc);
+ struct guc_log_buffer_state log_buf_state_local;
+ struct __guc_capture_bufstate buf;
+ bool new_overflow;
+ int ret, tmp;
+ u32 log_buf_state_offset;
+ u32 src_data_offset;
+
+ log_buf_state_offset = sizeof(struct guc_log_buffer_state) * GUC_LOG_BUFFER_CAPTURE;
+ src_data_offset = xe_guc_get_log_buffer_offset(&guc->log, GUC_LOG_BUFFER_CAPTURE);
+
+ /*
+ * Make a copy of the state structure, inside GuC log buffer
+ * (which is uncached mapped), on the stack to avoid reading
+ * from it multiple times.
+ */
+ xe_map_memcpy_from(guc_to_xe(guc), &log_buf_state_local, &guc->log.bo->vmap,
+ log_buf_state_offset, sizeof(struct guc_log_buffer_state));
+
+ buffer_size = xe_guc_get_log_buffer_size(&guc->log, GUC_LOG_BUFFER_CAPTURE);
+ read_offset = log_buf_state_local.read_ptr;
+ write_offset = log_buf_state_local.sampled_write_ptr;
+ full_count = FIELD_GET(GUC_LOG_BUFFER_STATE_BUFFER_FULL_CNT, log_buf_state_local.flags);
+
+ /* Bookkeeping stuff */
+ tmp = FIELD_GET(GUC_LOG_BUFFER_STATE_FLUSH_TO_FILE, log_buf_state_local.flags);
+ guc->log.stats[GUC_LOG_BUFFER_CAPTURE].flush += tmp;
+ new_overflow = xe_guc_check_log_buf_overflow(&guc->log, GUC_LOG_BUFFER_CAPTURE,
+ full_count);
+
+ /* Now copy the actual logs. */
+ if (unlikely(new_overflow)) {
+ /* copy the whole buffer in case of overflow */
+ read_offset = 0;
+ write_offset = buffer_size;
+ } else if (unlikely((read_offset > buffer_size) ||
+ (write_offset > buffer_size))) {
+ xe_gt_err(guc_to_gt(guc),
+ "Register capture buffer in invalid state: read = 0x%X, size = 0x%X!\n",
+ read_offset, buffer_size);
+ /* copy whole buffer as offsets are unreliable */
+ read_offset = 0;
+ write_offset = buffer_size;
+ }
+
+ buf.size = buffer_size;
+ buf.rd = read_offset;
+ buf.wr = write_offset;
+ buf.data_offset = src_data_offset;
+
+ if (!xe_guc_read_stopped(guc)) {
+ do {
+ ret = guc_capture_extract_reglists(guc, &buf);
+ if (ret && ret != -ENODATA)
+ xe_gt_dbg(guc_to_gt(guc), "Capture extraction failed:%d\n", ret);
+ } while (ret >= 0);
+ }
+
+ /* Update the state of log buffer err-cap state */
+ xe_map_wr(guc_to_xe(guc), &guc->log.bo->vmap,
+ log_buf_state_offset + offsetof(struct guc_log_buffer_state, read_ptr), u32,
+ write_offset);
+
+ /*
+ * Clear the flush_to_file from local first, the local was loaded by above
+ * xe_map_memcpy_from, then write out the "updated local" through
+ * xe_map_wr()
+ */
+ log_buf_state_local.flags &= ~GUC_LOG_BUFFER_STATE_FLUSH_TO_FILE;
+ xe_map_wr(guc_to_xe(guc), &guc->log.bo->vmap,
+ log_buf_state_offset + offsetof(struct guc_log_buffer_state, flags), u32,
+ log_buf_state_local.flags);
+ __guc_capture_flushlog_complete(guc);
+}
+
+/*
+ * xe_guc_capture_process - Process GuC register captured data
+ * @guc: The GuC object
+ *
+ * When GuC captured data is ready, GuC will send message
+ * XE_GUC_ACTION_STATE_CAPTURE_NOTIFICATION to host, this function will be
+ * called to process the data comes with the message.
+ *
+ * Returns: None
+ */
+void xe_guc_capture_process(struct xe_guc *guc)
+{
+ if (guc->capture)
+ __guc_capture_process_output(guc);
+}
+
+static struct __guc_capture_parsed_output *
+guc_capture_alloc_one_node(struct xe_guc *guc)
+{
+ struct drm_device *drm = guc_to_drm(guc);
+ struct __guc_capture_parsed_output *new;
+ int i;
+
+ new = drmm_kzalloc(drm, sizeof(*new), GFP_KERNEL);
+ if (!new)
+ return NULL;
+
+ for (i = 0; i < GUC_STATE_CAPTURE_TYPE_MAX; ++i) {
+ new->reginfo[i].regs = drmm_kzalloc(drm, guc->capture->max_mmio_per_node *
+ sizeof(struct guc_mmio_reg), GFP_KERNEL);
+ if (!new->reginfo[i].regs) {
+ while (i)
+ drmm_kfree(drm, new->reginfo[--i].regs);
+ drmm_kfree(drm, new);
+ return NULL;
+ }
+ }
+ guc_capture_init_node(guc, new);
+
+ return new;
+}
+
+static void
+__guc_capture_create_prealloc_nodes(struct xe_guc *guc)
+{
+ struct __guc_capture_parsed_output *node = NULL;
+ int i;
+
+ for (i = 0; i < PREALLOC_NODES_MAX_COUNT; ++i) {
+ node = guc_capture_alloc_one_node(guc);
+ if (!node) {
+ xe_gt_warn(guc_to_gt(guc), "Register capture pre-alloc-cache failure\n");
+ /* dont free the priors, use what we got and cleanup at shutdown */
+ return;
+ }
+ guc_capture_add_node_to_cachelist(guc->capture, node);
+ }
+}
+
+static int
+guc_get_max_reglist_count(struct xe_guc *guc)
+{
+ int i, j, k, tmp, maxregcount = 0;
+
+ for (i = 0; i < GUC_CAPTURE_LIST_INDEX_MAX; ++i) {
+ for (j = 0; j < GUC_STATE_CAPTURE_TYPE_MAX; ++j) {
+ for (k = 0; k < GUC_MAX_ENGINE_CLASSES; ++k) {
+ if (j == GUC_STATE_CAPTURE_TYPE_GLOBAL && k > 0)
+ continue;
+
+ tmp = guc_cap_list_num_regs(guc->capture, i, j, k);
+ if (tmp > maxregcount)
+ maxregcount = tmp;
+ }
+ }
+ }
+ if (!maxregcount)
+ maxregcount = PREALLOC_NODES_DEFAULT_NUMREGS;
+
+ return maxregcount;
+}
+
+static void
+guc_capture_create_prealloc_nodes(struct xe_guc *guc)
+{
+ /* skip if we've already done the pre-alloc */
+ if (guc->capture->max_mmio_per_node)
+ return;
+
+ guc->capture->max_mmio_per_node = guc_get_max_reglist_count(guc);
+ __guc_capture_create_prealloc_nodes(guc);
+}
+
/*
* xe_guc_capture_init - Init for GuC register capture
* @guc: The GuC object
@@ -684,7 +1391,9 @@ int xe_guc_capture_init(struct xe_guc *guc)
return -ENOMEM;
guc->capture->reglists = guc_capture_get_device_reglist(guc);
-
check_guc_capture_size(guc);
+ INIT_LIST_HEAD(&guc->capture->outlist);
+ INIT_LIST_HEAD(&guc->capture->cachelist);
+
return 0;
}
diff --git a/drivers/gpu/drm/xe/xe_guc_capture.h b/drivers/gpu/drm/xe/xe_guc_capture.h
index 55c1cedcb923..6bfa6ed73587 100644
--- a/drivers/gpu/drm/xe/xe_guc_capture.h
+++ b/drivers/gpu/drm/xe/xe_guc_capture.h
@@ -37,6 +37,7 @@ xe_engine_class_to_guc_capture_class(enum xe_engine_class class)
return xe_guc_class_to_capture_class(xe_engine_class_to_guc_class(class));
}
+void xe_guc_capture_process(struct xe_guc *guc);
int xe_guc_capture_getlist(struct xe_guc *guc, u32 owner, u32 type,
enum guc_capture_list_class_type capture_class, void **outptr);
int xe_guc_capture_getlistsize(struct xe_guc *guc, u32 owner, u32 type,
diff --git a/drivers/gpu/drm/xe/xe_guc_ct.c b/drivers/gpu/drm/xe/xe_guc_ct.c
index 7d2e937da1d8..7bf9e45abf49 100644
--- a/drivers/gpu/drm/xe/xe_guc_ct.c
+++ b/drivers/gpu/drm/xe/xe_guc_ct.c
@@ -1106,6 +1106,8 @@ static int process_g2h_msg(struct xe_guc_ct *ct, u32 *msg, u32 len)
/* Selftest only at the moment */
break;
case XE_GUC_ACTION_STATE_CAPTURE_NOTIFICATION:
+ ret = xe_guc_error_capture_handler(guc, payload, adj_len);
+ break;
case XE_GUC_ACTION_NOTIFY_FLUSH_LOG_BUFFER_TO_FILE:
/* FIXME: Handle this */
break;
diff --git a/drivers/gpu/drm/xe/xe_guc_submit.c b/drivers/gpu/drm/xe/xe_guc_submit.c
index 26f8c6a4f665..9996b2a635d1 100644
--- a/drivers/gpu/drm/xe/xe_guc_submit.c
+++ b/drivers/gpu/drm/xe/xe_guc_submit.c
@@ -800,7 +800,7 @@ static void guc_exec_queue_free_job(struct drm_sched_job *drm_job)
xe_sched_job_put(job);
}
-static int guc_read_stopped(struct xe_guc *guc)
+int xe_guc_read_stopped(struct xe_guc *guc)
{
return atomic_read(&guc->submission_state.stopped);
}
@@ -822,7 +822,7 @@ static void disable_scheduling_deregister(struct xe_guc *guc,
set_min_preemption_timeout(guc, q);
smp_rmb();
ret = wait_event_timeout(guc->ct.wq, !exec_queue_pending_enable(q) ||
- guc_read_stopped(guc), HZ * 5);
+ xe_guc_read_stopped(guc), HZ * 5);
if (!ret) {
struct xe_gpu_scheduler *sched = &q->guc->sched;
@@ -948,7 +948,7 @@ static void xe_guc_exec_queue_lr_cleanup(struct work_struct *w)
*/
ret = wait_event_timeout(guc->ct.wq,
!exec_queue_pending_disable(q) ||
- guc_read_stopped(guc), HZ * 5);
+ xe_guc_read_stopped(guc), HZ * 5);
if (!ret) {
drm_warn(&xe->drm, "Schedule disable failed to respond");
xe_sched_submission_start(sched);
@@ -1016,8 +1016,8 @@ static void enable_scheduling(struct xe_exec_queue *q)
ret = wait_event_timeout(guc->ct.wq,
!exec_queue_pending_enable(q) ||
- guc_read_stopped(guc), HZ * 5);
- if (!ret || guc_read_stopped(guc)) {
+ xe_guc_read_stopped(guc), HZ * 5);
+ if (!ret || 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);
@@ -1123,8 +1123,8 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job)
*/
ret = wait_event_timeout(guc->ct.wq,
!exec_queue_pending_enable(q) ||
- guc_read_stopped(guc), HZ * 5);
- if (!ret || guc_read_stopped(guc))
+ xe_guc_read_stopped(guc), HZ * 5);
+ if (!ret || xe_guc_read_stopped(guc))
goto trigger_reset;
/*
@@ -1148,8 +1148,8 @@ 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) ||
- guc_read_stopped(guc), HZ * 5);
- if (!ret || guc_read_stopped(guc)) {
+ xe_guc_read_stopped(guc), HZ * 5);
+ if (!ret || xe_guc_read_stopped(guc)) {
trigger_reset:
if (!ret)
xe_gt_warn(guc_to_gt(guc), "Schedule disable failed to respond");
@@ -1340,7 +1340,7 @@ static void suspend_fence_signal(struct xe_exec_queue *q)
struct xe_device *xe = guc_to_xe(guc);
xe_assert(xe, exec_queue_suspended(q) || exec_queue_killed(q) ||
- guc_read_stopped(guc));
+ xe_guc_read_stopped(guc));
xe_assert(xe, q->guc->suspend_pending);
__suspend_fence_signal(q);
@@ -1354,9 +1354,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 ||
- guc_read_stopped(guc));
+ xe_guc_read_stopped(guc));
- if (!guc_read_stopped(guc)) {
+ if (!xe_guc_read_stopped(guc)) {
s64 since_resume_ms =
ktime_ms_delta(ktime_get(),
q->guc->resume_time);
@@ -1470,7 +1470,7 @@ static int guc_exec_queue_init(struct xe_exec_queue *q)
q->entity = &ge->entity;
- if (guc_read_stopped(guc))
+ if (xe_guc_read_stopped(guc))
xe_sched_stop(sched);
mutex_unlock(&guc->submission_state.lock);
@@ -1596,6 +1596,9 @@ 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);
+
+ wait_event(q->guc->suspend_wait, !q->guc->suspend_pending ||
+ xe_guc_read_stopped(guc));
int ret;
/*
@@ -1606,7 +1609,7 @@ static int guc_exec_queue_suspend_wait(struct xe_exec_queue *q)
ret = wait_event_timeout(q->guc->suspend_wait,
!READ_ONCE(q->guc->suspend_pending) ||
exec_queue_killed(q) ||
- guc_read_stopped(guc),
+ xe_guc_read_stopped(guc),
HZ * 5);
if (!ret) {
@@ -1729,7 +1732,7 @@ int xe_guc_submit_reset_prepare(struct xe_guc *guc)
void xe_guc_submit_reset_wait(struct xe_guc *guc)
{
wait_event(guc->ct.wq, xe_device_wedged(guc_to_xe(guc)) ||
- !guc_read_stopped(guc));
+ !xe_guc_read_stopped(guc));
}
void xe_guc_submit_stop(struct xe_guc *guc)
@@ -1738,7 +1741,7 @@ void xe_guc_submit_stop(struct xe_guc *guc)
unsigned long index;
struct xe_device *xe = guc_to_xe(guc);
- xe_assert(xe, guc_read_stopped(guc) == 1);
+ xe_assert(xe, xe_guc_read_stopped(guc) == 1);
mutex_lock(&guc->submission_state.lock);
@@ -1776,7 +1779,7 @@ int xe_guc_submit_start(struct xe_guc *guc)
unsigned long index;
struct xe_device *xe = guc_to_xe(guc);
- xe_assert(xe, guc_read_stopped(guc) == 1);
+ xe_assert(xe, xe_guc_read_stopped(guc) == 1);
mutex_lock(&guc->submission_state.lock);
atomic_dec(&guc->submission_state.stopped);
@@ -1967,6 +1970,36 @@ int xe_guc_exec_queue_reset_handler(struct xe_guc *guc, u32 *msg, u32 len)
return 0;
}
+/*
+ * xe_guc_error_capture_handler - Handler of GuC captured message
+ * @guc: The GuC object
+ * @msg: Point to the message
+ * @len: The message length
+ *
+ * When GuC captured data is ready, GuC will send message
+ * XE_GUC_ACTION_STATE_CAPTURE_NOTIFICATION to host, this function will be
+ * called 1st to check status before process the data comes with the message.
+ *
+ * Returns: None
+ */
+int xe_guc_error_capture_handler(struct xe_guc *guc, u32 *msg, u32 len)
+{
+ u32 status;
+
+ if (unlikely(len != XE_GUC_ACTION_STATE_CAPTURE_NOTIFICATION_DATA_LEN)) {
+ xe_gt_dbg(guc_to_gt(guc), "Invalid length %u", len);
+ return -EPROTO;
+ }
+
+ status = msg[0] & XE_GUC_STATE_CAPTURE_EVENT_STATUS_MASK;
+ if (status == XE_GUC_STATE_CAPTURE_EVENT_STATUS_NOSPACE)
+ xe_gt_warn(guc_to_gt(guc), "G2H-Error capture no space");
+
+ xe_guc_capture_process(guc);
+
+ return 0;
+}
+
int xe_guc_exec_queue_memory_cat_error_handler(struct xe_guc *guc, u32 *msg,
u32 len)
{
diff --git a/drivers/gpu/drm/xe/xe_guc_submit.h b/drivers/gpu/drm/xe/xe_guc_submit.h
index bdf8c9f3d24a..9b71a986c6ca 100644
--- a/drivers/gpu/drm/xe/xe_guc_submit.h
+++ b/drivers/gpu/drm/xe/xe_guc_submit.h
@@ -20,12 +20,14 @@ void xe_guc_submit_stop(struct xe_guc *guc);
int xe_guc_submit_start(struct xe_guc *guc);
void xe_guc_submit_wedge(struct xe_guc *guc);
+int xe_guc_read_stopped(struct xe_guc *guc);
int xe_guc_sched_done_handler(struct xe_guc *guc, u32 *msg, u32 len);
int xe_guc_deregister_done_handler(struct xe_guc *guc, u32 *msg, u32 len);
int xe_guc_exec_queue_reset_handler(struct xe_guc *guc, u32 *msg, u32 len);
int xe_guc_exec_queue_memory_cat_error_handler(struct xe_guc *guc, u32 *msg,
u32 len);
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);
struct xe_guc_submit_exec_queue_snapshot *
xe_guc_exec_queue_snapshot_capture(struct xe_exec_queue *q);
--
2.34.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v13 5/5] drm/xe/guc: Plumb GuC-capture into dev coredump
2024-07-19 20:29 [PATCH v13 0/5] drm/xe/guc: Add GuC based register capture for error capture Zhanjun Dong
` (3 preceding siblings ...)
2024-07-19 20:29 ` [PATCH v13 4/5] drm/xe/guc: Extract GuC error capture lists Zhanjun Dong
@ 2024-07-19 20:29 ` Zhanjun Dong
2024-07-19 20:34 ` ✓ CI.Patch_applied: success for drm/xe/guc: Add GuC based register capture for error capture (rev13) Patchwork
` (2 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Zhanjun Dong @ 2024-07-19 20:29 UTC (permalink / raw)
To: intel-xe; +Cc: Zhanjun Dong
Add xe_hw_engine_snapshot_from_capture to take snapshot from captured
node list.
Add pre-capture by read from hw engine if GuC capture data is not ready,
the pre-captured data will be refereshed if GuC capture is ready at later
time.
Add data struct to map captured register value to a snapshot field.
Add register name to register list.
Provide xe_guc_capture_get_reg_desc_list to get the register dscriptor
list.
Add function to check if capture is ready for a job.
Sort out snapshot registers into types of global, class, instance and
direct read.
Signed-off-by: Zhanjun Dong <zhanjun.dong@intel.com>
---
drivers/gpu/drm/xe/regs/xe_gt_regs.h | 2 +
drivers/gpu/drm/xe/xe_devcoredump.c | 15 +-
drivers/gpu/drm/xe/xe_devcoredump_types.h | 2 +
drivers/gpu/drm/xe/xe_guc_capture.c | 273 ++++++++++++++++++++--
drivers/gpu/drm/xe/xe_guc_capture.h | 19 ++
drivers/gpu/drm/xe/xe_guc_capture_types.h | 2 +
drivers/gpu/drm/xe/xe_guc_submit.c | 21 +-
drivers/gpu/drm/xe/xe_hw_engine.c | 240 +++++++++++++------
drivers/gpu/drm/xe/xe_hw_engine.h | 2 +
drivers/gpu/drm/xe/xe_hw_engine_types.h | 180 +++++++++-----
drivers/gpu/drm/xe/xe_lrc.h | 1 +
11 files changed, 588 insertions(+), 169 deletions(-)
diff --git a/drivers/gpu/drm/xe/regs/xe_gt_regs.h b/drivers/gpu/drm/xe/regs/xe_gt_regs.h
index 8a94a94d2267..0d85adf684a7 100644
--- a/drivers/gpu/drm/xe/regs/xe_gt_regs.h
+++ b/drivers/gpu/drm/xe/regs/xe_gt_regs.h
@@ -596,4 +596,6 @@
#define GT_CS_MASTER_ERROR_INTERRUPT REG_BIT(3)
#define GT_RENDER_USER_INTERRUPT REG_BIT(0)
+#define SFC_DONE(n) XE_REG(0x1cc000 + (n) * 0x1000)
+
#endif
diff --git a/drivers/gpu/drm/xe/xe_devcoredump.c b/drivers/gpu/drm/xe/xe_devcoredump.c
index 62c2b10fbf1d..bb7d28bfe60f 100644
--- a/drivers/gpu/drm/xe/xe_devcoredump.c
+++ b/drivers/gpu/drm/xe/xe_devcoredump.c
@@ -16,6 +16,7 @@
#include "xe_force_wake.h"
#include "xe_gt.h"
#include "xe_gt_printk.h"
+#include "xe_guc_capture.h"
#include "xe_guc_ct.h"
#include "xe_guc_submit.h"
#include "xe_hw_engine.h"
@@ -152,10 +153,12 @@ static void xe_devcoredump_free(void *data)
if (coredump->snapshot.hwe[i])
xe_hw_engine_snapshot_free(coredump->snapshot.hwe[i]);
xe_vm_snapshot_free(coredump->snapshot.vm);
+ xe_guc_capture_free(&coredump->snapshot.gt->uc.guc);
/* To prevent stale data on next snapshot, clear everything */
memset(&coredump->snapshot, 0, sizeof(coredump->snapshot));
coredump->captured = false;
+ coredump->job = NULL;
drm_info(&coredump_to_xe(coredump)->drm,
"Xe device coredump has been deleted.\n");
}
@@ -166,8 +169,6 @@ static void devcoredump_snapshot(struct xe_devcoredump *coredump,
struct xe_devcoredump_snapshot *ss = &coredump->snapshot;
struct xe_exec_queue *q = job->q;
struct xe_guc *guc = exec_queue_to_guc(q);
- struct xe_hw_engine *hwe;
- enum xe_hw_engine_id id;
u32 adj_logical_mask = q->logical_mask;
u32 width_mask = (0x1 << q->width) - 1;
const char *process_name = "no process";
@@ -189,6 +190,7 @@ static void devcoredump_snapshot(struct xe_devcoredump *coredump,
put_task_struct(task);
ss->gt = q->gt;
+ coredump->job = job;
INIT_WORK(&ss->work, xe_devcoredump_deferred_snap_work);
cookie = dma_fence_begin_signalling();
@@ -210,14 +212,7 @@ static void devcoredump_snapshot(struct xe_devcoredump *coredump,
coredump->snapshot.job = xe_sched_job_snapshot_capture(job);
coredump->snapshot.vm = xe_vm_snapshot_capture(q->vm);
- for_each_hw_engine(hwe, q->gt, id) {
- if (hwe->class != q->hwe->class ||
- !(BIT(hwe->logical_instance) & adj_logical_mask)) {
- coredump->snapshot.hwe[id] = NULL;
- continue;
- }
- coredump->snapshot.hwe[id] = xe_hw_engine_snapshot_capture(hwe);
- }
+ xe_hw_engine_snapshot_capture_for_job(job);
queue_work(system_unbound_wq, &ss->work);
diff --git a/drivers/gpu/drm/xe/xe_devcoredump_types.h b/drivers/gpu/drm/xe/xe_devcoredump_types.h
index 923cdf72a816..c39ab73a9f6a 100644
--- a/drivers/gpu/drm/xe/xe_devcoredump_types.h
+++ b/drivers/gpu/drm/xe/xe_devcoredump_types.h
@@ -61,6 +61,8 @@ struct xe_devcoredump {
bool captured;
/** @snapshot: Snapshot is captured at time of the first crash */
struct xe_devcoredump_snapshot snapshot;
+ /** @job: Point to the issue job */
+ struct xe_sched_job *job;
};
#endif
diff --git a/drivers/gpu/drm/xe/xe_guc_capture.c b/drivers/gpu/drm/xe/xe_guc_capture.c
index d4a63fbe303d..aeedde4eca74 100644
--- a/drivers/gpu/drm/xe/xe_guc_capture.c
+++ b/drivers/gpu/drm/xe/xe_guc_capture.c
@@ -27,11 +27,15 @@
#include "xe_guc_capture.h"
#include "xe_guc_capture_types.h"
#include "xe_guc_ct.h"
+#include "xe_guc_exec_queue_types.h"
#include "xe_guc_log.h"
+#include "xe_guc_submit_types.h"
#include "xe_guc_submit.h"
#include "xe_hw_engine_types.h"
+#include "xe_lrc.h"
#include "xe_macros.h"
#include "xe_map.h"
+#include "xe_sched_job.h"
/*
* struct __guc_capture_bufstate
@@ -84,28 +88,85 @@ struct __guc_capture_parsed_output {
* from the engine-mmio-base
*/
#define COMMON_XELP_BASE_GLOBAL \
- { FORCEWAKE_GT, 0, 0}
+ { FORCEWAKE_GT, 0, 0, "FORCEWAKE_GT",\
+ offsetof(struct snapshot_regs, forcewake_gt) }
#define COMMON_BASE_ENGINE_INSTANCE \
- { RING_ESR(0), 0, 0}, \
- { RING_EMR(0), 0, 0}, \
- { RING_EIR(0), 0, 0}, \
- { RING_EXECLIST_STATUS_HI(0), 0, 0}, \
- { RING_EXECLIST_STATUS_LO(0), 0, 0}, \
- { RING_DMA_FADD(0), 0, 0}, \
- { RING_DMA_FADD_UDW(0), 0, 0}, \
- { RING_IPEHR(0), 0, 0}, \
- { RING_BBADDR(0), 0, 0}, \
- { RING_BBADDR_UDW(0), 0, 0}, \
- { RING_ACTHD(0), 0, 0}, \
- { RING_ACTHD_UDW(0), 0, 0}, \
- { RING_START(0), 0, 0}, \
- { RING_HEAD(0), 0, 0}, \
- { RING_TAIL(0), 0, 0}, \
- { RING_CTL(0), 0, 0}, \
- { RING_MI_MODE(0), 0, 0}, \
- { RING_HWS_PGA(0), 0, 0}, \
- { RING_MODE(0), 0, 0}
+ { RING_HWSTAM(0), 0, 0, "HWSTAM",\
+ offsetof(struct snapshot_regs, ring_hwstam) }, \
+ { RING_HWS_PGA(0), 0, 0, "RING_HWS_PGA",\
+ offsetof(struct snapshot_regs, ring_hws_pga) }, \
+ { RING_HEAD(0), 0, 0, "RING_HEAD",\
+ offsetof(struct snapshot_regs, ring_head) }, \
+ { RING_TAIL(0), 0, 0, "RING_TAIL",\
+ offsetof(struct snapshot_regs, ring_tail) }, \
+ { RING_CTL(0), 0, 0, "RING_CTL",\
+ offsetof(struct snapshot_regs, ring_ctl) }, \
+ { RING_MI_MODE(0), 0, 0, "RING_MI_MODE",\
+ offsetof(struct snapshot_regs, ring_mi_mode) }, \
+ { RING_MODE(0), 0, 0, "RING_MODE",\
+ offsetof(struct snapshot_regs, ring_mode) }, \
+ { RING_ESR(0), 0, 0, "RING_ESR",\
+ offsetof(struct snapshot_regs, ring_esr) }, \
+ { RING_EMR(0), 0, 0, "RING_EMR",\
+ offsetof(struct snapshot_regs, ring_emr) }, \
+ { RING_EIR(0), 0, 0, "RING_EIR",\
+ offsetof(struct snapshot_regs, ring_eir) }, \
+ { RING_IMR(0), 0, 0, "RING_IMR",\
+ offsetof(struct snapshot_regs, ring_imr) }, \
+ { RING_IPEHR(0), 0, 0, "IPEHR",\
+ offsetof(struct snapshot_regs, ipehr) }, \
+ /* 64 bit register - Start */ \
+ { RING_ACTHD(0), 0, 0, "ACTHD",\
+ offsetof(struct snapshot_regs, u64_regs.ring_acthd) }, \
+ { RING_ACTHD_UDW(0), 0, 0, NULL,\
+ offsetof(struct snapshot_regs, u64_regs.ring_acthd) + 4}, \
+ { RING_BBADDR(0), 0, 0, "RING_BBADDR",\
+ offsetof(struct snapshot_regs, u64_regs.ring_bbaddr) }, \
+ { RING_BBADDR_UDW(0), 0, 0, NULL,\
+ offsetof(struct snapshot_regs, u64_regs.ring_bbaddr) + 4}, \
+ { RING_START(0), 0, 0, "RING_START",\
+ offsetof(struct snapshot_regs, u64_regs.ring_start) }, \
+ { RING_START_UDW(0), 0, 0, NULL,\
+ offsetof(struct snapshot_regs, u64_regs.ring_start) + 4}, \
+ { RING_DMA_FADD(0), 0, 0, "RING_DMA_FADD",\
+ offsetof(struct snapshot_regs, u64_regs.ring_dma_fadd) }, \
+ { RING_DMA_FADD_UDW(0), 0, 0, NULL,\
+ offsetof(struct snapshot_regs, u64_regs.ring_dma_fadd) + 4}, \
+ { RING_EXECLIST_STATUS_LO(0), 0, 0, "RING_EXECLIST_STATUS",\
+ offsetof(struct snapshot_regs, u64_regs.ring_execlist_status)}, \
+ { RING_EXECLIST_STATUS_HI(0), 0, 0, NULL,\
+ offsetof(struct snapshot_regs, u64_regs.ring_execlist_status) + 4}, \
+ { RING_EXECLIST_SQ_CONTENTS_LO(0), 0, 0, "RING_EXECLIST_SQ_CONTENTS",\
+ offsetof(struct snapshot_regs, u64_regs.ring_execlist_sq_contents)}, \
+ { RING_EXECLIST_SQ_CONTENTS_HI(0), 0, 0, NULL,\
+ offsetof(struct snapshot_regs, u64_regs.ring_execlist_sq_contents) + 4}, \
+ /* 64 bit register - End */ \
+ /* Extra handling registers */ \
+ { INDIRECT_RING_STATE(0), 0, 0, "INDIRECT_RING_STATE",\
+ offsetof(struct snapshot_regs, extra_op.indirect_ring_state)}
+ /*
+ * GuC support limited registers ranges to be captured for debug
+ * purpose, for registers out of these ranges, direct read is the only
+ * way to access.
+ * For registers requires extra condition check, add into this list.
+ * GuC based register capture process will ignore list of DIRECT_READ
+ * type.
+ */
+
+#define COMMON_XELP_RC_CLASS \
+ { RCU_MODE, 0, 0, "RCU_MODE",\
+ offsetof(struct snapshot_regs, rcu_mode) }
+
+#define XELP_DIRECT_READ_VEC \
+ { SFC_DONE(0), 0, 0, "SFC_DONE[0]", \
+ offsetof(struct snapshot_regs, sfc_done_0) }, \
+ { SFC_DONE(1), 0, 0, "SFC_DONE[1]", \
+ offsetof(struct snapshot_regs, sfc_done_1) }, \
+ { SFC_DONE(2), 0, 0, "SFC_DONE[2]", \
+ offsetof(struct snapshot_regs, sfc_done_2) }, \
+ { SFC_DONE(3), 0, 0, "SFC_DONE[3]", \
+ offsetof(struct snapshot_regs, sfc_done_3) }
/* XE_LP Global */
static const struct __guc_mmio_reg_descr xe_lp_global_regs[] = {
@@ -117,6 +178,11 @@ static const struct __guc_mmio_reg_descr xe_rc_inst_regs[] = {
COMMON_BASE_ENGINE_INSTANCE,
};
+/* Render / Compute Per-Engine-Instance */
+static const struct __guc_mmio_reg_descr xe_rc_class_regs[] = {
+ COMMON_XELP_RC_CLASS,
+};
+
/* Media Decode/Encode Per-Engine-Instance */
static const struct __guc_mmio_reg_descr xe_vd_inst_regs[] = {
COMMON_BASE_ENGINE_INSTANCE,
@@ -127,6 +193,11 @@ static const struct __guc_mmio_reg_descr xe_vec_inst_regs[] = {
COMMON_BASE_ENGINE_INSTANCE,
};
+/* Video Enhancement Per-Engine-Class */
+static const struct __guc_mmio_reg_descr xe_vec_direct_read_regs[] = {
+ XELP_DIRECT_READ_VEC,
+};
+
/* Blitter Per-Engine-Instance */
static const struct __guc_mmio_reg_descr xe_blt_inst_regs[] = {
COMMON_BASE_ENGINE_INSTANCE,
@@ -159,12 +230,13 @@ static const struct __guc_mmio_reg_descr empty_regs_list[] = {
/* List of lists */
static const struct __guc_mmio_reg_descr_group xe_lp_lists[] = {
MAKE_REGLIST(xe_lp_global_regs, PF, GLOBAL, 0),
- MAKE_REGLIST(empty_regs_list, PF, ENGINE_CLASS, GUC_CAPTURE_LIST_CLASS_RENDER_COMPUTE),
+ MAKE_REGLIST(xe_rc_class_regs, PF, ENGINE_CLASS, GUC_CAPTURE_LIST_CLASS_RENDER_COMPUTE),
MAKE_REGLIST(xe_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(empty_regs_list, PF, ENGINE_CLASS, GUC_CAPTURE_LIST_CLASS_VIDEOENHANCE),
MAKE_REGLIST(xe_vec_inst_regs, PF, ENGINE_INSTANCE, GUC_CAPTURE_LIST_CLASS_VIDEOENHANCE),
+ MAKE_REGLIST(xe_vec_direct_read_regs, PF, DIRECT_READ, 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),
@@ -233,6 +305,13 @@ guc_capture_get_one_list(const struct __guc_mmio_reg_descr_group *reglists,
return NULL;
}
+const struct __guc_mmio_reg_descr_group *
+xe_guc_capture_get_reg_desc_list(u32 owner, u32 type,
+ enum guc_capture_list_class_type capture_class)
+{
+ return guc_capture_get_one_list(xe_lp_lists, owner, type, capture_class);
+}
+
static struct __guc_mmio_reg_descr_group *
guc_capture_get_one_ext_list(struct __guc_mmio_reg_descr_group *reglists,
u32 owner, u32 type, u32 id)
@@ -1375,6 +1454,158 @@ guc_capture_create_prealloc_nodes(struct xe_guc *guc)
__guc_capture_create_prealloc_nodes(guc);
}
+static void
+cp_reg_to_snapshot(int type, const struct __guc_mmio_reg_descr_group *list, u32 offset, u32 value,
+ struct snapshot_regs *regs)
+{
+ int i;
+
+ for (i = 0; i < list->num_regs; i++) {
+ const struct __guc_mmio_reg_descr *sub_list = &list->list[i];
+
+ if (offset == sub_list->reg.addr) {
+ u32 *field = (u32 *)((uintptr_t)regs + sub_list->position_in_snapshot);
+ *field = value;
+ return;
+ }
+ }
+}
+
+static void
+guc_capture_find_ecode(struct __guc_capture_parsed_output *node,
+ struct xe_hw_engine_snapshot *snapshot,
+ enum guc_capture_list_class_type capture_class)
+{
+ int i, type;
+
+ if (!node)
+ return;
+
+ for (type = GUC_STATE_CAPTURE_TYPE_GLOBAL; type < GUC_STATE_CAPTURE_TYPE_MAX; type++) {
+ struct gcap_reg_list_info *reginfo = &node->reginfo[type];
+ struct guc_mmio_reg *regs = reginfo->regs;
+ const struct __guc_mmio_reg_descr_group *list;
+
+ /* Get register list for the type/class */
+ list = xe_guc_capture_get_reg_desc_list(GUC_CAPTURE_LIST_INDEX_PF,
+ type, capture_class);
+ if (!list)
+ return;
+
+ for (i = 0; i < reginfo->num_regs; i++)
+ cp_reg_to_snapshot(type, list, regs[i].offset, regs[i].value,
+ &snapshot->reg);
+ }
+}
+
+/**
+ * xe_hw_engine_snapshot_from_capture - Take a engine snapshot from GuC capture.
+ * @hwe: Xe HW Engine.
+ * @snapshot: Xe HW Engine snapshot object to save data, copied from error capture
+ *
+ * This can be printed out in a later stage like during dev_coredump
+ * analysis.
+ *
+ * Returns: None
+ */
+void
+xe_hw_engine_snapshot_from_capture(struct xe_hw_engine *hwe, struct xe_hw_engine_snapshot *snapshot)
+{
+ struct xe_gt *gt = hwe->gt;
+ struct xe_device *xe = gt_to_xe(gt);
+ struct xe_guc *guc = >->uc.guc;
+ struct __guc_capture_parsed_output *n, *ntmp;
+ struct xe_devcoredump *devcoredump = &xe->devcoredump;
+ struct list_head *list = &guc->capture->outlist;
+ struct xe_sched_job *job = devcoredump->job;
+ struct xe_exec_queue *q = job->q;
+ u16 guc_id = q->guc->id;
+ u32 lrca;
+ u16 guc_class = xe_engine_class_to_guc_class(hwe->class);
+
+ snapshot->source = XE_HW_ENGINE_SOURCE_GUC_CAPTURE;
+ lrca = xe_lrc_ggtt_addr(job->q->lrc[0]) & LRC_GTT_ADDRESS_MASK;
+
+ /*
+ * Look for a matching GuC reported error capture node from
+ * the internal output link-list based on engine class and instance.
+ */
+ list_for_each_entry_safe(n, ntmp, list, link) {
+ if (n->eng_class == guc_class && n->eng_inst == hwe->instance &&
+ n->guc_id == guc_id && (n->lrca & LRC_GTT_ADDRESS_MASK) == lrca) {
+ enum guc_capture_list_class_type capture_class;
+
+ capture_class = xe_guc_class_to_capture_class(guc_class);
+ guc_capture_find_ecode(n, snapshot, capture_class);
+ list_del(&n->link);
+ return;
+ }
+ }
+}
+
+/**
+ * xe_guc_capture_is_ready_for - Check if capture is ready for the job.
+ * @job: The job object.
+ *
+ * Search within the capture outlist for the job.
+ *
+ * Returns: True if found a node for the job
+ */
+bool xe_guc_capture_is_ready_for(struct xe_sched_job *job)
+{
+ struct xe_hw_engine *hwe;
+ enum xe_hw_engine_id id;
+ struct xe_exec_queue *q = job->q;
+ u16 guc_class = GUC_LAST_ENGINE_CLASS + 1;
+
+ /* Find hwe for the job */
+ for_each_hw_engine(hwe, q->gt, id) {
+ if (hwe != q->hwe)
+ continue;
+ guc_class = xe_engine_class_to_guc_class(hwe->class);
+ break;
+ }
+
+ if (guc_class <= GUC_LAST_ENGINE_CLASS) {
+ struct __guc_capture_parsed_output *n, *ntmp;
+ struct xe_guc *guc = &q->gt->uc.guc;
+ struct list_head *list = &guc->capture->outlist;
+ u16 guc_id = q->guc->id;
+ u32 lrca = xe_lrc_ggtt_addr(job->q->lrc[0]) & LRC_GTT_ADDRESS_MASK;
+
+ /*
+ * Look for a matching GuC reported error capture node from
+ * the internal output link-list based on engine, guc id and
+ * lrca info.
+ */
+ list_for_each_entry_safe(n, ntmp, list, link) {
+ if (n->eng_class == guc_class && n->eng_inst == hwe->instance &&
+ n->guc_id == guc_id && (n->lrca & LRC_GTT_ADDRESS_MASK) == lrca)
+ return true;
+ }
+ }
+ return false;
+}
+
+/*
+ * xe_guc_capture_free - Free the GuC captured register list
+ * @guc: The GuC object
+ *
+ * Free the GuC captured register list
+ */
+void xe_guc_capture_free(struct xe_guc *guc)
+{
+ if (guc->capture && !list_empty(&guc->capture->outlist)) {
+ struct __guc_capture_parsed_output *n, *ntmp;
+
+ list_for_each_entry_safe(n, ntmp, &guc->capture->outlist, link) {
+ list_del(&n->link);
+ /* put node back to cache list */
+ guc_capture_add_node_to_cachelist(guc->capture, n);
+ }
+ }
+}
+
/*
* xe_guc_capture_init - Init for GuC register capture
* @guc: The GuC object
diff --git a/drivers/gpu/drm/xe/xe_guc_capture.h b/drivers/gpu/drm/xe/xe_guc_capture.h
index 6bfa6ed73587..0cb6861443ae 100644
--- a/drivers/gpu/drm/xe/xe_guc_capture.h
+++ b/drivers/gpu/drm/xe/xe_guc_capture.h
@@ -12,6 +12,18 @@
#include "xe_guc_fwif.h"
struct xe_guc;
+struct xe_hw_engine;
+struct xe_hw_engine_snapshot;
+struct xe_sched_job;
+
+/*
+ * GuC support limited registers ranges to be captured for debug
+ * purpose, for registers out of these ranges, direct read is the only
+ * way to access.
+ * GuC based register capture process will ignore list of DIRECT_READ
+ * type.
+ */
+#define GUC_STATE_CAPTURE_TYPE_DIRECT_READ GUC_STATE_CAPTURE_TYPE_MAX
static inline enum guc_capture_list_class_type xe_guc_class_to_capture_class(u16 class)
{
@@ -44,6 +56,13 @@ int xe_guc_capture_getlistsize(struct xe_guc *guc, u32 owner, u32 type,
enum guc_capture_list_class_type capture_class, size_t *size);
int xe_guc_capture_getnullheader(struct xe_guc *guc, void **outptr, size_t *size);
size_t xe_guc_capture_ads_input_worst_size(struct xe_guc *guc);
+const struct __guc_mmio_reg_descr_group *
+xe_guc_capture_get_reg_desc_list(u32 owner, u32 type,
+ enum guc_capture_list_class_type capture_class);
+void xe_hw_engine_snapshot_from_capture(struct xe_hw_engine *hwe,
+ struct xe_hw_engine_snapshot *snapshot);
+bool xe_guc_capture_is_ready_for(struct xe_sched_job *job);
+void xe_guc_capture_free(struct xe_guc *guc);
int xe_guc_capture_init(struct xe_guc *guc);
#endif
diff --git a/drivers/gpu/drm/xe/xe_guc_capture_types.h b/drivers/gpu/drm/xe/xe_guc_capture_types.h
index 63cb4d7cf518..337dd4933124 100644
--- a/drivers/gpu/drm/xe/xe_guc_capture_types.h
+++ b/drivers/gpu/drm/xe/xe_guc_capture_types.h
@@ -28,6 +28,8 @@ struct __guc_mmio_reg_descr {
u32 mask;
/** @regname: Name of the register */
const char *regname;
+ /** @position_in_snapshot: The offset position in snapshot structure */
+ u32 position_in_snapshot;
};
/**
diff --git a/drivers/gpu/drm/xe/xe_guc_submit.c b/drivers/gpu/drm/xe/xe_guc_submit.c
index 9996b2a635d1..286b1075347f 100644
--- a/drivers/gpu/drm/xe/xe_guc_submit.c
+++ b/drivers/gpu/drm/xe/xe_guc_submit.c
@@ -27,6 +27,7 @@
#include "xe_gt_clock.h"
#include "xe_gt_printk.h"
#include "xe_guc.h"
+#include "xe_guc_capture.h"
#include "xe_guc_ct.h"
#include "xe_guc_exec_queue_types.h"
#include "xe_guc_id_mgr.h"
@@ -1073,6 +1074,7 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job)
struct xe_guc *guc = exec_queue_to_guc(q);
const char *process_name = "no process";
struct task_struct *task = NULL;
+ struct xe_device *xe = guc_to_xe(guc);
int err = -ETIME;
pid_t pid = -1;
int i = 0;
@@ -1100,6 +1102,19 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job)
if (!skip_timeout_check && !xe_sched_job_started(job))
goto rearm;
+ /* Pre-capture register snapshot, if devcoredump not captured and GuC capture not ready */
+ if (!exec_queue_killed(q) && !xe->devcoredump.captured && xe_device_uc_enabled(xe) &&
+ xe->wedged.mode >= 1 && !xe_guc_capture_is_ready_for(job)) {
+ /* take force wake before engine register pre-capture */
+ /* keep going if fw fails as we still want to save the memory and SW data */
+ if (xe_force_wake_get(gt_to_fw(q->gt), XE_FORCEWAKE_ALL))
+ xe_gt_info(q->gt, "failed to get forcewake for coredump capture\n");
+
+ xe_hw_engine_snapshot_capture_for_job(job);
+
+ xe_force_wake_put(gt_to_fw(q->gt), XE_FORCEWAKE_ALL);
+ }
+
/*
* XXX: Sampling timeout doesn't work in wedged mode as we have to
* modify scheduling state to read timestamp. We could read the
@@ -1187,7 +1202,7 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job)
trace_xe_sched_job_timedout(job);
if (!exec_queue_killed(q))
- xe_devcoredump(job);
+ xe_devcoredump(job); /* pre-captured data will be refreshed by GuC capture */
/*
* Kernel jobs should never fail, nor should VM jobs if they do
@@ -1953,8 +1968,6 @@ int xe_guc_exec_queue_reset_handler(struct xe_guc *guc, u32 *msg, u32 len)
xe_gt_info(gt, "Engine reset: engine_class=%s, logical_mask: 0x%x, guc_id=%d",
xe_hw_engine_class_to_str(q->class), q->logical_mask, guc_id);
- /* FIXME: Do error capture, most likely async */
-
trace_xe_exec_queue_reset(q);
/*
@@ -1980,7 +1993,7 @@ int xe_guc_exec_queue_reset_handler(struct xe_guc *guc, u32 *msg, u32 len)
* XE_GUC_ACTION_STATE_CAPTURE_NOTIFICATION to host, this function will be
* called 1st to check status before process the data comes with the message.
*
- * Returns: None
+ * Returns: error code. 0 if success
*/
int xe_guc_error_capture_handler(struct xe_guc *guc, u32 *msg, u32 len)
{
diff --git a/drivers/gpu/drm/xe/xe_hw_engine.c b/drivers/gpu/drm/xe/xe_hw_engine.c
index 07ed9fd28f19..291b3d1a16e9 100644
--- a/drivers/gpu/drm/xe/xe_hw_engine.c
+++ b/drivers/gpu/drm/xe/xe_hw_engine.c
@@ -20,6 +20,8 @@
#include "xe_gt_printk.h"
#include "xe_gt_mcr.h"
#include "xe_gt_topology.h"
+#include "xe_guc_capture.h"
+#include "xe_guc_capture_types.h"
#include "xe_hw_fence.h"
#include "xe_irq.h"
#include "xe_lrc.h"
@@ -288,6 +290,7 @@ static void hw_engine_mmio_write32(struct xe_hw_engine *hwe, struct xe_reg reg,
static u32 hw_engine_mmio_read32(struct xe_hw_engine *hwe, struct xe_reg reg)
{
xe_gt_assert(hwe->gt, !(reg.addr & hwe->mmio_base));
+
xe_force_wake_assert_held(gt_to_fw(hwe->gt), hwe->domain);
reg.addr += hwe->mmio_base;
@@ -827,6 +830,48 @@ xe_hw_engine_snapshot_instdone_capture(struct xe_hw_engine *hwe,
}
}
+static void
+xe_hw_engine_snapshot_from_hw_by_type(struct xe_hw_engine *hwe,
+ struct xe_hw_engine_snapshot *snapshot, int type)
+{
+ const struct __guc_mmio_reg_descr_group *list;
+ enum guc_capture_list_class_type capture_class;
+ int i;
+
+ capture_class = xe_engine_class_to_guc_capture_class(hwe->class);
+ list = xe_guc_capture_get_reg_desc_list(GUC_CAPTURE_LIST_INDEX_PF, type, capture_class);
+ if (!list)
+ return;
+
+ for (i = 0; i < list->num_regs; i++) {
+ u32 *field;
+ const struct __guc_mmio_reg_descr *sub_list = &list->list[i];
+
+ /* loop until extra operation registers zone */
+ if (sub_list->position_in_snapshot == offsetof(struct snapshot_regs, extra_op))
+ break;
+
+ field = (u32 *)((uintptr_t)&snapshot->reg + sub_list->position_in_snapshot);
+ if (type == GUC_STATE_CAPTURE_TYPE_ENGINE_INSTANCE)
+ *field = hw_engine_mmio_read32(hwe, sub_list->reg);
+ else
+ *field = xe_mmio_read32(hwe->gt, sub_list->reg);
+ }
+}
+
+static void
+xe_hw_engine_snapshot_from_hw(struct xe_hw_engine *hwe, struct xe_hw_engine_snapshot *snapshot)
+{
+ int type;
+
+ snapshot->source = XE_HW_ENGINE_SOURCE_HW_ENGINE;
+ for (type = GUC_STATE_CAPTURE_TYPE_GLOBAL; type < GUC_STATE_CAPTURE_TYPE_MAX; type++)
+ xe_hw_engine_snapshot_from_hw_by_type(hwe, snapshot, type);
+
+ /* Capture steering registers */
+ xe_hw_engine_snapshot_instdone_capture(hwe, snapshot);
+}
+
/**
* xe_hw_engine_snapshot_capture - Take a quick snapshot of the HW Engine.
* @hwe: Xe HW Engine.
@@ -841,8 +886,10 @@ struct xe_hw_engine_snapshot *
xe_hw_engine_snapshot_capture(struct xe_hw_engine *hwe)
{
struct xe_hw_engine_snapshot *snapshot;
+ struct xe_gt *gt = hwe->gt;
+ struct xe_device *xe = gt_to_xe(gt);
size_t len;
- u64 val;
+ u32 i;
if (!xe_hw_engine_is_valid(hwe))
return NULL;
@@ -883,65 +930,80 @@ xe_hw_engine_snapshot_capture(struct xe_hw_engine *hwe)
snapshot->mmio_base = hwe->mmio_base;
/* no more VF accessible data below this point */
- if (IS_SRIOV_VF(gt_to_xe(hwe->gt)))
+ if (IS_SRIOV_VF(xe))
return snapshot;
- snapshot->reg.ring_execlist_status =
- hw_engine_mmio_read32(hwe, RING_EXECLIST_STATUS_LO(0));
- val = hw_engine_mmio_read32(hwe, RING_EXECLIST_STATUS_HI(0));
- snapshot->reg.ring_execlist_status |= val << 32;
-
- snapshot->reg.ring_execlist_sq_contents =
- hw_engine_mmio_read32(hwe, RING_EXECLIST_SQ_CONTENTS_LO(0));
- val = hw_engine_mmio_read32(hwe, RING_EXECLIST_SQ_CONTENTS_HI(0));
- snapshot->reg.ring_execlist_sq_contents |= val << 32;
-
- snapshot->reg.ring_acthd = hw_engine_mmio_read32(hwe, RING_ACTHD(0));
- val = hw_engine_mmio_read32(hwe, RING_ACTHD_UDW(0));
- snapshot->reg.ring_acthd |= val << 32;
-
- snapshot->reg.ring_bbaddr = hw_engine_mmio_read32(hwe, RING_BBADDR(0));
- val = hw_engine_mmio_read32(hwe, RING_BBADDR_UDW(0));
- snapshot->reg.ring_bbaddr |= val << 32;
-
- snapshot->reg.ring_dma_fadd =
- hw_engine_mmio_read32(hwe, RING_DMA_FADD(0));
- val = hw_engine_mmio_read32(hwe, RING_DMA_FADD_UDW(0));
- snapshot->reg.ring_dma_fadd |= val << 32;
-
- snapshot->reg.ring_hwstam = hw_engine_mmio_read32(hwe, RING_HWSTAM(0));
- snapshot->reg.ring_hws_pga = hw_engine_mmio_read32(hwe, RING_HWS_PGA(0));
- snapshot->reg.ring_start = hw_engine_mmio_read32(hwe, RING_START(0));
- if (GRAPHICS_VERx100(hwe->gt->tile->xe) >= 2000) {
- val = hw_engine_mmio_read32(hwe, RING_START_UDW(0));
- snapshot->reg.ring_start |= val << 32;
- }
- if (xe_gt_has_indirect_ring_state(hwe->gt)) {
- snapshot->reg.indirect_ring_state =
+ /* If GuC not enabled, and capture outlist not empty, take it from engine */
+ if (xe_device_uc_enabled(xe) && xe->wedged.mode >= 1 && xe->devcoredump.job &&
+ xe_guc_capture_is_ready_for(xe->devcoredump.job))
+ xe_hw_engine_snapshot_from_capture(hwe, snapshot);
+ else
+ xe_hw_engine_snapshot_from_hw(hwe, snapshot);
+
+ /* Read direct ready registers */
+ xe_hw_engine_snapshot_from_hw_by_type(hwe, snapshot, GUC_STATE_CAPTURE_TYPE_DIRECT_READ);
+
+ /* Extra operation required registers zone - start */
+ if (xe_gt_has_indirect_ring_state(hwe->gt))
+ snapshot->reg.extra_op.indirect_ring_state =
hw_engine_mmio_read32(hwe, INDIRECT_RING_STATE(0));
- }
+ /* Extra operation required registers zone - End */
- snapshot->reg.ring_head =
- hw_engine_mmio_read32(hwe, RING_HEAD(0)) & HEAD_ADDR;
- snapshot->reg.ring_tail =
- hw_engine_mmio_read32(hwe, RING_TAIL(0)) & TAIL_ADDR;
- snapshot->reg.ring_ctl = hw_engine_mmio_read32(hwe, RING_CTL(0));
- snapshot->reg.ring_mi_mode =
- hw_engine_mmio_read32(hwe, RING_MI_MODE(0));
- snapshot->reg.ring_mode = hw_engine_mmio_read32(hwe, RING_MODE(0));
- snapshot->reg.ring_imr = hw_engine_mmio_read32(hwe, RING_IMR(0));
- snapshot->reg.ring_esr = hw_engine_mmio_read32(hwe, RING_ESR(0));
- snapshot->reg.ring_emr = hw_engine_mmio_read32(hwe, RING_EMR(0));
- snapshot->reg.ring_eir = hw_engine_mmio_read32(hwe, RING_EIR(0));
- snapshot->reg.ipehr = hw_engine_mmio_read32(hwe, RING_IPEHR(0));
- xe_hw_engine_snapshot_instdone_capture(hwe, snapshot);
+ /* appy mask for ring head and tail */
+ snapshot->reg.ring_head &= HEAD_ADDR;
+ snapshot->reg.ring_tail &= TAIL_ADDR;
- if (snapshot->hwe->class == XE_ENGINE_CLASS_COMPUTE)
- snapshot->reg.rcu_mode = xe_mmio_read32(hwe->gt, RCU_MODE);
+ /* adjust u64 endine in snapshot */
+ for (i = 0; i < sizeof(snapshot->reg.u64_regs); i += sizeof(u64)) {
+ u64 *pdata = (u64 *)((ulong)&snapshot->reg + i);
+ u32 *pl = (u32 *)pdata;
+ u32 *ph = (u32 *)((ulong)pdata + 4);
+
+ *pdata = ((u64)*ph) << 32 | *pl;
+ }
return snapshot;
}
+/**
+ * xe_hw_engine_snapshot_capture_for_job - Take snapshot of associated engine
+ * @job: The job object
+ *
+ * Take snapshot of associated HW Engine
+ *
+ * Returns: None.
+ */
+void
+xe_hw_engine_snapshot_capture_for_job(struct xe_sched_job *job)
+{
+ struct xe_exec_queue *q = job->q;
+ struct xe_device *xe = gt_to_xe(q->gt);
+ struct xe_devcoredump *coredump = &xe->devcoredump;
+ struct xe_hw_engine *hwe;
+ enum xe_hw_engine_id id;
+ u32 adj_logical_mask = q->logical_mask;
+
+ for_each_hw_engine(hwe, q->gt, id) {
+ if (hwe->class != q->hwe->class ||
+ !(BIT(hwe->logical_instance) & adj_logical_mask))
+ continue;
+
+ if (!coredump->snapshot.hwe[id]) {
+ coredump->snapshot.hwe[id] = xe_hw_engine_snapshot_capture(hwe);
+ } else {
+ /* If pre-captured and Guc capture ready now */
+ if (xe_device_uc_enabled(xe) && xe->wedged.mode >= 1 &&
+ xe_guc_capture_is_ready_for(job)) {
+ /* Clear snapshot registers, skip pointers */
+ memset(&coredump->snapshot.hwe[id]->reg, 0,
+ offsetof(struct snapshot_regs, instdone));
+ /* Refresh with GuC captured data */
+ xe_hw_engine_snapshot_from_capture(hwe, coredump->snapshot.hwe[id]);
+ }
+ }
+ }
+}
+
static void
xe_hw_engine_snapshot_instdone_print(struct xe_hw_engine_snapshot *snapshot, struct drm_printer *p)
{
@@ -995,42 +1057,68 @@ xe_hw_engine_snapshot_instdone_print(struct xe_hw_engine_snapshot *snapshot, str
void xe_hw_engine_snapshot_print(struct xe_hw_engine_snapshot *snapshot,
struct drm_printer *p)
{
+ int i, type;
+
if (!snapshot)
return;
+ xe_gt_assert(snapshot->hwe->gt, snapshot->source <= XE_HW_ENGINE_SOURCE_GUC_CAPTURE);
+
drm_printf(p, "%s (physical), logical instance=%d\n",
snapshot->name ? snapshot->name : "",
snapshot->logical_instance);
+ drm_printf(p, "\tCapture source: %s\n",
+ snapshot->source == XE_HW_ENGINE_SOURCE_GUC_CAPTURE ? "GuC" : "Engine");
drm_printf(p, "\tForcewake: domain 0x%x, ref %d\n",
snapshot->forcewake.domain, snapshot->forcewake.ref);
- drm_printf(p, "\tHWSTAM: 0x%08x\n", snapshot->reg.ring_hwstam);
- drm_printf(p, "\tRING_HWS_PGA: 0x%08x\n", snapshot->reg.ring_hws_pga);
- drm_printf(p, "\tRING_EXECLIST_STATUS: 0x%016llx\n",
- snapshot->reg.ring_execlist_status);
- drm_printf(p, "\tRING_EXECLIST_SQ_CONTENTS: 0x%016llx\n",
- snapshot->reg.ring_execlist_sq_contents);
- drm_printf(p, "\tRING_START: 0x%016llx\n", snapshot->reg.ring_start);
- drm_printf(p, "\tRING_HEAD: 0x%08x\n", snapshot->reg.ring_head);
- drm_printf(p, "\tRING_TAIL: 0x%08x\n", snapshot->reg.ring_tail);
- drm_printf(p, "\tRING_CTL: 0x%08x\n", snapshot->reg.ring_ctl);
- drm_printf(p, "\tRING_MI_MODE: 0x%08x\n", snapshot->reg.ring_mi_mode);
- drm_printf(p, "\tRING_MODE: 0x%08x\n",
- snapshot->reg.ring_mode);
- drm_printf(p, "\tRING_IMR: 0x%08x\n", snapshot->reg.ring_imr);
- drm_printf(p, "\tRING_ESR: 0x%08x\n", snapshot->reg.ring_esr);
- drm_printf(p, "\tRING_EMR: 0x%08x\n", snapshot->reg.ring_emr);
- drm_printf(p, "\tRING_EIR: 0x%08x\n", snapshot->reg.ring_eir);
- drm_printf(p, "\tACTHD: 0x%016llx\n", snapshot->reg.ring_acthd);
- drm_printf(p, "\tBBADDR: 0x%016llx\n", snapshot->reg.ring_bbaddr);
- drm_printf(p, "\tDMA_FADDR: 0x%016llx\n", snapshot->reg.ring_dma_fadd);
+
+ for (type = GUC_STATE_CAPTURE_TYPE_GLOBAL; type <= GUC_STATE_CAPTURE_TYPE_DIRECT_READ;
+ type++) {
+ const struct __guc_mmio_reg_descr_group *list;
+ enum guc_capture_list_class_type capture_class;
+
+ capture_class = xe_engine_class_to_guc_capture_class(snapshot->hwe->class);
+ list = xe_guc_capture_get_reg_desc_list(GUC_CAPTURE_LIST_INDEX_PF, type,
+ capture_class);
+ if (!list)
+ continue;
+
+ for (i = 0; i < list->num_regs; i++) {
+ u32 *field;
+ const struct __guc_mmio_reg_descr *sub_list = &list->list[i];
+
+ /* loop 32bit registers until 64 bit registers */
+ if (sub_list->position_in_snapshot ==
+ offsetof(struct snapshot_regs, u64_regs))
+ break;
+ field = (u32 *)((uintptr_t)&snapshot->reg + sub_list->position_in_snapshot);
+ drm_printf(p, "\t%s: 0x%08x\n", sub_list->regname, *field);
+ }
+
+ if (type != GUC_STATE_CAPTURE_TYPE_ENGINE_INSTANCE)
+ continue; /* 64bit registers is for engine instance only */
+
+ /* Each 64 bit registers take 2 entry in the list */
+ for (; i < list->num_regs; i += 2) {
+ u64 *field;
+ const struct __guc_mmio_reg_descr *sub_list = &list->list[i];
+
+ /* loop 64 bit registers until extra_op registers */
+ if (sub_list->position_in_snapshot ==
+ offsetof(struct snapshot_regs, extra_op))
+ break;
+ field = (u64 *)((uintptr_t)&snapshot->reg + sub_list->position_in_snapshot);
+ drm_printf(p, "\t%s: 0x%016llx\n", sub_list->regname, *field);
+ }
+ }
+
+ /* Extra operation required registers - Start */
drm_printf(p, "\tINDIRECT_RING_STATE: 0x%08x\n",
- snapshot->reg.indirect_ring_state);
- drm_printf(p, "\tIPEHR: 0x%08x\n", snapshot->reg.ipehr);
+ snapshot->reg.extra_op.indirect_ring_state);
+ /* Extra operation required registers - End */
+
xe_hw_engine_snapshot_instdone_print(snapshot, p);
- if (snapshot->hwe->class == XE_ENGINE_CLASS_COMPUTE)
- drm_printf(p, "\tRCU_MODE: 0x%08x\n",
- snapshot->reg.rcu_mode);
drm_puts(p, "\n");
}
diff --git a/drivers/gpu/drm/xe/xe_hw_engine.h b/drivers/gpu/drm/xe/xe_hw_engine.h
index 900c8c991430..52c3271238bd 100644
--- a/drivers/gpu/drm/xe/xe_hw_engine.h
+++ b/drivers/gpu/drm/xe/xe_hw_engine.h
@@ -9,6 +9,7 @@
#include "xe_hw_engine_types.h"
struct drm_printer;
+struct xe_sched_job;
#ifdef CONFIG_DRM_XE_JOB_TIMEOUT_MIN
#define XE_HW_ENGINE_JOB_TIMEOUT_MIN CONFIG_DRM_XE_JOB_TIMEOUT_MIN
@@ -55,6 +56,7 @@ u32 xe_hw_engine_mask_per_class(struct xe_gt *gt,
struct xe_hw_engine_snapshot *
xe_hw_engine_snapshot_capture(struct xe_hw_engine *hwe);
+void xe_hw_engine_snapshot_capture_for_job(struct xe_sched_job *job);
void xe_hw_engine_snapshot_free(struct xe_hw_engine_snapshot *snapshot);
void xe_hw_engine_snapshot_print(struct xe_hw_engine_snapshot *snapshot,
struct drm_printer *p);
diff --git a/drivers/gpu/drm/xe/xe_hw_engine_types.h b/drivers/gpu/drm/xe/xe_hw_engine_types.h
index 70e6434f150d..053007f0658d 100644
--- a/drivers/gpu/drm/xe/xe_hw_engine_types.h
+++ b/drivers/gpu/drm/xe/xe_hw_engine_types.h
@@ -152,6 +152,125 @@ struct xe_hw_engine {
struct xe_oa_unit *oa_unit;
};
+/**
+ * struct snapshot_regs - Hardware registers snapshot
+ *
+ * Contains the snapshot of useful hardware registers.
+ */
+struct snapshot_regs {
+ /* Global registers - start */
+ /** @forcewake_gt: The FORCEWAKE_GT register */
+ u32 forcewake_gt;
+ /* Global registers - end */
+
+ /* Engine class registers - start */
+ /** @rcu_mode: The RCU_MODE register */
+ u32 rcu_mode;
+ /* Engine class registers - end */
+
+ /* Direct read registers - start */
+ /** @sfc_done_0: The SFC_DONE[0] register */
+ u32 sfc_done_0;
+ /** @sfc_done_1: The SFC_DONE[1] register */
+ u32 sfc_done_1;
+ /** @sfc_done_2: The SFC_DONE[2] register */
+ u32 sfc_done_2;
+ /** @sfc_done_3: The SFC_DONE[3] register */
+ u32 sfc_done_3;
+ /* Direct read registers - end */
+
+ /* Engine instance registers - start */
+ /** @ring_hwstam: The RING_HWSTAM register */
+ u32 ring_hwstam;
+ /** @ring_hws_pga: The RING_HWS_PGA register */
+ u32 ring_hws_pga;
+ /** @ring_head: The RING_HEAD register */
+ u32 ring_head;
+ /** @ring_tail: The RING_TAIL register */
+ u32 ring_tail;
+ /** @ring_ctl: The RING_CTL register */
+ u32 ring_ctl;
+ /** @ring_mi_mode: The RING_MI_MODE register */
+ u32 ring_mi_mode;
+ /** @ring_mode: The RING_MODE register */
+ u32 ring_mode;
+ /** @ring_imr: The RING_IMR register */
+ u32 ring_imr;
+ /** @ring_esr: The RING_ESR register */
+ u32 ring_esr;
+ /** @ring_emr: The RING_EMR register */
+ u32 ring_emr;
+ /** @ring_eir: The RING_EIR register */
+ u32 ring_eir;
+ /** @ipehr: The IPEHR register */
+ u32 ipehr;
+
+ /* 64 bit engine instance registers - start */
+ /**
+ * @u64_regs: 64 bit registers
+ *
+ * u64 data captured by 2 u32 from GuC or by hw mmio read.
+ * Save data into this u64 zone will always write in format of:
+ * offset + 0000: [low 32]
+ * offset + 0004: [high 32]
+ * Once all data captured, data will be converted to CPU endian order at the
+ * end of xe_hw_engine_snapshot_capture
+ */
+ struct {
+ /** @u64_regs.ring_acthd: The RING_ACTHD register */
+ u64 ring_acthd;
+ /** @u64_regs.ring_bbaddr: The RING_BBADDR register */
+ u64 ring_bbaddr;
+ /** @u64_regs.ring_start: The RING_START register */
+ u64 ring_start;
+ /** @u64_regs.ring_dma_fadd: The RING_DMA_FADD register */
+ u64 ring_dma_fadd;
+ /** @u64_regs.ring_execlist_status: The RING_EXECLIST_STATUS register */
+ u64 ring_execlist_status;
+ /** @u64_regs.ring_execlist_sq_contents: The RING_EXECLIST_SQ_CONTENTS register */
+ u64 ring_execlist_sq_contents;
+ } u64_regs;
+ /* 64 bit engine instance registers - end */
+ /* Engine instance type - end */
+
+ /* Extra operation required registers - start */
+ /** @extra_op: Extra operation required registers */
+ struct {
+ /*
+ * If all members deleted from this structure,
+ * uncoment the "Reserved" below
+ */
+ /* u32 Reserved; */
+
+ /** @extra_op.indirect_ring_state: The INDIRECT_RING_STATE register */
+ u32 indirect_ring_state;
+ } extra_op;
+ /* Extra operation required registers - end */
+
+ /** @instdone: Steering registers */
+ struct {
+ /** @reg.instdone.ring: The RING_INSTDONE register */
+ u32 ring;
+ /** @reg.instdone.slice_common: The SC_INSTDONE register */
+ u32 *slice_common;
+ /** @reg.instdone.slice_common_extra: The SC_INSTDONE_EXTRA register */
+ u32 *slice_common_extra;
+ /** @reg.instdone.slice_common_extra2: The SC_INSTDONE_EXTRA2 register */
+ u32 *slice_common_extra2;
+ /** @reg.instdone.sampler: The SAMPLER_INSTDONE register */
+ u32 *sampler;
+ /** @reg.instdone.row: The ROW_INSTDONE register */
+ u32 *row;
+ /** @reg.instdone.geom_svg: The INSTDONE_GEOM_SVGUNIT register */
+ u32 *geom_svg;
+ } instdone;
+};
+
+enum xe_hw_engine_snapshot_source_id {
+ XE_HW_ENGINE_SOURCE_HW_ENGINE,
+ XE_HW_ENGINE_SOURCE_GUC_CAPTURE
+};
+
/**
* struct xe_hw_engine_snapshot - Hardware engine snapshot
*
@@ -160,6 +279,8 @@ struct xe_hw_engine {
struct xe_hw_engine_snapshot {
/** @name: name of the hw engine */
char *name;
+ /** @source: Data source, either hw_engine or GuC capture */
+ enum xe_hw_engine_snapshot_source_id source;
/** @hwe: hw engine */
struct xe_hw_engine *hwe;
/** @logical_instance: logical instance of this hw engine */
@@ -174,64 +295,7 @@ struct xe_hw_engine_snapshot {
/** @mmio_base: MMIO base address of this hw engine*/
u32 mmio_base;
/** @reg: Useful MMIO register snapshot */
- struct {
- /** @reg.ring_execlist_status: RING_EXECLIST_STATUS */
- u64 ring_execlist_status;
- /** @reg.ring_execlist_sq_contents: RING_EXECLIST_SQ_CONTENTS */
- u64 ring_execlist_sq_contents;
- /** @reg.ring_acthd: RING_ACTHD */
- u64 ring_acthd;
- /** @reg.ring_bbaddr: RING_BBADDR */
- u64 ring_bbaddr;
- /** @reg.ring_dma_fadd: RING_DMA_FADD */
- u64 ring_dma_fadd;
- /** @reg.ring_hwstam: RING_HWSTAM */
- u32 ring_hwstam;
- /** @reg.ring_hws_pga: RING_HWS_PGA */
- u32 ring_hws_pga;
- /** @reg.ring_start: RING_START */
- u64 ring_start;
- /** @reg.ring_head: RING_HEAD */
- u32 ring_head;
- /** @reg.ring_tail: RING_TAIL */
- u32 ring_tail;
- /** @reg.ring_ctl: RING_CTL */
- u32 ring_ctl;
- /** @reg.ring_mi_mode: RING_MI_MODE */
- u32 ring_mi_mode;
- /** @reg.ring_mode: RING_MODE */
- u32 ring_mode;
- /** @reg.ring_imr: RING_IMR */
- u32 ring_imr;
- /** @reg.ring_esr: RING_ESR */
- u32 ring_esr;
- /** @reg.ring_emr: RING_EMR */
- u32 ring_emr;
- /** @reg.ring_eir: RING_EIR */
- u32 ring_eir;
- /** @reg.indirect_ring_state: INDIRECT_RING_STATE */
- u32 indirect_ring_state;
- /** @reg.ipehr: IPEHR */
- u32 ipehr;
- /** @reg.rcu_mode: RCU_MODE */
- u32 rcu_mode;
- struct {
- /** @reg.instdone.ring: RING_INSTDONE */
- u32 ring;
- /** @reg.instdone.slice_common: SC_INSTDONE */
- u32 *slice_common;
- /** @reg.instdone.slice_common_extra: SC_INSTDONE_EXTRA */
- u32 *slice_common_extra;
- /** @reg.instdone.slice_common_extra2: SC_INSTDONE_EXTRA2 */
- u32 *slice_common_extra2;
- /** @reg.instdone.sampler: SAMPLER_INSTDONE */
- u32 *sampler;
- /** @reg.instdone.row: ROW_INSTDONE */
- u32 *row;
- /** @reg.instdone.geom_svg: INSTDONE_GEOM_SVGUNIT */
- u32 *geom_svg;
- } instdone;
- } reg;
+ struct snapshot_regs reg;
};
#endif
diff --git a/drivers/gpu/drm/xe/xe_lrc.h b/drivers/gpu/drm/xe/xe_lrc.h
index c24542e89318..cc00fa878d3c 100644
--- a/drivers/gpu/drm/xe/xe_lrc.h
+++ b/drivers/gpu/drm/xe/xe_lrc.h
@@ -21,6 +21,7 @@ struct xe_lrc_snapshot;
struct xe_vm;
#define LRC_PPHWSP_SCRATCH_ADDR (0x34 * 4)
+#define LRC_GTT_ADDRESS_MASK GENMASK(31, 12)
struct xe_lrc *xe_lrc_create(struct xe_hw_engine *hwe, struct xe_vm *vm,
u32 ring_size);
--
2.34.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* ✓ CI.Patch_applied: success for drm/xe/guc: Add GuC based register capture for error capture (rev13)
2024-07-19 20:29 [PATCH v13 0/5] drm/xe/guc: Add GuC based register capture for error capture Zhanjun Dong
` (4 preceding siblings ...)
2024-07-19 20:29 ` [PATCH v13 5/5] drm/xe/guc: Plumb GuC-capture into dev coredump Zhanjun Dong
@ 2024-07-19 20:34 ` Patchwork
2024-07-19 20:35 ` ✗ CI.checkpatch: warning " Patchwork
2024-07-19 20:35 ` ✗ CI.KUnit: failure " Patchwork
7 siblings, 0 replies; 9+ messages in thread
From: Patchwork @ 2024-07-19 20:34 UTC (permalink / raw)
To: Zhanjun Dong; +Cc: intel-xe
== Series Details ==
Series: drm/xe/guc: Add GuC based register capture for error capture (rev13)
URL : https://patchwork.freedesktop.org/series/128077/
State : success
== Summary ==
=== Applying kernel patches on branch 'drm-tip' with base: ===
Base commit: eb6045a759ea drm-tip: 2024y-07m-19d-11h-07m-10s UTC integration manifest
=== git am output follows ===
Applying: drm/xe/guc: Prepare GuC register list and update ADS size for error capture
Applying: drm/xe/guc: Add XE_LP steered register lists
Applying: drm/xe/guc: Add capture size check in GuC log buffer
Applying: drm/xe/guc: Extract GuC error capture lists
Applying: drm/xe/guc: Plumb GuC-capture into dev coredump
^ permalink raw reply [flat|nested] 9+ messages in thread
* ✗ CI.checkpatch: warning for drm/xe/guc: Add GuC based register capture for error capture (rev13)
2024-07-19 20:29 [PATCH v13 0/5] drm/xe/guc: Add GuC based register capture for error capture Zhanjun Dong
` (5 preceding siblings ...)
2024-07-19 20:34 ` ✓ CI.Patch_applied: success for drm/xe/guc: Add GuC based register capture for error capture (rev13) Patchwork
@ 2024-07-19 20:35 ` Patchwork
2024-07-19 20:35 ` ✗ CI.KUnit: failure " Patchwork
7 siblings, 0 replies; 9+ messages in thread
From: Patchwork @ 2024-07-19 20:35 UTC (permalink / raw)
To: Zhanjun Dong; +Cc: intel-xe
== Series Details ==
Series: drm/xe/guc: Add GuC based register capture for error capture (rev13)
URL : https://patchwork.freedesktop.org/series/128077/
State : warning
== Summary ==
+ KERNEL=/kernel
+ git clone https://gitlab.freedesktop.org/drm/maintainer-tools mt
Cloning into 'mt'...
warning: redirecting to https://gitlab.freedesktop.org/drm/maintainer-tools.git/
+ git -C mt rev-list -n1 origin/master
5ce3e132caaa5b45e5e50201b574a097d130967c
+ cd /kernel
+ git config --global --add safe.directory /kernel
+ git log -n1
commit 57be91a5730d4dbed7787a300eba9d74c058fa07
Author: Zhanjun Dong <zhanjun.dong@intel.com>
Date: Fri Jul 19 13:29:17 2024 -0700
drm/xe/guc: Plumb GuC-capture into dev coredump
Add xe_hw_engine_snapshot_from_capture to take snapshot from captured
node list.
Add pre-capture by read from hw engine if GuC capture data is not ready,
the pre-captured data will be refereshed if GuC capture is ready at later
time.
Add data struct to map captured register value to a snapshot field.
Add register name to register list.
Provide xe_guc_capture_get_reg_desc_list to get the register dscriptor
list.
Add function to check if capture is ready for a job.
Sort out snapshot registers into types of global, class, instance and
direct read.
Signed-off-by: Zhanjun Dong <zhanjun.dong@intel.com>
+ /mt/dim checkpatch eb6045a759ea13e8d159bdaea423e904b9e3717b drm-intel
9c6109bb335d drm/xe/guc: Prepare GuC register list and update ADS size for error capture
Traceback (most recent call last):
File "scripts/spdxcheck.py", line 6, in <module>
from ply import lex, yacc
ModuleNotFoundError: No module named 'ply'
Traceback (most recent call last):
File "scripts/spdxcheck.py", line 6, in <module>
from ply import lex, yacc
ModuleNotFoundError: No module named 'ply'
Traceback (most recent call last):
File "scripts/spdxcheck.py", line 6, in <module>
from ply import lex, yacc
ModuleNotFoundError: No module named 'ply'
Traceback (most recent call last):
File "scripts/spdxcheck.py", line 6, in <module>
from ply import lex, yacc
ModuleNotFoundError: No module named 'ply'
-:37: WARNING:FILE_PATH_CHANGES: added, moved or deleted file(s), does MAINTAINERS need updating?
#37:
new file mode 100644
-:536: ERROR:COMPLEX_MACRO: Macros with complex values should be enclosed in parentheses
#536: FILE: drivers/gpu/drm/xe/xe_guc_capture.c:42:
+#define COMMON_BASE_ENGINE_INSTANCE \
+ { RING_ESR(0), 0, 0}, \
+ { RING_EMR(0), 0, 0}, \
+ { RING_EIR(0), 0, 0}, \
+ { RING_EXECLIST_STATUS_HI(0), 0, 0}, \
+ { RING_EXECLIST_STATUS_LO(0), 0, 0}, \
+ { RING_DMA_FADD(0), 0, 0}, \
+ { RING_DMA_FADD_UDW(0), 0, 0}, \
+ { RING_IPEHR(0), 0, 0}, \
+ { RING_BBADDR(0), 0, 0}, \
+ { RING_BBADDR_UDW(0), 0, 0}, \
+ { RING_ACTHD(0), 0, 0}, \
+ { RING_ACTHD_UDW(0), 0, 0}, \
+ { RING_START(0), 0, 0}, \
+ { RING_HEAD(0), 0, 0}, \
+ { RING_TAIL(0), 0, 0}, \
+ { RING_CTL(0), 0, 0}, \
+ { RING_MI_MODE(0), 0, 0}, \
+ { RING_HWS_PGA(0), 0, 0}, \
+ { RING_MODE(0), 0, 0}
-:596: CHECK:MACRO_ARG_REUSE: Macro argument reuse 'regslist' - possible side-effects?
#596: FILE: drivers/gpu/drm/xe/xe_guc_capture.c:102:
+#define MAKE_REGLIST(regslist, regsowner, regstype, class) \
+ { \
+ regslist, \
+ ARRAY_SIZE(regslist), \
+ TO_GCAP_DEF_OWNER(regsowner), \
+ TO_GCAP_DEF_TYPE(regstype), \
+ class, \
+ }
total: 1 errors, 1 warnings, 1 checks, 1037 lines checked
55f6f7124df0 drm/xe/guc: Add XE_LP steered register lists
fcab271123e2 drm/xe/guc: Add capture size check in GuC log buffer
Traceback (most recent call last):
File "scripts/spdxcheck.py", line 6, in <module>
from ply import lex, yacc
ModuleNotFoundError: No module named 'ply'
-:13: WARNING:FILE_PATH_CHANGES: added, moved or deleted file(s), does MAINTAINERS need updating?
#13:
new file mode 100644
-:166: CHECK:MACRO_ARG_REUSE: Macro argument reuse 'size' - possible side-effects?
#166: FILE: drivers/gpu/drm/xe/xe_guc_log.c:25:
+#define GUC_LOG_CALC_COUNT(size) ((size) / GUC_LOG_CALC_UNIT(size) - 1)
total: 0 errors, 1 warnings, 1 checks, 337 lines checked
28ebff591436 drm/xe/guc: Extract GuC error capture lists
57be91a5730d drm/xe/guc: Plumb GuC-capture into dev coredump
-:217: ERROR:COMPLEX_MACRO: Macros with complex values should be enclosed in parentheses
#217: FILE: drivers/gpu/drm/xe/xe_guc_capture.c:161:
+#define XELP_DIRECT_READ_VEC \
+ { SFC_DONE(0), 0, 0, "SFC_DONE[0]", \
+ offsetof(struct snapshot_regs, sfc_done_0) }, \
+ { SFC_DONE(1), 0, 0, "SFC_DONE[1]", \
+ offsetof(struct snapshot_regs, sfc_done_1) }, \
+ { SFC_DONE(2), 0, 0, "SFC_DONE[2]", \
+ offsetof(struct snapshot_regs, sfc_done_2) }, \
+ { SFC_DONE(3), 0, 0, "SFC_DONE[3]", \
+ offsetof(struct snapshot_regs, sfc_done_3) }
total: 1 errors, 0 warnings, 0 checks, 1002 lines checked
^ permalink raw reply [flat|nested] 9+ messages in thread
* ✗ CI.KUnit: failure for drm/xe/guc: Add GuC based register capture for error capture (rev13)
2024-07-19 20:29 [PATCH v13 0/5] drm/xe/guc: Add GuC based register capture for error capture Zhanjun Dong
` (6 preceding siblings ...)
2024-07-19 20:35 ` ✗ CI.checkpatch: warning " Patchwork
@ 2024-07-19 20:35 ` Patchwork
7 siblings, 0 replies; 9+ messages in thread
From: Patchwork @ 2024-07-19 20:35 UTC (permalink / raw)
To: Zhanjun Dong; +Cc: intel-xe
== Series Details ==
Series: drm/xe/guc: Add GuC based register capture for error capture (rev13)
URL : https://patchwork.freedesktop.org/series/128077/
State : failure
== Summary ==
+ trap cleanup EXIT
+ /kernel/tools/testing/kunit/kunit.py run --kunitconfig /kernel/drivers/gpu/drm/xe/.kunitconfig
ERROR:root:In file included from ../drivers/gpu/drm/drm_atomic.c:46:
../drivers/gpu/drm/drm_crtc_internal.h:322:6: warning: no previous prototype for ‘drm_panic_is_enabled’ [-Wmissing-prototypes]
322 | bool drm_panic_is_enabled(struct drm_device *dev) {return false; }
| ^~~~~~~~~~~~~~~~~~~~
In file included from ../drivers/gpu/drm/drm_atomic_uapi.c:43:
../drivers/gpu/drm/drm_crtc_internal.h:322:6: warning: no previous prototype for ‘drm_panic_is_enabled’ [-Wmissing-prototypes]
322 | bool drm_panic_is_enabled(struct drm_device *dev) {return false; }
| ^~~~~~~~~~~~~~~~~~~~
In file included from ../drivers/gpu/drm/drm_blend.c:36:
../drivers/gpu/drm/drm_crtc_internal.h:322:6: warning: no previous prototype for ‘drm_panic_is_enabled’ [-Wmissing-prototypes]
322 | bool drm_panic_is_enabled(struct drm_device *dev) {return false; }
| ^~~~~~~~~~~~~~~~~~~~
In file included from ../drivers/gpu/drm/drm_bridge.c:38:
../drivers/gpu/drm/drm_crtc_internal.h:322:6: warning: no previous prototype for ‘drm_panic_is_enabled’ [-Wmissing-prototypes]
322 | bool drm_panic_is_enabled(struct drm_device *dev) {return false; }
| ^~~~~~~~~~~~~~~~~~~~
In file included from ../drivers/gpu/drm/drm_client_modeset.c:26:
../drivers/gpu/drm/drm_crtc_internal.h:322:6: warning: no previous prototype for ‘drm_panic_is_enabled’ [-Wmissing-prototypes]
322 | bool drm_panic_is_enabled(struct drm_device *dev) {return false; }
| ^~~~~~~~~~~~~~~~~~~~
In file included from ../drivers/gpu/drm/drm_client.c:23:
../drivers/gpu/drm/drm_crtc_internal.h:322:6: warning: no previous prototype for ‘drm_panic_is_enabled’ [-Wmissing-prototypes]
322 | bool drm_panic_is_enabled(struct drm_device *dev) {return false; }
| ^~~~~~~~~~~~~~~~~~~~
In file included from ../drivers/gpu/drm/drm_displayid.c:9:
../drivers/gpu/drm/drm_crtc_internal.h:322:6: warning: no previous prototype for ‘drm_panic_is_enabled’ [-Wmissing-prototypes]
322 | bool drm_panic_is_enabled(struct drm_device *dev) {return false; }
| ^~~~~~~~~~~~~~~~~~~~
In file included from ../drivers/gpu/drm/drm_color_mgmt.c:32:
../drivers/gpu/drm/drm_crtc_internal.h:322:6: warning: no previous prototype for ‘drm_panic_is_enabled’ [-Wmissing-prototypes]
322 | bool drm_panic_is_enabled(struct drm_device *dev) {return false; }
| ^~~~~~~~~~~~~~~~~~~~
In file included from ../drivers/gpu/drm/drm_connector.c:41:
../drivers/gpu/drm/drm_crtc_internal.h:322:6: warning: no previous prototype for ‘drm_panic_is_enabled’ [-Wmissing-prototypes]
322 | bool drm_panic_is_enabled(struct drm_device *dev) {return false; }
| ^~~~~~~~~~~~~~~~~~~~
In file included from ../drivers/gpu/drm/drm_eld.c:11:
../drivers/gpu/drm/drm_crtc_internal.h:322:6: warning: no previous prototype for ‘drm_panic_is_enabled’ [-Wmissing-prototypes]
322 | bool drm_panic_is_enabled(struct drm_device *dev) {return false; }
| ^~~~~~~~~~~~~~~~~~~~
In file included from ../drivers/gpu/drm/drm_crtc.c:52:
../drivers/gpu/drm/drm_crtc_internal.h:322:6: warning: no previous prototype for ‘drm_panic_is_enabled’ [-Wmissing-prototypes]
322 | bool drm_panic_is_enabled(struct drm_device *dev) {return false; }
| ^~~~~~~~~~~~~~~~~~~~
In file included from ../drivers/gpu/drm/drm_dumb_buffers.c:31:
../drivers/gpu/drm/drm_crtc_internal.h:322:6: warning: no previous prototype for ‘drm_panic_is_enabled’ [-Wmissing-prototypes]
322 | bool drm_panic_is_enabled(struct drm_device *dev) {return false; }
| ^~~~~~~~~~~~~~~~~~~~
In file included from ../drivers/gpu/drm/drm_drv.c:50:
../drivers/gpu/drm/drm_crtc_internal.h:322:6: warning: no previous prototype for ‘drm_panic_is_enabled’ [-Wmissing-prototypes]
322 | bool drm_panic_is_enabled(struct drm_device *dev) {return false; }
| ^~~~~~~~~~~~~~~~~~~~
In file included from ../drivers/gpu/drm/drm_encoder.c:32:
../drivers/gpu/drm/drm_crtc_internal.h:322:6: warning: no previous prototype for ‘drm_panic_is_enabled’ [-Wmissing-prototypes]
322 | bool drm_panic_is_enabled(struct drm_device *dev) {return false; }
| ^~~~~~~~~~~~~~~~~~~~
In file included from ../drivers/gpu/drm/drm_edid.c:49:
../drivers/gpu/drm/drm_crtc_internal.h:322:6: warning: no previous prototype for ‘drm_panic_is_enabled’ [-Wmissing-prototypes]
322 | bool drm_panic_is_enabled(struct drm_device *dev) {return false; }
| ^~~~~~~~~~~~~~~~~~~~
In file included from ../drivers/gpu/drm/drm_file.c:48:
../drivers/gpu/drm/drm_crtc_internal.h:322:6: warning: no previous prototype for ‘drm_panic_is_enabled’ [-Wmissing-prototypes]
322 | bool drm_panic_is_enabled(struct drm_device *dev) {return false; }
| ^~~~~~~~~~~~~~~~~~~~
In file included from ../drivers/gpu/drm/drm_framebuffer.c:38:
../drivers/gpu/drm/drm_crtc_internal.h:322:6: warning: no previous prototype for ‘drm_panic_is_enabled’ [-Wmissing-prototypes]
322 | bool drm_panic_is_enabled(struct drm_device *dev) {return false; }
| ^~~~~~~~~~~~~~~~~~~~
In file included from ../drivers/gpu/drm/drm_ioctl.c:43:
../drivers/gpu/drm/drm_crtc_internal.h:322:6: warning: no previous prototype for ‘drm_panic_is_enabled’ [-Wmissing-prototypes]
322 | bool drm_panic_is_enabled(struct drm_device *dev) {return false; }
| ^~~~~~~~~~~~~~~~~~~~
In file included from ../drivers/gpu/drm/drm_lease.c:15:
../drivers/gpu/drm/drm_crtc_internal.h:322:6: warning: no previous prototype for ‘drm_panic_is_enabled’ [-Wmissing-prototypes]
322 | bool drm_panic_is_enabled(struct drm_device *dev) {return false; }
| ^~~~~~~~~~~~~~~~~~~~
In file included from ../drivers/gpu/drm/drm_mode_config.c:34:
../drivers/gpu/drm/drm_crtc_internal.h:322:6: warning: no previous prototype for ‘drm_panic_is_enabled’ [-Wmissing-prototypes]
322 | bool drm_panic_is_enabled(struct drm_device *dev) {return false; }
| ^~~~~~~~~~~~~~~~~~~~
In file included from ../drivers/gpu/drm/drm_mode_object.c:33:
../drivers/gpu/drm/drm_crtc_internal.h:322:6: warning: no previous prototype for ‘drm_panic_is_enabled’ [-Wmissing-prototypes]
322 | bool drm_panic_is_enabled(struct drm_device *dev) {return false; }
| ^~~~~~~~~~~~~~~~~~~~
In file included from ../drivers/gpu/drm/drm_modes.c:50:
../drivers/gpu/drm/drm_crtc_internal.h:322:6: warning: no previous prototype for ‘drm_panic_is_enabled’ [-Wmissing-prototypes]
322 | bool drm_panic_is_enabled(struct drm_device *dev) {return false; }
| ^~~~~~~~~~~~~~~~~~~~
In file included from ../drivers/gpu/drm/drm_plane.c:36:
../drivers/gpu/drm/drm_crtc_internal.h:322:6: warning: no previous prototype for ‘drm_panic_is_enabled’ [-Wmissing-prototypes]
322 | bool drm_panic_is_enabled(struct drm_device *dev) {return false; }
| ^~~~~~~~~~~~~~~~~~~~
In file included from ../drivers/gpu/drm/drm_property.c:33:
../drivers/gpu/drm/drm_crtc_internal.h:322:6: warning: no previous prototype for ‘drm_panic_is_enabled’ [-Wmissing-prototypes]
322 | bool drm_panic_is_enabled(struct drm_device *dev) {return false; }
| ^~~~~~~~~~~~~~~~~~~~
In file included from ../drivers/gpu/drm/drm_sysfs.c:34:
../drivers/gpu/drm/drm_crtc_internal.h:322:6: warning: no previous prototype for ‘drm_panic_is_enabled’ [-Wmissing-prototypes]
322 | bool drm_panic_is_enabled(struct drm_device *dev) {return false; }
| ^~~~~~~~~~~~~~~~~~~~
../lib/iomap.c:156:5: warning: no previous prototype for ‘ioread64_lo_hi’ [-Wmissing-prototypes]
156 | u64 ioread64_lo_hi(const void __iomem *addr)
| ^~~~~~~~~~~~~~
../lib/iomap.c:163:5: warning: no previous prototype for ‘ioread64_hi_lo’ [-Wmissing-prototypes]
163 | u64 ioread64_hi_lo(const void __iomem *addr)
| ^~~~~~~~~~~~~~
../lib/iomap.c:170:5: warning: no previous prototype for ‘ioread64be_lo_hi’ [-Wmissing-prototypes]
170 | u64 ioread64be_lo_hi(const void __iomem *addr)
| ^~~~~~~~~~~~~~~~
../lib/iomap.c:178:5: warning: no previous prototype for ‘ioread64be_hi_lo’ [-Wmissing-prototypes]
178 | u64 ioread64be_hi_lo(const void __iomem *addr)
| ^~~~~~~~~~~~~~~~
../lib/iomap.c:264:6: warning: no previous prototype for ‘iowrite64_lo_hi’ [-Wmissing-prototypes]
264 | void iowrite64_lo_hi(u64 val, void __iomem *addr)
| ^~~~~~~~~~~~~~~
../lib/iomap.c:272:6: warning: no previous prototype for ‘iowrite64_hi_lo’ [-Wmissing-prototypes]
272 | void iowrite64_hi_lo(u64 val, void __iomem *addr)
| ^~~~~~~~~~~~~~~
../lib/iomap.c:280:6: warning: no previous prototype for ‘iowrite64be_lo_hi’ [-Wmissing-prototypes]
280 | void iowrite64be_lo_hi(u64 val, void __iomem *addr)
| ^~~~~~~~~~~~~~~~~
../lib/iomap.c:288:6: warning: no previous prototype for ‘iowrite64be_hi_lo’ [-Wmissing-prototypes]
288 | void iowrite64be_hi_lo(u64 val, void __iomem *addr)
| ^~~~~~~~~~~~~~~~~
In file included from ../drivers/gpu/drm/drm_debugfs.c:45:
../drivers/gpu/drm/drm_crtc_internal.h:322:6: warning: no previous prototype for ‘drm_panic_is_enabled’ [-Wmissing-prototypes]
322 | bool drm_panic_is_enabled(struct drm_device *dev) {return false; }
| ^~~~~~~~~~~~~~~~~~~~
In file included from ../drivers/gpu/drm/drm_atomic_helper.c:48:
../drivers/gpu/drm/drm_crtc_internal.h:322:6: warning: no previous prototype for ‘drm_panic_is_enabled’ [-Wmissing-prototypes]
322 | bool drm_panic_is_enabled(struct drm_device *dev) {return false; }
| ^~~~~~~~~~~~~~~~~~~~
In file included from ../drivers/gpu/drm/drm_fb_helper.c:47:
../drivers/gpu/drm/drm_crtc_internal.h:322:6: warning: no previous prototype for ‘drm_panic_is_enabled’ [-Wmissing-prototypes]
322 | bool drm_panic_is_enabled(struct drm_device *dev) {return false; }
| ^~~~~~~~~~~~~~~~~~~~
ld: drivers/gpu/drm/drm_atomic_uapi.o: in function `drm_panic_is_enabled':
drm_atomic_uapi.c:(.text+0x1120): multiple definition of `drm_panic_is_enabled'; drivers/gpu/drm/drm_atomic.o:drm_atomic.c:(.text+0x3130): first defined here
ld: drivers/gpu/drm/drm_blend.o: in function `drm_panic_is_enabled':
drm_blend.c:(.text+0x890): multiple definition of `drm_panic_is_enabled'; drivers/gpu/drm/drm_atomic.o:drm_atomic.c:(.text+0x3130): first defined here
ld: drivers/gpu/drm/drm_bridge.o: in function `drm_panic_is_enabled':
drm_bridge.c:(.text+0x1270): multiple definition of `drm_panic_is_enabled'; drivers/gpu/drm/drm_atomic.o:drm_atomic.c:(.text+0x3130): first defined here
ld: drivers/gpu/drm/drm_client.o: in function `drm_panic_is_enabled':
drm_client.c:(.text+0xbd0): multiple definition of `drm_panic_is_enabled'; drivers/gpu/drm/drm_atomic.o:drm_atomic.c:(.text+0x3130): first defined here
ld: drivers/gpu/drm/drm_client_modeset.o: in function `drm_panic_is_enabled':
drm_client_modeset.c:(.text+0x2bb0): multiple definition of `drm_panic_is_enabled'; drivers/gpu/drm/drm_atomic.o:drm_atomic.c:(.text+0x3130): first defined here
ld: drivers/gpu/drm/drm_color_mgmt.o: in function `drm_panic_is_enabled':
drm_color_mgmt.c:(.text+0x520): multiple definition of `drm_panic_is_enabled'; drivers/gpu/drm/drm_atomic.o:drm_atomic.c:(.text+0x3130): first defined here
ld: drivers/gpu/drm/drm_connector.o: in function `drm_panic_is_enabled':
drm_connector.c:(.text+0x2ae0): multiple definition of `drm_panic_is_enabled'; drivers/gpu/drm/drm_atomic.o:drm_atomic.c:(.text+0x3130): first defined here
ld: drivers/gpu/drm/drm_crtc.o: in function `drm_panic_is_enabled':
drm_crtc.c:(.text+0xd50): multiple definition of `drm_panic_is_enabled'; drivers/gpu/drm/drm_atomic.o:drm_atomic.c:(.text+0x3130): first defined here
ld: drivers/gpu/drm/drm_displayid.o: in function `drm_panic_is_enabled':
drm_displayid.c:(.text+0x0): multiple definition of `drm_panic_is_enabled'; drivers/gpu/drm/drm_atomic.o:drm_atomic.c:(.text+0x3130): first defined here
ld: drivers/gpu/drm/drm_drv.o: in function `drm_panic_is_enabled':
drm_drv.c:(.text+0x1500): multiple definition of `drm_panic_is_enabled'; drivers/gpu/drm/drm_atomic.o:drm_atomic.c:(.text+0x3130): first defined here
ld: drivers/gpu/drm/drm_dumb_buffers.o: in function `drm_panic_is_enabled':
drm_dumb_buffers.c:(.text+0x0): multiple definition of `drm_panic_is_enabled'; drivers/gpu/drm/drm_atomic.o:drm_atomic.c:(.text+0x3130): first defined here
ld: drivers/gpu/drm/drm_edid.o: in function `drm_panic_is_enabled':
drm_edid.c:(.text+0x7fb0): multiple definition of `drm_panic_is_enabled'; drivers/gpu/drm/drm_atomic.o:drm_atomic.c:(.text+0x3130): first defined here
ld: drivers/gpu/drm/drm_eld.o: in function `drm_panic_is_enabled':
drm_eld.c:(.text+0xa0): multiple definition of `drm_panic_is_enabled'; drivers/gpu/drm/drm_atomic.o:drm_atomic.c:(.text+0x3130): first defined here
ld: drivers/gpu/drm/drm_encoder.o: in function `drm_panic_is_enabled':
drm_encoder.c:(.text+0x620): multiple definition of `drm_panic_is_enabled'; drivers/gpu/drm/drm_atomic.o:drm_atomic.c:(.text+0x3130): first defined here
ld: drivers/gpu/drm/drm_file.o: in function `drm_panic_is_enabled':
drm_file.c:(.text+0xc20): multiple definition of `drm_panic_is_enabled'; drivers/gpu/drm/drm_atomic.o:drm_atomic.c:(.text+0x3130): first defined here
ld: drivers/gpu/drm/drm_framebuffer.o: in function `drm_panic_is_enabled':
drm_framebuffer.c:(.text+0xc30): multiple definition of `drm_panic_is_enabled'; drivers/gpu/drm/drm_atomic.o:drm_atomic.c:(.text+0x3130): first defined here
ld: drivers/gpu/drm/drm_ioctl.o: in function `drm_panic_is_enabled':
drm_ioctl.c:(.text+0x1080): multiple definition of `drm_panic_is_enabled'; drivers/gpu/drm/drm_atomic.o:drm_atomic.c:(.text+0x3130): first defined here
ld: drivers/gpu/drm/drm_lease.o: in function `drm_panic_is_enabled':
drm_lease.c:(.text+0x210): multiple definition of `drm_panic_is_enabled'; drivers/gpu/drm/drm_atomic.o:drm_atomic.c:(.text+0x3130): first defined here
ld: drivers/gpu/drm/drm_mode_config.o: in function `drm_panic_is_enabled':
drm_mode_config.c:(.text+0xfe0): multiple definition of `drm_panic_is_enabled'; drivers/gpu/drm/drm_atomic.o:drm_atomic.c:(.text+0x3130): first defined here
ld: drivers/gpu/drm/drm_mode_object.o: in function `drm_panic_is_enabled':
drm_mode_object.c:(.text+0x8b0): multiple definition of `drm_panic_is_enabled'; drivers/gpu/drm/drm_atomic.o:drm_atomic.c:(.text+0x3130): first defined here
ld: drivers/gpu/drm/drm_modes.o: in function `drm_panic_is_enabled':
drm_modes.c:(.text+0x2e10): multiple definition of `drm_panic_is_enabled'; drivers/gpu/drm/drm_atomic.o:drm_atomic.c:(.text+0x3130): first defined here
ld: drivers/gpu/drm/drm_plane.o: in function `drm_panic_is_enabled':
drm_plane.c:(.text+0x20a0): multiple definition of `drm_panic_is_enabled'; drivers/gpu/drm/drm_atomic.o:drm_atomic.c:(.text+0x3130): first defined here
ld: drivers/gpu/drm/drm_property.o: in function `drm_panic_is_enabled':
drm_property.c:(.text+0xe10): multiple definition of `drm_panic_is_enabled'; drivers/gpu/drm/drm_atomic.o:drm_atomic.c:(.text+0x3130): first defined here
ld: drivers/gpu/drm/drm_sysfs.o: in function `drm_panic_is_enabled':
drm_sysfs.c:(.text+0x8b0): multiple definition of `drm_panic_is_enabled'; drivers/gpu/drm/drm_atomic.o:drm_atomic.c:(.text+0x3130): first defined here
ld: drivers/gpu/drm/drm_debugfs.o: in function `drm_panic_is_enabled':
drm_debugfs.c:(.text+0x1370): multiple definition of `drm_panic_is_enabled'; drivers/gpu/drm/drm_atomic.o:drm_atomic.c:(.text+0x3130): first defined here
ld: drivers/gpu/drm/drm_atomic_helper.o: in function `drm_panic_is_enabled':
drm_atomic_helper.c:(.text+0x69c0): multiple definition of `drm_panic_is_enabled'; drivers/gpu/drm/drm_atomic.o:drm_atomic.c:(.text+0x3130): first defined here
ld: drivers/gpu/drm/drm_fb_helper.o: in function `drm_panic_is_enabled':
drm_fb_helper.c:(.text+0x2ea0): multiple definition of `drm_panic_is_enabled'; drivers/gpu/drm/drm_atomic.o:drm_atomic.c:(.text+0x3130): first defined here
make[3]: *** [../scripts/Makefile.vmlinux_o:62: vmlinux.o] Error 1
make[2]: *** [/kernel/Makefile:1152: vmlinux_o] Error 2
make[1]: *** [/kernel/Makefile:240: __sub-make] Error 2
make: *** [Makefile:240: __sub-make] Error 2
[20:35:18] Configuring KUnit Kernel ...
Generating .config ...
Populating config with:
$ make ARCH=um O=.kunit olddefconfig
[20:35:23] Building KUnit Kernel ...
Populating config with:
$ make ARCH=um O=.kunit olddefconfig
Building with:
$ make ARCH=um O=.kunit --jobs=48
+ cleanup
++ stat -c %u:%g /kernel
+ chown -R 1003:1003 /kernel
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2024-07-19 20:35 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-07-19 20:29 [PATCH v13 0/5] drm/xe/guc: Add GuC based register capture for error capture Zhanjun Dong
2024-07-19 20:29 ` [PATCH v13 1/5] drm/xe/guc: Prepare GuC register list and update ADS size " Zhanjun Dong
2024-07-19 20:29 ` [PATCH v13 2/5] drm/xe/guc: Add XE_LP steered register lists Zhanjun Dong
2024-07-19 20:29 ` [PATCH v13 3/5] drm/xe/guc: Add capture size check in GuC log buffer Zhanjun Dong
2024-07-19 20:29 ` [PATCH v13 4/5] drm/xe/guc: Extract GuC error capture lists Zhanjun Dong
2024-07-19 20:29 ` [PATCH v13 5/5] drm/xe/guc: Plumb GuC-capture into dev coredump Zhanjun Dong
2024-07-19 20:34 ` ✓ CI.Patch_applied: success for drm/xe/guc: Add GuC based register capture for error capture (rev13) Patchwork
2024-07-19 20:35 ` ✗ CI.checkpatch: warning " Patchwork
2024-07-19 20:35 ` ✗ CI.KUnit: failure " Patchwork
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox