drm/xe/debugfs: Add dump of default LRCs' MI instructions
authorMatt Roper <matthew.d.roper@intel.com>
Mon, 16 Oct 2023 16:34:55 +0000 (09:34 -0700)
committerRodrigo Vivi <rodrigo.vivi@intel.com>
Thu, 21 Dec 2023 16:43:00 +0000 (11:43 -0500)
For non-RCS engines, nearly all of the LRC state is composed of MI
instructions (specifically MI_LOAD_REGISTER_IMM).  Providing a dump
interface allows us to verify that the context image layout matches
what's documented in the bspec, and also allows us to check whether LRC
workarounds are being properly captured by the default state we record
at startup.

For now, the non-MI instructions found in the RCS and CCS engines will
dump as "unknown;" parsing of those will be added in a follow-up patch.

v2:
 - Add raw instruction header as well as decoded meaning.  (Lucas)
 - Check that num_dw isn't greater than remaining_dw for instructions
   that have a "# dwords" field.  (Lucas)
 - Clarify comment about skipping over ppHWSP.  (Lucas)

Bspec: 64993
Cc: Lucas De Marchi <lucas.demarchi@intel.com>
Reviewed-by: Lucas De Marchi <lucas.demarchi@intel.com>
Link: https://lore.kernel.org/r/20231016163449.1300701-13-matthew.d.roper@intel.com
Signed-off-by: Matt Roper <matthew.d.roper@intel.com>
Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
drivers/gpu/drm/xe/instructions/xe_instr_defs.h
drivers/gpu/drm/xe/instructions/xe_mi_commands.h
drivers/gpu/drm/xe/xe_gt_debugfs.c
drivers/gpu/drm/xe/xe_lrc.c
drivers/gpu/drm/xe/xe_lrc.h

index a7ec46395786ff945a905b22aeaa14a36edad650..e403b4fcc20a880628718eafd38a28c1cd1d0dfb 100644 (file)
@@ -15,6 +15,7 @@
  */
 #define XE_INSTR_CMD_TYPE              GENMASK(31, 29)
 #define   XE_INSTR_MI                  REG_FIELD_PREP(XE_INSTR_CMD_TYPE, 0x0)
+#define   XE_INSTR_GFXPIPE             REG_FIELD_PREP(XE_INSTR_CMD_TYPE, 0x3)
 
 /*
  * Most (but not all) instructions have a "length" field in the instruction
index 753ebf1efa78cf02e53fb23f210a563f89acc094..1cfa96167fde317eb0a94b613af0512a2b6c1590 100644 (file)
@@ -30,6 +30,9 @@
 #define   MI_ARB_DISABLE               0x0
 
 #define MI_BATCH_BUFFER_END            __MI_INSTR(0xA)
+#define MI_TOPOLOGY_FILTER             __MI_INSTR(0xD)
+#define MI_FORCE_WAKEUP                        __MI_INSTR(0x1D)
+
 #define MI_STORE_DATA_IMM              __MI_INSTR(0x20)
 #define   MI_SDI_GGTT                  REG_BIT(22)
 #define   MI_SDI_LEN_DW                        GENMASK(9, 0)
index cd6d28c7b923abf5879b4ce521497b8e191be11e..c4b67cf09f8f2a461d7f48f1bde6b5e78d34197d 100644 (file)
@@ -15,6 +15,7 @@
 #include "xe_gt_mcr.h"
 #include "xe_gt_topology.h"
 #include "xe_hw_engine.h"
+#include "xe_lrc.h"
 #include "xe_macros.h"
 #include "xe_pat.h"
 #include "xe_reg_sr.h"
@@ -149,6 +150,46 @@ static int pat(struct seq_file *m, void *data)
        return 0;
 }
 
+static int rcs_default_lrc(struct seq_file *m, void *data)
+{
+       struct drm_printer p = drm_seq_file_printer(m);
+
+       xe_lrc_dump_default(&p, node_to_gt(m->private), XE_ENGINE_CLASS_RENDER);
+       return 0;
+}
+
+static int ccs_default_lrc(struct seq_file *m, void *data)
+{
+       struct drm_printer p = drm_seq_file_printer(m);
+
+       xe_lrc_dump_default(&p, node_to_gt(m->private), XE_ENGINE_CLASS_COMPUTE);
+       return 0;
+}
+
+static int bcs_default_lrc(struct seq_file *m, void *data)
+{
+       struct drm_printer p = drm_seq_file_printer(m);
+
+       xe_lrc_dump_default(&p, node_to_gt(m->private), XE_ENGINE_CLASS_COPY);
+       return 0;
+}
+
+static int vcs_default_lrc(struct seq_file *m, void *data)
+{
+       struct drm_printer p = drm_seq_file_printer(m);
+
+       xe_lrc_dump_default(&p, node_to_gt(m->private), XE_ENGINE_CLASS_VIDEO_DECODE);
+       return 0;
+}
+
+static int vecs_default_lrc(struct seq_file *m, void *data)
+{
+       struct drm_printer p = drm_seq_file_printer(m);
+
+       xe_lrc_dump_default(&p, node_to_gt(m->private), XE_ENGINE_CLASS_VIDEO_ENHANCE);
+       return 0;
+}
+
 static const struct drm_info_list debugfs_list[] = {
        {"hw_engines", hw_engines, 0},
        {"force_reset", force_reset, 0},
@@ -159,6 +200,11 @@ static const struct drm_info_list debugfs_list[] = {
        {"register-save-restore", register_save_restore, 0},
        {"workarounds", workarounds, 0},
        {"pat", pat, 0},
+       {"default_lrc_rcs", rcs_default_lrc},
+       {"default_lrc_ccs", ccs_default_lrc},
+       {"default_lrc_bcs", bcs_default_lrc},
+       {"default_lrc_vcs", vcs_default_lrc},
+       {"default_lrc_vecs", vecs_default_lrc},
 };
 
 void xe_gt_debugfs_register(struct xe_gt *gt)
index a048671318395fbfec00b7697bce9ba66755985c..38e98c54464b65a8c1d6d1a37464d09a6268e53a 100644 (file)
@@ -16,6 +16,7 @@
 #include "xe_drm_client.h"
 #include "xe_exec_queue_types.h"
 #include "xe_gt.h"
+#include "xe_gt_printk.h"
 #include "xe_hw_fence.h"
 #include "xe_map.h"
 #include "xe_vm.h"
@@ -903,3 +904,99 @@ struct iosys_map xe_lrc_parallel_map(struct xe_lrc *lrc)
 {
        return __xe_lrc_parallel_map(lrc);
 }
+
+static int instr_dw(u32 cmd_header)
+{
+       /* Most instructions have the # of dwords (minus 2) in 7:0 */
+       return REG_FIELD_GET(XE_INSTR_LEN_MASK, cmd_header) + 2;
+}
+
+static int dump_mi_command(struct drm_printer *p,
+                          struct xe_gt *gt,
+                          u32 *dw,
+                          int remaining_dw)
+{
+       u32 inst_header = *dw;
+       u32 numdw = instr_dw(inst_header);
+       u32 opcode = REG_FIELD_GET(MI_OPCODE, inst_header);
+       int num_noop;
+
+       /* First check for commands that don't have/use a '# DW' field */
+       switch (inst_header & MI_OPCODE) {
+       case MI_NOOP:
+               num_noop = 1;
+               while (num_noop < remaining_dw &&
+                      (*(++dw) & REG_GENMASK(31, 23)) == MI_NOOP)
+                       num_noop++;
+               drm_printf(p, "[%#010x] MI_NOOP (%d dwords)\n", inst_header, num_noop);
+               return num_noop;
+
+       case MI_TOPOLOGY_FILTER:
+               drm_printf(p, "[%#010x] MI_TOPOLOGY_FILTER\n", inst_header);
+               return 1;
+
+       case MI_BATCH_BUFFER_END:
+               drm_printf(p, "[%#010x] MI_BATCH_BUFFER_END\n", inst_header);
+               /* Return 'remaining_dw' to consume the rest of the LRC */
+               return remaining_dw;
+       }
+
+       /*
+        * Any remaining commands include a # of dwords.  We should make sure
+        * it doesn't exceed the remaining size of the LRC.
+        */
+       if (xe_gt_WARN_ON(gt, numdw > remaining_dw))
+               numdw = remaining_dw;
+
+       switch (inst_header & MI_OPCODE) {
+       case MI_LOAD_REGISTER_IMM:
+               drm_printf(p, "[%#010x] MI_LOAD_REGISTER_IMM: %d regs\n",
+                          inst_header, (numdw - 1) / 2);
+               for (int i = 1; i < numdw; i += 2)
+                       drm_printf(p, " - %#6x = %#010x\n", dw[i], dw[i + 1]);
+               return numdw;
+
+       case MI_FORCE_WAKEUP:
+               drm_printf(p, "[%#010x] MI_FORCE_WAKEUP\n", inst_header);
+               return numdw;
+
+       default:
+               drm_printf(p, "[%#010x] unknown MI opcode %#x, likely %d dwords\n",
+                          inst_header, opcode, numdw);
+               return numdw;
+       }
+}
+
+void xe_lrc_dump_default(struct drm_printer *p,
+                        struct xe_gt *gt,
+                        enum xe_engine_class hwe_class)
+{
+       u32 *dw;
+       int remaining_dw, num_dw;
+
+       if (!gt->default_lrc[hwe_class]) {
+               drm_printf(p, "No default LRC for class %d\n", hwe_class);
+               return;
+       }
+
+       /*
+        * Skip the beginning of the LRC since it contains the per-process
+        * hardware status page.
+        */
+       dw = gt->default_lrc[hwe_class] + LRC_PPHWSP_SIZE;
+       remaining_dw = (xe_lrc_size(gt_to_xe(gt), hwe_class) - LRC_PPHWSP_SIZE) / 4;
+
+       while (remaining_dw > 0) {
+               if ((*dw & XE_INSTR_CMD_TYPE) == XE_INSTR_MI) {
+                       num_dw = dump_mi_command(p, gt, dw, remaining_dw);
+               } else {
+                       num_dw = min(instr_dw(*dw), remaining_dw);
+                       drm_printf(p, "[%#10x] Unknown instruction of type %#x, likely %d dwords\n",
+                                  *dw, REG_FIELD_GET(XE_INSTR_CMD_TYPE, *dw),
+                                  num_dw);
+               }
+
+               dw += num_dw;
+               remaining_dw -= num_dw;
+       }
+}
index 3a6e8fc5a8370232aa9e9f87c5c1962804d9cdc2..a7056eda5e0c4f674bf42539defd75b414089e30 100644 (file)
@@ -7,6 +7,7 @@
 
 #include "xe_lrc_types.h"
 
+struct drm_printer;
 struct xe_device;
 struct xe_exec_queue;
 enum xe_engine_class;
@@ -47,4 +48,8 @@ struct iosys_map xe_lrc_parallel_map(struct xe_lrc *lrc);
 
 size_t xe_lrc_skip_size(struct xe_device *xe);
 
+void xe_lrc_dump_default(struct drm_printer *p,
+                        struct xe_gt *gt,
+                        enum xe_engine_class);
+
 #endif