All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v7 00/13] Add GuC Error Capture Support
@ 2022-02-26  9:55 ` Alan Previn
  0 siblings, 0 replies; 32+ messages in thread
From: Alan Previn @ 2022-02-26  9:55 UTC (permalink / raw)
  To: intel-gfx
  Cc: Matthew Brost, Tvrtko Ursulin, Alan Previn, Jani Nikula,
	Lucas De Marchi, dri-devel, Umesh Nerlige Ramappa, John Harrison

This series:
  1. Enables support of GuC to report error-state-capture
     using a list of MMIO registers the driver registers
     and GuC will dump, log and notify right before a GuC
     triggered engine-reset event.
  2. Updates the ADS blob creation to register said lists
     of global, engine class and engine instance registers
     with GuC.
  3. Defines tables of register lists that are global or
     engine class or engine instance in scope.
  4. Updates usage and buffer-state data for the regions
     of the shared GuC log-buffer to accomdate both
     the existing relay logging of general debug logs
     along with the new error state capture usage.
  5. Using a pool of preallocated memory, provide ability
     to extract and format the GuC reported register-capture
     data into chunks consistent with existing i915 error-
     state collection flows and structures.
  6. Connects the i915_gpu_coredump reporting function
     to the GuC error capture module to print all GuC
     error state capture dumps that is reported.

This is the 7th rev of this series with the first 3 revs
labelled as RFC.

Prior receipts of rvb's:
  - Patch #5, #12 have received R-v-b's from Umesh Nerlige Ramappa
    <umesh.nerlige.ramappa@intel.com>
  - Patch #6 has received an R-v-b from Matthew Brost
    <matthew.brost@intel.com>

Changes from prior revs:
  v7: - Rebased on lastest drm_tip that has the ADS now using
        shmem based ads_blob_write utilities. Stress test
        was performed with this patch included to fix a
        legacy bug:
        https://patchwork.freedesktop.org/series/100768/

  v6: - In patch #1, ADS reg-list population, we now alloc
        regular memory to create the lists and cache them for
        simpler and faster use by GuC ADS module at init, 
        suspend-resume and reset cycles. This was in response
        to review comments from Lucas De Marchi that also
        wanted to ensure the GuC ADS module owns the final
        copying into the ADS phyical memory.
      - Thanks to Jani Nikula for pointing out that patch #2
        and #3 should ensure static tables as constant and
        dynamic lists should be allocated and cached but
        attached to the GT level for the case of multiple
        cards with different fusings for steered registers.
        These are addressed now along with multiple code
        style fixups (thanks to review comment from Umesh)
        and splitting the steered register list generation
        as a seperate patch.
      - The extraction functionality, Patch #10 and #11 (was
        patch #7), has fixed all of Umesh's review comments
        related to the code styling. Additionally, it was
        discovered during stress tests that the extraction
        function could be called by the ct processing thread
        at the same time as the start of a GT reset event.
        Thus, a redesign was done whereby the linked list of
        processed capture-output-nodes are allocated up
        front and reused throughout the driver's life to
        ensure no memory locks are taken during extraction.
      - For patch #6 (now 7, 8 and 9), updates to
        intel_guc_log was split into smaller chunks and the
        log_state structure was returned back to inside of
        the intel_guc_log struct as opposed to the
        intel_guc struct in prior rev. This is in response
        to review comments by Matt Brost.
      - #Patch 13 (previously #10) is mostly identical but
        addresses all of the code styling comments reviews
        from Umesh.
        
  v5: - Added Gen9->Gen11 register list for CI coverage that
        included Gen9 with GuC submission.
      - Redesigned the extraction of the GuC error-capture
        dumps by grouping them into complete per-engine-reset
        nodes. Complete here means each node includes the
        global, engine-class and engine-instance register
        lists in a single structure.
      - Extraction is decoupled from the print-out. We now
        do the extraction immediately when receiving the
        G2H for error-capture notification. A link list of
        nodes is maintained with a FIFO based threshold
        while awaiting retrieval from i915_gpu_coredump's
        capture_engine function.
      - Added new plumbing through the i915_gpu_coredump
        allocation and capture functions to include a flag
        that is used indicate that GuC had triggered the
        reset. This new plumbing guarantees an exact match
        from i915_gpu_coredump's per-engine vma recording
        and node-retrieval from the guc-error-capture.
      - Broke the coredump gt_global capture and recording
        functions into smaller subsets so we can reuse as
        much of the existing legacy register reading + printing
        functions and only rely on GuC error-capture for
        the smaller subset of registers that are tied to
        engine workload execution.
      - Updated the register list to follow the legacy execlist
        format of printout.
  v4:
      - Rebased on latest drm-tip that has been merged with the
        support of GuC firmware version 69.0.3 that is required
        for GuC error-state-catpure to work.
      - Added register list for DG2 which is the same as XE_LP
        except an additional steering register set.
      - Fixed a bug in the end of capture parsing loop in
        intel_guc_capture_out_print_next_group that was not
        properly comparing the engine-instance and engine-
        class being parsed against the one that triggered
        the i915_gpu_coredump.
  v3:
      - Fixed all review comments from rev2 except the following:
          - Michal Wajdeczko proposed adding a seperate function
            to lookup register string nameslookup (based on offset)
            but decided against it because of offset conflicts
            and the current table layout is easier to maintain.
          - Last set of checkpatch errors pertaining to "COMPLEX
            MACROS" should be fixed on next rev.
      - Abstracted internal-to-guc-capture information into a new
        __guc_state_capture_priv structure that allows the exclusion
        of intel_guc.h and intel_guc_fwif.h from intel_guc_capture.h.
        Now, only the first 2 patches have a wider build time
        impact because of the changes to intel_guc_fwif.h but
        subsequent changes to guc-capture internal structures
        or firmware interfaces used solely by guc-capture module
        shoudn't impact the rest of the driver build.
      - Added missing Gen12LP registers and added slice+subslice
        indices when reporting extended steered registers.
      - Add additional checks to ensure that the GuC reported
        error capture information matches the i915_gpu_coredump
        that is being printed before we print out the corresponding
        VMA dumps such as the batch buffer.
   v2:
      - Ignore - failed CI retest.

Alan Previn (13):
  drm/i915/guc: Update GuC ADS size for error capture lists
  drm/i915/guc: Add XE_LP static registers for GuC error capture.
  drm/i915/guc: Add XE_LP steered register lists support
  drm/i915/guc: Add DG2 registers for GuC error state capture.
  drm/i915/guc: Add Gen9 registers for GuC error state capture.
  drm/i915/guc: Add GuC's error state capture output structures.
  drm/i915/guc: Update GuC-log relay function names
  drm/i915/guc: Add capture region into intel_guc_log
  drm/i915/guc: Check sizing of guc_capture output
  drm/i915/guc: Extract GuC error capture lists on G2H notification.
  drm/i915/guc: Pre-allocate output nodes for extraction
  drm/i915/guc: Plumb GuC-capture into gpu_coredump
  drm/i915/guc: Print the GuC error capture output register list.

 drivers/gpu/drm/i915/Makefile                 |    1 +
 drivers/gpu/drm/i915/gt/intel_engine_cs.c     |    4 +-
 .../drm/i915/gt/intel_execlists_submission.c  |    4 +-
 drivers/gpu/drm/i915/gt/intel_reset.c         |    2 +-
 .../gpu/drm/i915/gt/uc/abi/guc_actions_abi.h  |    7 +
 drivers/gpu/drm/i915/gt/uc/guc_capture_fwif.h |  216 +++
 drivers/gpu/drm/i915/gt/uc/intel_guc.c        |   13 +-
 drivers/gpu/drm/i915/gt/uc/intel_guc.h        |   14 +-
 drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c    |  132 +-
 .../gpu/drm/i915/gt/uc/intel_guc_capture.c    | 1619 +++++++++++++++++
 .../gpu/drm/i915/gt/uc/intel_guc_capture.h    |   34 +
 drivers/gpu/drm/i915/gt/uc/intel_guc_fwif.h   |   12 +-
 drivers/gpu/drm/i915/gt/uc/intel_guc_log.c    |  126 +-
 drivers/gpu/drm/i915/gt/uc/intel_guc_log.h    |    7 +-
 .../gpu/drm/i915/gt/uc/intel_guc_submission.c |   18 +-
 drivers/gpu/drm/i915/i915_debugfs.c           |    3 +-
 drivers/gpu/drm/i915/i915_gpu_error.c         |  282 ++-
 drivers/gpu/drm/i915/i915_gpu_error.h         |   35 +-
 18 files changed, 2348 insertions(+), 181 deletions(-)
 create mode 100644 drivers/gpu/drm/i915/gt/uc/guc_capture_fwif.h
 create mode 100644 drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c
 create mode 100644 drivers/gpu/drm/i915/gt/uc/intel_guc_capture.h

-- 
2.25.1


^ permalink raw reply	[flat|nested] 32+ messages in thread

* [Intel-gfx] [PATCH v7 00/13] Add GuC Error Capture Support
@ 2022-02-26  9:55 ` Alan Previn
  0 siblings, 0 replies; 32+ messages in thread
From: Alan Previn @ 2022-02-26  9:55 UTC (permalink / raw)
  To: intel-gfx; +Cc: Alan Previn, Jani Nikula, Lucas De Marchi, dri-devel

This series:
  1. Enables support of GuC to report error-state-capture
     using a list of MMIO registers the driver registers
     and GuC will dump, log and notify right before a GuC
     triggered engine-reset event.
  2. Updates the ADS blob creation to register said lists
     of global, engine class and engine instance registers
     with GuC.
  3. Defines tables of register lists that are global or
     engine class or engine instance in scope.
  4. Updates usage and buffer-state data for the regions
     of the shared GuC log-buffer to accomdate both
     the existing relay logging of general debug logs
     along with the new error state capture usage.
  5. Using a pool of preallocated memory, provide ability
     to extract and format the GuC reported register-capture
     data into chunks consistent with existing i915 error-
     state collection flows and structures.
  6. Connects the i915_gpu_coredump reporting function
     to the GuC error capture module to print all GuC
     error state capture dumps that is reported.

This is the 7th rev of this series with the first 3 revs
labelled as RFC.

Prior receipts of rvb's:
  - Patch #5, #12 have received R-v-b's from Umesh Nerlige Ramappa
    <umesh.nerlige.ramappa@intel.com>
  - Patch #6 has received an R-v-b from Matthew Brost
    <matthew.brost@intel.com>

Changes from prior revs:
  v7: - Rebased on lastest drm_tip that has the ADS now using
        shmem based ads_blob_write utilities. Stress test
        was performed with this patch included to fix a
        legacy bug:
        https://patchwork.freedesktop.org/series/100768/

  v6: - In patch #1, ADS reg-list population, we now alloc
        regular memory to create the lists and cache them for
        simpler and faster use by GuC ADS module at init, 
        suspend-resume and reset cycles. This was in response
        to review comments from Lucas De Marchi that also
        wanted to ensure the GuC ADS module owns the final
        copying into the ADS phyical memory.
      - Thanks to Jani Nikula for pointing out that patch #2
        and #3 should ensure static tables as constant and
        dynamic lists should be allocated and cached but
        attached to the GT level for the case of multiple
        cards with different fusings for steered registers.
        These are addressed now along with multiple code
        style fixups (thanks to review comment from Umesh)
        and splitting the steered register list generation
        as a seperate patch.
      - The extraction functionality, Patch #10 and #11 (was
        patch #7), has fixed all of Umesh's review comments
        related to the code styling. Additionally, it was
        discovered during stress tests that the extraction
        function could be called by the ct processing thread
        at the same time as the start of a GT reset event.
        Thus, a redesign was done whereby the linked list of
        processed capture-output-nodes are allocated up
        front and reused throughout the driver's life to
        ensure no memory locks are taken during extraction.
      - For patch #6 (now 7, 8 and 9), updates to
        intel_guc_log was split into smaller chunks and the
        log_state structure was returned back to inside of
        the intel_guc_log struct as opposed to the
        intel_guc struct in prior rev. This is in response
        to review comments by Matt Brost.
      - #Patch 13 (previously #10) is mostly identical but
        addresses all of the code styling comments reviews
        from Umesh.
        
  v5: - Added Gen9->Gen11 register list for CI coverage that
        included Gen9 with GuC submission.
      - Redesigned the extraction of the GuC error-capture
        dumps by grouping them into complete per-engine-reset
        nodes. Complete here means each node includes the
        global, engine-class and engine-instance register
        lists in a single structure.
      - Extraction is decoupled from the print-out. We now
        do the extraction immediately when receiving the
        G2H for error-capture notification. A link list of
        nodes is maintained with a FIFO based threshold
        while awaiting retrieval from i915_gpu_coredump's
        capture_engine function.
      - Added new plumbing through the i915_gpu_coredump
        allocation and capture functions to include a flag
        that is used indicate that GuC had triggered the
        reset. This new plumbing guarantees an exact match
        from i915_gpu_coredump's per-engine vma recording
        and node-retrieval from the guc-error-capture.
      - Broke the coredump gt_global capture and recording
        functions into smaller subsets so we can reuse as
        much of the existing legacy register reading + printing
        functions and only rely on GuC error-capture for
        the smaller subset of registers that are tied to
        engine workload execution.
      - Updated the register list to follow the legacy execlist
        format of printout.
  v4:
      - Rebased on latest drm-tip that has been merged with the
        support of GuC firmware version 69.0.3 that is required
        for GuC error-state-catpure to work.
      - Added register list for DG2 which is the same as XE_LP
        except an additional steering register set.
      - Fixed a bug in the end of capture parsing loop in
        intel_guc_capture_out_print_next_group that was not
        properly comparing the engine-instance and engine-
        class being parsed against the one that triggered
        the i915_gpu_coredump.
  v3:
      - Fixed all review comments from rev2 except the following:
          - Michal Wajdeczko proposed adding a seperate function
            to lookup register string nameslookup (based on offset)
            but decided against it because of offset conflicts
            and the current table layout is easier to maintain.
          - Last set of checkpatch errors pertaining to "COMPLEX
            MACROS" should be fixed on next rev.
      - Abstracted internal-to-guc-capture information into a new
        __guc_state_capture_priv structure that allows the exclusion
        of intel_guc.h and intel_guc_fwif.h from intel_guc_capture.h.
        Now, only the first 2 patches have a wider build time
        impact because of the changes to intel_guc_fwif.h but
        subsequent changes to guc-capture internal structures
        or firmware interfaces used solely by guc-capture module
        shoudn't impact the rest of the driver build.
      - Added missing Gen12LP registers and added slice+subslice
        indices when reporting extended steered registers.
      - Add additional checks to ensure that the GuC reported
        error capture information matches the i915_gpu_coredump
        that is being printed before we print out the corresponding
        VMA dumps such as the batch buffer.
   v2:
      - Ignore - failed CI retest.

Alan Previn (13):
  drm/i915/guc: Update GuC ADS size for error capture lists
  drm/i915/guc: Add XE_LP static registers for GuC error capture.
  drm/i915/guc: Add XE_LP steered register lists support
  drm/i915/guc: Add DG2 registers for GuC error state capture.
  drm/i915/guc: Add Gen9 registers for GuC error state capture.
  drm/i915/guc: Add GuC's error state capture output structures.
  drm/i915/guc: Update GuC-log relay function names
  drm/i915/guc: Add capture region into intel_guc_log
  drm/i915/guc: Check sizing of guc_capture output
  drm/i915/guc: Extract GuC error capture lists on G2H notification.
  drm/i915/guc: Pre-allocate output nodes for extraction
  drm/i915/guc: Plumb GuC-capture into gpu_coredump
  drm/i915/guc: Print the GuC error capture output register list.

 drivers/gpu/drm/i915/Makefile                 |    1 +
 drivers/gpu/drm/i915/gt/intel_engine_cs.c     |    4 +-
 .../drm/i915/gt/intel_execlists_submission.c  |    4 +-
 drivers/gpu/drm/i915/gt/intel_reset.c         |    2 +-
 .../gpu/drm/i915/gt/uc/abi/guc_actions_abi.h  |    7 +
 drivers/gpu/drm/i915/gt/uc/guc_capture_fwif.h |  216 +++
 drivers/gpu/drm/i915/gt/uc/intel_guc.c        |   13 +-
 drivers/gpu/drm/i915/gt/uc/intel_guc.h        |   14 +-
 drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c    |  132 +-
 .../gpu/drm/i915/gt/uc/intel_guc_capture.c    | 1619 +++++++++++++++++
 .../gpu/drm/i915/gt/uc/intel_guc_capture.h    |   34 +
 drivers/gpu/drm/i915/gt/uc/intel_guc_fwif.h   |   12 +-
 drivers/gpu/drm/i915/gt/uc/intel_guc_log.c    |  126 +-
 drivers/gpu/drm/i915/gt/uc/intel_guc_log.h    |    7 +-
 .../gpu/drm/i915/gt/uc/intel_guc_submission.c |   18 +-
 drivers/gpu/drm/i915/i915_debugfs.c           |    3 +-
 drivers/gpu/drm/i915/i915_gpu_error.c         |  282 ++-
 drivers/gpu/drm/i915/i915_gpu_error.h         |   35 +-
 18 files changed, 2348 insertions(+), 181 deletions(-)
 create mode 100644 drivers/gpu/drm/i915/gt/uc/guc_capture_fwif.h
 create mode 100644 drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c
 create mode 100644 drivers/gpu/drm/i915/gt/uc/intel_guc_capture.h

-- 
2.25.1


^ permalink raw reply	[flat|nested] 32+ messages in thread

* [Intel-gfx] [PATCH v7 01/13] drm/i915/guc: Update GuC ADS size for error capture lists
  2022-02-26  9:55 ` [Intel-gfx] " Alan Previn
  (?)
@ 2022-02-26  9:55 ` Alan Previn
  2022-02-26 20:22   ` Teres Alexis, Alan Previn
                     ` (2 more replies)
  -1 siblings, 3 replies; 32+ messages in thread
From: Alan Previn @ 2022-02-26  9:55 UTC (permalink / raw)
  To: intel-gfx; +Cc: Alan Previn

Update GuC ADS size allocation to include space for
the lists of error state capture register descriptors.

Also, populate 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.

NOTE: Start with a sample table of register lists to layout the
framework before adding real registers in subsequent patch.

Signed-off-by: Alan Previn <alan.previn.teres.alexis@intel.com>
---
 drivers/gpu/drm/i915/Makefile                 |   1 +
 drivers/gpu/drm/i915/gt/uc/guc_capture_fwif.h |  89 +++++
 drivers/gpu/drm/i915/gt/uc/intel_guc.c        |  13 +-
 drivers/gpu/drm/i915/gt/uc/intel_guc.h        |  11 +-
 drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c    | 132 ++++++-
 .../gpu/drm/i915/gt/uc/intel_guc_capture.c    | 352 ++++++++++++++++++
 .../gpu/drm/i915/gt/uc/intel_guc_capture.h    |  22 ++
 drivers/gpu/drm/i915/gt/uc/intel_guc_fwif.h   |   8 +
 8 files changed, 611 insertions(+), 17 deletions(-)
 create mode 100644 drivers/gpu/drm/i915/gt/uc/guc_capture_fwif.h
 create mode 100644 drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c
 create mode 100644 drivers/gpu/drm/i915/gt/uc/intel_guc_capture.h

diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
index 9d588d936e3d..547adc36d4e9 100644
--- a/drivers/gpu/drm/i915/Makefile
+++ b/drivers/gpu/drm/i915/Makefile
@@ -184,6 +184,7 @@ i915-y += gt/uc/intel_uc.o \
 	  gt/uc/intel_uc_fw.o \
 	  gt/uc/intel_guc.o \
 	  gt/uc/intel_guc_ads.o \
+	  gt/uc/intel_guc_capture.o \
 	  gt/uc/intel_guc_ct.o \
 	  gt/uc/intel_guc_debugfs.o \
 	  gt/uc/intel_guc_fw.o \
diff --git a/drivers/gpu/drm/i915/gt/uc/guc_capture_fwif.h b/drivers/gpu/drm/i915/gt/uc/guc_capture_fwif.h
new file mode 100644
index 000000000000..858f85478636
--- /dev/null
+++ b/drivers/gpu/drm/i915/gt/uc/guc_capture_fwif.h
@@ -0,0 +1,89 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2021-2022 Intel Corporation
+ */
+
+#ifndef _INTEL_GUC_CAPTURE_FWIF_H
+#define _INTEL_GUC_CAPTURE_FWIF_H
+
+#include <linux/types.h>
+#include "intel_guc_fwif.h"
+
+struct intel_guc;
+struct file;
+
+/**
+ * struct guc_debug_capture_list_header / struct guc_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 {
+	u32 info;
+#define GUC_CAPTURELISTHDR_NUMDESCR GENMASK(15, 0)
+} __packed;
+
+struct guc_debug_capture_list {
+	struct guc_debug_capture_list_header header;
+} __packed;
+
+/**
+ * struct __guc_mmio_reg_descr / struct __guc_mmio_reg_descr_group
+ *
+ * intel_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 {
+	i915_reg_t reg;
+	u32 flags;
+	u32 mask;
+	const char *regname;
+};
+
+struct __guc_mmio_reg_descr_group {
+	const struct __guc_mmio_reg_descr *list;
+	u32 num_regs;
+	u32 owner; /* see enum guc_capture_owner */
+	u32 type; /* see enum guc_capture_type */
+	u32 engine; /* as per MAX_ENGINE_CLASS */
+};
+
+/**
+ * struct __guc_capture_ads_cache
+ *
+ * A structure to cache register lists that were populated and registered
+ * with GuC at startup during ADS registration. This allows much quicker
+ * GuC resets without re-parsing all the tables for the given gt.
+ */
+struct __guc_capture_ads_cache {
+	bool is_valid;
+	struct file *file;
+	size_t size;
+	int status;
+};
+
+/**
+ * struct __guc_state_capture_priv
+ *
+ * Internal context of the intel_guc_capture module.
+ */
+struct __guc_state_capture_priv {
+	/**
+	 * @reglists: static table of register lists used for error-capture state.
+	 */
+	const struct __guc_mmio_reg_descr_group *reglists;
+
+	/**
+	 * @ads_cache: cached register lists that is ADS format ready
+	 */
+	struct __guc_capture_ads_cache ads_cache[GUC_CAPTURE_LIST_INDEX_MAX]
+						[GUC_CAPTURE_LIST_TYPE_MAX]
+						[GUC_MAX_ENGINE_CLASSES];
+};
+
+#endif /* _INTEL_GUC_CAPTURE_FWIF_H */
diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc.c b/drivers/gpu/drm/i915/gt/uc/intel_guc.c
index 447a976c9f25..cda7e4bb8bac 100644
--- a/drivers/gpu/drm/i915/gt/uc/intel_guc.c
+++ b/drivers/gpu/drm/i915/gt/uc/intel_guc.c
@@ -9,8 +9,9 @@
 #include "gt/intel_gt_pm_irq.h"
 #include "gt/intel_gt_regs.h"
 #include "intel_guc.h"
-#include "intel_guc_slpc.h"
 #include "intel_guc_ads.h"
+#include "intel_guc_capture.h"
+#include "intel_guc_slpc.h"
 #include "intel_guc_submission.h"
 #include "i915_drv.h"
 #include "i915_irq.h"
@@ -362,9 +363,14 @@ int intel_guc_init(struct intel_guc *guc)
 	if (ret)
 		goto err_fw;
 
-	ret = intel_guc_ads_create(guc);
+	ret = intel_guc_capture_init(guc);
 	if (ret)
 		goto err_log;
+
+	ret = intel_guc_ads_create(guc);
+	if (ret)
+		goto err_capture;
+
 	GEM_BUG_ON(!guc->ads_vma);
 
 	ret = intel_guc_ct_init(&guc->ct);
@@ -403,6 +409,8 @@ int intel_guc_init(struct intel_guc *guc)
 	intel_guc_ct_fini(&guc->ct);
 err_ads:
 	intel_guc_ads_destroy(guc);
+err_capture:
+	intel_guc_capture_destroy(guc);
 err_log:
 	intel_guc_log_destroy(&guc->log);
 err_fw:
@@ -430,6 +438,7 @@ void intel_guc_fini(struct intel_guc *guc)
 	intel_guc_ct_fini(&guc->ct);
 
 	intel_guc_ads_destroy(guc);
+	intel_guc_capture_destroy(guc);
 	intel_guc_log_destroy(&guc->log);
 	intel_uc_fw_fini(&guc->fw);
 }
diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc.h b/drivers/gpu/drm/i915/gt/uc/intel_guc.h
index bf7079480d47..908c6b1dd51a 100644
--- a/drivers/gpu/drm/i915/gt/uc/intel_guc.h
+++ b/drivers/gpu/drm/i915/gt/uc/intel_guc.h
@@ -10,18 +10,19 @@
 #include <linux/iosys-map.h>
 #include <linux/xarray.h>
 
-#include "intel_uncore.h"
+#include "intel_guc_ct.h"
 #include "intel_guc_fw.h"
 #include "intel_guc_fwif.h"
-#include "intel_guc_ct.h"
 #include "intel_guc_log.h"
 #include "intel_guc_reg.h"
 #include "intel_guc_slpc_types.h"
 #include "intel_uc_fw.h"
+#include "intel_uncore.h"
 #include "i915_utils.h"
 #include "i915_vma.h"
 
 struct __guc_ads_blob;
+struct __guc_state_capture_priv;
 
 /**
  * struct intel_guc - Top level structure of GuC.
@@ -38,6 +39,10 @@ struct intel_guc {
 	struct intel_guc_ct ct;
 	/** @slpc: sub-structure containing SLPC related data and objects */
 	struct intel_guc_slpc slpc;
+	/** @capture: the error-state-capture module's data and objects */
+	struct intel_guc_state_capture {
+		struct __guc_state_capture_priv *priv;
+	} capture;
 
 	/** @sched_engine: Global engine used to submit requests to GuC */
 	struct i915_sched_engine *sched_engine;
@@ -160,6 +165,8 @@ struct intel_guc {
 	struct guc_mmio_reg *ads_regset;
 	/** @ads_golden_ctxt_size: size of the golden contexts in the ADS */
 	u32 ads_golden_ctxt_size;
+	/** @ads_capture_size: size of register lists in the ADS used for error capture */
+	u32 ads_capture_size;
 	/** @ads_engine_usage_size: size of engine usage in the ADS */
 	u32 ads_engine_usage_size;
 
diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c
index 847e00390b00..8bed68299bba 100644
--- a/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c
+++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c
@@ -11,6 +11,7 @@
 #include "gt/intel_lrc.h"
 #include "gt/shmem_utils.h"
 #include "intel_guc_ads.h"
+#include "intel_guc_capture.h"
 #include "intel_guc_fwif.h"
 #include "intel_uc.h"
 #include "i915_drv.h"
@@ -86,8 +87,7 @@ static u32 guc_ads_golden_ctxt_size(struct intel_guc *guc)
 
 static u32 guc_ads_capture_size(struct intel_guc *guc)
 {
-	/* FIXME: Allocate a proper capture list */
-	return PAGE_ALIGN(PAGE_SIZE);
+	return PAGE_ALIGN(guc->ads_capture_size);
 }
 
 static u32 guc_ads_private_data_size(struct intel_guc *guc)
@@ -584,24 +584,124 @@ static void guc_init_golden_context(struct intel_guc *guc)
 	GEM_BUG_ON(guc->ads_golden_ctxt_size != total_size);
 }
 
-static void guc_capture_list_init(struct intel_guc *guc)
+static int
+guc_capture_prep_lists(struct intel_guc *guc)
 {
+	struct intel_gt *gt = guc_to_gt(guc);
+	struct drm_i915_private *i915 = guc_to_gt(guc)->i915;
+	u32 ggtt, capture_offset, null_ggtt, total_size = 0;
+	struct guc_gt_system_info local_info;
+	struct iosys_map info_map;
+	u32 null_header[2]={0};
+	struct file *file;
+	size_t size = 0;
 	int i, j;
-	u32 addr_ggtt, offset;
 
-	offset = guc_ads_capture_offset(guc);
-	addr_ggtt = intel_guc_ggtt_offset(guc, guc->ads_vma) + offset;
+	if (!iosys_map_is_null(&guc->ads_map)) {
+		capture_offset = guc_ads_capture_offset(guc);
+		ggtt = intel_guc_ggtt_offset(guc, guc->ads_vma) + capture_offset;
+		info_map = IOSYS_MAP_INIT_OFFSET(&guc->ads_map,
+						 offsetof(struct __guc_ads_blob, system_info));
+	} else {
+		memset(&local_info, 0, sizeof(local_info));
+		iosys_map_set_vaddr(&info_map, &local_info);
+		fill_engine_enable_masks(gt, &info_map);
+	}
 
-	/* FIXME: Populate a proper capture list */
+	/* first, set aside the first page for a capture_list with zero descriptors */
+	total_size = PAGE_SIZE;
+	if (!iosys_map_is_null(&guc->ads_map)) {
+		file = shmem_create_from_data("guc-err-cap", null_header, sizeof(null_header));
+		if (!IS_ERR(file)) {
+			shmem_read_to_iosys_map(file, 0, &guc->ads_map,
+						ggtt, sizeof(null_header));
+			fput(file);
+		} else {
+			drm_dbg(&i915->drm, "GuC-capture: failed shmem for nulllist = 0x%016lx",
+				PTR_ERR(file));
+		}
+		null_ggtt = ggtt;
+		ggtt += PAGE_SIZE;
+	}
 
 	for (i = 0; i < GUC_CAPTURE_LIST_INDEX_MAX; i++) {
 		for (j = 0; j < GUC_MAX_ENGINE_CLASSES; j++) {
-			ads_blob_write(guc, ads.capture_instance[i][j], addr_ggtt);
-			ads_blob_write(guc, ads.capture_class[i][j], addr_ggtt);
-		}
 
-		ads_blob_write(guc, ads.capture_global[i], addr_ggtt);
+			/* null list if we dont have said engine or list */
+			if (!info_map_read(&info_map, engine_enabled_masks[j])) {
+
+				if (!iosys_map_is_null(&guc->ads_map)) {
+					ads_blob_write(guc, ads.capture_class[i][j], null_ggtt);
+					ads_blob_write(guc, ads.capture_instance[i][j], null_ggtt);
+				}
+				continue;
+			}
+			if (intel_guc_capture_getlistsize(guc, i,
+							  GUC_CAPTURE_LIST_TYPE_ENGINE_CLASS,
+							  j, &size)) {
+				if (!iosys_map_is_null(&guc->ads_map))
+					ads_blob_write(guc, ads.capture_class[i][j], null_ggtt);
+				goto engine_instance_list;
+			}
+			total_size += size;
+			if (!iosys_map_is_null(&guc->ads_map)) {
+				if (total_size > guc->ads_capture_size ||
+				    intel_guc_capture_getlist(guc, i,
+							      GUC_CAPTURE_LIST_TYPE_ENGINE_CLASS,
+							      j, &file)) {
+					ads_blob_write(guc, ads.capture_class[i][j], null_ggtt);
+					continue;
+				}
+				ads_blob_write(guc, ads.capture_class[i][j], ggtt);
+				shmem_read_to_iosys_map(file, 0, &guc->ads_map, ggtt, size);
+				ggtt += size;
+			}
+engine_instance_list:
+			if (intel_guc_capture_getlistsize(guc, i,
+							  GUC_CAPTURE_LIST_TYPE_ENGINE_INSTANCE,
+							  j, &size)) {
+				if (!iosys_map_is_null(&guc->ads_map))
+					ads_blob_write(guc, ads.capture_instance[i][j], null_ggtt);
+				continue;
+			}
+			total_size += size;
+			if (!iosys_map_is_null(&guc->ads_map)) {
+				if (total_size > guc->ads_capture_size ||
+				    intel_guc_capture_getlist(guc, i,
+							      GUC_CAPTURE_LIST_TYPE_ENGINE_INSTANCE,
+							      j, &file)) {
+					ads_blob_write(guc, ads.capture_instance[i][j], null_ggtt);
+					continue;
+				}
+				ads_blob_write(guc, ads.capture_instance[i][j], ggtt);
+				shmem_read_to_iosys_map(file, 0, &guc->ads_map, ggtt, size);
+				ggtt += size;
+			}
+		}
+		if (intel_guc_capture_getlistsize(guc, i, GUC_CAPTURE_LIST_TYPE_GLOBAL, 0, &size)) {
+			if (!iosys_map_is_null(&guc->ads_map))
+				ads_blob_write(guc, ads.capture_global[i], null_ggtt);
+			continue;
+		}
+		total_size += size;
+		if (!iosys_map_is_null(&guc->ads_map)) {
+			if (total_size > guc->ads_capture_size ||
+			    intel_guc_capture_getlist(guc, i, GUC_CAPTURE_LIST_TYPE_GLOBAL, 0,
+						      &file)) {
+				ads_blob_write(guc, ads.capture_global[i], null_ggtt);
+				continue;
+			}
+			ads_blob_write(guc, ads.capture_global[i], ggtt);
+			shmem_read_to_iosys_map(file, 0, &guc->ads_map, ggtt, size);
+			ggtt += size;
+		}
 	}
+
+	if (guc->ads_capture_size && guc->ads_capture_size != PAGE_ALIGN(total_size))
+		drm_warn(&i915->drm, "GuC->ADS->Capture alloc size changed from %d to %d\n",
+			 guc->ads_capture_size, PAGE_ALIGN(total_size));
+
+	return PAGE_ALIGN(total_size);
 }
 
 static void __guc_ads_init(struct intel_guc *guc)
@@ -639,8 +739,8 @@ static void __guc_ads_init(struct intel_guc *guc)
 
 	base = intel_guc_ggtt_offset(guc, guc->ads_vma);
 
-	/* Capture list for hang debug */
-	guc_capture_list_init(guc);
+	/* Lists for error capture debug */
+	guc_capture_prep_lists(guc);
 
 	/* ADS */
 	ads_blob_write(guc, ads.scheduler_policies, base +
@@ -688,6 +788,12 @@ int intel_guc_ads_create(struct intel_guc *guc)
 		return ret;
 	guc->ads_golden_ctxt_size = ret;
 
+	/* Likewise the capture lists: */
+	ret = guc_capture_prep_lists(guc);
+	if (ret < 0)
+		return ret;
+	guc->ads_capture_size = ret;
+
 	/* Now the total size can be determined: */
 	size = guc_ads_blob_size(guc);
 
diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c
new file mode 100644
index 000000000000..647a118aa3c3
--- /dev/null
+++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c
@@ -0,0 +1,352 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2021-2022 Intel Corporation
+ */
+
+#include <linux/types.h>
+
+#include <drm/drm_print.h>
+
+#include "gt/intel_engine_regs.h"
+#include "gt/intel_gt.h"
+#include "gt/intel_gt_regs.h"
+#include "gt/shmem_utils.h"
+#include "guc_capture_fwif.h"
+#include "intel_guc_fwif.h"
+#include "i915_drv.h"
+#include "i915_memcpy.h"
+#include "i915_reg.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
+ */
+/* XE_LPD - Global */
+static const struct __guc_mmio_reg_descr xe_lpd_global_regs[] = {
+	{GEN12_RING_FAULT_REG,     0,      0, "GEN12_RING_FAULT_REG"}
+};
+
+/* XE_LPD - Render / Compute Per-Class */
+static const struct __guc_mmio_reg_descr xe_lpd_rc_class_regs[] = {
+	{EIR,                      0,      0, "EIR"}
+};
+
+/* XE_LPD - Render / Compute Per-Engine-Instance */
+static const struct __guc_mmio_reg_descr xe_lpd_rc_inst_regs[] = {
+	{RING_HEAD(0),             0,      0, "RING_HEAD"},
+	{RING_TAIL(0),             0,      0, "RING_TAIL"},
+};
+
+/* XE_LPD - Media Decode/Encode Per-Class */
+static const struct __guc_mmio_reg_descr xe_lpd_vd_class_regs[] = {
+};
+
+/* XE_LPD - Media Decode/Encode Per-Engine-Instance */
+static const struct __guc_mmio_reg_descr xe_lpd_vd_inst_regs[] = {
+	{RING_HEAD(0),             0,      0, "RING_HEAD"},
+	{RING_TAIL(0),             0,      0, "RING_TAIL"},
+};
+
+/* XE_LPD - Video Enhancement Per-Class */
+static const struct __guc_mmio_reg_descr xe_lpd_vec_class_regs[] = {
+};
+
+/* XE_LPD - Video Enhancement Per-Engine-Instance */
+static const struct __guc_mmio_reg_descr xe_lpd_vec_inst_regs[] = {
+	{RING_HEAD(0),             0,      0, "RING_HEAD"},
+	{RING_TAIL(0),             0,      0, "RING_TAIL"},
+};
+
+#define TO_GCAP_DEF_OWNER(x) (GUC_CAPTURE_LIST_INDEX_##x)
+#define TO_GCAP_DEF_TYPE(x) (GUC_CAPTURE_LIST_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_lpd_lists[] = {
+	MAKE_REGLIST(xe_lpd_global_regs, PF, GLOBAL, 0),
+	MAKE_REGLIST(xe_lpd_rc_class_regs, PF, ENGINE_CLASS, GUC_RENDER_CLASS),
+	MAKE_REGLIST(xe_lpd_rc_inst_regs, PF, ENGINE_INSTANCE, GUC_RENDER_CLASS),
+	MAKE_REGLIST(xe_lpd_vd_class_regs, PF, ENGINE_CLASS, GUC_VIDEO_CLASS),
+	MAKE_REGLIST(xe_lpd_vd_inst_regs, PF, ENGINE_INSTANCE, GUC_VIDEO_CLASS),
+	MAKE_REGLIST(xe_lpd_vec_class_regs, PF, ENGINE_CLASS, GUC_VIDEOENHANCE_CLASS),
+	MAKE_REGLIST(xe_lpd_vec_inst_regs, PF, ENGINE_INSTANCE, GUC_VIDEOENHANCE_CLASS),
+	{}
+};
+
+static const struct __guc_mmio_reg_descr_group *
+guc_capture_get_device_reglist(struct intel_guc *guc)
+{
+	struct drm_i915_private *i915 = guc_to_gt(guc)->i915;
+
+	if (IS_TIGERLAKE(i915) || IS_ROCKETLAKE(i915) ||
+	    IS_ALDERLAKE_S(i915) || IS_ALDERLAKE_P(i915)) {
+		return xe_lpd_lists;
+	}
+
+	return NULL;
+}
+
+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, u32 id)
+{
+	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 == id || reglists[i].type == GUC_CAPTURE_LIST_TYPE_GLOBAL))
+		return &reglists[i];
+	}
+
+	return NULL;
+}
+
+static const char *
+__stringify_owner(u32 owner)
+{
+	switch (owner) {
+	case GUC_CAPTURE_LIST_INDEX_PF:
+		return "PF";
+	case GUC_CAPTURE_LIST_INDEX_VF:
+		return "VF";
+	default:
+		return "unknown";
+	}
+
+	return "";
+}
+
+static const char *
+__stringify_type(u32 type)
+{
+	switch (type) {
+	case GUC_CAPTURE_LIST_TYPE_GLOBAL:
+		return "Global";
+	case GUC_CAPTURE_LIST_TYPE_ENGINE_CLASS:
+		return "Class";
+	case GUC_CAPTURE_LIST_TYPE_ENGINE_INSTANCE:
+		return "Instance";
+	default:
+		return "unknown";
+	}
+
+	return "";
+}
+
+static const char *
+__stringify_engclass(u32 class)
+{
+	switch (class) {
+	case GUC_RENDER_CLASS:
+		return "Render";
+	case GUC_VIDEO_CLASS:
+		return "Video";
+	case GUC_VIDEOENHANCE_CLASS:
+		return "VideoEnhance";
+	case GUC_BLITTER_CLASS:
+		return "Blitter";
+	case GUC_RESERVED_CLASS:
+		return "Reserved";
+	default:
+		return "unknown";
+	}
+
+	return "";
+}
+
+static void
+guc_capture_warn_with_list_info(struct drm_i915_private *i915, char *msg,
+				u32 owner, u32 type, u32 classid)
+{
+	if (type == GUC_CAPTURE_LIST_TYPE_GLOBAL)
+		drm_dbg(&i915->drm, "GuC-capture: %s for %s %s-Registers.\n", msg,
+			__stringify_owner(owner), __stringify_type(type));
+	else
+		drm_dbg(&i915->drm, "GuC-capture: %s for %s %s-Registers on %s-Engine\n", msg,
+			__stringify_owner(owner), __stringify_type(type),
+			__stringify_engclass(classid));
+}
+
+static int
+guc_capture_list_init(struct intel_guc *guc, u32 owner, u32 type, u32 classid,
+		      struct guc_mmio_reg *ptr, u16 num_entries)
+{
+	u32 i = 0;
+	struct drm_i915_private *i915 = guc_to_gt(guc)->i915;
+	const struct __guc_mmio_reg_descr_group *reglists = guc->capture.priv->reglists;
+	const struct __guc_mmio_reg_descr_group *match;
+
+	if (!reglists)
+		return -ENODEV;
+
+	match = guc_capture_get_one_list(reglists, owner, type, classid);
+	if (match) {
+		for (i = 0; i < num_entries && i < match->num_regs; ++i) {
+			ptr[i].offset = match->list[i].reg.reg;
+			ptr[i].value = 0xDEADF00D;
+			ptr[i].flags = match->list[i].flags;
+			ptr[i].mask = match->list[i].mask;
+		}
+		return 0;
+	}
+
+	guc_capture_warn_with_list_info(i915, "Missing register list init", owner, type,
+					classid);
+
+	return -ENODATA;
+}
+
+static int
+guc_cap_list_num_regs(struct __guc_state_capture_priv *gc, u32 owner, u32 type, u32 classid)
+{
+	const struct __guc_mmio_reg_descr_group *match;
+
+	match = guc_capture_get_one_list(gc->reglists, owner, type, classid);
+	if (!match)
+		return 0;
+
+	return match->num_regs;
+}
+
+int
+intel_guc_capture_getlistsize(struct intel_guc *guc, u32 owner, u32 type, u32 classid,
+			      size_t *size)
+{
+	struct drm_i915_private *i915 = guc_to_gt(guc)->i915;
+	struct __guc_state_capture_priv *gc = guc->capture.priv;
+	struct __guc_capture_ads_cache *cache = &gc->ads_cache[owner][type][classid];
+	int num_regs;
+
+	if (!gc->reglists)
+		return -ENODEV;
+
+	if (cache->is_valid) {
+		*size = cache->size;
+		return cache->status;
+	}
+
+	num_regs = guc_cap_list_num_regs(gc, owner, type, classid);
+	if (!num_regs) {
+		guc_capture_warn_with_list_info(i915, "Missing register list size",
+						owner, type, classid);
+		return -ENODATA;
+	}
+
+	*size = PAGE_ALIGN((sizeof(struct guc_debug_capture_list)) +
+			   (num_regs * sizeof(struct guc_mmio_reg)));
+
+	return 0;
+}
+
+int
+intel_guc_capture_getlist(struct intel_guc *guc, u32 owner, u32 type, u32 classid,
+			  struct file **fileoutptr)
+{
+	struct __guc_state_capture_priv *gc = guc->capture.priv;
+	struct __guc_capture_ads_cache *cache = &gc->ads_cache[owner][type][classid];
+	struct drm_i915_private *i915 = guc_to_gt(guc)->i915;
+	struct guc_debug_capture_list *listnode;
+	struct file *file;
+	u8 *caplist, *tmp;
+	size_t size = 0;
+	int ret, num_regs;
+
+	if (!gc->reglists)
+		return -ENODEV;
+
+	if (cache->is_valid) {
+		*fileoutptr = cache->file;
+		return cache->status;
+	}
+
+	ret = intel_guc_capture_getlistsize(guc, owner, type, classid, &size);
+	if (ret) {
+		cache->is_valid = true;
+		cache->file = NULL;
+		cache->size = 0;
+		cache->status = ret;
+		return ret;
+	}
+
+	caplist = kzalloc(size, GFP_KERNEL);
+	if (!caplist)
+		return -ENOMEM;
+
+	/* populate capture list header */
+	tmp = caplist;
+	num_regs = guc_cap_list_num_regs(guc->capture.priv, owner, type, classid);
+	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, classid, (struct guc_mmio_reg *)tmp, num_regs);
+
+	/* ADS only takes file handles, so we keep that as our cache */
+	file = shmem_create_from_data("guc-err-cap", caplist, size);
+	kfree(caplist);
+	if (IS_ERR(file)) {
+		drm_warn(&i915->drm, "GuC-capture: cant create shmem for caplist = 0x%016lx", PTR_ERR(file));
+		return PTR_ERR(file);
+	}
+
+	/* cache this list */
+	cache->file = file;
+	cache->size = size;
+	cache->status = 0;
+	cache->is_valid = true;
+
+	*fileoutptr = file;
+
+	return 0;
+}
+
+static void
+guc_capture_free_ads_cache(struct __guc_state_capture_priv *gc)
+{
+	int i, j, k;
+	struct __guc_capture_ads_cache *cache;
+
+	for (i = 0; i < GUC_CAPTURE_LIST_INDEX_MAX; ++i) {
+		for (j = 0; j < GUC_CAPTURE_LIST_TYPE_MAX; ++j) {
+			for (k = 0; k < GUC_MAX_ENGINE_CLASSES; ++k) {
+				cache = &gc->ads_cache[i][j][k];
+				if (cache->is_valid && cache->file)
+					fput(cache->file);
+			}
+		}
+	}
+}
+
+void intel_guc_capture_destroy(struct intel_guc *guc)
+{
+	if (!guc->capture.priv)
+		return;
+
+	guc_capture_free_ads_cache(guc->capture.priv);
+
+	kfree(guc->capture.priv);
+	guc->capture.priv = NULL;
+}
+
+int intel_guc_capture_init(struct intel_guc *guc)
+{
+	guc->capture.priv = kzalloc(sizeof(*guc->capture.priv), GFP_KERNEL);
+	if (!guc->capture.priv)
+		return -ENOMEM;
+
+	guc->capture.priv->reglists = guc_capture_get_device_reglist(guc);
+
+	return 0;
+}
diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.h b/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.h
new file mode 100644
index 000000000000..f05365239a2f
--- /dev/null
+++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2021-2021 Intel Corporation
+ */
+
+#ifndef _INTEL_GUC_CAPTURE_H
+#define _INTEL_GUC_CAPTURE_H
+
+#include <linux/types.h>
+
+struct file;
+struct guc_gt_system_info;
+struct intel_guc;
+
+int intel_guc_capture_getlist(struct intel_guc *guc, u32 owner, u32 type, u32 classid,
+			      struct file **fileptr);
+int intel_guc_capture_getlistsize(struct intel_guc *guc, u32 owner, u32 type, u32 classid,
+				  size_t *size);
+void intel_guc_capture_destroy(struct intel_guc *guc);
+int intel_guc_capture_init(struct intel_guc *guc);
+
+#endif /* _INTEL_GUC_CAPTURE_H */
diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_fwif.h b/drivers/gpu/drm/i915/gt/uc/intel_guc_fwif.h
index 6a4612a852e2..b5f59c6a2424 100644
--- a/drivers/gpu/drm/i915/gt/uc/intel_guc_fwif.h
+++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_fwif.h
@@ -297,6 +297,14 @@ enum {
 	GUC_CAPTURE_LIST_INDEX_MAX = 2,
 };
 
+/*Register-types of GuC capture register lists */
+enum guc_capture_type {
+	GUC_CAPTURE_LIST_TYPE_GLOBAL = 0,
+	GUC_CAPTURE_LIST_TYPE_ENGINE_CLASS,
+	GUC_CAPTURE_LIST_TYPE_ENGINE_INSTANCE,
+	GUC_CAPTURE_LIST_TYPE_MAX,
+};
+
 /* GuC Additional Data Struct */
 struct guc_ads {
 	struct guc_mmio_reg_set reg_state_list[GUC_MAX_ENGINE_CLASSES][GUC_MAX_INSTANCES_PER_CLASS];
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [Intel-gfx] [PATCH v7 02/13] drm/i915/guc: Add XE_LP static registers for GuC error capture.
  2022-02-26  9:55 ` [Intel-gfx] " Alan Previn
  (?)
  (?)
@ 2022-02-26  9:55 ` Alan Previn
  2022-03-10  0:05   ` Umesh Nerlige Ramappa
  -1 siblings, 1 reply; 32+ messages in thread
From: Alan Previn @ 2022-02-26  9:55 UTC (permalink / raw)
  To: intel-gfx; +Cc: Alan Previn

Add device specific tables and register lists to cover different engines
class types for GuC error state capture for XE_LP products.

Signed-off-by: Alan Previn <alan.previn.teres.alexis@intel.com>
---
 .../gpu/drm/i915/gt/uc/intel_guc_capture.c    | 116 ++++++++++++++----
 drivers/gpu/drm/i915/gt/uc/intel_guc_fwif.h   |   4 +-
 2 files changed, 97 insertions(+), 23 deletions(-)

diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c
index 647a118aa3c3..fb3ca734ef97 100644
--- a/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c
+++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c
@@ -19,43 +19,109 @@
 
 /*
  * 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
+ * NOTE1: For engine-registers, GuC only needs the register offsets
+ *        from the engine-mmio-base
  */
+#define COMMON_GEN12BASE_GLOBAL() \
+	{GEN12_FAULT_TLB_DATA0,    0,      0, "GEN12_FAULT_TLB_DATA0"}, \
+	{GEN12_FAULT_TLB_DATA1,    0,      0, "GEN12_FAULT_TLB_DATA1"}, \
+	{FORCEWAKE_MT,             0,      0, "FORCEWAKE"}, \
+	{GEN12_AUX_ERR_DBG,        0,      0, "AUX_ERR_DBG"}, \
+	{GEN12_GAM_DONE,           0,      0, "GAM_DONE"}, \
+	{GEN12_RING_FAULT_REG,     0,      0, "FAULT_REG"}
+
+#define COMMON_GEN12BASE_ENGINE_INSTANCE() \
+	{RING_PSMI_CTL(0),         0,      0, "RC PSMI"}, \
+	{RING_ESR(0),              0,      0, "ESR"}, \
+	{RING_DMA_FADD(0),         0,      0, "RING_DMA_FADD_LDW"}, \
+	{RING_DMA_FADD_UDW(0),     0,      0, "RING_DMA_FADD_UDW"}, \
+	{RING_IPEIR(0),            0,      0, "IPEIR"}, \
+	{RING_IPEHR(0),            0,      0, "IPEHR"}, \
+	{RING_INSTPS(0),           0,      0, "INSTPS"}, \
+	{RING_BBADDR(0),           0,      0, "RING_BBADDR_LOW32"}, \
+	{RING_BBADDR_UDW(0),       0,      0, "RING_BBADDR_UP32"}, \
+	{RING_BBSTATE(0),          0,      0, "BB_STATE"}, \
+	{CCID(0),                  0,      0, "CCID"}, \
+	{RING_ACTHD(0),            0,      0, "ACTHD_LDW"}, \
+	{RING_ACTHD_UDW(0),        0,      0, "ACTHD_UDW"}, \
+	{RING_INSTPM(0),           0,      0, "INSTPM"}, \
+	{RING_INSTDONE(0),         0,      0, "INSTDONE"}, \
+	{RING_NOPID(0),            0,      0, "RING_NOPID"}, \
+	{RING_START(0),            0,      0, "START"}, \
+	{RING_HEAD(0),             0,      0, "HEAD"}, \
+	{RING_TAIL(0),             0,      0, "TAIL"}, \
+	{RING_CTL(0),              0,      0, "CTL"}, \
+	{RING_MI_MODE(0),          0,      0, "MODE"}, \
+	{RING_CONTEXT_CONTROL(0),  0,      0, "RING_CONTEXT_CONTROL"}, \
+	{RING_HWS_PGA(0),          0,      0, "HWS"}, \
+	{RING_MODE_GEN7(0),        0,      0, "GFX_MODE"}, \
+	{GEN8_RING_PDP_LDW(0, 0),  0,      0, "PDP0_LDW"}, \
+	{GEN8_RING_PDP_UDW(0, 0),  0,      0, "PDP0_UDW"}, \
+	{GEN8_RING_PDP_LDW(0, 1),  0,      0, "PDP1_LDW"}, \
+	{GEN8_RING_PDP_UDW(0, 1),  0,      0, "PDP1_UDW"}, \
+	{GEN8_RING_PDP_LDW(0, 2),  0,      0, "PDP2_LDW"}, \
+	{GEN8_RING_PDP_UDW(0, 2),  0,      0, "PDP2_UDW"}, \
+	{GEN8_RING_PDP_LDW(0, 3),  0,      0, "PDP3_LDW"}, \
+	{GEN8_RING_PDP_UDW(0, 3),  0,      0, "PDP3_UDW"}
+
+#define COMMON_GEN12BASE_HAS_EU() \
+	{EIR,                      0,      0, "EIR"}
+
+#define COMMON_GEN12BASE_RENDER() \
+	{GEN7_SC_INSTDONE,         0,      0, "GEN7_SC_INSTDONE"}, \
+	{GEN12_SC_INSTDONE_EXTRA,  0,      0, "GEN12_SC_INSTDONE_EXTRA"}, \
+	{GEN12_SC_INSTDONE_EXTRA2, 0,      0, "GEN12_SC_INSTDONE_EXTRA2"}
+
+#define COMMON_GEN12BASE_VEC() \
+	{GEN12_SFC_DONE(0),        0,      0, "SFC_DONE[0]"}, \
+	{GEN12_SFC_DONE(1),        0,      0, "SFC_DONE[1]"}, \
+	{GEN12_SFC_DONE(2),        0,      0, "SFC_DONE[2]"}, \
+	{GEN12_SFC_DONE(3),        0,      0, "SFC_DONE[3]"}
+
 /* XE_LPD - Global */
 static const struct __guc_mmio_reg_descr xe_lpd_global_regs[] = {
-	{GEN12_RING_FAULT_REG,     0,      0, "GEN12_RING_FAULT_REG"}
+	COMMON_GEN12BASE_GLOBAL(),
 };
 
 /* XE_LPD - Render / Compute Per-Class */
 static const struct __guc_mmio_reg_descr xe_lpd_rc_class_regs[] = {
-	{EIR,                      0,      0, "EIR"}
+	COMMON_GEN12BASE_HAS_EU(),
+	COMMON_GEN12BASE_RENDER(),
 };
 
 /* XE_LPD - Render / Compute Per-Engine-Instance */
 static const struct __guc_mmio_reg_descr xe_lpd_rc_inst_regs[] = {
-	{RING_HEAD(0),             0,      0, "RING_HEAD"},
-	{RING_TAIL(0),             0,      0, "RING_TAIL"},
+	COMMON_GEN12BASE_ENGINE_INSTANCE(),
 };
 
 /* XE_LPD - Media Decode/Encode Per-Class */
 static const struct __guc_mmio_reg_descr xe_lpd_vd_class_regs[] = {
+	COMMON_GEN12BASE_ENGINE_INSTANCE(),
 };
 
 /* XE_LPD - Media Decode/Encode Per-Engine-Instance */
 static const struct __guc_mmio_reg_descr xe_lpd_vd_inst_regs[] = {
-	{RING_HEAD(0),             0,      0, "RING_HEAD"},
-	{RING_TAIL(0),             0,      0, "RING_TAIL"},
+	COMMON_GEN12BASE_ENGINE_INSTANCE(),
 };
 
 /* XE_LPD - Video Enhancement Per-Class */
 static const struct __guc_mmio_reg_descr xe_lpd_vec_class_regs[] = {
+	COMMON_GEN12BASE_VEC(),
 };
 
 /* XE_LPD - Video Enhancement Per-Engine-Instance */
 static const struct __guc_mmio_reg_descr xe_lpd_vec_inst_regs[] = {
-	{RING_HEAD(0),             0,      0, "RING_HEAD"},
-	{RING_TAIL(0),             0,      0, "RING_TAIL"},
+	COMMON_GEN12BASE_ENGINE_INSTANCE(),
+};
+
+/* XE_LPD - Blitter Per-Engine-Instance */
+static const struct __guc_mmio_reg_descr xe_lpd_blt_inst_regs[] = {
+	COMMON_GEN12BASE_ENGINE_INSTANCE(),
+};
+
+/* XE_LPD - Blitter Per-Class */
+/* XE_LPD - Media Decode/Encode Per-Class */
+static const struct __guc_mmio_reg_descr empty_regs_list[] = {
 };
 
 #define TO_GCAP_DEF_OWNER(x) (GUC_CAPTURE_LIST_INDEX_##x)
@@ -74,10 +140,12 @@ static const struct __guc_mmio_reg_descr_group xe_lpd_lists[] = {
 	MAKE_REGLIST(xe_lpd_global_regs, PF, GLOBAL, 0),
 	MAKE_REGLIST(xe_lpd_rc_class_regs, PF, ENGINE_CLASS, GUC_RENDER_CLASS),
 	MAKE_REGLIST(xe_lpd_rc_inst_regs, PF, ENGINE_INSTANCE, GUC_RENDER_CLASS),
-	MAKE_REGLIST(xe_lpd_vd_class_regs, PF, ENGINE_CLASS, GUC_VIDEO_CLASS),
+	MAKE_REGLIST(empty_regs_list, PF, ENGINE_CLASS, GUC_VIDEO_CLASS),
 	MAKE_REGLIST(xe_lpd_vd_inst_regs, PF, ENGINE_INSTANCE, GUC_VIDEO_CLASS),
 	MAKE_REGLIST(xe_lpd_vec_class_regs, PF, ENGINE_CLASS, GUC_VIDEOENHANCE_CLASS),
 	MAKE_REGLIST(xe_lpd_vec_inst_regs, PF, ENGINE_INSTANCE, GUC_VIDEOENHANCE_CLASS),
+	MAKE_REGLIST(empty_regs_list, PF, ENGINE_CLASS, GUC_BLITTER_CLASS),
+	MAKE_REGLIST(xe_lpd_blt_inst_regs, PF, ENGINE_INSTANCE, GUC_BLITTER_CLASS),
 	{}
 };
 
@@ -191,20 +259,24 @@ guc_capture_list_init(struct intel_guc *guc, u32 owner, u32 type, u32 classid,
 		return -ENODEV;
 
 	match = guc_capture_get_one_list(reglists, owner, type, classid);
-	if (match) {
-		for (i = 0; i < num_entries && i < match->num_regs; ++i) {
-			ptr[i].offset = match->list[i].reg.reg;
-			ptr[i].value = 0xDEADF00D;
-			ptr[i].flags = match->list[i].flags;
-			ptr[i].mask = match->list[i].mask;
-		}
-		return 0;
+	if (!match) {
+		guc_capture_warn_with_list_info(i915, "Missing register list init", owner, type,
+						classid);
+		return -ENODATA;
+	}
+
+	for (i = 0; i < num_entries && i < match->num_regs; ++i) {
+		ptr[i].offset = match->list[i].reg.reg;
+		ptr[i].value = 0xDEADF00D;
+		ptr[i].flags = match->list[i].flags;
+		ptr[i].mask = match->list[i].mask;
 	}
 
-	guc_capture_warn_with_list_info(i915, "Missing register list init", owner, type,
-					classid);
+	if (i < num_entries)
+		drm_dbg(&i915->drm, "GuC-capture: Init reglist short %d out %d.\n",
+			(int)i, (int)num_entries);
 
-	return -ENODATA;
+	return 0;
 }
 
 static int
diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_fwif.h b/drivers/gpu/drm/i915/gt/uc/intel_guc_fwif.h
index b5f59c6a2424..14ab4c9588ae 100644
--- a/drivers/gpu/drm/i915/gt/uc/intel_guc_fwif.h
+++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_fwif.h
@@ -271,10 +271,12 @@ 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)
+#define GUC_REGSET_STEERING_GROUP       GENMASK(15, 12)
+#define GUC_REGSET_STEERING_INSTANCE    GENMASK(23, 20)
+	u32 mask;
 } __packed;
 
 /* GuC register sets */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [Intel-gfx] [PATCH v7 03/13] drm/i915/guc: Add XE_LP steered register lists support
  2022-02-26  9:55 ` [Intel-gfx] " Alan Previn
                   ` (2 preceding siblings ...)
  (?)
@ 2022-02-26  9:55 ` Alan Previn
  2022-03-10  0:56   ` Umesh Nerlige Ramappa
  -1 siblings, 1 reply; 32+ messages in thread
From: Alan Previn @ 2022-02-26  9:55 UTC (permalink / raw)
  To: intel-gfx; +Cc: 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: Alan Previn <alan.previn.teres.alexis@intel.com>
---
 drivers/gpu/drm/i915/gt/uc/guc_capture_fwif.h |   9 +
 .../gpu/drm/i915/gt/uc/intel_guc_capture.c    | 175 ++++++++++++++++--
 2 files changed, 173 insertions(+), 11 deletions(-)

diff --git a/drivers/gpu/drm/i915/gt/uc/guc_capture_fwif.h b/drivers/gpu/drm/i915/gt/uc/guc_capture_fwif.h
index 858f85478636..27b89539d0d5 100644
--- a/drivers/gpu/drm/i915/gt/uc/guc_capture_fwif.h
+++ b/drivers/gpu/drm/i915/gt/uc/guc_capture_fwif.h
@@ -51,6 +51,7 @@ struct __guc_mmio_reg_descr_group {
 	u32 owner; /* see enum guc_capture_owner */
 	u32 type; /* see enum guc_capture_type */
 	u32 engine; /* as per MAX_ENGINE_CLASS */
+	struct __guc_mmio_reg_descr *extlist; /* only used for steered registers */
 };
 
 /**
@@ -78,6 +79,14 @@ struct __guc_state_capture_priv {
 	 */
 	const struct __guc_mmio_reg_descr_group *reglists;
 
+	/**
+	 * @extlists: allocated table of steered register lists used for error-capture state.
+	 *
+	 * NOTE: steered registers have multiple instances depending on the HW configuration
+	 * (slices or dual-sub-slices) and thus depends on HW fuses discovered at startup
+	 */
+	struct __guc_mmio_reg_descr_group *extlists;
+
 	/**
 	 * @ads_cache: cached register lists that is ADS format ready
 	 */
diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c
index fb3ca734ef97..6370943ea300 100644
--- a/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c
+++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c
@@ -133,6 +133,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 */
@@ -150,28 +151,33 @@ static const struct __guc_mmio_reg_descr_group xe_lpd_lists[] = {
 };
 
 static const struct __guc_mmio_reg_descr_group *
-guc_capture_get_device_reglist(struct intel_guc *guc)
+guc_capture_get_one_list(const struct __guc_mmio_reg_descr_group *reglists,
+			 u32 owner, u32 type, u32 id)
 {
-	struct drm_i915_private *i915 = guc_to_gt(guc)->i915;
+	int i;
 
-	if (IS_TIGERLAKE(i915) || IS_ROCKETLAKE(i915) ||
-	    IS_ALDERLAKE_S(i915) || IS_ALDERLAKE_P(i915)) {
-		return xe_lpd_lists;
+	if (!reglists)
+		return NULL;
+
+	for (i = 0; reglists[i].list; ++i) {
+		if (reglists[i].owner == owner && reglists[i].type == type &&
+		    (reglists[i].engine == id || reglists[i].type == GUC_CAPTURE_LIST_TYPE_GLOBAL))
+		return &reglists[i];
 	}
 
 	return NULL;
 }
 
-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, u32 id)
+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].list; ++i) {
+	for (i = 0; reglists[i].extlist; ++i) {
 		if (reglists[i].owner == owner && reglists[i].type == type &&
 		    (reglists[i].engine == id || reglists[i].type == GUC_CAPTURE_LIST_TYPE_GLOBAL))
 		return &reglists[i];
@@ -180,6 +186,127 @@ guc_capture_get_one_list(const struct __guc_mmio_reg_descr_group *reglists,
 	return NULL;
 }
 
+static void guc_capture_free_extlists(struct __guc_mmio_reg_descr_group *reglists)
+{
+	int i = 0;
+
+	if (!reglists)
+		return;
+
+	while (reglists[i].extlist)
+		kfree(reglists[i++].extlist);
+}
+
+struct __ext_steer_reg {
+	const char *name;
+	i915_reg_t reg;
+};
+
+static const struct __ext_steer_reg xe_extregs[] = {
+	{"GEN7_SAMPLER_INSTDONE", GEN7_SAMPLER_INSTDONE},
+	{"GEN7_ROW_INSTDONE", GEN7_ROW_INSTDONE}
+};
+
+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 = extlist->reg;
+	ext->flags = FIELD_PREP(GUC_REGSET_STEERING_GROUP, slice_id);
+	ext->flags |= FIELD_PREP(GUC_REGSET_STEERING_INSTANCE, subslice_id);
+	ext->regname = extlist->name;
+}
+
+static int
+__alloc_ext_regs(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 = kcalloc(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_xe_lpd(struct intel_guc *guc,
+				       const struct __guc_mmio_reg_descr_group *lists)
+{
+	struct intel_gt *gt = guc_to_gt(guc);
+	struct drm_i915_private *i915 = guc_to_gt(guc)->i915;
+	int slice, subslice, 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;
+	struct sseu_dev_info *sseu;
+
+	/* In XE_LPD we only have steered registers for the render-class */
+	list = guc_capture_get_one_list(lists, GUC_CAPTURE_LIST_INDEX_PF,
+					GUC_CAPTURE_LIST_TYPE_ENGINE_CLASS, GUC_RENDER_CLASS);
+	/* skip if extlists was previously allocated */
+	if (!list || guc->capture.priv->extlists)
+		return;
+
+	num_steer_regs = ARRAY_SIZE(xe_extregs);
+
+	sseu = &gt->info.sseu;
+	for_each_instdone_slice_subslice(i915, sseu, slice, subslice)
+		num_tot_regs += num_steer_regs;
+
+	if (!num_tot_regs)
+		return;
+
+	/* allocate an extra for an end marker */
+	extlists = kcalloc(2, sizeof(struct __guc_mmio_reg_descr_group), GFP_KERNEL);
+	if (!extlists)
+		return;
+
+	if (__alloc_ext_regs(&extlists[0], list, num_tot_regs)) {
+		kfree(extlists);
+		return;
+	}
+
+	extarray = extlists[0].extlist;
+	for_each_instdone_slice_subslice(i915, sseu, slice, subslice) {
+		for (i = 0; i < num_steer_regs; ++i) {
+			__fill_ext_reg(extarray, &xe_extregs[i], slice, subslice);
+			++extarray;
+		}
+	}
+
+	guc->capture.priv->extlists = extlists;
+}
+
+static const struct __guc_mmio_reg_descr_group *
+guc_capture_get_device_reglist(struct intel_guc *guc)
+{
+	struct drm_i915_private *i915 = guc_to_gt(guc)->i915;
+
+	if (IS_TIGERLAKE(i915) || IS_ROCKETLAKE(i915) ||
+	    IS_ALDERLAKE_S(i915) || IS_ALDERLAKE_P(i915)) {
+		/*
+		 * 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_xe_lpd(guc, xe_lpd_lists);
+		return xe_lpd_lists;
+	}
+
+	drm_warn(&i915->drm, "No GuC-capture register lists\n");
+
+	return NULL;
+}
+
 static const char *
 __stringify_owner(u32 owner)
 {
@@ -250,10 +377,12 @@ static int
 guc_capture_list_init(struct intel_guc *guc, u32 owner, u32 type, u32 classid,
 		      struct guc_mmio_reg *ptr, u16 num_entries)
 {
-	u32 i = 0;
+	u32 i = 0, j = 0;
 	struct drm_i915_private *i915 = guc_to_gt(guc)->i915;
 	const struct __guc_mmio_reg_descr_group *reglists = guc->capture.priv->reglists;
+	struct __guc_mmio_reg_descr_group *extlists = guc->capture.priv->extlists;
 	const struct __guc_mmio_reg_descr_group *match;
+	struct __guc_mmio_reg_descr_group *matchext;
 
 	if (!reglists)
 		return -ENODEV;
@@ -272,6 +401,17 @@ guc_capture_list_init(struct intel_guc *guc, u32 owner, u32 type, u32 classid,
 		ptr[i].mask = match->list[i].mask;
 	}
 
+	matchext = guc_capture_get_one_ext_list(extlists, owner, type, classid);
+	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.reg;
+			ptr[i].value = 0xDEADF00D;
+			ptr[i].flags = matchext->extlist[j].flags;
+			ptr[i].mask = matchext->extlist[j].mask;
+		}
+	}
 	if (i < num_entries)
 		drm_dbg(&i915->drm, "GuC-capture: Init reglist short %d out %d.\n",
 			(int)i, (int)num_entries);
@@ -283,12 +423,20 @@ static int
 guc_cap_list_num_regs(struct __guc_state_capture_priv *gc, u32 owner, u32 type, u32 classid)
 {
 	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, classid);
 	if (!match)
 		return 0;
 
-	return match->num_regs;
+	num_regs = match->num_regs;
+
+	matchext = guc_capture_get_one_ext_list(gc->extlists, owner, type, classid);
+	if (matchext)
+		num_regs += matchext->num_regs;
+
+	return num_regs;
 }
 
 int
@@ -408,6 +556,11 @@ void intel_guc_capture_destroy(struct intel_guc *guc)
 
 	guc_capture_free_ads_cache(guc->capture.priv);
 
+	if (guc->capture.priv->extlists) {
+		guc_capture_free_extlists(guc->capture.priv->extlists);
+		kfree(guc->capture.priv->extlists);
+	}
+
 	kfree(guc->capture.priv);
 	guc->capture.priv = NULL;
 }
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [Intel-gfx] [PATCH v7 04/13] drm/i915/guc: Add DG2 registers for GuC error state capture.
  2022-02-26  9:55 ` [Intel-gfx] " Alan Previn
                   ` (3 preceding siblings ...)
  (?)
@ 2022-02-26  9:55 ` Alan Previn
  2022-03-10  1:53   ` Umesh Nerlige Ramappa
  -1 siblings, 1 reply; 32+ messages in thread
From: Alan Previn @ 2022-02-26  9:55 UTC (permalink / raw)
  To: intel-gfx; +Cc: Alan Previn

Add additional DG2 registers for GuC error state capture.

Signed-off-by: Alan Previn <alan.previn.teres.alexis@intel.com>
---
 .../gpu/drm/i915/gt/uc/intel_guc_capture.c    | 80 ++++++++++++++++++-
 1 file changed, 78 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c
index 6370943ea300..c8441ca1566b 100644
--- a/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c
+++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c
@@ -285,20 +285,96 @@ guc_capture_alloc_steered_lists_xe_lpd(struct intel_guc *guc,
 	guc->capture.priv->extlists = extlists;
 }
 
+static const struct __ext_steer_reg xehpg_extregs[] = {
+	{"XEHPG_INSTDONE_GEOM_SVG", XEHPG_INSTDONE_GEOM_SVG}
+};
+
+static bool __has_xehpg_extregs(u32 ipver)
+{
+	return (ipver >= IP_VER(12, 55));
+}
+
+static void
+guc_capture_alloc_steered_lists_xe_hpg(struct intel_guc *guc,
+				       const struct __guc_mmio_reg_descr_group *lists,
+				       u32 ipver)
+{
+	struct intel_gt *gt = guc_to_gt(guc);
+	struct drm_i915_private *i915 = guc_to_gt(guc)->i915;
+	struct sseu_dev_info *sseu;
+	int slice, subslice, i, iter, 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;
+
+	/* In XE_LP / HPG we only have render-class steering registers during error-capture */
+	list = guc_capture_get_one_list(lists, GUC_CAPTURE_LIST_INDEX_PF,
+					GUC_CAPTURE_LIST_TYPE_ENGINE_CLASS, GUC_RENDER_CLASS);
+	/* skip if extlists was previously allocated */
+	if (!list || guc->capture.priv->extlists)
+		return;
+
+	num_steer_regs = ARRAY_SIZE(xe_extregs);
+	if (__has_xehpg_extregs(ipver))
+		num_steer_regs += ARRAY_SIZE(xehpg_extregs);
+
+	sseu = &gt->info.sseu;
+	for_each_instdone_gslice_dss_xehp(i915, sseu, iter, slice, subslice) {
+		num_tot_regs += num_steer_regs;
+	}
+
+	if (!num_tot_regs)
+		return;
+
+	/* allocate an extra for an end marker */
+	extlists = kcalloc(2, sizeof(struct __guc_mmio_reg_descr_group), GFP_KERNEL);
+	if (!extlists)
+		return;
+
+	if (__alloc_ext_regs(&extlists[0], list, num_tot_regs)) {
+		kfree(extlists);
+		return;
+	}
+
+	extarray = extlists[0].extlist;
+	for_each_instdone_gslice_dss_xehp(i915, sseu, iter, 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(ipver)) {
+			for (i = 0; i < ARRAY_SIZE(xehpg_extregs); ++i) {
+				__fill_ext_reg(extarray, &xehpg_extregs[i], slice, subslice);
+				++extarray;
+			}
+		}
+	}
+
+	drm_dbg(&i915->drm, "GuC-capture found %d-ext-regs.\n", num_tot_regs);
+	guc->capture.priv->extlists = extlists;
+}
+
 static const struct __guc_mmio_reg_descr_group *
 guc_capture_get_device_reglist(struct intel_guc *guc)
 {
 	struct drm_i915_private *i915 = guc_to_gt(guc)->i915;
 
 	if (IS_TIGERLAKE(i915) || IS_ROCKETLAKE(i915) ||
-	    IS_ALDERLAKE_S(i915) || IS_ALDERLAKE_P(i915)) {
+	    IS_ALDERLAKE_S(i915) || IS_ALDERLAKE_P(i915) ||
+	    IS_DG2(i915) || IS_XEHPSDV(i915)) {
 		/*
 		 * 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_xe_lpd(guc, xe_lpd_lists);
+		if (IS_DG2(i915))
+			guc_capture_alloc_steered_lists_xe_hpg(guc, xe_lpd_lists, IP_VER(12, 55));
+		else if (IS_XEHPSDV(i915))
+			guc_capture_alloc_steered_lists_xe_hpg(guc, xe_lpd_lists, IP_VER(12, 50));
+		else
+			guc_capture_alloc_steered_lists_xe_lpd(guc, xe_lpd_lists);
+
 		return xe_lpd_lists;
 	}
 
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [Intel-gfx] [PATCH v7 05/13] drm/i915/guc: Add Gen9 registers for GuC error state capture.
  2022-02-26  9:55 ` [Intel-gfx] " Alan Previn
                   ` (4 preceding siblings ...)
  (?)
@ 2022-02-26  9:55 ` Alan Previn
  -1 siblings, 0 replies; 32+ messages in thread
From: Alan Previn @ 2022-02-26  9:55 UTC (permalink / raw)
  To: intel-gfx; +Cc: Alan Previn

Abstract out a Gen9 register list as the default for all other
platforms we don't yet formally support GuC submission on.

Signed-off-by: Alan Previn <alan.previn.teres.alexis@intel.com>
Reviewed-by: Umesh Nerlige Ramappa <umesh.nerlige.ramappa@intel.com>
---
 .../gpu/drm/i915/gt/uc/intel_guc_capture.c    | 82 +++++++++++++------
 1 file changed, 59 insertions(+), 23 deletions(-)

diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c
index c8441ca1566b..eb22f979d720 100644
--- a/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c
+++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c
@@ -22,15 +22,24 @@
  * NOTE1: For engine-registers, GuC only needs the register offsets
  *        from the engine-mmio-base
  */
+#define COMMON_BASE_GLOBAL() \
+	{FORCEWAKE_MT,             0,      0, "FORCEWAKE"}
+
+#define COMMON_GEN9BASE_GLOBAL() \
+	{GEN8_FAULT_TLB_DATA0,     0,      0, "GEN8_FAULT_TLB_DATA0"}, \
+	{GEN8_FAULT_TLB_DATA1,     0,      0, "GEN8_FAULT_TLB_DATA1"}, \
+	{ERROR_GEN6,               0,      0, "ERROR_GEN6"}, \
+	{DONE_REG,                 0,      0, "DONE_REG"}, \
+	{HSW_GTT_CACHE_EN,         0,      0, "HSW_GTT_CACHE_EN"}
+
 #define COMMON_GEN12BASE_GLOBAL() \
 	{GEN12_FAULT_TLB_DATA0,    0,      0, "GEN12_FAULT_TLB_DATA0"}, \
 	{GEN12_FAULT_TLB_DATA1,    0,      0, "GEN12_FAULT_TLB_DATA1"}, \
-	{FORCEWAKE_MT,             0,      0, "FORCEWAKE"}, \
 	{GEN12_AUX_ERR_DBG,        0,      0, "AUX_ERR_DBG"}, \
 	{GEN12_GAM_DONE,           0,      0, "GAM_DONE"}, \
 	{GEN12_RING_FAULT_REG,     0,      0, "FAULT_REG"}
 
-#define COMMON_GEN12BASE_ENGINE_INSTANCE() \
+#define COMMON_BASE_ENGINE_INSTANCE() \
 	{RING_PSMI_CTL(0),         0,      0, "RC PSMI"}, \
 	{RING_ESR(0),              0,      0, "ESR"}, \
 	{RING_DMA_FADD(0),         0,      0, "RING_DMA_FADD_LDW"}, \
@@ -64,11 +73,13 @@
 	{GEN8_RING_PDP_LDW(0, 3),  0,      0, "PDP3_LDW"}, \
 	{GEN8_RING_PDP_UDW(0, 3),  0,      0, "PDP3_UDW"}
 
-#define COMMON_GEN12BASE_HAS_EU() \
+#define COMMON_BASE_HAS_EU() \
 	{EIR,                      0,      0, "EIR"}
 
+#define COMMON_BASE_RENDER() \
+	{GEN7_SC_INSTDONE,         0,      0, "GEN7_SC_INSTDONE"}
+
 #define COMMON_GEN12BASE_RENDER() \
-	{GEN7_SC_INSTDONE,         0,      0, "GEN7_SC_INSTDONE"}, \
 	{GEN12_SC_INSTDONE_EXTRA,  0,      0, "GEN12_SC_INSTDONE_EXTRA"}, \
 	{GEN12_SC_INSTDONE_EXTRA2, 0,      0, "GEN12_SC_INSTDONE_EXTRA2"}
 
@@ -80,28 +91,26 @@
 
 /* XE_LPD - Global */
 static const struct __guc_mmio_reg_descr xe_lpd_global_regs[] = {
+	COMMON_BASE_GLOBAL(),
+	COMMON_GEN9BASE_GLOBAL(),
 	COMMON_GEN12BASE_GLOBAL(),
 };
 
 /* XE_LPD - Render / Compute Per-Class */
 static const struct __guc_mmio_reg_descr xe_lpd_rc_class_regs[] = {
-	COMMON_GEN12BASE_HAS_EU(),
+	COMMON_BASE_HAS_EU(),
+	COMMON_BASE_RENDER(),
 	COMMON_GEN12BASE_RENDER(),
 };
 
-/* XE_LPD - Render / Compute Per-Engine-Instance */
+/* GEN9/XE_LPD - Render / Compute Per-Engine-Instance */
 static const struct __guc_mmio_reg_descr xe_lpd_rc_inst_regs[] = {
-	COMMON_GEN12BASE_ENGINE_INSTANCE(),
+	COMMON_BASE_ENGINE_INSTANCE(),
 };
 
-/* XE_LPD - Media Decode/Encode Per-Class */
-static const struct __guc_mmio_reg_descr xe_lpd_vd_class_regs[] = {
-	COMMON_GEN12BASE_ENGINE_INSTANCE(),
-};
-
-/* XE_LPD - Media Decode/Encode Per-Engine-Instance */
+/* GEN9/XE_LPD - Media Decode/Encode Per-Engine-Instance */
 static const struct __guc_mmio_reg_descr xe_lpd_vd_inst_regs[] = {
-	COMMON_GEN12BASE_ENGINE_INSTANCE(),
+	COMMON_BASE_ENGINE_INSTANCE(),
 };
 
 /* XE_LPD - Video Enhancement Per-Class */
@@ -109,18 +118,33 @@ static const struct __guc_mmio_reg_descr xe_lpd_vec_class_regs[] = {
 	COMMON_GEN12BASE_VEC(),
 };
 
-/* XE_LPD - Video Enhancement Per-Engine-Instance */
+/* GEN9/XE_LPD - Video Enhancement Per-Engine-Instance */
 static const struct __guc_mmio_reg_descr xe_lpd_vec_inst_regs[] = {
-	COMMON_GEN12BASE_ENGINE_INSTANCE(),
+	COMMON_BASE_ENGINE_INSTANCE(),
 };
 
-/* XE_LPD - Blitter Per-Engine-Instance */
+/* GEN9/XE_LPD - Blitter Per-Engine-Instance */
 static const struct __guc_mmio_reg_descr xe_lpd_blt_inst_regs[] = {
-	COMMON_GEN12BASE_ENGINE_INSTANCE(),
+	COMMON_BASE_ENGINE_INSTANCE(),
 };
 
-/* XE_LPD - Blitter Per-Class */
-/* XE_LPD - Media Decode/Encode Per-Class */
+/* GEN9 - Global */
+static const struct __guc_mmio_reg_descr default_global_regs[] = {
+	COMMON_BASE_GLOBAL(),
+	COMMON_GEN9BASE_GLOBAL(),
+};
+
+static const struct __guc_mmio_reg_descr default_rc_class_regs[] = {
+	COMMON_BASE_HAS_EU(),
+	COMMON_BASE_RENDER(),
+};
+
+/*
+ * Empty lists:
+ * GEN9/XE_LPD - Blitter Per-Class
+ * GEN9/XE_LPD - Media Decode/Encode Per-Class
+ * GEN9 - VEC Class
+ */
 static const struct __guc_mmio_reg_descr empty_regs_list[] = {
 };
 
@@ -137,6 +161,19 @@ static const struct __guc_mmio_reg_descr empty_regs_list[] = {
 	}
 
 /* List of lists */
+static struct __guc_mmio_reg_descr_group default_lists[] = {
+	MAKE_REGLIST(default_global_regs, PF, GLOBAL, 0),
+	MAKE_REGLIST(default_rc_class_regs, PF, ENGINE_CLASS, GUC_RENDER_CLASS),
+	MAKE_REGLIST(xe_lpd_rc_inst_regs, PF, ENGINE_INSTANCE, GUC_RENDER_CLASS),
+	MAKE_REGLIST(empty_regs_list, PF, ENGINE_CLASS, GUC_VIDEO_CLASS),
+	MAKE_REGLIST(xe_lpd_vd_inst_regs, PF, ENGINE_INSTANCE, GUC_VIDEO_CLASS),
+	MAKE_REGLIST(empty_regs_list, PF, ENGINE_CLASS, GUC_VIDEOENHANCE_CLASS),
+	MAKE_REGLIST(xe_lpd_vec_inst_regs, PF, ENGINE_INSTANCE, GUC_VIDEOENHANCE_CLASS),
+	MAKE_REGLIST(empty_regs_list, PF, ENGINE_CLASS, GUC_BLITTER_CLASS),
+	MAKE_REGLIST(xe_lpd_blt_inst_regs, PF, ENGINE_INSTANCE, GUC_BLITTER_CLASS),
+	{}
+};
+
 static const struct __guc_mmio_reg_descr_group xe_lpd_lists[] = {
 	MAKE_REGLIST(xe_lpd_global_regs, PF, GLOBAL, 0),
 	MAKE_REGLIST(xe_lpd_rc_class_regs, PF, ENGINE_CLASS, GUC_RENDER_CLASS),
@@ -378,9 +415,8 @@ guc_capture_get_device_reglist(struct intel_guc *guc)
 		return xe_lpd_lists;
 	}
 
-	drm_warn(&i915->drm, "No GuC-capture register lists\n");
-
-	return NULL;
+	/* if GuC submission is enabled on a non-POR platform, just use a common baseline */
+	return default_lists;
 }
 
 static const char *
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [Intel-gfx] [PATCH v7 06/13] drm/i915/guc: Add GuC's error state capture output structures.
  2022-02-26  9:55 ` [Intel-gfx] " Alan Previn
                   ` (5 preceding siblings ...)
  (?)
@ 2022-02-26  9:55 ` Alan Previn
  -1 siblings, 0 replies; 32+ messages in thread
From: Alan Previn @ 2022-02-26  9:55 UTC (permalink / raw)
  To: intel-gfx; +Cc: Alan Previn

Add GuC's error capture output structures and definitions as how
they would appear in GuC log buffer's error capture subregion after
an error state capture G2H event notification.

Signed-off-by: Alan Previn <alan.previn.teres.alexis@intel.com>
Reviewed-by: Matthew Brost <matthew.brost@intel.com>
---
 drivers/gpu/drm/i915/gt/uc/guc_capture_fwif.h | 47 +++++++++++++++++++
 1 file changed, 47 insertions(+)

diff --git a/drivers/gpu/drm/i915/gt/uc/guc_capture_fwif.h b/drivers/gpu/drm/i915/gt/uc/guc_capture_fwif.h
index 27b89539d0d5..d5cb7d5d4ca7 100644
--- a/drivers/gpu/drm/i915/gt/uc/guc_capture_fwif.h
+++ b/drivers/gpu/drm/i915/gt/uc/guc_capture_fwif.h
@@ -54,6 +54,53 @@ struct __guc_mmio_reg_descr_group {
 	struct __guc_mmio_reg_descr *extlist; /* only used for steered registers */
 };
 
+/**
+ * struct guc_state_capture_header_t / struct guc_state_capture_t /
+ * guc_state_capture_group_header_t / guc_state_capture_group_t
+ *
+ * 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 {
+	u32 owner;
+#define CAP_HDR_CAPTURE_VFID GENMASK(7, 0)
+	u32 info;
+#define CAP_HDR_CAPTURE_TYPE GENMASK(3, 0) /* see enum guc_capture_type */
+#define CAP_HDR_ENGINE_CLASS GENMASK(7, 4) /* see GUC_MAX_ENGINE_CLASSES */
+#define CAP_HDR_ENGINE_INSTANCE GENMASK(11, 8)
+	u32 lrca; /* if type-instance, LRCA (address) that hung, else set to ~0 */
+	u32 guc_id; /* if type-instance, context index of hung context, else set to ~0 */
+	u32 num_mmios;
+#define CAP_HDR_NUM_MMIOS GENMASK(9, 0)
+} __packed;
+
+struct guc_state_capture_t {
+	struct guc_state_capture_header_t header;
+	struct guc_mmio_reg mmio_entries[0];
+} __packed;
+
+enum guc_capture_group_types {
+	GUC_STATE_CAPTURE_GROUP_TYPE_FULL,
+	GUC_STATE_CAPTURE_GROUP_TYPE_PARTIAL,
+	GUC_STATE_CAPTURE_GROUP_TYPE_MAX,
+};
+
+struct guc_state_capture_group_header_t {
+	u32 owner;
+#define CAP_GRP_HDR_CAPTURE_VFID GENMASK(7, 0)
+	u32 info;
+#define CAP_GRP_HDR_NUM_CAPTURES GENMASK(7, 0)
+#define CAP_GRP_HDR_CAPTURE_TYPE GENMASK(15, 8) /* guc_capture_group_types */
+} __packed;
+
+/* this is the top level structure where an error-capture dump starts */
+struct guc_state_capture_group_t {
+	struct guc_state_capture_group_header_t grp_header;
+	struct guc_state_capture_t capture_entries[0];
+} __packed;
+
 /**
  * struct __guc_capture_ads_cache
  *
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [Intel-gfx] [PATCH v7 07/13] drm/i915/guc: Update GuC-log relay function names
  2022-02-26  9:55 ` [Intel-gfx] " Alan Previn
                   ` (6 preceding siblings ...)
  (?)
@ 2022-02-26  9:55 ` Alan Previn
  -1 siblings, 0 replies; 32+ messages in thread
From: Alan Previn @ 2022-02-26  9:55 UTC (permalink / raw)
  To: intel-gfx; +Cc: Alan Previn

For the sake of better code readibility, change previous
relay logging function names with "capture_logs" to
"copy_debug_logs" to differentiate from error capture
functions that will use a different region of the same buffer.

Signed-off-by: Alan Previn <alan.previn.teres.alexis@intel.com>
---
 drivers/gpu/drm/i915/gt/uc/intel_guc_log.c | 35 ++++++++++++----------
 1 file changed, 19 insertions(+), 16 deletions(-)

diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_log.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_log.c
index b53f61f3101f..bf3abb7e69b0 100644
--- a/drivers/gpu/drm/i915/gt/uc/intel_guc_log.c
+++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_log.c
@@ -11,7 +11,7 @@
 #include "i915_memcpy.h"
 #include "intel_guc_log.h"
 
-static void guc_log_capture_logs(struct intel_guc_log *log);
+static void guc_log_copy_debuglogs_for_relay(struct intel_guc_log *log);
 
 /**
  * DOC: GuC firmware log
@@ -197,7 +197,7 @@ static unsigned int guc_get_log_buffer_size(enum guc_log_buffer_type type)
 	return 0;
 }
 
-static void guc_read_update_log_buffer(struct intel_guc_log *log)
+static void _guc_log_copy_debuglogs_for_relay(struct intel_guc_log *log)
 {
 	unsigned int buffer_size, read_offset, write_offset, bytes_to_copy, full_cnt;
 	struct guc_log_buffer_state *log_buf_state, *log_buf_snapshot_state;
@@ -222,7 +222,7 @@ static void guc_read_update_log_buffer(struct intel_guc_log *log)
 		 * Used rate limited to avoid deluge of messages, logs might be
 		 * getting consumed by User at a slow rate.
 		 */
-		DRM_ERROR_RATELIMITED("no sub-buffer to capture logs\n");
+		DRM_ERROR_RATELIMITED("no sub-buffer to copy general logs\n");
 		log->relay.full_count++;
 
 		goto out_unlock;
@@ -300,15 +300,15 @@ static void guc_read_update_log_buffer(struct intel_guc_log *log)
 	mutex_unlock(&log->relay.lock);
 }
 
-static void capture_logs_work(struct work_struct *work)
+static void copy_debug_logs_work(struct work_struct *work)
 {
 	struct intel_guc_log *log =
 		container_of(work, struct intel_guc_log, relay.flush_work);
 
-	guc_log_capture_logs(log);
+	guc_log_copy_debuglogs_for_relay(log);
 }
 
-static int guc_log_map(struct intel_guc_log *log)
+static int guc_log_relay_map(struct intel_guc_log *log)
 {
 	void *vaddr;
 
@@ -331,7 +331,7 @@ static int guc_log_map(struct intel_guc_log *log)
 	return 0;
 }
 
-static void guc_log_unmap(struct intel_guc_log *log)
+static void guc_log_relay_unmap(struct intel_guc_log *log)
 {
 	lockdep_assert_held(&log->relay.lock);
 
@@ -342,7 +342,7 @@ static void guc_log_unmap(struct intel_guc_log *log)
 void intel_guc_log_init_early(struct intel_guc_log *log)
 {
 	mutex_init(&log->relay.lock);
-	INIT_WORK(&log->relay.flush_work, capture_logs_work);
+	INIT_WORK(&log->relay.flush_work, copy_debug_logs_work);
 	log->relay.started = false;
 }
 
@@ -357,8 +357,11 @@ static int guc_log_relay_create(struct intel_guc_log *log)
 	lockdep_assert_held(&log->relay.lock);
 	GEM_BUG_ON(!log->vma);
 
-	 /* Keep the size of sub buffers same as shared log buffer */
-	subbuf_size = log->vma->size;
+	 /*
+	  * Keep the size of sub buffers same as shared log buffer
+	  * but GuC log-events excludes the error-state-capture logs
+	  */
+	subbuf_size = log->vma->size - CAPTURE_BUFFER_SIZE;
 
 	/*
 	 * Store up to 8 snapshots, which is large enough to buffer sufficient
@@ -393,13 +396,13 @@ static void guc_log_relay_destroy(struct intel_guc_log *log)
 	log->relay.channel = NULL;
 }
 
-static void guc_log_capture_logs(struct intel_guc_log *log)
+static void guc_log_copy_debuglogs_for_relay(struct intel_guc_log *log)
 {
 	struct intel_guc *guc = log_to_guc(log);
 	struct drm_i915_private *dev_priv = guc_to_gt(guc)->i915;
 	intel_wakeref_t wakeref;
 
-	guc_read_update_log_buffer(log);
+	_guc_log_copy_debuglogs_for_relay(log);
 
 	/*
 	 * Generally device is expected to be active only at this
@@ -565,7 +568,7 @@ int intel_guc_log_relay_open(struct intel_guc_log *log)
 	if (ret)
 		goto out_unlock;
 
-	ret = guc_log_map(log);
+	ret = guc_log_relay_map(log);
 	if (ret)
 		goto out_relay;
 
@@ -615,8 +618,8 @@ void intel_guc_log_relay_flush(struct intel_guc_log *log)
 	with_intel_runtime_pm(guc_to_gt(guc)->uncore->rpm, wakeref)
 		guc_action_flush_log(guc);
 
-	/* GuC would have updated log buffer by now, so capture it */
-	guc_log_capture_logs(log);
+	/* GuC would have updated log buffer by now, so copy it */
+	guc_log_copy_debuglogs_for_relay(log);
 }
 
 /*
@@ -645,7 +648,7 @@ void intel_guc_log_relay_close(struct intel_guc_log *log)
 
 	mutex_lock(&log->relay.lock);
 	GEM_BUG_ON(!intel_guc_log_relay_created(log));
-	guc_log_unmap(log);
+	guc_log_relay_unmap(log);
 	guc_log_relay_destroy(log);
 	mutex_unlock(&log->relay.lock);
 }
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [Intel-gfx] [PATCH v7 08/13] drm/i915/guc: Add capture region into intel_guc_log
  2022-02-26  9:55 ` [Intel-gfx] " Alan Previn
                   ` (7 preceding siblings ...)
  (?)
@ 2022-02-26  9:55 ` Alan Previn
  -1 siblings, 0 replies; 32+ messages in thread
From: Alan Previn @ 2022-02-26  9:55 UTC (permalink / raw)
  To: intel-gfx; +Cc: Alan Previn

GuC log buffer regions for debug-log-events, crash-dumps and
error-state-capture are all part of a single bo allocation that
also includes the guc_log_buffer_state structures. Now that we
support it, increase the size allocation for error-capture.

Since the error-capture region is accessed at non-deterministic
times (as part of GuC triggered context reset) while debug-log-
events region is accessed as part of relay logging or during
debugfs triggered dumps, move the mapping and unmapping of the
shared buffer into intel_guc_log_create and intel_guc_log_destroy
so that it's always mapped throughout life of GuC operation.

Additionally, while here, update the guc log region layout
diagram to follow the order according to the enum definition
as per the GuC interface.

Signed-off-by: Alan Previn <alan.previn.teres.alexis@intel.com>
---
 drivers/gpu/drm/i915/gt/uc/intel_guc_log.c | 58 +++++++++++++---------
 drivers/gpu/drm/i915/gt/uc/intel_guc_log.h |  3 +-
 2 files changed, 36 insertions(+), 25 deletions(-)

diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_log.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_log.c
index bf3abb7e69b0..2cc52f1eedf3 100644
--- a/drivers/gpu/drm/i915/gt/uc/intel_guc_log.c
+++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_log.c
@@ -25,7 +25,8 @@ static void guc_log_copy_debuglogs_for_relay(struct intel_guc_log *log);
 static int guc_action_flush_log_complete(struct intel_guc *guc)
 {
 	u32 action[] = {
-		INTEL_GUC_ACTION_LOG_BUFFER_FILE_FLUSH_COMPLETE
+		INTEL_GUC_ACTION_LOG_BUFFER_FILE_FLUSH_COMPLETE,
+		GUC_DEBUG_LOG_BUFFER
 	};
 
 	return intel_guc_send(guc, action, ARRAY_SIZE(action));
@@ -136,7 +137,7 @@ static void guc_move_to_next_buf(struct intel_guc_log *log)
 	smp_wmb();
 
 	/* All data has been written, so now move the offset of sub buffer. */
-	relay_reserve(log->relay.channel, log->vma->obj->base.size);
+	relay_reserve(log->relay.channel, log->vma->obj->base.size - CAPTURE_BUFFER_SIZE);
 
 	/* Switch to the next sub buffer */
 	relay_flush(log->relay.channel);
@@ -212,7 +213,7 @@ static void _guc_log_copy_debuglogs_for_relay(struct intel_guc_log *log)
 		goto out_unlock;
 
 	/* Get the pointer to shared GuC log buffer */
-	log_buf_state = src_data = log->relay.buf_addr;
+	log_buf_state = src_data = log->buf_addr;
 
 	/* Get the pointer to local buffer to store the logs */
 	log_buf_snapshot_state = dst_data = guc_get_write_buffer(log);
@@ -232,7 +233,8 @@ static void _guc_log_copy_debuglogs_for_relay(struct intel_guc_log *log)
 	src_data += PAGE_SIZE;
 	dst_data += PAGE_SIZE;
 
-	for (type = GUC_DEBUG_LOG_BUFFER; type < GUC_MAX_LOG_BUFFER; type++) {
+	/* For relay logging, we exclude error state capture */
+	for (type = GUC_DEBUG_LOG_BUFFER; type <= GUC_CRASH_DUMP_LOG_BUFFER; type++) {
 		/*
 		 * Make a copy of the state structure, inside GuC log buffer
 		 * (which is uncached mapped), on the stack to avoid reading
@@ -310,23 +312,17 @@ static void copy_debug_logs_work(struct work_struct *work)
 
 static int guc_log_relay_map(struct intel_guc_log *log)
 {
-	void *vaddr;
-
 	lockdep_assert_held(&log->relay.lock);
 
-	if (!log->vma)
+	if (!log->vma || !log->buf_addr)
 		return -ENODEV;
 
 	/*
-	 * Create a WC (Uncached for read) vmalloc mapping of log
-	 * buffer pages, so that we can directly get the data
-	 * (up-to-date) from memory.
+	 * WC vmalloc mapping of log buffer pages was done at
+	 * GuC Log Init time, but lets keep a ref for book-keeping
 	 */
-	vaddr = i915_gem_object_pin_map_unlocked(log->vma->obj, I915_MAP_WC);
-	if (IS_ERR(vaddr))
-		return PTR_ERR(vaddr);
-
-	log->relay.buf_addr = vaddr;
+	i915_gem_object_get(log->vma->obj);
+	log->relay.buf_in_use = true;
 
 	return 0;
 }
@@ -335,8 +331,8 @@ static void guc_log_relay_unmap(struct intel_guc_log *log)
 {
 	lockdep_assert_held(&log->relay.lock);
 
-	i915_gem_object_unpin_map(log->vma->obj);
-	log->relay.buf_addr = NULL;
+	i915_gem_object_put(log->vma->obj);
+	log->relay.buf_in_use = false;
 }
 
 void intel_guc_log_init_early(struct intel_guc_log *log)
@@ -442,6 +438,7 @@ int intel_guc_log_create(struct intel_guc_log *log)
 {
 	struct intel_guc *guc = log_to_guc(log);
 	struct i915_vma *vma;
+	void *vaddr;
 	u32 guc_log_size;
 	int ret;
 
@@ -449,20 +446,21 @@ int intel_guc_log_create(struct intel_guc_log *log)
 
 	/*
 	 *  GuC Log buffer Layout
+	 * (this ordering must follow "enum guc_log_buffer_type" definition)
 	 *
 	 *  +===============================+ 00B
-	 *  |    Crash dump state header    |
-	 *  +-------------------------------+ 32B
 	 *  |      Debug state header       |
+	 *  +-------------------------------+ 32B
+	 *  |    Crash dump state header    |
 	 *  +-------------------------------+ 64B
 	 *  |     Capture state header      |
 	 *  +-------------------------------+ 96B
 	 *  |                               |
 	 *  +===============================+ PAGE_SIZE (4KB)
-	 *  |        Crash Dump logs        |
-	 *  +===============================+ + CRASH_SIZE
 	 *  |          Debug logs           |
 	 *  +===============================+ + DEBUG_SIZE
+	 *  |        Crash Dump logs        |
+	 *  +===============================+ + CRASH_SIZE
 	 *  |         Capture logs          |
 	 *  +===============================+ + CAPTURE_SIZE
 	 */
@@ -476,6 +474,17 @@ int intel_guc_log_create(struct intel_guc_log *log)
 	}
 
 	log->vma = vma;
+	/*
+	 * Create a WC (Uncached for read) vmalloc mapping up front immediate access to
+	 * data from memory during  critical events such as error capture
+	 */
+	vaddr = i915_gem_object_pin_map_unlocked(log->vma->obj, I915_MAP_WC);
+	if (IS_ERR(vaddr)) {
+		ret = PTR_ERR(vaddr);
+		i915_vma_unpin_and_release(&log->vma, 0);
+		goto err;
+	}
+	log->buf_addr = vaddr;
 
 	log->level = __get_default_log_level(log);
 	DRM_DEBUG_DRIVER("guc_log_level=%d (%s, verbose:%s, verbosity:%d)\n",
@@ -486,13 +495,14 @@ int intel_guc_log_create(struct intel_guc_log *log)
 	return 0;
 
 err:
-	DRM_ERROR("Failed to allocate GuC log buffer. %d\n", ret);
+	DRM_ERROR("Failed to allocate or map GuC log buffer. %d\n", ret);
 	return ret;
 }
 
 void intel_guc_log_destroy(struct intel_guc_log *log)
 {
-	i915_vma_unpin_and_release(&log->vma, 0);
+	log->buf_addr = NULL;
+	i915_vma_unpin_and_release(&log->vma, I915_VMA_RELEASE_MAP);
 }
 
 int intel_guc_log_set_level(struct intel_guc_log *log, u32 level)
@@ -537,7 +547,7 @@ int intel_guc_log_set_level(struct intel_guc_log *log, u32 level)
 
 bool intel_guc_log_relay_created(const struct intel_guc_log *log)
 {
-	return log->relay.buf_addr;
+	return log->buf_addr;
 }
 
 int intel_guc_log_relay_open(struct intel_guc_log *log)
diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_log.h b/drivers/gpu/drm/i915/gt/uc/intel_guc_log.h
index d7e1b6471fed..e1345fca7729 100644
--- a/drivers/gpu/drm/i915/gt/uc/intel_guc_log.h
+++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_log.h
@@ -49,8 +49,9 @@ struct intel_guc;
 struct intel_guc_log {
 	u32 level;
 	struct i915_vma *vma;
+	void *buf_addr;
 	struct {
-		void *buf_addr;
+		bool buf_in_use;
 		bool started;
 		struct work_struct flush_work;
 		struct rchan *channel;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [Intel-gfx] [PATCH v7 09/13] drm/i915/guc: Check sizing of guc_capture output
  2022-02-26  9:55 ` [Intel-gfx] " Alan Previn
                   ` (8 preceding siblings ...)
  (?)
@ 2022-02-26  9:55 ` Alan Previn
  2022-02-28 22:32   ` kernel test robot
  -1 siblings, 1 reply; 32+ messages in thread
From: Alan Previn @ 2022-02-26  9:55 UTC (permalink / raw)
  To: intel-gfx; +Cc: Alan Previn

Add intel_guc_capture_output_min_size_est function to
provide a reasonable minimum size for error-capture
region before allocating the shared buffer.

Signed-off-by: Alan Previn <alan.previn.teres.alexis@intel.com>
---
 .../gpu/drm/i915/gt/uc/intel_guc_capture.c    | 47 +++++++++++++++++++
 .../gpu/drm/i915/gt/uc/intel_guc_capture.h    |  1 +
 drivers/gpu/drm/i915/gt/uc/intel_guc_log.c    |  7 ++-
 3 files changed, 54 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c
index eb22f979d720..ed78995bcc35 100644
--- a/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c
+++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c
@@ -644,6 +644,53 @@ intel_guc_capture_getlist(struct intel_guc *guc, u32 owner, u32 type, u32 classi
 	return 0;
 }
 
+#define GUC_CAPTURE_OVERBUFFER_MULTIPLIER 3
+int intel_guc_capture_output_min_size_est(struct intel_guc *guc)
+{
+	struct intel_gt *gt = guc_to_gt(guc);
+	struct intel_engine_cs *engine;
+	enum intel_engine_id id;
+	int worst_min_size = 0, num_regs = 0;
+	size_t tmp = 0;
+
+	/*
+	 * 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). Finally, let's multiply the whole thing by 3x (just so we are
+	 * not limited to just 1 round of data in a worst case full register dump log)
+	 *
+	 * NOTE: intel_guc_log that allocates the log buffer would round this size up to
+	 * a power of two.
+	 */
+
+	for_each_engine(engine, gt, id) {
+		worst_min_size += sizeof(struct guc_state_capture_group_header_t) +
+				  (3 * sizeof(struct guc_state_capture_header_t));
+
+		if (!intel_guc_capture_getlistsize(guc, 0, GUC_CAPTURE_LIST_TYPE_GLOBAL, 0, &tmp))
+			num_regs += tmp;
+
+		if (!intel_guc_capture_getlistsize(guc, 0, GUC_CAPTURE_LIST_TYPE_ENGINE_CLASS,
+						   engine->class, &tmp)) {
+			num_regs += tmp;
+		}
+		if (!intel_guc_capture_getlistsize(guc, 0, GUC_CAPTURE_LIST_TYPE_ENGINE_INSTANCE,
+						   engine->class, &tmp)) {
+			num_regs += tmp;
+		}
+	}
+
+	worst_min_size += (num_regs * sizeof(struct guc_mmio_reg));
+
+	return (worst_min_size * GUC_CAPTURE_OVERBUFFER_MULTIPLIER);
+}
+
 static void
 guc_capture_free_ads_cache(struct __guc_state_capture_priv *gc)
 {
diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.h b/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.h
index f05365239a2f..24a11f33f7d9 100644
--- a/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.h
+++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.h
@@ -12,6 +12,7 @@ struct file;
 struct guc_gt_system_info;
 struct intel_guc;
 
+int intel_guc_capture_output_min_size_est(struct intel_guc *guc);
 int intel_guc_capture_getlist(struct intel_guc *guc, u32 owner, u32 type, u32 classid,
 			      struct file **fileptr);
 int intel_guc_capture_getlistsize(struct intel_guc *guc, u32 owner, u32 type, u32 classid,
diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_log.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_log.c
index 2cc52f1eedf3..e9a865c2f4cb 100644
--- a/drivers/gpu/drm/i915/gt/uc/intel_guc_log.c
+++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_log.c
@@ -6,10 +6,11 @@
 #include <linux/debugfs.h>
 
 #include "gt/intel_gt.h"
+#include "intel_guc_capture.h"
+#include "intel_guc_log.h"
 #include "i915_drv.h"
 #include "i915_irq.h"
 #include "i915_memcpy.h"
-#include "intel_guc_log.h"
 
 static void guc_log_copy_debuglogs_for_relay(struct intel_guc_log *log);
 
@@ -464,6 +465,10 @@ int intel_guc_log_create(struct intel_guc_log *log)
 	 *  |         Capture logs          |
 	 *  +===============================+ + CAPTURE_SIZE
 	 */
+	if (intel_guc_capture_output_min_size_est(guc) > CAPTURE_BUFFER_SIZE)
+		DRM_WARN("GuC log buffer for state_capture maybe too small. %d < %d\n",
+			 CAPTURE_BUFFER_SIZE, intel_guc_capture_output_min_size_est(guc));
+
 	guc_log_size = PAGE_SIZE + CRASH_BUFFER_SIZE + DEBUG_BUFFER_SIZE +
 		       CAPTURE_BUFFER_SIZE;
 
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [Intel-gfx] [PATCH v7 10/13] drm/i915/guc: Extract GuC error capture lists on G2H notification.
  2022-02-26  9:55 ` [Intel-gfx] " Alan Previn
                   ` (9 preceding siblings ...)
  (?)
@ 2022-02-26  9:55 ` Alan Previn
  2022-03-11 18:16   ` Umesh Nerlige Ramappa
  -1 siblings, 1 reply; 32+ messages in thread
From: Alan Previn @ 2022-02-26  9:55 UTC (permalink / raw)
  To: intel-gfx; +Cc: Alan Previn

- 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 i915_gpu_codedump, duplicate global and engine-class register
  lists for each engine-instance register dump if we find
  dependent-engine resets in a engine-capture-group.
- When i915_gpu_coredump calls into capture_engine, (in a
  subsequent patch) we detach the matching node (guc-id,
  LRCA, etc) from the link list above and attach it to
  i915_gpu_coredump's intel_engine_coredump structure when have
  matching LRCA/guc-id/engine-instance.

Additional notes to be aware of:
- GuC generates the error capture dump into the GuC log buffer but
  this buffer is one big log buffer with 3 independent subregions
  within it. Each subregion is populated with different content
  and used in different ways and timings but all regions operate
  behave as independent ring buffers. Each guc-log subregion
  (general-logs, crash-dump and error- capture) has it's own
  guc_log_buffer_state that contain independent read and write
  pointers.

Signed-off-by: Alan Previn <alan.previn.teres.alexis@intel.com>
---
 .../gpu/drm/i915/gt/uc/abi/guc_actions_abi.h  |   7 +
 drivers/gpu/drm/i915/gt/uc/guc_capture_fwif.h |  56 ++
 .../gpu/drm/i915/gt/uc/intel_guc_capture.c    | 546 +++++++++++++++++-
 .../gpu/drm/i915/gt/uc/intel_guc_capture.h    |   1 +
 drivers/gpu/drm/i915/gt/uc/intel_guc_log.c    |  26 +-
 drivers/gpu/drm/i915/gt/uc/intel_guc_log.h    |   4 +
 .../gpu/drm/i915/gt/uc/intel_guc_submission.c |  10 +-
 7 files changed, 639 insertions(+), 11 deletions(-)

diff --git a/drivers/gpu/drm/i915/gt/uc/abi/guc_actions_abi.h b/drivers/gpu/drm/i915/gt/uc/abi/guc_actions_abi.h
index 7afdadc7656f..ae6448fcaf90 100644
--- a/drivers/gpu/drm/i915/gt/uc/abi/guc_actions_abi.h
+++ b/drivers/gpu/drm/i915/gt/uc/abi/guc_actions_abi.h
@@ -173,4 +173,11 @@ enum intel_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 intel_guc_state_capture_event_status {
+	INTEL_GUC_STATE_CAPTURE_EVENT_STATUS_SUCCESS = 0x0,
+	INTEL_GUC_STATE_CAPTURE_EVENT_STATUS_NOSPACE = 0x1,
+};
+
+#define INTEL_GUC_STATE_CAPTURE_EVENT_STATUS_MASK      0x000000FF
+
 #endif /* _ABI_GUC_ACTIONS_ABI_H */
diff --git a/drivers/gpu/drm/i915/gt/uc/guc_capture_fwif.h b/drivers/gpu/drm/i915/gt/uc/guc_capture_fwif.h
index d5cb7d5d4ca7..2b09aa05d8b7 100644
--- a/drivers/gpu/drm/i915/gt/uc/guc_capture_fwif.h
+++ b/drivers/gpu/drm/i915/gt/uc/guc_capture_fwif.h
@@ -12,6 +12,52 @@
 struct intel_guc;
 struct file;
 
+/**
+ * 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;
+	void *data;
+	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 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
+	 * i915_gpu_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_CAPTURE_LIST_TYPE_MAX];
+#define GCAP_PARSED_REGLIST_INDEX_GLOBAL   BIT(GUC_CAPTURE_LIST_TYPE_GLOBAL)
+#define GCAP_PARSED_REGLIST_INDEX_ENGCLASS BIT(GUC_CAPTURE_LIST_TYPE_ENGINE_CLASS)
+#define GCAP_PARSED_REGLIST_INDEX_ENGINST  BIT(GUC_CAPTURE_LIST_TYPE_ENGINE_INSTANCE)
+};
+
 /**
  * struct guc_debug_capture_list_header / struct guc_debug_capture_list
  *
@@ -140,6 +186,16 @@ struct __guc_state_capture_priv {
 	struct __guc_capture_ads_cache ads_cache[GUC_CAPTURE_LIST_INDEX_MAX]
 						[GUC_CAPTURE_LIST_TYPE_MAX]
 						[GUC_MAX_ENGINE_CLASSES];
+
+	/**
+	 * @outlist: allocated nodes with parsed engine-instance error capture data
+	 *
+	 * A linked list of parsed GuC error-capture output data before
+	 * reporting with formatting via i915_gpu_coredump. Each node in this linked list shall
+	 * contain a single engine-capture including global, engine-class and
+	 * engine-instance register dumps as per guc_capture_parsed_output_node
+	 */
+	struct list_head outlist;
 };
 
 #endif /* _INTEL_GUC_CAPTURE_FWIF_H */
diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c
index ed78995bcc35..9308157d9a74 100644
--- a/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c
+++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c
@@ -14,6 +14,8 @@
 #include "guc_capture_fwif.h"
 #include "intel_guc_fwif.h"
 #include "i915_drv.h"
+#include "i915_gpu_error.h"
+#include "i915_irq.h"
 #include "i915_memcpy.h"
 #include "i915_reg.h"
 
@@ -653,6 +655,9 @@ int intel_guc_capture_output_min_size_est(struct intel_guc *guc)
 	int worst_min_size = 0, num_regs = 0;
 	size_t tmp = 0;
 
+	if (!guc->capture.priv)
+		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
@@ -671,7 +676,7 @@ int intel_guc_capture_output_min_size_est(struct intel_guc *guc)
 
 	for_each_engine(engine, gt, id) {
 		worst_min_size += sizeof(struct guc_state_capture_group_header_t) +
-				  (3 * sizeof(struct guc_state_capture_header_t));
+					 (3 * sizeof(struct guc_state_capture_header_t));
 
 		if (!intel_guc_capture_getlistsize(guc, 0, GUC_CAPTURE_LIST_TYPE_GLOBAL, 0, &tmp))
 			num_regs += tmp;
@@ -691,6 +696,541 @@ int intel_guc_capture_output_min_size_est(struct intel_guc *guc)
 	return (worst_min_size * GUC_CAPTURE_OVERBUFFER_MULTIPLIER);
 }
 
+/*
+ * KMD Init time flows:
+ * --------------------
+ *     --> alloc A: GuC input capture regs lists (registered to GuC via ADS).
+ *                  intel_guc_ads acquires the register lists by calling
+ *                  intel_guc_capture_list_size and intel_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--> intel_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 dynamically allocated and populated with the error-capture
+ *                   data from GuC and then it's added into guc->capture->priv->outlist linked
+ *                   list. This list is used for matchup and printout by i915_gpu_coredump
+ *                   and err_print_gt, (when user invokes the error capture sysfs).
+ */
+
+static int guc_capture_buf_cnt(struct __guc_capture_bufstate *buf)
+{
+	if (buf->rd == buf->wr)
+		return 0;
+	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);
+}
+
+static int
+guc_capture_log_remove_dw(struct intel_guc *guc, struct __guc_capture_bufstate *buf,
+			  u32 *dw)
+{
+	struct drm_i915_private *i915 = guc_to_gt(guc)->i915;
+	int tries = 2;
+	int avail = 0;
+	u32 *src_data;
+
+	if (!guc_capture_buf_cnt(buf))
+		return 0;
+
+	while (tries--) {
+		avail = guc_capture_buf_cnt_to_end(buf);
+		if (avail >= sizeof(u32)) {
+			src_data = (u32 *)(buf->data + buf->rd);
+			*dw = *src_data;
+			buf->rd += 4;
+			return 4;
+		}
+		if (avail)
+			drm_dbg(&i915->drm, "GuC-Cap-Logs not dword aligned, skipping.\n");
+		buf->rd = 0;
+	}
+
+	return 0;
+}
+
+static bool
+guc_capture_data_extracted(struct __guc_capture_bufstate *b,
+			   int size, void *dest)
+{
+	if (guc_capture_buf_cnt_to_end(b) >= size) {
+		memcpy(dest, (b->data + b->rd), size);
+		b->rd += size;
+		return true;
+	}
+	return false;
+}
+
+static int
+guc_capture_log_get_group_hdr(struct intel_guc *guc, struct __guc_capture_bufstate *buf,
+			      struct guc_state_capture_group_header_t *ghdr)
+{
+	int read = 0;
+	int fullsize = sizeof(struct guc_state_capture_group_header_t);
+
+	if (fullsize > guc_capture_buf_cnt(buf))
+		return -1;
+
+	if (guc_capture_data_extracted(buf, fullsize, (void *)ghdr))
+		return 0;
+
+	read += guc_capture_log_remove_dw(guc, buf, &ghdr->owner);
+	read += guc_capture_log_remove_dw(guc, buf, &ghdr->info);
+	if (read != fullsize)
+		return -1;
+
+	return 0;
+}
+
+static int
+guc_capture_log_get_data_hdr(struct intel_guc *guc, struct __guc_capture_bufstate *buf,
+			     struct guc_state_capture_header_t *hdr)
+{
+	int read = 0;
+	int fullsize = sizeof(struct guc_state_capture_header_t);
+
+	if (fullsize > guc_capture_buf_cnt(buf))
+		return -1;
+
+	if (guc_capture_data_extracted(buf, fullsize, (void *)hdr))
+		return 0;
+
+	read += guc_capture_log_remove_dw(guc, buf, &hdr->owner);
+	read += guc_capture_log_remove_dw(guc, buf, &hdr->info);
+	read += guc_capture_log_remove_dw(guc, buf, &hdr->lrca);
+	read += guc_capture_log_remove_dw(guc, buf, &hdr->guc_id);
+	read += guc_capture_log_remove_dw(guc, buf, &hdr->num_mmios);
+	if (read != fullsize)
+		return -1;
+
+	return 0;
+}
+
+static int
+guc_capture_log_get_register(struct intel_guc *guc, struct __guc_capture_bufstate *buf,
+			     struct guc_mmio_reg *reg)
+{
+	int read = 0;
+	int fullsize = sizeof(struct guc_mmio_reg);
+
+	if (fullsize > guc_capture_buf_cnt(buf))
+		return -1;
+
+	if (guc_capture_data_extracted(buf, fullsize, (void *)reg))
+		return 0;
+
+	read += guc_capture_log_remove_dw(guc, buf, &reg->offset);
+	read += guc_capture_log_remove_dw(guc, buf, &reg->value);
+	read += guc_capture_log_remove_dw(guc, buf, &reg->flags);
+	read += guc_capture_log_remove_dw(guc, buf, &reg->mask);
+	if (read != fullsize)
+		return -1;
+
+	return 0;
+}
+
+static void
+guc_capture_delete_one_node(struct intel_guc *guc, struct __guc_capture_parsed_output *node)
+{
+	int i;
+
+	for (i = 0; i < GUC_CAPTURE_LIST_TYPE_MAX; ++i) {
+		if (node->reginfo[i].regs)
+			kfree(node->reginfo[i].regs);
+	}
+	list_del(&node->link);
+	kfree(node);
+}
+
+static void
+guc_capture_delete_nodes(struct intel_guc *guc)
+{
+	/*
+	 * NOTE: At the end of driver operation, we must assume that we
+	 * have nodes in outlist from unclaimed error capture events
+	 * that occurred prior to shutdown.
+	 */
+	if (!list_empty(&guc->capture.priv->outlist)) {
+		struct __guc_capture_parsed_output *n, *ntmp;
+
+		list_for_each_entry_safe(n, ntmp, &guc->capture.priv->outlist, link)
+			guc_capture_delete_one_node(guc, n);
+	}
+}
+
+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 __guc_state_capture_priv *gc,
+				struct __guc_capture_parsed_output *node)
+{
+	guc_capture_add_node_to_list(node, &gc->outlist);
+}
+
+static void
+guc_capture_init_node(struct intel_guc *guc, struct __guc_capture_parsed_output *node)
+{
+	INIT_LIST_HEAD(&node->link);
+}
+
+static struct __guc_capture_parsed_output *
+guc_capture_alloc_one_node(struct intel_guc *guc)
+{
+	struct __guc_capture_parsed_output *new;
+
+	new = kzalloc(sizeof(*new), GFP_KERNEL);
+	if (!new)
+		return NULL;
+
+	guc_capture_init_node(guc, new);
+
+	return new;
+}
+
+static struct __guc_capture_parsed_output *
+guc_capture_clone_node(struct intel_guc *guc, struct __guc_capture_parsed_output *ori,
+		       u32 keep_reglist_mask)
+{
+	struct __guc_capture_parsed_output *new;
+	int i;
+
+	new = guc_capture_alloc_one_node(guc);
+	if (!new)
+		return NULL;
+	if (!ori)
+		return new;
+
+	new->is_partial = ori->is_partial;
+
+	/* copy reg-lists that we want to clone */
+	for (i = 0; i < GUC_CAPTURE_LIST_TYPE_MAX; ++i) {
+		if (keep_reglist_mask & BIT(i)) {
+			new->reginfo[i].regs = kcalloc(ori->reginfo[i].num_regs,
+						       sizeof(struct guc_mmio_reg), GFP_KERNEL);
+			if (!new->reginfo[i].regs)
+				goto bail_clone;
+
+			memcpy(new->reginfo[i].regs, ori->reginfo[i].regs,
+			       ori->reginfo[i].num_regs * sizeof(struct guc_mmio_reg));
+			new->reginfo[i].num_regs = ori->reginfo[i].num_regs;
+			new->reginfo[i].vfid  = ori->reginfo[i].vfid;
+
+			if (i == GUC_CAPTURE_LIST_TYPE_ENGINE_CLASS) {
+				new->eng_class = ori->eng_class;
+			} else if (i == GUC_CAPTURE_LIST_TYPE_ENGINE_INSTANCE) {
+				new->eng_inst = ori->eng_inst;
+				new->guc_id = ori->guc_id;
+				new->lrca = ori->lrca;
+			}
+		}
+	}
+
+	return new;
+
+bail_clone:
+	for (i = 0; i < GUC_CAPTURE_LIST_TYPE_MAX; ++i) {
+		if (new->reginfo[i].regs)
+			kfree(new->reginfo[i].regs);
+	}
+	kfree(new);
+	return NULL;
+}
+
+static int
+guc_capture_extract_reglists(struct intel_guc *guc, struct __guc_capture_bufstate *buf)
+{
+	struct drm_i915_private *i915 = guc_to_gt(guc)->i915;
+	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_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)) {
+		drm_warn(&i915->drm, "GuC Capture new entries unaligned\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(CAP_GRP_HDR_CAPTURE_TYPE, ghdr.info);
+	numlists = FIELD_GET(CAP_GRP_HDR_NUM_CAPTURES, ghdr.info);
+
+	while (numlists--) {
+		if (guc_capture_log_get_data_hdr(guc, buf, &hdr)) {
+			ret = -EIO;
+			break;
+		}
+
+		datatype = FIELD_GET(CAP_HDR_CAPTURE_TYPE, hdr.info);
+		if (datatype > GUC_CAPTURE_LIST_TYPE_ENGINE_INSTANCE) {
+			/* unknown capture type - skip over to next capture set */
+			numregs = FIELD_GET(CAP_HDR_NUM_MMIOS, hdr.num_mmios);
+			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 i915_gpu_coredump 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_CAPTURE_LIST_TYPE_GLOBAL) {
+				guc_capture_add_node_to_outlist(guc->capture.priv, node);
+				node = NULL;
+			} else if (datatype == GUC_CAPTURE_LIST_TYPE_ENGINE_CLASS &&
+				   node->reginfo[GUC_CAPTURE_LIST_TYPE_ENGINE_CLASS].regs) {
+				/* Add to list, clone node and duplicate global list */
+				guc_capture_add_node_to_outlist(guc->capture.priv, node);
+				node = guc_capture_clone_node(guc, node,
+							      GCAP_PARSED_REGLIST_INDEX_GLOBAL);
+			} else if (datatype == GUC_CAPTURE_LIST_TYPE_ENGINE_INSTANCE &&
+				   node->reginfo[GUC_CAPTURE_LIST_TYPE_ENGINE_INSTANCE].regs) {
+				/* Add to list, clone node and duplicate global + class lists */
+				guc_capture_add_node_to_outlist(guc->capture.priv, node);
+				node = guc_capture_clone_node(guc, node,
+							      (GCAP_PARSED_REGLIST_INDEX_GLOBAL |
+							      GCAP_PARSED_REGLIST_INDEX_ENGCLASS));
+			}
+		}
+
+		if (!node) {
+			node = guc_capture_alloc_one_node(guc);
+			if (!node) {
+				ret = -ENOMEM;
+				break;
+			}
+			if (datatype != GUC_CAPTURE_LIST_TYPE_GLOBAL)
+				drm_dbg(&i915->drm, "GuC Capture missing global dump: %08x!\n",
+					datatype);
+		}
+		node->is_partial = is_partial;
+		node->reginfo[datatype].vfid = FIELD_GET(CAP_HDR_CAPTURE_VFID, hdr.owner);
+		switch (datatype) {
+		case GUC_CAPTURE_LIST_TYPE_ENGINE_INSTANCE:
+			node->eng_class = FIELD_GET(CAP_HDR_ENGINE_CLASS, hdr.info);
+			node->eng_inst = FIELD_GET(CAP_HDR_ENGINE_INSTANCE, hdr.info);
+			node->lrca = hdr.lrca;
+			node->guc_id = hdr.guc_id;
+			break;
+		case GUC_CAPTURE_LIST_TYPE_ENGINE_CLASS:
+			node->eng_class = FIELD_GET(CAP_HDR_ENGINE_CLASS, hdr.info);
+			break;
+		default:
+			break;
+		}
+
+		regs = NULL;
+		numregs = FIELD_GET(CAP_HDR_NUM_MMIOS, hdr.num_mmios);
+		if (numregs) {
+			regs = kcalloc(numregs, sizeof(struct guc_mmio_reg), GFP_KERNEL);
+			if (!regs) {
+				ret = -ENOMEM;
+				break;
+			}
+		}
+		node->reginfo[datatype].num_regs = numregs;
+		node->reginfo[datatype].regs = regs;
+		i = 0;
+		while (numregs--) {
+			if (guc_capture_log_get_register(guc, buf, &regs[i++])) {
+				ret = -EIO;
+				break;
+			}
+		}
+	}
+
+bailout:
+	if (node) {
+		/* If we have data, add to linked list for match-up when i915_gpu_coredump calls */
+		for (i = GUC_CAPTURE_LIST_TYPE_GLOBAL; i < GUC_CAPTURE_LIST_TYPE_MAX; ++i) {
+			if (node->reginfo[i].regs) {
+				guc_capture_add_node_to_outlist(guc->capture.priv, node);
+				node = NULL;
+				break;
+			}
+		}
+		if (node) /* else free it */
+			kfree(node);
+	}
+	return ret;
+}
+
+static int __guc_capture_flushlog_complete(struct intel_guc *guc)
+{
+	u32 action[] = {
+		INTEL_GUC_ACTION_LOG_BUFFER_FILE_FLUSH_COMPLETE,
+		2
+	};
+
+	return intel_guc_send(guc, action, ARRAY_SIZE(action));
+}
+
+static void __guc_capture_process_output(struct intel_guc *guc)
+{
+	unsigned int buffer_size, read_offset, write_offset, full_count;
+	struct intel_uc *uc = container_of(guc, typeof(*uc), guc);
+	struct drm_i915_private *i915 = guc_to_gt(guc)->i915;
+	struct guc_log_buffer_state log_buf_state_local;
+	struct guc_log_buffer_state *log_buf_state;
+	struct __guc_capture_bufstate buf;
+	void *src_data = NULL;
+	bool new_overflow;
+	int ret;
+
+	log_buf_state = guc->log.buf_addr +
+			(sizeof(struct guc_log_buffer_state) * GUC_CAPTURE_LOG_BUFFER);
+	src_data = guc->log.buf_addr + intel_guc_get_log_buffer_offset(GUC_CAPTURE_LOG_BUFFER);
+
+	/*
+	 * 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.
+	 */
+	memcpy(&log_buf_state_local, log_buf_state, sizeof(struct guc_log_buffer_state));
+	buffer_size = intel_guc_get_log_buffer_size(GUC_CAPTURE_LOG_BUFFER);
+	read_offset = log_buf_state_local.read_ptr;
+	write_offset = log_buf_state_local.sampled_write_ptr;
+	full_count = log_buf_state_local.buffer_full_cnt;
+
+	/* Bookkeeping stuff */
+	guc->log.stats[GUC_CAPTURE_LOG_BUFFER].flush += log_buf_state_local.flush_to_file;
+	new_overflow = intel_guc_check_log_buf_overflow(&guc->log, GUC_CAPTURE_LOG_BUFFER,
+							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))) {
+		drm_err(&i915->drm, "invalid GuC log capture buffer state!\n");
+		/* 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 = src_data;
+
+	if (!uc->reset_in_progress) {
+		do {
+			ret = guc_capture_extract_reglists(guc, &buf);
+		} while (ret >= 0);
+	}
+
+	/* Update the state of log buffer err-cap state */
+	log_buf_state->read_ptr = write_offset;
+	log_buf_state->flush_to_file = 0;
+	__guc_capture_flushlog_complete(guc);
+}
+
+void intel_guc_capture_process(struct intel_guc *guc)
+{
+	if (guc->capture.priv)
+		__guc_capture_process_output(guc);
+}
+
 static void
 guc_capture_free_ads_cache(struct __guc_state_capture_priv *gc)
 {
@@ -715,6 +1255,8 @@ void intel_guc_capture_destroy(struct intel_guc *guc)
 
 	guc_capture_free_ads_cache(guc->capture.priv);
 
+	guc_capture_delete_nodes(guc);
+
 	if (guc->capture.priv->extlists) {
 		guc_capture_free_extlists(guc->capture.priv->extlists);
 		kfree(guc->capture.priv->extlists);
@@ -732,5 +1274,7 @@ int intel_guc_capture_init(struct intel_guc *guc)
 
 	guc->capture.priv->reglists = guc_capture_get_device_reglist(guc);
 
+	INIT_LIST_HEAD(&guc->capture.priv->outlist);
+
 	return 0;
 }
diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.h b/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.h
index 24a11f33f7d9..3c79460c7ca5 100644
--- a/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.h
+++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.h
@@ -12,6 +12,7 @@ struct file;
 struct guc_gt_system_info;
 struct intel_guc;
 
+void intel_guc_capture_process(struct intel_guc *guc);
 int intel_guc_capture_output_min_size_est(struct intel_guc *guc);
 int intel_guc_capture_getlist(struct intel_guc *guc, u32 owner, u32 type, u32 classid,
 			      struct file **fileptr);
diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_log.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_log.c
index e9a865c2f4cb..8d59a11ec595 100644
--- a/drivers/gpu/drm/i915/gt/uc/intel_guc_log.c
+++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_log.c
@@ -158,9 +158,9 @@ static void *guc_get_write_buffer(struct intel_guc_log *log)
 	return relay_reserve(log->relay.channel, 0);
 }
 
-static bool guc_check_log_buf_overflow(struct intel_guc_log *log,
-				       enum guc_log_buffer_type type,
-				       unsigned int full_cnt)
+bool intel_guc_check_log_buf_overflow(struct intel_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;
@@ -183,7 +183,7 @@ static bool guc_check_log_buf_overflow(struct intel_guc_log *log,
 	return overflow;
 }
 
-static unsigned int guc_get_log_buffer_size(enum guc_log_buffer_type type)
+unsigned int intel_guc_get_log_buffer_size(enum guc_log_buffer_type type)
 {
 	switch (type) {
 	case GUC_DEBUG_LOG_BUFFER:
@@ -199,6 +199,20 @@ static unsigned int guc_get_log_buffer_size(enum guc_log_buffer_type type)
 	return 0;
 }
 
+size_t intel_guc_get_log_buffer_offset(enum guc_log_buffer_type type)
+{
+	enum guc_log_buffer_type i;
+	size_t offset = PAGE_SIZE;/* for the log_buffer_states */
+
+	for (i = GUC_DEBUG_LOG_BUFFER; i < GUC_MAX_LOG_BUFFER; ++i) {
+		if (i == type)
+			break;
+		offset += intel_guc_get_log_buffer_size(i);
+	}
+
+	return offset;
+}
+
 static void _guc_log_copy_debuglogs_for_relay(struct intel_guc_log *log)
 {
 	unsigned int buffer_size, read_offset, write_offset, bytes_to_copy, full_cnt;
@@ -243,14 +257,14 @@ static void _guc_log_copy_debuglogs_for_relay(struct intel_guc_log *log)
 		 */
 		memcpy(&log_buf_state_local, log_buf_state,
 		       sizeof(struct guc_log_buffer_state));
-		buffer_size = guc_get_log_buffer_size(type);
+		buffer_size = intel_guc_get_log_buffer_size(type);
 		read_offset = log_buf_state_local.read_ptr;
 		write_offset = log_buf_state_local.sampled_write_ptr;
 		full_cnt = log_buf_state_local.buffer_full_cnt;
 
 		/* Bookkeeping stuff */
 		log->stats[type].flush += log_buf_state_local.flush_to_file;
-		new_overflow = guc_check_log_buf_overflow(log, type, full_cnt);
+		new_overflow = intel_guc_check_log_buf_overflow(log, type, full_cnt);
 
 		/* Update the state of shared log buffer */
 		log_buf_state->read_ptr = write_offset;
diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_log.h b/drivers/gpu/drm/i915/gt/uc/intel_guc_log.h
index e1345fca7729..18007e639be9 100644
--- a/drivers/gpu/drm/i915/gt/uc/intel_guc_log.h
+++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_log.h
@@ -67,6 +67,10 @@ struct intel_guc_log {
 };
 
 void intel_guc_log_init_early(struct intel_guc_log *log);
+bool intel_guc_check_log_buf_overflow(struct intel_guc_log *log, enum guc_log_buffer_type type,
+				      unsigned int full_cnt);
+unsigned int intel_guc_get_log_buffer_size(enum guc_log_buffer_type type);
+size_t intel_guc_get_log_buffer_offset(enum guc_log_buffer_type type);
 int intel_guc_log_create(struct intel_guc_log *log);
 void intel_guc_log_destroy(struct intel_guc_log *log);
 
diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c
index ab3cea352fb3..870b48456e9c 100644
--- a/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c
+++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c
@@ -25,6 +25,7 @@
 #include "gt/intel_ring.h"
 
 #include "intel_guc_ads.h"
+#include "intel_guc_capture.h"
 #include "intel_guc_submission.h"
 
 #include "i915_drv.h"
@@ -4070,17 +4071,18 @@ int intel_guc_context_reset_process_msg(struct intel_guc *guc,
 int intel_guc_error_capture_process_msg(struct intel_guc *guc,
 					const u32 *msg, u32 len)
 {
-	int status;
+	u32 status;
 
 	if (unlikely(len != 1)) {
 		drm_dbg(&guc_to_gt(guc)->i915->drm, "Invalid length %u", len);
 		return -EPROTO;
 	}
 
-	status = msg[0];
-	drm_info(&guc_to_gt(guc)->i915->drm, "Got error capture: status = %d", status);
+	status = msg[0] & INTEL_GUC_STATE_CAPTURE_EVENT_STATUS_MASK;
+	if (status == INTEL_GUC_STATE_CAPTURE_EVENT_STATUS_NOSPACE)
+		drm_warn(&guc_to_gt(guc)->i915->drm, "G2H-Error capture no space");
 
-	/* FIXME: Do something with the capture */
+	intel_guc_capture_process(guc);
 
 	return 0;
 }
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [Intel-gfx] [PATCH v7 11/13] drm/i915/guc: Pre-allocate output nodes for extraction
  2022-02-26  9:55 ` [Intel-gfx] " Alan Previn
                   ` (10 preceding siblings ...)
  (?)
@ 2022-02-26  9:55 ` Alan Previn
  2022-03-11 19:40   ` Umesh Nerlige Ramappa
  -1 siblings, 1 reply; 32+ messages in thread
From: Alan Previn @ 2022-02-26  9:55 UTC (permalink / raw)
  To: intel-gfx; +Cc: Alan Previn

In the rare but possible scenario where we are in the midst of
multiple GuC error-capture (and engine reset) events and the
user also triggers a forced full GT reset or the internal watchdog
triggers the same, intel_guc_submission_reset_prepare's call
to flush_work(&guc->ct.requests.worker) can cause the G2H message
handler to trigger intel_guc_capture_store_snapshot upon
receiving new G2H error-capture notifications. This can happen
despite the prior call to disable_submission(guc);. However,
there's no race-free way for intel_guc_capture_store_snapshot to
know that we are in the midst of a reset. That said, we can never
dynamically allocate the output nodes in this handler. Thus, we
shall 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: Alan Previn <alan.previn.teres.alexis@intel.com>
---
 drivers/gpu/drm/i915/gt/uc/guc_capture_fwif.h |  19 +-
 .../gpu/drm/i915/gt/uc/intel_guc_capture.c    | 178 ++++++++++++++----
 2 files changed, 160 insertions(+), 37 deletions(-)

diff --git a/drivers/gpu/drm/i915/gt/uc/guc_capture_fwif.h b/drivers/gpu/drm/i915/gt/uc/guc_capture_fwif.h
index 2b09aa05d8b7..a77a6274e0b0 100644
--- a/drivers/gpu/drm/i915/gt/uc/guc_capture_fwif.h
+++ b/drivers/gpu/drm/i915/gt/uc/guc_capture_fwif.h
@@ -31,7 +31,7 @@ struct __guc_capture_bufstate {
  *
  * 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 outlist below.
+ * See cachelist and outlist below.
  */
 struct __guc_capture_parsed_output {
 	/*
@@ -188,7 +188,22 @@ struct __guc_state_capture_priv {
 						[GUC_MAX_ENGINE_CLASSES];
 
 	/**
-	 * @outlist: allocated nodes with parsed engine-instance error capture data
+	 * @cachelist: Pool of pre-allocated nodes for error capture output
+	 *
+	 * We need this pool of pre-allocated nodes because we cannot
+	 * dynamically allocate new nodes when receiving the G2H notification
+	 * because the event handlers for all G2H event-processing is called
+	 * by the ct processing worker queue and when that queue is being
+	 * processed, there is no absoluate guarantee that we are not in the
+	 * midst of a GT reset operation (which doesn't allow allocations).
+	 */
+	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;
+
+	/**
+	 * @outlist: Pool of pre-allocated nodes for error capture output
 	 *
 	 * A linked list of parsed GuC error-capture output data before
 	 * reporting with formatting via i915_gpu_coredump. Each node in this linked list shall
diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c
index 9308157d9a74..7bd297515504 100644
--- a/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c
+++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c
@@ -583,6 +583,8 @@ intel_guc_capture_getlistsize(struct intel_guc *guc, u32 owner, u32 type, u32 cl
 	return 0;
 }
 
+static void guc_capture_create_prealloc_nodes(struct intel_guc *guc);
+
 int
 intel_guc_capture_getlist(struct intel_guc *guc, u32 owner, u32 type, u32 classid,
 			  struct file **fileoutptr)
@@ -604,6 +606,12 @@ intel_guc_capture_getlist(struct intel_guc *guc, u32 owner, u32 type, u32 classi
 		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 = intel_guc_capture_getlistsize(guc, owner, type, classid, &size);
 	if (ret) {
 		cache->is_valid = true;
@@ -721,7 +729,8 @@ int intel_guc_capture_output_min_size_est(struct intel_guc *guc)
  *                                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 dynamically allocated and populated with the error-capture
+ *                   This node is created from a pre-allocated list of blank nodes in
+ *                   guc->capture->priv->cachelist and populated with the error-capture
  *                   data from GuC and then it's added into guc->capture->priv->outlist linked
  *                   list. This list is used for matchup and printout by i915_gpu_coredump
  *                   and err_print_gt, (when user invokes the error capture sysfs).
@@ -865,19 +874,20 @@ guc_capture_delete_one_node(struct intel_guc *guc, struct __guc_capture_parsed_o
 }
 
 static void
-guc_capture_delete_nodes(struct intel_guc *guc)
+guc_capture_delete_prealloc_nodes(struct intel_guc *guc)
 {
+	struct __guc_capture_parsed_output *n, *ntmp;
+
 	/*
 	 * NOTE: At the end of driver operation, we must assume that we
-	 * have nodes in outlist from unclaimed error capture events
-	 * that occurred prior to shutdown.
+	 * have prealloc nodes in both the cachelist as well as outlist
+	 * if unclaimed error capture events occurred prior to shutdown.
 	 */
-	if (!list_empty(&guc->capture.priv->outlist)) {
-		struct __guc_capture_parsed_output *n, *ntmp;
+	list_for_each_entry_safe(n, ntmp, &guc->capture.priv->outlist, link)
+		guc_capture_delete_one_node(guc, n);
 
-		list_for_each_entry_safe(n, ntmp, &guc->capture.priv->outlist, link)
-			guc_capture_delete_one_node(guc, n);
-	}
+	list_for_each_entry_safe(n, ntmp, &guc->capture.priv->cachelist, link)
+		guc_capture_delete_one_node(guc, n);
 }
 
 static void
@@ -894,21 +904,80 @@ guc_capture_add_node_to_outlist(struct __guc_state_capture_priv *gc,
 	guc_capture_add_node_to_list(node, &gc->outlist);
 }
 
+static void
+guc_capture_add_node_to_cachelist(struct __guc_state_capture_priv *gc,
+				  struct __guc_capture_parsed_output *node)
+{
+	guc_capture_add_node_to_list(node, &gc->cachelist);
+}
+
 static void
 guc_capture_init_node(struct intel_guc *guc, struct __guc_capture_parsed_output *node)
 {
+	struct guc_mmio_reg *tmp[GUC_CAPTURE_LIST_TYPE_MAX];
+	int i;
+
+	for (i = 0; i < GUC_CAPTURE_LIST_TYPE_MAX; ++i) {
+		tmp[i] = node->reginfo[i].regs;
+		memset(tmp[i], 0, sizeof(struct guc_mmio_reg) *
+		       guc->capture.priv->max_mmio_per_node);
+	}
+	memset(node, 0, sizeof(*node));
+	for (i = 0; i < GUC_CAPTURE_LIST_TYPE_MAX; ++i)
+		node->reginfo[i].regs = tmp[i];
+
 	INIT_LIST_HEAD(&node->link);
 }
 
+static struct __guc_capture_parsed_output *
+guc_capture_get_prealloc_node(struct intel_guc *guc)
+{
+	struct __guc_capture_parsed_output *found = NULL;
+
+	if (!list_empty(&guc->capture.priv->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.priv->cachelist, link) {
+			found = n;
+			list_del(&n->link);
+			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.priv->outlist, link) {
+			found = n;
+		}
+		if (found)
+			list_del(&found->link);
+	}
+	if (found)
+		guc_capture_init_node(guc, found);
+
+	return found;
+}
+
 static struct __guc_capture_parsed_output *
 guc_capture_alloc_one_node(struct intel_guc *guc)
 {
 	struct __guc_capture_parsed_output *new;
+	int i;
 
 	new = kzalloc(sizeof(*new), GFP_KERNEL);
 	if (!new)
 		return NULL;
 
+	for (i = 0; i < GUC_CAPTURE_LIST_TYPE_MAX; ++i) {
+		new->reginfo[i].regs = kcalloc(guc->capture.priv->max_mmio_per_node,
+					       sizeof(struct guc_mmio_reg), GFP_KERNEL);
+		if (!new->reginfo[i].regs) {
+			while (i)
+				kfree(new->reginfo[--i].regs);
+			return NULL;
+		}
+	}
 	guc_capture_init_node(guc, new);
 
 	return new;
@@ -921,7 +990,7 @@ guc_capture_clone_node(struct intel_guc *guc, struct __guc_capture_parsed_output
 	struct __guc_capture_parsed_output *new;
 	int i;
 
-	new = guc_capture_alloc_one_node(guc);
+	new = guc_capture_get_prealloc_node(guc);
 	if (!new)
 		return NULL;
 	if (!ori)
@@ -932,16 +1001,14 @@ guc_capture_clone_node(struct intel_guc *guc, struct __guc_capture_parsed_output
 	/* copy reg-lists that we want to clone */
 	for (i = 0; i < GUC_CAPTURE_LIST_TYPE_MAX; ++i) {
 		if (keep_reglist_mask & BIT(i)) {
-			new->reginfo[i].regs = kcalloc(ori->reginfo[i].num_regs,
-						       sizeof(struct guc_mmio_reg), GFP_KERNEL);
-			if (!new->reginfo[i].regs)
-				goto bail_clone;
+			GEM_BUG_ON(ori->reginfo[i].num_regs  >
+				   guc->capture.priv->max_mmio_per_node);
 
 			memcpy(new->reginfo[i].regs, ori->reginfo[i].regs,
 			       ori->reginfo[i].num_regs * sizeof(struct guc_mmio_reg));
+
 			new->reginfo[i].num_regs = ori->reginfo[i].num_regs;
 			new->reginfo[i].vfid  = ori->reginfo[i].vfid;
-
 			if (i == GUC_CAPTURE_LIST_TYPE_ENGINE_CLASS) {
 				new->eng_class = ori->eng_class;
 			} else if (i == GUC_CAPTURE_LIST_TYPE_ENGINE_INSTANCE) {
@@ -953,14 +1020,58 @@ guc_capture_clone_node(struct intel_guc *guc, struct __guc_capture_parsed_output
 	}
 
 	return new;
+}
 
-bail_clone:
-	for (i = 0; i < GUC_CAPTURE_LIST_TYPE_MAX; ++i) {
-		if (new->reginfo[i].regs)
-			kfree(new->reginfo[i].regs);
+static void
+__guc_capture_create_prealloc_nodes(struct intel_guc *guc)
+{
+	struct __guc_capture_parsed_output *node = NULL;
+	struct drm_i915_private *i915 = guc_to_gt(guc)->i915;
+	int i;
+
+	for (i = 0; i < PREALLOC_NODES_MAX_COUNT; ++i) {
+		node = guc_capture_alloc_one_node(guc);
+		if (!node) {
+			drm_warn(&i915->drm, "GuC 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.priv, node);
 	}
-	kfree(new);
-	return NULL;
+}
+
+static int
+guc_get_max_reglist_count(struct intel_guc *guc)
+{
+	int i, j, k, tmp, maxregcount = 0;
+
+	for (i = 0; i < GUC_CAPTURE_LIST_INDEX_MAX; ++i) {
+		for (j = 0; j < GUC_CAPTURE_LIST_TYPE_MAX; ++j) {
+			for (k = 0; k < GUC_MAX_ENGINE_CLASSES; ++k) {
+				if (j == GUC_CAPTURE_LIST_TYPE_GLOBAL && k > 0)
+					continue;
+
+				tmp = guc_cap_list_num_regs(guc->capture.priv, 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 intel_guc *guc)
+{
+	/* skip if we've already done the pre-alloc */
+	if (guc->capture.priv->max_mmio_per_node)
+		return;
+
+	guc->capture.priv->max_mmio_per_node = guc_get_max_reglist_count(guc);
+	__guc_capture_create_prealloc_nodes(guc);
 }
 
 static int
@@ -1076,13 +1187,13 @@ guc_capture_extract_reglists(struct intel_guc *guc, struct __guc_capture_bufstat
 				guc_capture_add_node_to_outlist(guc->capture.priv, node);
 				node = NULL;
 			} else if (datatype == GUC_CAPTURE_LIST_TYPE_ENGINE_CLASS &&
-				   node->reginfo[GUC_CAPTURE_LIST_TYPE_ENGINE_CLASS].regs) {
+				   node->reginfo[GUC_CAPTURE_LIST_TYPE_ENGINE_CLASS].num_regs) {
 				/* Add to list, clone node and duplicate global list */
 				guc_capture_add_node_to_outlist(guc->capture.priv, node);
 				node = guc_capture_clone_node(guc, node,
 							      GCAP_PARSED_REGLIST_INDEX_GLOBAL);
 			} else if (datatype == GUC_CAPTURE_LIST_TYPE_ENGINE_INSTANCE &&
-				   node->reginfo[GUC_CAPTURE_LIST_TYPE_ENGINE_INSTANCE].regs) {
+				   node->reginfo[GUC_CAPTURE_LIST_TYPE_ENGINE_INSTANCE].num_regs) {
 				/* Add to list, clone node and duplicate global + class lists */
 				guc_capture_add_node_to_outlist(guc->capture.priv, node);
 				node = guc_capture_clone_node(guc, node,
@@ -1092,7 +1203,7 @@ guc_capture_extract_reglists(struct intel_guc *guc, struct __guc_capture_bufstat
 		}
 
 		if (!node) {
-			node = guc_capture_alloc_one_node(guc);
+			node = guc_capture_get_prealloc_node(guc);
 			if (!node) {
 				ret = -ENOMEM;
 				break;
@@ -1117,17 +1228,13 @@ guc_capture_extract_reglists(struct intel_guc *guc, struct __guc_capture_bufstat
 			break;
 		}
 
-		regs = NULL;
 		numregs = FIELD_GET(CAP_HDR_NUM_MMIOS, hdr.num_mmios);
-		if (numregs) {
-			regs = kcalloc(numregs, sizeof(struct guc_mmio_reg), GFP_KERNEL);
-			if (!regs) {
-				ret = -ENOMEM;
-				break;
-			}
+		if (numregs > guc->capture.priv->max_mmio_per_node) {
+			drm_dbg(&i915->drm, "GuC Capture list extraction clipped by prealloc!\n");
+			numregs = guc->capture.priv->max_mmio_per_node;
 		}
 		node->reginfo[datatype].num_regs = numregs;
-		node->reginfo[datatype].regs = regs;
+		regs = node->reginfo[datatype].regs;
 		i = 0;
 		while (numregs--) {
 			if (guc_capture_log_get_register(guc, buf, &regs[i++])) {
@@ -1147,8 +1254,8 @@ guc_capture_extract_reglists(struct intel_guc *guc, struct __guc_capture_bufstat
 				break;
 			}
 		}
-		if (node) /* else free it */
-			kfree(node);
+		if (node) /* else return it back to cache list */
+			guc_capture_add_node_to_cachelist(guc->capture.priv, node);
 	}
 	return ret;
 }
@@ -1255,7 +1362,7 @@ void intel_guc_capture_destroy(struct intel_guc *guc)
 
 	guc_capture_free_ads_cache(guc->capture.priv);
 
-	guc_capture_delete_nodes(guc);
+	guc_capture_delete_prealloc_nodes(guc);
 
 	if (guc->capture.priv->extlists) {
 		guc_capture_free_extlists(guc->capture.priv->extlists);
@@ -1275,6 +1382,7 @@ int intel_guc_capture_init(struct intel_guc *guc)
 	guc->capture.priv->reglists = guc_capture_get_device_reglist(guc);
 
 	INIT_LIST_HEAD(&guc->capture.priv->outlist);
+	INIT_LIST_HEAD(&guc->capture.priv->cachelist);
 
 	return 0;
 }
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [Intel-gfx] [PATCH v7 12/13] drm/i915/guc: Plumb GuC-capture into gpu_coredump
  2022-02-26  9:55 ` [Intel-gfx] " Alan Previn
                   ` (11 preceding siblings ...)
  (?)
@ 2022-02-26  9:55 ` Alan Previn
  -1 siblings, 0 replies; 32+ messages in thread
From: Alan Previn @ 2022-02-26  9:55 UTC (permalink / raw)
  To: intel-gfx; +Cc: Alan Previn

Add a flags parameter through all of the coredump creation
functions. Add a bitmask flag to indicate if the top
level gpu_coredump event is triggered in response to
a GuC context reset notification.

Using that flag, ensure all coredump functions that
read or print mmio-register values related to work submission
or command-streamer engines are skipped and replaced with
a calls guc-capture module equivalent functions to retrieve
or print the register dump.

While here, split out display related register reading
and printing into its own function that is called agnostic
to whether GuC had triggered the reset.

For now, introduce an empty printing function that can
filled in on a subsequent patch just to handle formatting.

Signed-off-by: Alan Previn <alan.previn.teres.alexis@intel.com>
Reviewed-by: Umesh Nerlige Ramappa <umesh.nerlige.ramappa@intel.com>
---
 .../drm/i915/gt/intel_execlists_submission.c  |   4 +-
 drivers/gpu/drm/i915/gt/intel_reset.c         |   2 +-
 .../gpu/drm/i915/gt/uc/intel_guc_capture.c    |  69 +++++
 .../gpu/drm/i915/gt/uc/intel_guc_capture.h    |  10 +
 .../gpu/drm/i915/gt/uc/intel_guc_submission.c |   2 +-
 drivers/gpu/drm/i915/i915_debugfs.c           |   2 +-
 drivers/gpu/drm/i915/i915_gpu_error.c         | 266 ++++++++++++------
 drivers/gpu/drm/i915/i915_gpu_error.h         |  30 +-
 8 files changed, 288 insertions(+), 97 deletions(-)

diff --git a/drivers/gpu/drm/i915/gt/intel_execlists_submission.c b/drivers/gpu/drm/i915/gt/intel_execlists_submission.c
index 961d795220a3..fc7c27df5d44 100644
--- a/drivers/gpu/drm/i915/gt/intel_execlists_submission.c
+++ b/drivers/gpu/drm/i915/gt/intel_execlists_submission.c
@@ -2231,11 +2231,11 @@ static struct execlists_capture *capture_regs(struct intel_engine_cs *engine)
 	if (!cap->error)
 		goto err_cap;
 
-	cap->error->gt = intel_gt_coredump_alloc(engine->gt, gfp);
+	cap->error->gt = intel_gt_coredump_alloc(engine->gt, gfp, CORE_DUMP_FLAG_NONE);
 	if (!cap->error->gt)
 		goto err_gpu;
 
-	cap->error->gt->engine = intel_engine_coredump_alloc(engine, gfp);
+	cap->error->gt->engine = intel_engine_coredump_alloc(engine, gfp, CORE_DUMP_FLAG_NONE);
 	if (!cap->error->gt->engine)
 		goto err_gt;
 
diff --git a/drivers/gpu/drm/i915/gt/intel_reset.c b/drivers/gpu/drm/i915/gt/intel_reset.c
index 82713264b96c..2d120bd391a2 100644
--- a/drivers/gpu/drm/i915/gt/intel_reset.c
+++ b/drivers/gpu/drm/i915/gt/intel_reset.c
@@ -1318,7 +1318,7 @@ void intel_gt_handle_error(struct intel_gt *gt,
 	engine_mask &= gt->info.engine_mask;
 
 	if (flags & I915_ERROR_CAPTURE) {
-		i915_capture_error_state(gt, engine_mask);
+		i915_capture_error_state(gt, engine_mask, CORE_DUMP_FLAG_NONE);
 		intel_gt_clear_error_registers(gt, engine_mask);
 	}
 
diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c
index 7bd297515504..621c0b4537a9 100644
--- a/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c
+++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c
@@ -734,6 +734,18 @@ int intel_guc_capture_output_min_size_est(struct intel_guc *guc)
  *                   data from GuC and then it's added into guc->capture->priv->outlist linked
  *                   list. This list is used for matchup and printout by i915_gpu_coredump
  *                   and err_print_gt, (when user invokes the error capture sysfs).
+ *
+ * GUC --> notify context reset:
+ * -----------------------------
+ *     --> G2H CONTEXT RESET
+ *                   L--> guc_handle_context_reset --> i915_capture_error_state
+ *                          L--> i915_gpu_coredump(..IS_GUC_CAPTURE) --> gt_record_engines
+ *                               --> capture_engine(..IS_GUC_CAPTURE)
+ *                               L--> intel_guc_capture_get_matching_node is where
+ *                                    detach C from internal linked list and add it into
+ *                                    intel_engine_coredump struct (if the context and
+ *                                    engine of the event notification matches a node
+ *                                    in the link list).
  */
 
 static int guc_capture_buf_cnt(struct __guc_capture_bufstate *buf)
@@ -1332,6 +1344,63 @@ static void __guc_capture_process_output(struct intel_guc *guc)
 	__guc_capture_flushlog_complete(guc);
 }
 
+#if IS_ENABLED(CONFIG_DRM_I915_CAPTURE_ERROR)
+
+int intel_guc_capture_print_engine_node(struct drm_i915_error_state_buf *ebuf,
+					const struct intel_engine_coredump *ee)
+{
+	return 0;
+}
+
+#endif //CONFIG_DRM_I915_CAPTURE_ERROR
+
+void intel_guc_capture_free_node(struct intel_engine_coredump *ee)
+{
+	if (!ee || !ee->guc_capture_node)
+		return;
+
+	guc_capture_add_node_to_cachelist(ee->capture->priv, ee->guc_capture_node);
+	ee->capture = NULL;
+	ee->guc_capture_node = NULL;
+}
+
+void intel_guc_capture_get_matching_node(struct intel_gt *gt,
+					 struct intel_engine_coredump *ee,
+					 struct intel_context *ce)
+{
+	struct __guc_capture_parsed_output *n, *ntmp;
+	struct drm_i915_private *i915;
+	struct intel_guc *guc;
+
+	if (!gt || !ee || !ce)
+		return;
+
+	i915 = gt->i915;
+	guc = &gt->uc.guc;
+	if (!guc->capture.priv)
+		return;
+
+	GEM_BUG_ON(ee->guc_capture_node);
+	/*
+	 * Look for a matching GuC reported error capture node from
+	 * the internal output link-list based on lrca, guc-id and engine
+	 * identification.
+	 */
+	list_for_each_entry_safe(n, ntmp, &guc->capture.priv->outlist, link) {
+		if (n->eng_inst == GUC_ID_TO_ENGINE_INSTANCE(ee->engine->guc_id) &&
+			n->eng_class == GUC_ID_TO_ENGINE_CLASS(ee->engine->guc_id) &&
+			n->guc_id && n->guc_id == ce->guc_id.id &&
+			(n->lrca & CTX_GTT_ADDRESS_MASK) && (n->lrca & CTX_GTT_ADDRESS_MASK) ==
+			(ce->lrc.lrca & CTX_GTT_ADDRESS_MASK)) {
+			list_del(&n->link);
+			ee->guc_capture_node = n;
+			ee->capture = &guc->capture;
+			return;
+		}
+	}
+	drm_dbg(&i915->drm, "GuC capture can't match ee to node\n");
+}
+
 void intel_guc_capture_process(struct intel_guc *guc)
 {
 	if (guc->capture.priv)
diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.h b/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.h
index 3c79460c7ca5..ae527c98e2f6 100644
--- a/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.h
+++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.h
@@ -8,10 +8,20 @@
 
 #include <linux/types.h>
 
+struct drm_i915_error_state_buf;
 struct file;
+struct guc_ads;
 struct guc_gt_system_info;
+struct intel_context;
+struct intel_engine_coredump;
+struct intel_gt;
 struct intel_guc;
 
+void intel_guc_capture_free_node(struct intel_engine_coredump *ee);
+int intel_guc_capture_print_engine_node(struct drm_i915_error_state_buf *m,
+					const struct intel_engine_coredump *ee);
+void intel_guc_capture_get_matching_node(struct intel_gt *gt, struct intel_engine_coredump *ee,
+					 struct intel_context *ce);
 void intel_guc_capture_process(struct intel_guc *guc);
 int intel_guc_capture_output_min_size_est(struct intel_guc *guc);
 int intel_guc_capture_getlist(struct intel_guc *guc, u32 owner, u32 type, u32 classid,
diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c
index 870b48456e9c..5d41dc95f066 100644
--- a/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c
+++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c
@@ -4005,7 +4005,7 @@ static void capture_error_state(struct intel_guc *guc,
 
 	intel_engine_set_hung_context(engine, ce);
 	with_intel_runtime_pm(&i915->runtime_pm, wakeref)
-		i915_capture_error_state(gt, engine->mask);
+		i915_capture_error_state(gt, engine->mask, CORE_DUMP_FLAG_IS_GUC_CAPTURE);
 	atomic_inc(&i915->gpu_error.reset_engine_count[engine->uabi_class]);
 }
 
diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
index 946bbe57bfe5..274abf267f35 100644
--- a/drivers/gpu/drm/i915/i915_debugfs.c
+++ b/drivers/gpu/drm/i915/i915_debugfs.c
@@ -307,7 +307,7 @@ static int i915_gpu_info_open(struct inode *inode, struct file *file)
 
 	gpu = NULL;
 	with_intel_runtime_pm(&i915->runtime_pm, wakeref)
-		gpu = i915_gpu_coredump(to_gt(i915), ALL_ENGINES);
+		gpu = i915_gpu_coredump(to_gt(i915), ALL_ENGINES, CORE_DUMP_FLAG_NONE);
 	if (IS_ERR(gpu))
 		return PTR_ERR(gpu);
 
diff --git a/drivers/gpu/drm/i915/i915_gpu_error.c b/drivers/gpu/drm/i915/i915_gpu_error.c
index 1d042551619e..3d566bbe228d 100644
--- a/drivers/gpu/drm/i915/i915_gpu_error.c
+++ b/drivers/gpu/drm/i915/i915_gpu_error.c
@@ -46,6 +46,7 @@
 #include "gt/intel_gt.h"
 #include "gt/intel_gt_pm.h"
 #include "gt/intel_gt_regs.h"
+#include "gt/uc/intel_guc_capture.h"
 
 #include "i915_driver.h"
 #include "i915_drv.h"
@@ -593,15 +594,11 @@ static void error_print_engine(struct drm_i915_error_state_buf *m,
 				   ee->vm_info.pp_dir_base);
 		}
 	}
-	err_printf(m, "  hung: %u\n", ee->hung);
-	err_printf(m, "  engine reset count: %u\n", ee->reset_count);
 
 	for (n = 0; n < ee->num_ports; n++) {
 		err_printf(m, "  ELSP[%d]:", n);
 		error_print_request(m, " ", &ee->execlist[n]);
 	}
-
-	error_print_context(m, "  Active context: ", &ee->context);
 }
 
 void i915_error_printf(struct drm_i915_error_state_buf *e, const char *f, ...)
@@ -713,23 +710,30 @@ static void err_print_gt_info(struct drm_i915_error_state_buf *m,
 	intel_sseu_print_topology(&gt->info.sseu, &p);
 }
 
-static void err_print_gt(struct drm_i915_error_state_buf *m,
-			 struct intel_gt_coredump *gt)
+static void err_print_gt_display(struct drm_i915_error_state_buf *m,
+				 struct intel_gt_coredump *gt)
+{
+	err_printf(m, "IER: 0x%08x\n", gt->ier);
+	err_printf(m, "DERRMR: 0x%08x\n", gt->derrmr);
+}
+
+static void err_print_gt_global_nonguc(struct drm_i915_error_state_buf *m,
+				       struct intel_gt_coredump *gt)
 {
-	const struct intel_engine_coredump *ee;
 	int i;
 
 	err_printf(m, "GT awake: %s\n", yesno(gt->awake));
 	err_printf(m, "EIR: 0x%08x\n", gt->eir);
-	err_printf(m, "IER: 0x%08x\n", gt->ier);
+	err_printf(m, "PGTBL_ER: 0x%08x\n", gt->pgtbl_er);
+
 	for (i = 0; i < gt->ngtier; i++)
 		err_printf(m, "GTIER[%d]: 0x%08x\n", i, gt->gtier[i]);
-	err_printf(m, "PGTBL_ER: 0x%08x\n", gt->pgtbl_er);
-	err_printf(m, "FORCEWAKE: 0x%08x\n", gt->forcewake);
-	err_printf(m, "DERRMR: 0x%08x\n", gt->derrmr);
+}
 
-	for (i = 0; i < gt->nfence; i++)
-		err_printf(m, "  fence[%d] = %08llx\n", i, gt->fence[i]);
+static void err_print_gt_global(struct drm_i915_error_state_buf *m,
+				struct intel_gt_coredump *gt)
+{
+	err_printf(m, "FORCEWAKE: 0x%08x\n", gt->forcewake);
 
 	if (IS_GRAPHICS_VER(m->i915, 6, 11)) {
 		err_printf(m, "ERROR: 0x%08x\n", gt->error);
@@ -768,19 +772,38 @@ static void err_print_gt(struct drm_i915_error_state_buf *m,
 
 		err_printf(m, "  GAM_DONE: 0x%08x\n", gt->gam_done);
 	}
+}
+
+static void err_print_gt_fences(struct drm_i915_error_state_buf *m,
+				struct intel_gt_coredump *gt)
+{
+	int i;
+
+	for (i = 0; i < gt->nfence; i++)
+		err_printf(m, "  fence[%d] = %08llx\n", i, gt->fence[i]);
+}
+
+static void err_print_gt_engines(struct drm_i915_error_state_buf *m,
+				 struct intel_gt_coredump *gt)
+{
+	const struct intel_engine_coredump *ee;
 
 	for (ee = gt->engine; ee; ee = ee->next) {
 		const struct i915_vma_coredump *vma;
 
-		error_print_engine(m, ee);
+		if (ee->guc_capture_node)
+			intel_guc_capture_print_engine_node(m, ee);
+		else
+			error_print_engine(m, ee);
+
+		err_printf(m, "  hung: %u\n", ee->hung);
+		err_printf(m, "  engine reset count: %u\n", ee->reset_count);
+		error_print_context(m, "  Active context: ", &ee->context);
+
 		for (vma = ee->vma; vma; vma = vma->next)
 			print_error_vma(m, ee->engine, vma);
 	}
 
-	if (gt->uc)
-		err_print_uc(m, gt->uc);
-
-	err_print_gt_info(m, gt);
 }
 
 static void __err_print_to_sgl(struct drm_i915_error_state_buf *m,
@@ -836,8 +859,30 @@ static void __err_print_to_sgl(struct drm_i915_error_state_buf *m,
 	err_printf(m, "RPM wakelock: %s\n", yesno(error->wakelock));
 	err_printf(m, "PM suspended: %s\n", yesno(error->suspended));
 
-	if (error->gt)
-		err_print_gt(m, error->gt);
+	if (error->gt) {
+		bool print_guc_capture = false;
+
+		if (error->gt->uc && error->gt->uc->is_guc_capture)
+			print_guc_capture = true;
+
+		err_print_gt_display(m, error->gt);
+		err_print_gt_global_nonguc(m, error->gt);
+		err_print_gt_fences(m, error->gt);
+
+		/*
+		 * GuC dumped global, eng-class and eng-instance registers together
+		 * as part of engine state dump so we print in err_print_gt_engines
+		 */
+		if (!print_guc_capture)
+			err_print_gt_global(m, error->gt);
+
+		err_print_gt_engines(m, error->gt);
+
+		if (error->gt->uc)
+			err_print_uc(m, error->gt->uc);
+
+		err_print_gt_info(m, error->gt);
+	}
 
 	if (error->overlay)
 		intel_overlay_print_error_state(m, error->overlay);
@@ -985,6 +1030,7 @@ static void cleanup_gt(struct intel_gt_coredump *gt)
 		gt->engine = ee->next;
 
 		i915_vma_coredump_free(ee->vma);
+		intel_guc_capture_free_node(ee);
 		kfree(ee);
 	}
 
@@ -1436,7 +1482,7 @@ static void add_vma_coredump(struct intel_engine_coredump *ee,
 }
 
 struct intel_engine_coredump *
-intel_engine_coredump_alloc(struct intel_engine_cs *engine, gfp_t gfp)
+intel_engine_coredump_alloc(struct intel_engine_cs *engine, gfp_t gfp, u32 dump_flags)
 {
 	struct intel_engine_coredump *ee;
 
@@ -1446,8 +1492,10 @@ intel_engine_coredump_alloc(struct intel_engine_cs *engine, gfp_t gfp)
 
 	ee->engine = engine;
 
-	engine_record_registers(ee);
-	engine_record_execlists(ee);
+	if (!(dump_flags & CORE_DUMP_FLAG_IS_GUC_CAPTURE)) {
+		engine_record_registers(ee);
+		engine_record_execlists(ee);
+	}
 
 	return ee;
 }
@@ -1511,7 +1559,8 @@ intel_engine_coredump_add_vma(struct intel_engine_coredump *ee,
 
 static struct intel_engine_coredump *
 capture_engine(struct intel_engine_cs *engine,
-	       struct i915_vma_compress *compress)
+	       struct i915_vma_compress *compress,
+	       u32 dump_flags)
 {
 	struct intel_engine_capture_vma *capture = NULL;
 	struct intel_engine_coredump *ee;
@@ -1519,7 +1568,7 @@ capture_engine(struct intel_engine_cs *engine,
 	struct i915_request *rq = NULL;
 	unsigned long flags;
 
-	ee = intel_engine_coredump_alloc(engine, ALLOW_FAIL);
+	ee = intel_engine_coredump_alloc(engine, ALLOW_FAIL, dump_flags);
 	if (!ee)
 		return NULL;
 
@@ -1552,6 +1601,8 @@ capture_engine(struct intel_engine_cs *engine,
 		i915_request_put(rq);
 		goto no_request_capture;
 	}
+	if (dump_flags & CORE_DUMP_FLAG_IS_GUC_CAPTURE)
+		intel_guc_capture_get_matching_node(engine->gt, ee, ce);
 
 	intel_engine_coredump_add_vma(ee, capture, compress);
 	i915_request_put(rq);
@@ -1566,7 +1617,8 @@ capture_engine(struct intel_engine_cs *engine,
 static void
 gt_record_engines(struct intel_gt_coredump *gt,
 		  intel_engine_mask_t engine_mask,
-		  struct i915_vma_compress *compress)
+		  struct i915_vma_compress *compress,
+		  u32 dump_flags)
 {
 	struct intel_engine_cs *engine;
 	enum intel_engine_id id;
@@ -1577,7 +1629,7 @@ gt_record_engines(struct intel_gt_coredump *gt,
 		/* Refill our page pool before entering atomic section */
 		pool_refill(&compress->pool, ALLOW_FAIL);
 
-		ee = capture_engine(engine, compress);
+		ee = capture_engine(engine, compress, dump_flags);
 		if (!ee)
 			continue;
 
@@ -1585,6 +1637,8 @@ gt_record_engines(struct intel_gt_coredump *gt,
 
 		gt->simulated |= ee->simulated;
 		if (ee->simulated) {
+			if (dump_flags & CORE_DUMP_FLAG_IS_GUC_CAPTURE)
+				intel_guc_capture_free_node(ee);
 			kfree(ee);
 			continue;
 		}
@@ -1620,8 +1674,74 @@ gt_record_uc(struct intel_gt_coredump *gt,
 	return error_uc;
 }
 
-/* Capture all registers which don't fit into another category. */
-static void gt_record_regs(struct intel_gt_coredump *gt)
+/* Capture display registers. */
+static void gt_record_display_regs(struct intel_gt_coredump *gt)
+{
+	struct intel_uncore *uncore = gt->_gt->uncore;
+	struct drm_i915_private *i915 = uncore->i915;
+
+	if (GRAPHICS_VER(i915) >= 6)
+		gt->derrmr = intel_uncore_read(uncore, DERRMR);
+
+	if (GRAPHICS_VER(i915) >= 8)
+		gt->ier = intel_uncore_read(uncore, GEN8_DE_MISC_IER);
+	else if (IS_VALLEYVIEW(i915))
+		gt->ier = intel_uncore_read(uncore, VLV_IER);
+	else if (HAS_PCH_SPLIT(i915))
+		gt->ier = intel_uncore_read(uncore, DEIER);
+	else if (GRAPHICS_VER(i915) == 2)
+		gt->ier = intel_uncore_read16(uncore, GEN2_IER);
+	else
+		gt->ier = intel_uncore_read(uncore, GEN2_IER);
+}
+
+/* Capture all other registers that GuC doesn't capture. */
+static void gt_record_global_nonguc_regs(struct intel_gt_coredump *gt)
+{
+	struct intel_uncore *uncore = gt->_gt->uncore;
+	struct drm_i915_private *i915 = uncore->i915;
+	int i;
+
+	if (IS_VALLEYVIEW(i915)) {
+		gt->gtier[0] = intel_uncore_read(uncore, GTIER);
+		gt->ngtier = 1;
+	} else if (GRAPHICS_VER(i915) >= 11) {
+		gt->gtier[0] =
+			intel_uncore_read(uncore,
+					  GEN11_RENDER_COPY_INTR_ENABLE);
+		gt->gtier[1] =
+			intel_uncore_read(uncore, GEN11_VCS_VECS_INTR_ENABLE);
+		gt->gtier[2] =
+			intel_uncore_read(uncore, GEN11_GUC_SG_INTR_ENABLE);
+		gt->gtier[3] =
+			intel_uncore_read(uncore,
+					  GEN11_GPM_WGBOXPERF_INTR_ENABLE);
+		gt->gtier[4] =
+			intel_uncore_read(uncore,
+					  GEN11_CRYPTO_RSVD_INTR_ENABLE);
+		gt->gtier[5] =
+			intel_uncore_read(uncore,
+					  GEN11_GUNIT_CSME_INTR_ENABLE);
+		gt->ngtier = 6;
+	} else if (GRAPHICS_VER(i915) >= 8) {
+		for (i = 0; i < 4; i++)
+			gt->gtier[i] =
+				intel_uncore_read(uncore, GEN8_GT_IER(i));
+		gt->ngtier = 4;
+	} else if (HAS_PCH_SPLIT(i915)) {
+		gt->gtier[0] = intel_uncore_read(uncore, GTIER);
+		gt->ngtier = 1;
+	}
+
+	gt->eir = intel_uncore_read(uncore, EIR);
+	gt->pgtbl_er = intel_uncore_read(uncore, PGTBL_ER);
+}
+
+/*
+ * Capture all registers that relate to workload submission.
+ * NOTE: In GuC submission, when GuC resets an engine, it can dump these for us
+ */
+static void gt_record_global_regs(struct intel_gt_coredump *gt)
 {
 	struct intel_uncore *uncore = gt->_gt->uncore;
 	struct drm_i915_private *i915 = uncore->i915;
@@ -1637,11 +1757,8 @@ static void gt_record_regs(struct intel_gt_coredump *gt)
 	 */
 
 	/* 1: Registers specific to a single generation */
-	if (IS_VALLEYVIEW(i915)) {
-		gt->gtier[0] = intel_uncore_read(uncore, GTIER);
-		gt->ier = intel_uncore_read(uncore, VLV_IER);
+	if (IS_VALLEYVIEW(i915))
 		gt->forcewake = intel_uncore_read_fw(uncore, FORCEWAKE_VLV);
-	}
 
 	if (GRAPHICS_VER(i915) == 7)
 		gt->err_int = intel_uncore_read(uncore, GEN7_ERR_INT);
@@ -1669,7 +1786,6 @@ static void gt_record_regs(struct intel_gt_coredump *gt)
 		gt->forcewake = intel_uncore_read_fw(uncore, FORCEWAKE_MT);
 
 	if (GRAPHICS_VER(i915) >= 6) {
-		gt->derrmr = intel_uncore_read(uncore, DERRMR);
 		if (GRAPHICS_VER(i915) < 12) {
 			gt->error = intel_uncore_read(uncore, ERROR_GEN6);
 			gt->done_reg = intel_uncore_read(uncore, DONE_REG);
@@ -1705,44 +1821,6 @@ static void gt_record_regs(struct intel_gt_coredump *gt)
 
 		gt->gam_done = intel_uncore_read(uncore, GEN12_GAM_DONE);
 	}
-
-	/* 4: Everything else */
-	if (GRAPHICS_VER(i915) >= 11) {
-		gt->ier = intel_uncore_read(uncore, GEN8_DE_MISC_IER);
-		gt->gtier[0] =
-			intel_uncore_read(uncore,
-					  GEN11_RENDER_COPY_INTR_ENABLE);
-		gt->gtier[1] =
-			intel_uncore_read(uncore, GEN11_VCS_VECS_INTR_ENABLE);
-		gt->gtier[2] =
-			intel_uncore_read(uncore, GEN11_GUC_SG_INTR_ENABLE);
-		gt->gtier[3] =
-			intel_uncore_read(uncore,
-					  GEN11_GPM_WGBOXPERF_INTR_ENABLE);
-		gt->gtier[4] =
-			intel_uncore_read(uncore,
-					  GEN11_CRYPTO_RSVD_INTR_ENABLE);
-		gt->gtier[5] =
-			intel_uncore_read(uncore,
-					  GEN11_GUNIT_CSME_INTR_ENABLE);
-		gt->ngtier = 6;
-	} else if (GRAPHICS_VER(i915) >= 8) {
-		gt->ier = intel_uncore_read(uncore, GEN8_DE_MISC_IER);
-		for (i = 0; i < 4; i++)
-			gt->gtier[i] =
-				intel_uncore_read(uncore, GEN8_GT_IER(i));
-		gt->ngtier = 4;
-	} else if (HAS_PCH_SPLIT(i915)) {
-		gt->ier = intel_uncore_read(uncore, DEIER);
-		gt->gtier[0] = intel_uncore_read(uncore, GTIER);
-		gt->ngtier = 1;
-	} else if (GRAPHICS_VER(i915) == 2) {
-		gt->ier = intel_uncore_read16(uncore, GEN2_IER);
-	} else if (!IS_VALLEYVIEW(i915)) {
-		gt->ier = intel_uncore_read(uncore, GEN2_IER);
-	}
-	gt->eir = intel_uncore_read(uncore, EIR);
-	gt->pgtbl_er = intel_uncore_read(uncore, PGTBL_ER);
 }
 
 static void gt_record_info(struct intel_gt_coredump *gt)
@@ -1854,7 +1932,7 @@ i915_gpu_coredump_alloc(struct drm_i915_private *i915, gfp_t gfp)
 #define DAY_AS_SECONDS(x) (24 * 60 * 60 * (x))
 
 struct intel_gt_coredump *
-intel_gt_coredump_alloc(struct intel_gt *gt, gfp_t gfp)
+intel_gt_coredump_alloc(struct intel_gt *gt, gfp_t gfp, u32 dump_flags)
 {
 	struct intel_gt_coredump *gc;
 
@@ -1865,7 +1943,21 @@ intel_gt_coredump_alloc(struct intel_gt *gt, gfp_t gfp)
 	gc->_gt = gt;
 	gc->awake = intel_gt_pm_is_awake(gt);
 
-	gt_record_regs(gc);
+	gt_record_display_regs(gc);
+	gt_record_global_nonguc_regs(gc);
+
+	/*
+	 * GuC dumps global, eng-class and eng-instance registers
+	 * (that can change as part of engine state during execution)
+	 * before an engine is reset due to a hung context.
+	 * GuC captures and reports all three groups of registers
+	 * together as a single set before the engine is reset.
+	 * Thus, if GuC triggered the context reset we retrieve
+	 * the register values as part of gt_record_engines.
+	 */
+	if (!(dump_flags & CORE_DUMP_FLAG_IS_GUC_CAPTURE))
+		gt_record_global_regs(gc);
+
 	gt_record_fences(gc);
 
 	return gc;
@@ -1899,7 +1991,7 @@ void i915_vma_capture_finish(struct intel_gt_coredump *gt,
 }
 
 static struct i915_gpu_coredump *
-__i915_gpu_coredump(struct intel_gt *gt, intel_engine_mask_t engine_mask)
+__i915_gpu_coredump(struct intel_gt *gt, intel_engine_mask_t engine_mask, u32 dump_flags)
 {
 	struct drm_i915_private *i915 = gt->i915;
 	struct i915_gpu_coredump *error;
@@ -1913,7 +2005,7 @@ __i915_gpu_coredump(struct intel_gt *gt, intel_engine_mask_t engine_mask)
 	if (!error)
 		return ERR_PTR(-ENOMEM);
 
-	error->gt = intel_gt_coredump_alloc(gt, ALLOW_FAIL);
+	error->gt = intel_gt_coredump_alloc(gt, ALLOW_FAIL, dump_flags);
 	if (error->gt) {
 		struct i915_vma_compress *compress;
 
@@ -1924,11 +2016,19 @@ __i915_gpu_coredump(struct intel_gt *gt, intel_engine_mask_t engine_mask)
 			return ERR_PTR(-ENOMEM);
 		}
 
+		if (INTEL_INFO(i915)->has_gt_uc) {
+			error->gt->uc = gt_record_uc(error->gt, compress);
+			if (error->gt->uc) {
+				if (dump_flags & CORE_DUMP_FLAG_IS_GUC_CAPTURE)
+					error->gt->uc->is_guc_capture = true;
+				else
+					GEM_BUG_ON(error->gt->uc->is_guc_capture);
+			}
+		}
+
 		gt_record_info(error->gt);
-		gt_record_engines(error->gt, engine_mask, compress);
+		gt_record_engines(error->gt, engine_mask, compress, dump_flags);
 
-		if (INTEL_INFO(i915)->has_gt_uc)
-			error->gt->uc = gt_record_uc(error->gt, compress);
 
 		i915_vma_capture_finish(error->gt, compress);
 
@@ -1941,7 +2041,7 @@ __i915_gpu_coredump(struct intel_gt *gt, intel_engine_mask_t engine_mask)
 }
 
 struct i915_gpu_coredump *
-i915_gpu_coredump(struct intel_gt *gt, intel_engine_mask_t engine_mask)
+i915_gpu_coredump(struct intel_gt *gt, intel_engine_mask_t engine_mask, u32 dump_flags)
 {
 	static DEFINE_MUTEX(capture_mutex);
 	int ret = mutex_lock_interruptible(&capture_mutex);
@@ -1950,7 +2050,7 @@ i915_gpu_coredump(struct intel_gt *gt, intel_engine_mask_t engine_mask)
 	if (ret)
 		return ERR_PTR(ret);
 
-	dump = __i915_gpu_coredump(gt, engine_mask);
+	dump = __i915_gpu_coredump(gt, engine_mask, dump_flags);
 	mutex_unlock(&capture_mutex);
 
 	return dump;
@@ -1997,11 +2097,11 @@ void i915_error_state_store(struct i915_gpu_coredump *error)
  * to pick up.
  */
 void i915_capture_error_state(struct intel_gt *gt,
-			      intel_engine_mask_t engine_mask)
+			      intel_engine_mask_t engine_mask, u32 dump_flags)
 {
 	struct i915_gpu_coredump *error;
 
-	error = i915_gpu_coredump(gt, engine_mask);
+	error = i915_gpu_coredump(gt, engine_mask, dump_flags);
 	if (IS_ERR(error)) {
 		cmpxchg(&gt->i915->gpu_error.first_error, NULL, error);
 		return;
diff --git a/drivers/gpu/drm/i915/i915_gpu_error.h b/drivers/gpu/drm/i915/i915_gpu_error.h
index 903d838e2e63..64b999d8ac27 100644
--- a/drivers/gpu/drm/i915/i915_gpu_error.h
+++ b/drivers/gpu/drm/i915/i915_gpu_error.h
@@ -53,6 +53,8 @@ struct i915_request_coredump {
 	struct i915_sched_attr sched_attr;
 };
 
+struct __guc_capture_parsed_output;
+
 struct intel_engine_coredump {
 	const struct intel_engine_cs *engine;
 
@@ -84,6 +86,10 @@ struct intel_engine_coredump {
 	u32 rc_psmi; /* sleep state */
 	struct intel_instdone instdone;
 
+	/* GuC matched capture-lists info */
+	struct intel_guc_state_capture *capture;
+	struct __guc_capture_parsed_output *guc_capture_node;
+
 	struct i915_gem_context_coredump {
 		char comm[TASK_COMM_LEN];
 
@@ -124,7 +130,6 @@ struct intel_gt_coredump {
 	u32 pgtbl_er;
 	u32 ier;
 	u32 gtier[6], ngtier;
-	u32 derrmr;
 	u32 forcewake;
 	u32 error; /* gen6+ */
 	u32 err_int; /* gen7 */
@@ -137,9 +142,12 @@ struct intel_gt_coredump {
 	u32 gfx_mode;
 	u32 gtt_cache;
 	u32 aux_err; /* gen12 */
-	u32 sfc_done[GEN12_SFC_DONE_MAX]; /* gen12 */
 	u32 gam_done; /* gen12 */
 
+	/* Display related */
+	u32 derrmr;
+	u32 sfc_done[GEN12_SFC_DONE_MAX]; /* gen12 */
+
 	u32 nfence;
 	u64 fence[I915_MAX_NUM_FENCES];
 
@@ -149,6 +157,7 @@ struct intel_gt_coredump {
 		struct intel_uc_fw guc_fw;
 		struct intel_uc_fw huc_fw;
 		struct i915_vma_coredump *guc_log;
+		bool is_guc_capture;
 	} *uc;
 
 	struct intel_gt_coredump *next;
@@ -221,24 +230,27 @@ static inline u32 i915_reset_engine_count(struct i915_gpu_error *error,
 	return atomic_read(&error->reset_engine_count[engine->uabi_class]);
 }
 
+#define CORE_DUMP_FLAG_NONE           0x0
+#define CORE_DUMP_FLAG_IS_GUC_CAPTURE BIT(0)
+
 #if IS_ENABLED(CONFIG_DRM_I915_CAPTURE_ERROR)
 
 __printf(2, 3)
 void i915_error_printf(struct drm_i915_error_state_buf *e, const char *f, ...);
 
 struct i915_gpu_coredump *i915_gpu_coredump(struct intel_gt *gt,
-					    intel_engine_mask_t engine_mask);
+					    intel_engine_mask_t engine_mask, u32 dump_flags);
 void i915_capture_error_state(struct intel_gt *gt,
-			      intel_engine_mask_t engine_mask);
+			      intel_engine_mask_t engine_mask, u32 dump_flags);
 
 struct i915_gpu_coredump *
 i915_gpu_coredump_alloc(struct drm_i915_private *i915, gfp_t gfp);
 
 struct intel_gt_coredump *
-intel_gt_coredump_alloc(struct intel_gt *gt, gfp_t gfp);
+intel_gt_coredump_alloc(struct intel_gt *gt, gfp_t gfp, u32 dump_flags);
 
 struct intel_engine_coredump *
-intel_engine_coredump_alloc(struct intel_engine_cs *engine, gfp_t gfp);
+intel_engine_coredump_alloc(struct intel_engine_cs *engine, gfp_t gfp, u32 dump_flags);
 
 struct intel_engine_capture_vma *
 intel_engine_coredump_add_request(struct intel_engine_coredump *ee,
@@ -282,7 +294,7 @@ void i915_disable_error_state(struct drm_i915_private *i915, int err);
 #else
 
 static inline void
-i915_capture_error_state(struct intel_gt *gt, intel_engine_mask_t engine_mask)
+i915_capture_error_state(struct intel_gt *gt, intel_engine_mask_t engine_mask, u32 dump_flags)
 {
 }
 
@@ -293,13 +305,13 @@ i915_gpu_coredump_alloc(struct drm_i915_private *i915, gfp_t gfp)
 }
 
 static inline struct intel_gt_coredump *
-intel_gt_coredump_alloc(struct intel_gt *gt, gfp_t gfp)
+intel_gt_coredump_alloc(struct intel_gt *gt, gfp_t gfp, u32 dump_flags)
 {
 	return NULL;
 }
 
 static inline struct intel_engine_coredump *
-intel_engine_coredump_alloc(struct intel_engine_cs *engine, gfp_t gfp)
+intel_engine_coredump_alloc(struct intel_engine_cs *engine, gfp_t gfp, u32 dump_flags)
 {
 	return NULL;
 }
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [Intel-gfx] [PATCH v7 13/13] drm/i915/guc: Print the GuC error capture output register list.
  2022-02-26  9:55 ` [Intel-gfx] " Alan Previn
                   ` (12 preceding siblings ...)
  (?)
@ 2022-02-26  9:55 ` Alan Previn
  2022-03-11  5:26   ` Umesh Nerlige Ramappa
  -1 siblings, 1 reply; 32+ messages in thread
From: Alan Previn @ 2022-02-26  9:55 UTC (permalink / raw)
  To: intel-gfx; +Cc: Alan Previn

Print the GuC captured error state register list (string names
and values) when gpu_coredump_state printout is invoked via
the i915 debugfs for flushing the gpu error-state that was
captured prior.

Since GuC could have reported multiple engine register dumps
in a single notification event, parse the captured data
(appearing as a stream of structures) to identify each dump as
a different 'engine-capture-group-output'.

Finally, for each 'engine-capture-group-output' that is found,
verify if the engine register dump corresponds to the
engine_coredump content that was previously populated by the
i915_gpu_coredump function. That function would have copied
the context's vma's including the bacth buffer during the
G2H-context-reset notification that occurred earlier. Perform
this verification check by comparing guc_id, lrca and engine-
instance obtained from the 'engine-capture-group-output' vs a
copy of that same info taken during i915_gpu_coredump. If
they match, then print those vma's as well (such as the batch
buffers).

Signed-off-by: Alan Previn <alan.previn.teres.alexis@intel.com>
---
 drivers/gpu/drm/i915/gt/intel_engine_cs.c     |   4 +-
 drivers/gpu/drm/i915/gt/uc/intel_guc.h        |   3 +
 .../gpu/drm/i915/gt/uc/intel_guc_capture.c    | 162 ++++++++++++++++++
 .../gpu/drm/i915/gt/uc/intel_guc_capture.h    |   2 +-
 .../gpu/drm/i915/gt/uc/intel_guc_submission.c |   6 +-
 drivers/gpu/drm/i915/i915_debugfs.c           |   1 +
 drivers/gpu/drm/i915/i915_gpu_error.c         |  16 +-
 drivers/gpu/drm/i915/i915_gpu_error.h         |   5 +
 8 files changed, 184 insertions(+), 15 deletions(-)

diff --git a/drivers/gpu/drm/i915/gt/intel_engine_cs.c b/drivers/gpu/drm/i915/gt/intel_engine_cs.c
index e855c801ba28..4643745e5d09 100644
--- a/drivers/gpu/drm/i915/gt/intel_engine_cs.c
+++ b/drivers/gpu/drm/i915/gt/intel_engine_cs.c
@@ -1629,9 +1629,7 @@ static void intel_engine_print_registers(struct intel_engine_cs *engine,
 		drm_printf(m, "\tIPEHR: 0x%08x\n", ENGINE_READ(engine, IPEHR));
 	}
 
-	if (intel_engine_uses_guc(engine)) {
-		/* nothing to print yet */
-	} else if (HAS_EXECLISTS(dev_priv)) {
+	if (HAS_EXECLISTS(dev_priv) && !intel_engine_uses_guc(engine)) {
 		struct i915_request * const *port, *rq;
 		const u32 *hws =
 			&engine->status_page.addr[I915_HWS_CSB_BUF0_INDEX];
diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc.h b/drivers/gpu/drm/i915/gt/uc/intel_guc.h
index 908c6b1dd51a..08327294c1e3 100644
--- a/drivers/gpu/drm/i915/gt/uc/intel_guc.h
+++ b/drivers/gpu/drm/i915/gt/uc/intel_guc.h
@@ -438,6 +438,9 @@ int intel_guc_engine_failure_process_msg(struct intel_guc *guc,
 int intel_guc_error_capture_process_msg(struct intel_guc *guc,
 					const u32 *msg, u32 len);
 
+struct intel_engine_cs *
+intel_guc_lookup_engine(struct intel_guc *guc, u8 guc_class, u8 instance);
+
 void intel_guc_find_hung_context(struct intel_engine_cs *engine);
 
 int intel_guc_global_policies_update(struct intel_guc *guc);
diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c
index 621c0b4537a9..2f96bdf2b90f 100644
--- a/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c
+++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c
@@ -746,6 +746,21 @@ int intel_guc_capture_output_min_size_est(struct intel_guc *guc)
  *                                    intel_engine_coredump struct (if the context and
  *                                    engine of the event notification matches a node
  *                                    in the link list).
+ *
+ * User Sysfs / Debugfs
+ * --------------------
+ *      --> i915_gpu_coredump_copy_to_buffer->
+ *                   L--> err_print_to_sgl --> err_print_gt
+ *                        L--> error_print_guc_captures
+ *                             L--> intel_guc_capture_print_node prints the
+ *                                  register lists values of the attached node
+ *                                  on the error-engine-dump being reported.
+ *                   L--> i915_reset_error_state ... -->__i915_gpu_coredump_free
+ *                        L--> ... cleanup_gt -->
+ *                             L--> intel_guc_capture_free_node returns the
+ *                                  capture-output-node back to the internal
+ *                                  cachelist for reuse.
+ *
  */
 
 static int guc_capture_buf_cnt(struct __guc_capture_bufstate *buf)
@@ -1346,9 +1361,156 @@ static void __guc_capture_process_output(struct intel_guc *guc)
 
 #if IS_ENABLED(CONFIG_DRM_I915_CAPTURE_ERROR)
 
+static const char *
+guc_capture_reg_to_str(const struct intel_guc *guc, u32 owner, u32 type,
+		       u32 class, u32 id, u32 offset, u32 *is_ext)
+{
+	const struct __guc_mmio_reg_descr_group *reglists = guc->capture.priv->reglists;
+	struct __guc_mmio_reg_descr_group *extlists = guc->capture.priv->extlists;
+	const struct __guc_mmio_reg_descr_group *match;
+	struct __guc_mmio_reg_descr_group *matchext;
+	int j;
+
+	*is_ext = 0;
+	if (!reglists)
+		return NULL;
+
+	match = guc_capture_get_one_list(reglists, owner, type, id);
+	if (!match)
+		return NULL;
+
+	for (j = 0; j < match->num_regs; ++j) {
+		if (offset == match->list[j].reg.reg)
+			return match->list[j].regname;
+	}
+	if (extlists) {
+		matchext = guc_capture_get_one_ext_list(extlists, owner, type, id);
+		if (!matchext)
+			return NULL;
+		for (j = 0; j < matchext->num_regs; ++j) {
+			if (offset == matchext->extlist[j].reg.reg) {
+				*is_ext = 1;
+				return matchext->extlist[j].regname;
+			}
+		}
+	}
+
+	return NULL;
+}
+
+#ifdef CONFIG_DRM_I915_DEBUG_GUC
+#define __out(a, ...) \
+	do { \
+		drm_warn((&(a)->drm), __VA_ARGS__); \
+		i915_error_printf((a), __VA_ARGS__); \
+	} while (0)
+#else
+#define __out(a, ...) \
+	i915_error_printf(a, __VA_ARGS__)
+#endif
+
+#define GCAP_PRINT_INTEL_ENG_INFO(ebuf, eng) \
+	do { \
+		__out(ebuf, "    i915-Eng-Name: %s command stream\n", \
+		      (eng)->name); \
+		__out(ebuf, "    i915-Eng-Inst-Class: 0x%02x\n", (eng)->class); \
+		__out(ebuf, "    i915-Eng-Inst-Id: 0x%02x\n", (eng)->instance); \
+		__out(ebuf, "    i915-Eng-LogicalMask: 0x%08x\n", \
+		      (eng)->logical_mask); \
+	} while (0)
+
+#define GCAP_PRINT_GUC_INST_INFO(ebuf, node) \
+	do { \
+		__out(ebuf, "    GuC-Engine-Inst-Id: 0x%08x\n", \
+		      (node)->eng_inst); \
+		__out(ebuf, "    GuC-Context-Id: 0x%08x\n", (node)->guc_id); \
+		__out(ebuf, "    LRCA: 0x%08x\n", (node)->lrca); \
+	} while (0)
+
 int intel_guc_capture_print_engine_node(struct drm_i915_error_state_buf *ebuf,
 					const struct intel_engine_coredump *ee)
 {
+	const char *grptype[GUC_STATE_CAPTURE_GROUP_TYPE_MAX] = {
+		"full-capture",
+		"partial-capture"
+	};
+	const char *datatype[GUC_CAPTURE_LIST_TYPE_MAX] = {
+		"Global",
+		"Engine-Class",
+		"Engine-Instance"
+	};
+	struct intel_guc_state_capture *cap;
+	struct __guc_capture_parsed_output *node;
+	struct drm_i915_private *i915;
+	struct guc_mmio_reg *regs;
+	struct intel_guc *guc;
+	struct intel_engine_cs *eng;
+	const char *str;
+	int numregs, i, j;
+	u32 is_ext;
+
+	if (!ebuf || !ee)
+		return -EINVAL;
+	cap = ee->capture;
+	if (!cap->priv || !ee->engine)
+		return -ENODEV;
+
+	guc = container_of(cap, struct intel_guc, capture);
+	i915 = (container_of(guc, struct intel_gt, uc.guc))->i915;
+	__out(ebuf, "global --- GuC Error Capture on %s command stream:\n",
+	      ee->engine->name);
+
+	node = ee->guc_capture_node;
+	if (!node) {
+		__out(ebuf, "  No matching ee-node\n");
+		return 0;
+	}
+
+	__out(ebuf, "Coverage:  %s\n", grptype[node->is_partial]);
+
+	for (i = GUC_CAPTURE_LIST_TYPE_GLOBAL; i < GUC_CAPTURE_LIST_TYPE_MAX; ++i) {
+		__out(ebuf, "  RegListType: %s\n",
+		      datatype[i % GUC_CAPTURE_LIST_TYPE_MAX]);
+		__out(ebuf, "    Owner-Id: %d\n", node->reginfo[i].vfid);
+
+		switch (i) {
+		case GUC_CAPTURE_LIST_TYPE_GLOBAL:
+		default:
+			break;
+		case GUC_CAPTURE_LIST_TYPE_ENGINE_CLASS:
+			__out(ebuf, "    GuC-Eng-Class: %d\n", node->eng_class);
+			__out(ebuf, "    i915-Eng-Class: %d\n",
+			      guc_class_to_engine_class(node->eng_class));
+			break;
+		case GUC_CAPTURE_LIST_TYPE_ENGINE_INSTANCE:
+			eng = intel_guc_lookup_engine(guc, node->eng_class, node->eng_inst);
+			if (eng)
+				GCAP_PRINT_INTEL_ENG_INFO(ebuf, eng);
+			else
+				__out(ebuf, "    i915-Eng-Lookup Fail!\n");
+			GCAP_PRINT_GUC_INST_INFO(ebuf, node);
+			break;
+		}
+
+		numregs = node->reginfo[i].num_regs;
+		__out(ebuf, "    NumRegs: %d\n", numregs);
+		j = 0;
+		while (numregs--) {
+			regs = node->reginfo[i].regs;
+			str = guc_capture_reg_to_str(guc, GUC_CAPTURE_LIST_INDEX_PF, i,
+						     node->eng_class, 0, regs[j].offset, &is_ext);
+			if (!str)
+				__out(ebuf, "      REG-0x%08x", regs[j].offset);
+			else
+				__out(ebuf, "      %s", str);
+			if (is_ext)
+				__out(ebuf, "[%ld][%ld]",
+				      FIELD_GET(GUC_REGSET_STEERING_GROUP, regs[j].flags),
+				      FIELD_GET(GUC_REGSET_STEERING_INSTANCE, regs[j].flags));
+			__out(ebuf, ":  0x%08x\n", regs[j].value);
+			++j;
+		}
+	}
 	return 0;
 }
 
diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.h b/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.h
index ae527c98e2f6..d544efcf0e9f 100644
--- a/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.h
+++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.h
@@ -12,8 +12,8 @@ struct drm_i915_error_state_buf;
 struct file;
 struct guc_ads;
 struct guc_gt_system_info;
-struct intel_context;
 struct intel_engine_coredump;
+struct intel_context;
 struct intel_gt;
 struct intel_guc;
 
diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c
index 5d41dc95f066..29f35f9f3c6d 100644
--- a/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c
+++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c
@@ -4087,8 +4087,8 @@ int intel_guc_error_capture_process_msg(struct intel_guc *guc,
 	return 0;
 }
 
-static struct intel_engine_cs *
-guc_lookup_engine(struct intel_guc *guc, u8 guc_class, u8 instance)
+struct intel_engine_cs *
+intel_guc_lookup_engine(struct intel_guc *guc, u8 guc_class, u8 instance)
 {
 	struct intel_gt *gt = guc_to_gt(guc);
 	u8 engine_class = guc_class_to_engine_class(guc_class);
@@ -4137,7 +4137,7 @@ int intel_guc_engine_failure_process_msg(struct intel_guc *guc,
 	instance = msg[1];
 	reason = msg[2];
 
-	engine = guc_lookup_engine(guc, guc_class, instance);
+	engine = intel_guc_lookup_engine(guc, guc_class, instance);
 	if (unlikely(!engine)) {
 		drm_err(&gt->i915->drm,
 			"Invalid engine %d:%d", guc_class, instance);
diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
index 274abf267f35..71489ce52746 100644
--- a/drivers/gpu/drm/i915/i915_debugfs.c
+++ b/drivers/gpu/drm/i915/i915_debugfs.c
@@ -308,6 +308,7 @@ static int i915_gpu_info_open(struct inode *inode, struct file *file)
 	gpu = NULL;
 	with_intel_runtime_pm(&i915->runtime_pm, wakeref)
 		gpu = i915_gpu_coredump(to_gt(i915), ALL_ENGINES, CORE_DUMP_FLAG_NONE);
+
 	if (IS_ERR(gpu))
 		return PTR_ERR(gpu);
 
diff --git a/drivers/gpu/drm/i915/i915_gpu_error.c b/drivers/gpu/drm/i915/i915_gpu_error.c
index 3d566bbe228d..3c7b9d0e8627 100644
--- a/drivers/gpu/drm/i915/i915_gpu_error.c
+++ b/drivers/gpu/drm/i915/i915_gpu_error.c
@@ -530,8 +530,8 @@ __find_vma(struct i915_vma_coredump *vma, const char *name)
 	return NULL;
 }
 
-static struct i915_vma_coredump *
-find_batch(const struct intel_engine_coredump *ee)
+struct i915_vma_coredump *
+intel_gpu_error_find_batch(const struct intel_engine_coredump *ee)
 {
 	return __find_vma(ee->vma, "batch");
 }
@@ -559,7 +559,7 @@ static void error_print_engine(struct drm_i915_error_state_buf *m,
 
 	error_print_instdone(m, ee);
 
-	batch = find_batch(ee);
+	batch = intel_gpu_error_find_batch(ee);
 	if (batch) {
 		u64 start = batch->gtt_offset;
 		u64 end = start + batch->gtt_size;
@@ -610,9 +610,9 @@ void i915_error_printf(struct drm_i915_error_state_buf *e, const char *f, ...)
 	va_end(args);
 }
 
-static void print_error_vma(struct drm_i915_error_state_buf *m,
-			    const struct intel_engine_cs *engine,
-			    const struct i915_vma_coredump *vma)
+void intel_gpu_error_print_vma(struct drm_i915_error_state_buf *m,
+			       const struct intel_engine_cs *engine,
+			       const struct i915_vma_coredump *vma)
 {
 	char out[ASCII85_BUFSZ];
 	struct page *page;
@@ -681,7 +681,7 @@ static void err_print_uc(struct drm_i915_error_state_buf *m,
 
 	intel_uc_fw_dump(&error_uc->guc_fw, &p);
 	intel_uc_fw_dump(&error_uc->huc_fw, &p);
-	print_error_vma(m, NULL, error_uc->guc_log);
+	intel_gpu_error_print_vma(m, NULL, error_uc->guc_log);
 }
 
 static void err_free_sgl(struct scatterlist *sgl)
@@ -801,7 +801,7 @@ static void err_print_gt_engines(struct drm_i915_error_state_buf *m,
 		error_print_context(m, "  Active context: ", &ee->context);
 
 		for (vma = ee->vma; vma; vma = vma->next)
-			print_error_vma(m, ee->engine, vma);
+			intel_gpu_error_print_vma(m, ee->engine, vma);
 	}
 
 }
diff --git a/drivers/gpu/drm/i915/i915_gpu_error.h b/drivers/gpu/drm/i915/i915_gpu_error.h
index 64b999d8ac27..9554ce7a71ef 100644
--- a/drivers/gpu/drm/i915/i915_gpu_error.h
+++ b/drivers/gpu/drm/i915/i915_gpu_error.h
@@ -237,6 +237,11 @@ static inline u32 i915_reset_engine_count(struct i915_gpu_error *error,
 
 __printf(2, 3)
 void i915_error_printf(struct drm_i915_error_state_buf *e, const char *f, ...);
+void intel_gpu_error_print_vma(struct drm_i915_error_state_buf *m,
+			       const struct intel_engine_cs *engine,
+			       const struct i915_vma_coredump *vma);
+struct i915_vma_coredump *
+intel_gpu_error_find_batch(const struct intel_engine_coredump *ee);
 
 struct i915_gpu_coredump *i915_gpu_coredump(struct intel_gt *gt,
 					    intel_engine_mask_t engine_mask, u32 dump_flags);
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [Intel-gfx] ✗ Fi.CI.CHECKPATCH: warning for Add GuC Error Capture Support (rev7)
  2022-02-26  9:55 ` [Intel-gfx] " Alan Previn
                   ` (13 preceding siblings ...)
  (?)
@ 2022-02-26 10:11 ` Patchwork
  -1 siblings, 0 replies; 32+ messages in thread
From: Patchwork @ 2022-02-26 10:11 UTC (permalink / raw)
  To: Alan Previn; +Cc: intel-gfx

== Series Details ==

Series: Add GuC Error Capture Support (rev7)
URL   : https://patchwork.freedesktop.org/series/97187/
State : warning

== Summary ==

$ dim checkpatch origin/drm-tip
fa51e0f956f6 drm/i915/guc: Update GuC ADS size for error capture lists
-:32: WARNING:FILE_PATH_CHANGES: added, moved or deleted file(s), does MAINTAINERS need updating?
#32: 
new file mode 100644

-:255: ERROR:SPACING: spaces required around that '=' (ctx:VxV)
#255: FILE: drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c:595:
+	u32 null_header[2]={0};
 	                  ^

-:300: CHECK:BRACES: Blank lines aren't necessary after an open brace '{'
#300: FILE: drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c:632:
+			if (!info_map_read(&info_map, engine_enabled_masks[j])) {
+

-:468: CHECK:MACRO_ARG_REUSE: Macro argument reuse 'regslist' - possible side-effects?
#468: FILE: drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c:63:
+#define MAKE_REGLIST(regslist, regsowner, regstype, class) \
+	{ \
+		regslist, \
+		ARRAY_SIZE(regslist), \
+		TO_GCAP_DEF_OWNER(regsowner), \
+		TO_GCAP_DEF_TYPE(regstype), \
+		class, \
+	}

-:512: WARNING:SUSPECT_CODE_INDENT: suspect code indent for conditional statements (16, 16)
#512: FILE: drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c:107:
+		if (reglists[i].owner == owner && reglists[i].type == type &&
[...]
+		return &reglists[i];

-:705: WARNING:TYPO_SPELLING: 'cant' may be misspelled - perhaps 'can't'?
#705: FILE: drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c:300:
+		drm_warn(&i915->drm, "GuC-capture: cant create shmem for caplist = 0x%016lx", PTR_ERR(file));
 		                                   ^^^^

-:705: WARNING:LONG_LINE: line length of 109 exceeds 100 columns
#705: FILE: drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c:300:
+		drm_warn(&i915->drm, "GuC-capture: cant create shmem for caplist = 0x%016lx", PTR_ERR(file));

total: 1 errors, 4 warnings, 2 checks, 734 lines checked
47efff77442d drm/i915/guc: Add XE_LP static registers for GuC error capture.
-:25: ERROR:COMPLEX_MACRO: Macros with complex values should be enclosed in parentheses
#25: FILE: drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c:25:
+#define COMMON_GEN12BASE_GLOBAL() \
+	{GEN12_FAULT_TLB_DATA0,    0,      0, "GEN12_FAULT_TLB_DATA0"}, \
+	{GEN12_FAULT_TLB_DATA1,    0,      0, "GEN12_FAULT_TLB_DATA1"}, \
+	{FORCEWAKE_MT,             0,      0, "FORCEWAKE"}, \
+	{GEN12_AUX_ERR_DBG,        0,      0, "AUX_ERR_DBG"}, \
+	{GEN12_GAM_DONE,           0,      0, "GAM_DONE"}, \
+	{GEN12_RING_FAULT_REG,     0,      0, "FAULT_REG"}

-:33: ERROR:COMPLEX_MACRO: Macros with complex values should be enclosed in parentheses
#33: FILE: drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c:33:
+#define COMMON_GEN12BASE_ENGINE_INSTANCE() \
+	{RING_PSMI_CTL(0),         0,      0, "RC PSMI"}, \
+	{RING_ESR(0),              0,      0, "ESR"}, \
+	{RING_DMA_FADD(0),         0,      0, "RING_DMA_FADD_LDW"}, \
+	{RING_DMA_FADD_UDW(0),     0,      0, "RING_DMA_FADD_UDW"}, \
+	{RING_IPEIR(0),            0,      0, "IPEIR"}, \
+	{RING_IPEHR(0),            0,      0, "IPEHR"}, \
+	{RING_INSTPS(0),           0,      0, "INSTPS"}, \
+	{RING_BBADDR(0),           0,      0, "RING_BBADDR_LOW32"}, \
+	{RING_BBADDR_UDW(0),       0,      0, "RING_BBADDR_UP32"}, \
+	{RING_BBSTATE(0),          0,      0, "BB_STATE"}, \
+	{CCID(0),                  0,      0, "CCID"}, \
+	{RING_ACTHD(0),            0,      0, "ACTHD_LDW"}, \
+	{RING_ACTHD_UDW(0),        0,      0, "ACTHD_UDW"}, \
+	{RING_INSTPM(0),           0,      0, "INSTPM"}, \
+	{RING_INSTDONE(0),         0,      0, "INSTDONE"}, \
+	{RING_NOPID(0),            0,      0, "RING_NOPID"}, \
+	{RING_START(0),            0,      0, "START"}, \
+	{RING_HEAD(0),             0,      0, "HEAD"}, \
+	{RING_TAIL(0),             0,      0, "TAIL"}, \
+	{RING_CTL(0),              0,      0, "CTL"}, \
+	{RING_MI_MODE(0),          0,      0, "MODE"}, \
+	{RING_CONTEXT_CONTROL(0),  0,      0, "RING_CONTEXT_CONTROL"}, \
+	{RING_HWS_PGA(0),          0,      0, "HWS"}, \
+	{RING_MODE_GEN7(0),        0,      0, "GFX_MODE"}, \
+	{GEN8_RING_PDP_LDW(0, 0),  0,      0, "PDP0_LDW"}, \
+	{GEN8_RING_PDP_UDW(0, 0),  0,      0, "PDP0_UDW"}, \
+	{GEN8_RING_PDP_LDW(0, 1),  0,      0, "PDP1_LDW"}, \
+	{GEN8_RING_PDP_UDW(0, 1),  0,      0, "PDP1_UDW"}, \
+	{GEN8_RING_PDP_LDW(0, 2),  0,      0, "PDP2_LDW"}, \
+	{GEN8_RING_PDP_UDW(0, 2),  0,      0, "PDP2_UDW"}, \
+	{GEN8_RING_PDP_LDW(0, 3),  0,      0, "PDP3_LDW"}, \
+	{GEN8_RING_PDP_UDW(0, 3),  0,      0, "PDP3_UDW"}

-:70: ERROR:COMPLEX_MACRO: Macros with complex values should be enclosed in parentheses
#70: FILE: drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c:70:
+#define COMMON_GEN12BASE_RENDER() \
+	{GEN7_SC_INSTDONE,         0,      0, "GEN7_SC_INSTDONE"}, \
+	{GEN12_SC_INSTDONE_EXTRA,  0,      0, "GEN12_SC_INSTDONE_EXTRA"}, \
+	{GEN12_SC_INSTDONE_EXTRA2, 0,      0, "GEN12_SC_INSTDONE_EXTRA2"}

-:75: ERROR:COMPLEX_MACRO: Macros with complex values should be enclosed in parentheses
#75: FILE: drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c:75:
+#define COMMON_GEN12BASE_VEC() \
+	{GEN12_SFC_DONE(0),        0,      0, "SFC_DONE[0]"}, \
+	{GEN12_SFC_DONE(1),        0,      0, "SFC_DONE[1]"}, \
+	{GEN12_SFC_DONE(2),        0,      0, "SFC_DONE[2]"}, \
+	{GEN12_SFC_DONE(3),        0,      0, "SFC_DONE[3]"}

total: 4 errors, 0 warnings, 0 checks, 180 lines checked
6de0bc96b40d drm/i915/guc: Add XE_LP steered register lists support
-:69: WARNING:SUSPECT_CODE_INDENT: suspect code indent for conditional statements (16, 16)
#69: FILE: drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c:163:
+		if (reglists[i].owner == owner && reglists[i].type == type &&
[...]
+		return &reglists[i];

total: 0 errors, 1 warnings, 0 checks, 259 lines checked
b3559be6c43d drm/i915/guc: Add DG2 registers for GuC error state capture.
630f7006ffde drm/i915/guc: Add Gen9 registers for GuC error state capture.
-:23: ERROR:COMPLEX_MACRO: Macros with complex values should be enclosed in parentheses
#23: FILE: drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c:28:
+#define COMMON_GEN9BASE_GLOBAL() \
+	{GEN8_FAULT_TLB_DATA0,     0,      0, "GEN8_FAULT_TLB_DATA0"}, \
+	{GEN8_FAULT_TLB_DATA1,     0,      0, "GEN8_FAULT_TLB_DATA1"}, \
+	{ERROR_GEN6,               0,      0, "ERROR_GEN6"}, \
+	{DONE_REG,                 0,      0, "DONE_REG"}, \
+	{HSW_GTT_CACHE_EN,         0,      0, "HSW_GTT_CACHE_EN"}

-:39: ERROR:COMPLEX_MACRO: Macros with complex values should be enclosed in parentheses
#39: FILE: drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c:42:
+#define COMMON_BASE_ENGINE_INSTANCE() \
 	{RING_PSMI_CTL(0),         0,      0, "RC PSMI"}, \
 	{RING_ESR(0),              0,      0, "ESR"}, \
 	{RING_DMA_FADD(0),         0,      0, "RING_DMA_FADD_LDW"}, \

total: 2 errors, 0 warnings, 0 checks, 146 lines checked
5811889fcbe8 drm/i915/guc: Add GuC's error state capture output structures.
fb2d26808423 drm/i915/guc: Update GuC-log relay function names
ab1d23c2c860 drm/i915/guc: Add capture region into intel_guc_log
-:52: CHECK:MULTIPLE_ASSIGNMENTS: multiple assignments should be avoided
#52: FILE: drivers/gpu/drm/i915/gt/uc/intel_guc_log.c:216:
+	log_buf_state = src_data = log->buf_addr;

total: 0 errors, 0 warnings, 1 checks, 155 lines checked
c7f73c19f253 drm/i915/guc: Check sizing of guc_capture output
1ca524a02f27 drm/i915/guc: Extract GuC error capture lists on G2H notification.
-:323: WARNING:NEEDLESS_IF: kfree(NULL) is safe and this check is probably not required
#323: FILE: drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c:861:
+		if (node->reginfo[i].regs)
+			kfree(node->reginfo[i].regs);

-:422: WARNING:NEEDLESS_IF: kfree(NULL) is safe and this check is probably not required
#422: FILE: drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c:960:
+		if (new->reginfo[i].regs)
+			kfree(new->reginfo[i].regs);

-:613: WARNING:NEEDLESS_IF: kfree(NULL) is safe and this check is probably not required
#613: FILE: drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c:1151:
+		if (node) /* else free it */
+			kfree(node);

total: 0 errors, 3 warnings, 0 checks, 762 lines checked
ee1209b4b0cb drm/i915/guc: Pre-allocate output nodes for extraction
29b77d53fd35 drm/i915/guc: Plumb GuC-capture into gpu_coredump
-:129: CHECK:PARENTHESIS_ALIGNMENT: Alignment should match open parenthesis
#129: FILE: drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c:1391:
+		if (n->eng_inst == GUC_ID_TO_ENGINE_INSTANCE(ee->engine->guc_id) &&
+			n->eng_class == GUC_ID_TO_ENGINE_CLASS(ee->engine->guc_id) &&

total: 0 errors, 0 warnings, 1 checks, 686 lines checked
ba46f0a21c51 drm/i915/guc: Print the GuC error capture output register list.
-:128: CHECK:MACRO_ARG_REUSE: Macro argument reuse 'a' - possible side-effects?
#128: FILE: drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c:1402:
+#define __out(a, ...) \
+	do { \
+		drm_warn((&(a)->drm), __VA_ARGS__); \
+		i915_error_printf((a), __VA_ARGS__); \
+	} while (0)

-:138: CHECK:MACRO_ARG_REUSE: Macro argument reuse 'ebuf' - possible side-effects?
#138: FILE: drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c:1412:
+#define GCAP_PRINT_INTEL_ENG_INFO(ebuf, eng) \
+	do { \
+		__out(ebuf, "    i915-Eng-Name: %s command stream\n", \
+		      (eng)->name); \
+		__out(ebuf, "    i915-Eng-Inst-Class: 0x%02x\n", (eng)->class); \
+		__out(ebuf, "    i915-Eng-Inst-Id: 0x%02x\n", (eng)->instance); \
+		__out(ebuf, "    i915-Eng-LogicalMask: 0x%08x\n", \
+		      (eng)->logical_mask); \
+	} while (0)

-:138: CHECK:MACRO_ARG_REUSE: Macro argument reuse 'eng' - possible side-effects?
#138: FILE: drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c:1412:
+#define GCAP_PRINT_INTEL_ENG_INFO(ebuf, eng) \
+	do { \
+		__out(ebuf, "    i915-Eng-Name: %s command stream\n", \
+		      (eng)->name); \
+		__out(ebuf, "    i915-Eng-Inst-Class: 0x%02x\n", (eng)->class); \
+		__out(ebuf, "    i915-Eng-Inst-Id: 0x%02x\n", (eng)->instance); \
+		__out(ebuf, "    i915-Eng-LogicalMask: 0x%08x\n", \
+		      (eng)->logical_mask); \
+	} while (0)

-:148: CHECK:MACRO_ARG_REUSE: Macro argument reuse 'ebuf' - possible side-effects?
#148: FILE: drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c:1422:
+#define GCAP_PRINT_GUC_INST_INFO(ebuf, node) \
+	do { \
+		__out(ebuf, "    GuC-Engine-Inst-Id: 0x%08x\n", \
+		      (node)->eng_inst); \
+		__out(ebuf, "    GuC-Context-Id: 0x%08x\n", (node)->guc_id); \
+		__out(ebuf, "    LRCA: 0x%08x\n", (node)->lrca); \
+	} while (0)

-:148: CHECK:MACRO_ARG_REUSE: Macro argument reuse 'node' - possible side-effects?
#148: FILE: drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c:1422:
+#define GCAP_PRINT_GUC_INST_INFO(ebuf, node) \
+	do { \
+		__out(ebuf, "    GuC-Engine-Inst-Id: 0x%08x\n", \
+		      (node)->eng_inst); \
+		__out(ebuf, "    GuC-Context-Id: 0x%08x\n", (node)->guc_id); \
+		__out(ebuf, "    LRCA: 0x%08x\n", (node)->lrca); \
+	} while (0)

total: 0 errors, 0 warnings, 5 checks, 287 lines checked



^ permalink raw reply	[flat|nested] 32+ messages in thread

* [Intel-gfx] ✗ Fi.CI.SPARSE: warning for Add GuC Error Capture Support (rev7)
  2022-02-26  9:55 ` [Intel-gfx] " Alan Previn
                   ` (14 preceding siblings ...)
  (?)
@ 2022-02-26 10:12 ` Patchwork
  -1 siblings, 0 replies; 32+ messages in thread
From: Patchwork @ 2022-02-26 10:12 UTC (permalink / raw)
  To: Alan Previn; +Cc: intel-gfx

== Series Details ==

Series: Add GuC Error Capture Support (rev7)
URL   : https://patchwork.freedesktop.org/series/97187/
State : warning

== Summary ==

$ dim sparse --fast origin/drm-tip
Sparse version: v0.6.2
Fast mode used, each commit won't be checked separately.



^ permalink raw reply	[flat|nested] 32+ messages in thread

* [Intel-gfx] ✗ Fi.CI.BAT: failure for Add GuC Error Capture Support (rev7)
  2022-02-26  9:55 ` [Intel-gfx] " Alan Previn
                   ` (15 preceding siblings ...)
  (?)
@ 2022-02-26 10:43 ` Patchwork
  -1 siblings, 0 replies; 32+ messages in thread
From: Patchwork @ 2022-02-26 10:43 UTC (permalink / raw)
  To: Alan Previn; +Cc: intel-gfx

[-- Attachment #1: Type: text/plain, Size: 7880 bytes --]

== Series Details ==

Series: Add GuC Error Capture Support (rev7)
URL   : https://patchwork.freedesktop.org/series/97187/
State : failure

== Summary ==

CI Bug Log - changes from CI_DRM_11291 -> Patchwork_22428
====================================================

Summary
-------

  **FAILURE**

  Serious unknown changes coming with Patchwork_22428 absolutely need to be
  verified manually.
  
  If you think the reported changes have nothing to do with the changes
  introduced in Patchwork_22428, please notify your bug team to allow them
  to document this new failure mode, which will reduce false positives in CI.

  External URL: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_22428/index.html

Participating hosts (40 -> 41)
------------------------------

  Additional (2): bat-jsl-2 fi-pnv-d510 
  Missing    (1): fi-bsw-cyan 

Possible new issues
-------------------

  Here are the unknown changes that may have been introduced in Patchwork_22428:

### IGT changes ###

#### Possible regressions ####

  * igt@i915_hangman@error-state-basic:
    - bat-dg1-5:          [PASS][1] -> [DMESG-WARN][2]
   [1]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_11291/bat-dg1-5/igt@i915_hangman@error-state-basic.html
   [2]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_22428/bat-dg1-5/igt@i915_hangman@error-state-basic.html
    - bat-dg1-6:          [PASS][3] -> [DMESG-WARN][4]
   [3]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_11291/bat-dg1-6/igt@i915_hangman@error-state-basic.html
   [4]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_22428/bat-dg1-6/igt@i915_hangman@error-state-basic.html

  
#### Suppressed ####

  The following results come from untrusted machines, tests, or statuses.
  They do not affect the overall result.

  * igt@i915_hangman@error-state-basic:
    - {bat-adlp-6}:       [PASS][5] -> [DMESG-WARN][6]
   [5]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_11291/bat-adlp-6/igt@i915_hangman@error-state-basic.html
   [6]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_22428/bat-adlp-6/igt@i915_hangman@error-state-basic.html

  * igt@runner@aborted:
    - {bat-dg2-9}:        [FAIL][7] ([i915#4312]) -> [FAIL][8]
   [7]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_11291/bat-dg2-9/igt@runner@aborted.html
   [8]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_22428/bat-dg2-9/igt@runner@aborted.html

  
Known issues
------------

  Here are the changes found in Patchwork_22428 that come from known issues:

### IGT changes ###

#### Issues hit ####

  * igt@gem_exec_suspend@basic-s3:
    - fi-skl-6600u:       NOTRUN -> [FAIL][9] ([i915#4547])
   [9]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_22428/fi-skl-6600u/igt@gem_exec_suspend@basic-s3.html

  * igt@i915_hangman@error-state-basic:
    - fi-kbl-guc:         [PASS][10] -> [DMESG-WARN][11] ([i915#1610])
   [10]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_11291/fi-kbl-guc/igt@i915_hangman@error-state-basic.html
   [11]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_22428/fi-kbl-guc/igt@i915_hangman@error-state-basic.html
    - fi-skl-guc:         [PASS][12] -> [DMESG-WARN][13] ([i915#1610])
   [12]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_11291/fi-skl-guc/igt@i915_hangman@error-state-basic.html
   [13]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_22428/fi-skl-guc/igt@i915_hangman@error-state-basic.html
    - fi-cfl-guc:         [PASS][14] -> [DMESG-WARN][15] ([i915#1610])
   [14]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_11291/fi-cfl-guc/igt@i915_hangman@error-state-basic.html
   [15]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_22428/fi-cfl-guc/igt@i915_hangman@error-state-basic.html

  * igt@prime_vgem@basic-userptr:
    - fi-pnv-d510:        NOTRUN -> [SKIP][16] ([fdo#109271]) +57 similar issues
   [16]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_22428/fi-pnv-d510/igt@prime_vgem@basic-userptr.html

  * igt@runner@aborted:
    - bat-dg1-5:          NOTRUN -> [FAIL][17] ([i915#4312])
   [17]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_22428/bat-dg1-5/igt@runner@aborted.html
    - fi-kbl-guc:         NOTRUN -> [FAIL][18] ([i915#2426] / [i915#4312])
   [18]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_22428/fi-kbl-guc/igt@runner@aborted.html
    - fi-rkl-guc:         NOTRUN -> [FAIL][19] ([i915#2426])
   [19]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_22428/fi-rkl-guc/igt@runner@aborted.html
    - bat-dg1-6:          NOTRUN -> [FAIL][20] ([i915#4312])
   [20]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_22428/bat-dg1-6/igt@runner@aborted.html
    - fi-cfl-guc:         NOTRUN -> [FAIL][21] ([i915#2426] / [i915#4312])
   [21]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_22428/fi-cfl-guc/igt@runner@aborted.html
    - fi-skl-guc:         NOTRUN -> [FAIL][22] ([i915#2426] / [i915#4312])
   [22]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_22428/fi-skl-guc/igt@runner@aborted.html

  
#### Possible fixes ####

  * igt@i915_selftest@live@perf:
    - {fi-tgl-dsi}:       [DMESG-WARN][23] ([i915#2867]) -> [PASS][24] +17 similar issues
   [23]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_11291/fi-tgl-dsi/igt@i915_selftest@live@perf.html
   [24]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_22428/fi-tgl-dsi/igt@i915_selftest@live@perf.html

  
  {name}: This element is suppressed. This means it is ignored when computing
          the status of the difference (SUCCESS, WARNING, or FAILURE).

  [fdo#109271]: https://bugs.freedesktop.org/show_bug.cgi?id=109271
  [fdo#109285]: https://bugs.freedesktop.org/show_bug.cgi?id=109285
  [fdo#111827]: https://bugs.freedesktop.org/show_bug.cgi?id=111827
  [i915#1610]: https://gitlab.freedesktop.org/drm/intel/issues/1610
  [i915#2190]: https://gitlab.freedesktop.org/drm/intel/issues/2190
  [i915#2426]: https://gitlab.freedesktop.org/drm/intel/issues/2426
  [i915#2867]: https://gitlab.freedesktop.org/drm/intel/issues/2867
  [i915#3301]: https://gitlab.freedesktop.org/drm/intel/issues/3301
  [i915#4103]: https://gitlab.freedesktop.org/drm/intel/issues/4103
  [i915#4312]: https://gitlab.freedesktop.org/drm/intel/issues/4312
  [i915#4547]: https://gitlab.freedesktop.org/drm/intel/issues/4547
  [i915#4613]: https://gitlab.freedesktop.org/drm/intel/issues/4613
  [i915#5127]: https://gitlab.freedesktop.org/drm/intel/issues/5127
  [i915#533]: https://gitlab.freedesktop.org/drm/intel/issues/533


Build changes
-------------

  * Linux: CI_DRM_11291 -> Patchwork_22428

  CI-20190529: 20190529
  CI_DRM_11291: 1e3f898ee67cb2fd8c58799ae8a094f73054c8b9 @ git://anongit.freedesktop.org/gfx-ci/linux
  IGT_6359: 57049558c452272b27eeb099fac07e55a924bbf9 @ https://gitlab.freedesktop.org/drm/igt-gpu-tools.git
  Patchwork_22428: ba46f0a21c5138624da12566e3e6ab9ea1d5fe65 @ git://anongit.freedesktop.org/gfx-ci/linux


== Linux commits ==

ba46f0a21c51 drm/i915/guc: Print the GuC error capture output register list.
29b77d53fd35 drm/i915/guc: Plumb GuC-capture into gpu_coredump
ee1209b4b0cb drm/i915/guc: Pre-allocate output nodes for extraction
1ca524a02f27 drm/i915/guc: Extract GuC error capture lists on G2H notification.
c7f73c19f253 drm/i915/guc: Check sizing of guc_capture output
ab1d23c2c860 drm/i915/guc: Add capture region into intel_guc_log
fb2d26808423 drm/i915/guc: Update GuC-log relay function names
5811889fcbe8 drm/i915/guc: Add GuC's error state capture output structures.
630f7006ffde drm/i915/guc: Add Gen9 registers for GuC error state capture.
b3559be6c43d drm/i915/guc: Add DG2 registers for GuC error state capture.
6de0bc96b40d drm/i915/guc: Add XE_LP steered register lists support
47efff77442d drm/i915/guc: Add XE_LP static registers for GuC error capture.
fa51e0f956f6 drm/i915/guc: Update GuC ADS size for error capture lists

== Logs ==

For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_22428/index.html

[-- Attachment #2: Type: text/html, Size: 9042 bytes --]

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [Intel-gfx] [PATCH v7 01/13] drm/i915/guc: Update GuC ADS size for error capture lists
  2022-02-26  9:55 ` [Intel-gfx] [PATCH v7 01/13] drm/i915/guc: Update GuC ADS size for error capture lists Alan Previn
@ 2022-02-26 20:22   ` Teres Alexis, Alan Previn
  2022-02-27 21:02   ` kernel test robot
  2022-03-10  5:30   ` Matthew Brost
  2 siblings, 0 replies; 32+ messages in thread
From: Teres Alexis, Alan Previn @ 2022-02-26 20:22 UTC (permalink / raw)
  To: intel-gfx

[-- Attachment #1: Type: text/plain, Size: 1004 bytes --]

On 2/26/2022 1:55 AM, Alan Previn wrote:

> -static void guc_capture_list_init(struct intel_guc *guc)
> +static int
> +guc_capture_prep_lists(struct intel_guc *guc)
>   {

...

> -	/* FIXME: Populate a proper capture list */
> +	/* first, set aside the first page for a capture_list with zero descriptors */
> +	total_size = PAGE_SIZE;
> +	if (!iosys_map_is_null(&guc->ads_map)) {
> +		file = shmem_create_from_data("guc-err-cap", null_header, sizeof(null_header));

Alan: CI caught a bug - above line was triggering memory allocation
i completely forgot ... will fix to match the other ADS err-capture lists
in this function - i.e. intel_guc_capture will allocate on first boot
and cache it.

> +		if (!IS_ERR(file)) {
> +			shmem_read_to_iosys_map(file, 0, &guc->ads_map,
> +						ggtt, sizeof(null_header));
> +			fput(file);
> +		} else {
> +			drm_dbg(&i915->drm, "GuC-capture: failed shmem for nulllist = 0x%016lx",
> +				PTR_ERR(file));
> +		}
> +		null_ggtt = ggtt;
> +		ggtt += PAGE_SIZE;
> +	}

[-- Attachment #2: Type: text/html, Size: 1726 bytes --]

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [Intel-gfx] [PATCH v7 01/13] drm/i915/guc: Update GuC ADS size for error capture lists
  2022-02-26  9:55 ` [Intel-gfx] [PATCH v7 01/13] drm/i915/guc: Update GuC ADS size for error capture lists Alan Previn
  2022-02-26 20:22   ` Teres Alexis, Alan Previn
@ 2022-02-27 21:02   ` kernel test robot
  2022-03-10  5:30   ` Matthew Brost
  2 siblings, 0 replies; 32+ messages in thread
From: kernel test robot @ 2022-02-27 21:02 UTC (permalink / raw)
  To: Alan Previn, intel-gfx; +Cc: llvm, kbuild-all, Alan Previn

Hi Alan,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on drm-tip/drm-tip]
[cannot apply to drm-intel/for-linux-next drm-exynos/exynos-drm-next drm/drm-next tegra-drm/drm/tegra/for-next airlied/drm-next v5.17-rc5 next-20220225]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Alan-Previn/Add-GuC-Error-Capture-Support/20220226-175600
base:   git://anongit.freedesktop.org/drm/drm-tip drm-tip
config: i386-allyesconfig (https://download.01.org/0day-ci/archive/20220228/202202280456.Z5q4zizH-lkp@intel.com/config)
compiler: clang version 15.0.0 (https://github.com/llvm/llvm-project d271fc04d5b97b12e6b797c6067d3c96a8d7470e)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/0day-ci/linux/commit/9ea292ae1b189632494171f6dae9cbf7e4e5f30c
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Alan-Previn/Add-GuC-Error-Capture-Support/20220226-175600
        git checkout 9ea292ae1b189632494171f6dae9cbf7e4e5f30c
        # save the config file to linux build tree
        mkdir build_dir
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=i386 SHELL=/bin/bash

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

>> drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c:223:1: error: no previous prototype for function 'intel_guc_capture_getlistsize' [-Werror,-Wmissing-prototypes]
   intel_guc_capture_getlistsize(struct intel_guc *guc, u32 owner, u32 type, u32 classid,
   ^
   drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c:222:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
   int
   ^
   static 
>> drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c:253:1: error: no previous prototype for function 'intel_guc_capture_getlist' [-Werror,-Wmissing-prototypes]
   intel_guc_capture_getlist(struct intel_guc *guc, u32 owner, u32 type, u32 classid,
   ^
   drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c:252:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
   int
   ^
   static 
   drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c:332:6: error: no previous prototype for function 'intel_guc_capture_destroy' [-Werror,-Wmissing-prototypes]
   void intel_guc_capture_destroy(struct intel_guc *guc)
        ^
   drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c:332:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
   void intel_guc_capture_destroy(struct intel_guc *guc)
   ^
   static 
   drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c:343:5: error: no previous prototype for function 'intel_guc_capture_init' [-Werror,-Wmissing-prototypes]
   int intel_guc_capture_init(struct intel_guc *guc)
       ^
   drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c:343:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
   int intel_guc_capture_init(struct intel_guc *guc)
   ^
   static 
   4 errors generated.


vim +/intel_guc_capture_getlistsize +223 drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c

   221	
   222	int
 > 223	intel_guc_capture_getlistsize(struct intel_guc *guc, u32 owner, u32 type, u32 classid,
   224				      size_t *size)
   225	{
   226		struct drm_i915_private *i915 = guc_to_gt(guc)->i915;
   227		struct __guc_state_capture_priv *gc = guc->capture.priv;
   228		struct __guc_capture_ads_cache *cache = &gc->ads_cache[owner][type][classid];
   229		int num_regs;
   230	
   231		if (!gc->reglists)
   232			return -ENODEV;
   233	
   234		if (cache->is_valid) {
   235			*size = cache->size;
   236			return cache->status;
   237		}
   238	
   239		num_regs = guc_cap_list_num_regs(gc, owner, type, classid);
   240		if (!num_regs) {
   241			guc_capture_warn_with_list_info(i915, "Missing register list size",
   242							owner, type, classid);
   243			return -ENODATA;
   244		}
   245	
   246		*size = PAGE_ALIGN((sizeof(struct guc_debug_capture_list)) +
   247				   (num_regs * sizeof(struct guc_mmio_reg)));
   248	
   249		return 0;
   250	}
   251	
   252	int
 > 253	intel_guc_capture_getlist(struct intel_guc *guc, u32 owner, u32 type, u32 classid,
   254				  struct file **fileoutptr)
   255	{
   256		struct __guc_state_capture_priv *gc = guc->capture.priv;
   257		struct __guc_capture_ads_cache *cache = &gc->ads_cache[owner][type][classid];
   258		struct drm_i915_private *i915 = guc_to_gt(guc)->i915;
   259		struct guc_debug_capture_list *listnode;
   260		struct file *file;
   261		u8 *caplist, *tmp;
   262		size_t size = 0;
   263		int ret, num_regs;
   264	
   265		if (!gc->reglists)
   266			return -ENODEV;
   267	
   268		if (cache->is_valid) {
   269			*fileoutptr = cache->file;
   270			return cache->status;
   271		}
   272	
   273		ret = intel_guc_capture_getlistsize(guc, owner, type, classid, &size);
   274		if (ret) {
   275			cache->is_valid = true;
   276			cache->file = NULL;
   277			cache->size = 0;
   278			cache->status = ret;
   279			return ret;
   280		}
   281	
   282		caplist = kzalloc(size, GFP_KERNEL);
   283		if (!caplist)
   284			return -ENOMEM;
   285	
   286		/* populate capture list header */
   287		tmp = caplist;
   288		num_regs = guc_cap_list_num_regs(guc->capture.priv, owner, type, classid);
   289		listnode = (struct guc_debug_capture_list *)tmp;
   290		listnode->header.info = FIELD_PREP(GUC_CAPTURELISTHDR_NUMDESCR, (u32)num_regs);
   291	
   292		/* populate list of register descriptor */
   293		tmp += sizeof(struct guc_debug_capture_list);
   294		guc_capture_list_init(guc, owner, type, classid, (struct guc_mmio_reg *)tmp, num_regs);
   295	
   296		/* ADS only takes file handles, so we keep that as our cache */
   297		file = shmem_create_from_data("guc-err-cap", caplist, size);
   298		kfree(caplist);
   299		if (IS_ERR(file)) {
   300			drm_warn(&i915->drm, "GuC-capture: cant create shmem for caplist = 0x%016lx", PTR_ERR(file));
   301			return PTR_ERR(file);
   302		}
   303	
   304		/* cache this list */
   305		cache->file = file;
   306		cache->size = size;
   307		cache->status = 0;
   308		cache->is_valid = true;
   309	
   310		*fileoutptr = file;
   311	
   312		return 0;
   313	}
   314	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [Intel-gfx] [PATCH v7 09/13] drm/i915/guc: Check sizing of guc_capture output
  2022-02-26  9:55 ` [Intel-gfx] [PATCH v7 09/13] drm/i915/guc: Check sizing of guc_capture output Alan Previn
@ 2022-02-28 22:32   ` kernel test robot
  0 siblings, 0 replies; 32+ messages in thread
From: kernel test robot @ 2022-02-28 22:32 UTC (permalink / raw)
  To: Alan Previn, intel-gfx; +Cc: llvm, kbuild-all, Alan Previn

Hi Alan,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on drm-tip/drm-tip]
[cannot apply to drm-intel/for-linux-next drm-exynos/exynos-drm-next drm/drm-next tegra-drm/drm/tegra/for-next airlied/drm-next v5.17-rc6 next-20220228]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Alan-Previn/Add-GuC-Error-Capture-Support/20220226-175600
base:   git://anongit.freedesktop.org/drm/drm-tip drm-tip
config: i386-allyesconfig (https://download.01.org/0day-ci/archive/20220301/202203010622.2JyDEoHX-lkp@intel.com/config)
compiler: clang version 15.0.0 (https://github.com/llvm/llvm-project d271fc04d5b97b12e6b797c6067d3c96a8d7470e)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/0day-ci/linux/commit/4c41838b35d9a5c0bcb4380e0064cb2d5d33661f
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Alan-Previn/Add-GuC-Error-Capture-Support/20220226-175600
        git checkout 4c41838b35d9a5c0bcb4380e0064cb2d5d33661f
        # save the config file to linux build tree
        mkdir build_dir
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=i386 SHELL=/bin/bash

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

   drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c:555:1: error: no previous prototype for function 'intel_guc_capture_getlistsize' [-Werror,-Wmissing-prototypes]
   intel_guc_capture_getlistsize(struct intel_guc *guc, u32 owner, u32 type, u32 classid,
   ^
   drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c:554:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
   int
   ^
   static 
   drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c:585:1: error: no previous prototype for function 'intel_guc_capture_getlist' [-Werror,-Wmissing-prototypes]
   intel_guc_capture_getlist(struct intel_guc *guc, u32 owner, u32 type, u32 classid,
   ^
   drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c:584:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
   int
   ^
   static 
>> drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c:648:5: error: no previous prototype for function 'intel_guc_capture_output_min_size_est' [-Werror,-Wmissing-prototypes]
   int intel_guc_capture_output_min_size_est(struct intel_guc *guc)
       ^
   drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c:648:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
   int intel_guc_capture_output_min_size_est(struct intel_guc *guc)
   ^
   static 
   drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c:711:6: error: no previous prototype for function 'intel_guc_capture_destroy' [-Werror,-Wmissing-prototypes]
   void intel_guc_capture_destroy(struct intel_guc *guc)
        ^
   drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c:711:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
   void intel_guc_capture_destroy(struct intel_guc *guc)
   ^
   static 
   drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c:727:5: error: no previous prototype for function 'intel_guc_capture_init' [-Werror,-Wmissing-prototypes]
   int intel_guc_capture_init(struct intel_guc *guc)
       ^
   drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c:727:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
   int intel_guc_capture_init(struct intel_guc *guc)
   ^
   static 
   5 errors generated.


vim +/intel_guc_capture_output_min_size_est +648 drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c

   646	
   647	#define GUC_CAPTURE_OVERBUFFER_MULTIPLIER 3
 > 648	int intel_guc_capture_output_min_size_est(struct intel_guc *guc)
   649	{
   650		struct intel_gt *gt = guc_to_gt(guc);
   651		struct intel_engine_cs *engine;
   652		enum intel_engine_id id;
   653		int worst_min_size = 0, num_regs = 0;
   654		size_t tmp = 0;
   655	
   656		/*
   657		 * If every single engine-instance suffered a failure in quick succession but
   658		 * were all unrelated, then a burst of multiple error-capture events would dump
   659		 * registers for every one engine instance, one at a time. In this case, GuC
   660		 * would even dump the global-registers repeatedly.
   661		 *
   662		 * For each engine instance, there would be 1 x guc_state_capture_group_t output
   663		 * followed by 3 x guc_state_capture_t lists. The latter is how the register
   664		 * dumps are split across different register types (where the '3' are global vs class
   665		 * vs instance). Finally, let's multiply the whole thing by 3x (just so we are
   666		 * not limited to just 1 round of data in a worst case full register dump log)
   667		 *
   668		 * NOTE: intel_guc_log that allocates the log buffer would round this size up to
   669		 * a power of two.
   670		 */
   671	
   672		for_each_engine(engine, gt, id) {
   673			worst_min_size += sizeof(struct guc_state_capture_group_header_t) +
   674					  (3 * sizeof(struct guc_state_capture_header_t));
   675	
   676			if (!intel_guc_capture_getlistsize(guc, 0, GUC_CAPTURE_LIST_TYPE_GLOBAL, 0, &tmp))
   677				num_regs += tmp;
   678	
   679			if (!intel_guc_capture_getlistsize(guc, 0, GUC_CAPTURE_LIST_TYPE_ENGINE_CLASS,
   680							   engine->class, &tmp)) {
   681				num_regs += tmp;
   682			}
   683			if (!intel_guc_capture_getlistsize(guc, 0, GUC_CAPTURE_LIST_TYPE_ENGINE_INSTANCE,
   684							   engine->class, &tmp)) {
   685				num_regs += tmp;
   686			}
   687		}
   688	
   689		worst_min_size += (num_regs * sizeof(struct guc_mmio_reg));
   690	
   691		return (worst_min_size * GUC_CAPTURE_OVERBUFFER_MULTIPLIER);
   692	}
   693	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [Intel-gfx] [PATCH v7 02/13] drm/i915/guc: Add XE_LP static registers for GuC error capture.
  2022-02-26  9:55 ` [Intel-gfx] [PATCH v7 02/13] drm/i915/guc: Add XE_LP static registers for GuC error capture Alan Previn
@ 2022-03-10  0:05   ` Umesh Nerlige Ramappa
  0 siblings, 0 replies; 32+ messages in thread
From: Umesh Nerlige Ramappa @ 2022-03-10  0:05 UTC (permalink / raw)
  To: Alan Previn; +Cc: intel-gfx

On Sat, Feb 26, 2022 at 01:55:30AM -0800, Alan Previn wrote:
>Add device specific tables and register lists to cover different engines
>class types for GuC error state capture for XE_LP products.
>
>Signed-off-by: Alan Previn <alan.previn.teres.alexis@intel.com>

lgtm,
Reviewed-by: Umesh Nerlige Ramappa <umesh.nerlige.ramappa@intel.com>

Umesh

>---
> .../gpu/drm/i915/gt/uc/intel_guc_capture.c    | 116 ++++++++++++++----
> drivers/gpu/drm/i915/gt/uc/intel_guc_fwif.h   |   4 +-
> 2 files changed, 97 insertions(+), 23 deletions(-)
>
>diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c
>index 647a118aa3c3..fb3ca734ef97 100644
>--- a/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c
>+++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c
>@@ -19,43 +19,109 @@
>
> /*
>  * 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
>+ * NOTE1: For engine-registers, GuC only needs the register offsets
>+ *        from the engine-mmio-base
>  */
>+#define COMMON_GEN12BASE_GLOBAL() \
>+	{GEN12_FAULT_TLB_DATA0,    0,      0, "GEN12_FAULT_TLB_DATA0"}, \
>+	{GEN12_FAULT_TLB_DATA1,    0,      0, "GEN12_FAULT_TLB_DATA1"}, \
>+	{FORCEWAKE_MT,             0,      0, "FORCEWAKE"}, \
>+	{GEN12_AUX_ERR_DBG,        0,      0, "AUX_ERR_DBG"}, \
>+	{GEN12_GAM_DONE,           0,      0, "GAM_DONE"}, \
>+	{GEN12_RING_FAULT_REG,     0,      0, "FAULT_REG"}
>+
>+#define COMMON_GEN12BASE_ENGINE_INSTANCE() \
>+	{RING_PSMI_CTL(0),         0,      0, "RC PSMI"}, \
>+	{RING_ESR(0),              0,      0, "ESR"}, \
>+	{RING_DMA_FADD(0),         0,      0, "RING_DMA_FADD_LDW"}, \
>+	{RING_DMA_FADD_UDW(0),     0,      0, "RING_DMA_FADD_UDW"}, \
>+	{RING_IPEIR(0),            0,      0, "IPEIR"}, \
>+	{RING_IPEHR(0),            0,      0, "IPEHR"}, \
>+	{RING_INSTPS(0),           0,      0, "INSTPS"}, \
>+	{RING_BBADDR(0),           0,      0, "RING_BBADDR_LOW32"}, \
>+	{RING_BBADDR_UDW(0),       0,      0, "RING_BBADDR_UP32"}, \
>+	{RING_BBSTATE(0),          0,      0, "BB_STATE"}, \
>+	{CCID(0),                  0,      0, "CCID"}, \
>+	{RING_ACTHD(0),            0,      0, "ACTHD_LDW"}, \
>+	{RING_ACTHD_UDW(0),        0,      0, "ACTHD_UDW"}, \
>+	{RING_INSTPM(0),           0,      0, "INSTPM"}, \
>+	{RING_INSTDONE(0),         0,      0, "INSTDONE"}, \
>+	{RING_NOPID(0),            0,      0, "RING_NOPID"}, \
>+	{RING_START(0),            0,      0, "START"}, \
>+	{RING_HEAD(0),             0,      0, "HEAD"}, \
>+	{RING_TAIL(0),             0,      0, "TAIL"}, \
>+	{RING_CTL(0),              0,      0, "CTL"}, \
>+	{RING_MI_MODE(0),          0,      0, "MODE"}, \
>+	{RING_CONTEXT_CONTROL(0),  0,      0, "RING_CONTEXT_CONTROL"}, \
>+	{RING_HWS_PGA(0),          0,      0, "HWS"}, \
>+	{RING_MODE_GEN7(0),        0,      0, "GFX_MODE"}, \
>+	{GEN8_RING_PDP_LDW(0, 0),  0,      0, "PDP0_LDW"}, \
>+	{GEN8_RING_PDP_UDW(0, 0),  0,      0, "PDP0_UDW"}, \
>+	{GEN8_RING_PDP_LDW(0, 1),  0,      0, "PDP1_LDW"}, \
>+	{GEN8_RING_PDP_UDW(0, 1),  0,      0, "PDP1_UDW"}, \
>+	{GEN8_RING_PDP_LDW(0, 2),  0,      0, "PDP2_LDW"}, \
>+	{GEN8_RING_PDP_UDW(0, 2),  0,      0, "PDP2_UDW"}, \
>+	{GEN8_RING_PDP_LDW(0, 3),  0,      0, "PDP3_LDW"}, \
>+	{GEN8_RING_PDP_UDW(0, 3),  0,      0, "PDP3_UDW"}
>+
>+#define COMMON_GEN12BASE_HAS_EU() \
>+	{EIR,                      0,      0, "EIR"}
>+
>+#define COMMON_GEN12BASE_RENDER() \
>+	{GEN7_SC_INSTDONE,         0,      0, "GEN7_SC_INSTDONE"}, \
>+	{GEN12_SC_INSTDONE_EXTRA,  0,      0, "GEN12_SC_INSTDONE_EXTRA"}, \
>+	{GEN12_SC_INSTDONE_EXTRA2, 0,      0, "GEN12_SC_INSTDONE_EXTRA2"}
>+
>+#define COMMON_GEN12BASE_VEC() \
>+	{GEN12_SFC_DONE(0),        0,      0, "SFC_DONE[0]"}, \
>+	{GEN12_SFC_DONE(1),        0,      0, "SFC_DONE[1]"}, \
>+	{GEN12_SFC_DONE(2),        0,      0, "SFC_DONE[2]"}, \
>+	{GEN12_SFC_DONE(3),        0,      0, "SFC_DONE[3]"}
>+
> /* XE_LPD - Global */
> static const struct __guc_mmio_reg_descr xe_lpd_global_regs[] = {
>-	{GEN12_RING_FAULT_REG,     0,      0, "GEN12_RING_FAULT_REG"}
>+	COMMON_GEN12BASE_GLOBAL(),
> };
>
> /* XE_LPD - Render / Compute Per-Class */
> static const struct __guc_mmio_reg_descr xe_lpd_rc_class_regs[] = {
>-	{EIR,                      0,      0, "EIR"}
>+	COMMON_GEN12BASE_HAS_EU(),
>+	COMMON_GEN12BASE_RENDER(),
> };
>
> /* XE_LPD - Render / Compute Per-Engine-Instance */
> static const struct __guc_mmio_reg_descr xe_lpd_rc_inst_regs[] = {
>-	{RING_HEAD(0),             0,      0, "RING_HEAD"},
>-	{RING_TAIL(0),             0,      0, "RING_TAIL"},
>+	COMMON_GEN12BASE_ENGINE_INSTANCE(),
> };
>
> /* XE_LPD - Media Decode/Encode Per-Class */
> static const struct __guc_mmio_reg_descr xe_lpd_vd_class_regs[] = {
>+	COMMON_GEN12BASE_ENGINE_INSTANCE(),
> };
>
> /* XE_LPD - Media Decode/Encode Per-Engine-Instance */
> static const struct __guc_mmio_reg_descr xe_lpd_vd_inst_regs[] = {
>-	{RING_HEAD(0),             0,      0, "RING_HEAD"},
>-	{RING_TAIL(0),             0,      0, "RING_TAIL"},
>+	COMMON_GEN12BASE_ENGINE_INSTANCE(),
> };
>
> /* XE_LPD - Video Enhancement Per-Class */
> static const struct __guc_mmio_reg_descr xe_lpd_vec_class_regs[] = {
>+	COMMON_GEN12BASE_VEC(),
> };
>
> /* XE_LPD - Video Enhancement Per-Engine-Instance */
> static const struct __guc_mmio_reg_descr xe_lpd_vec_inst_regs[] = {
>-	{RING_HEAD(0),             0,      0, "RING_HEAD"},
>-	{RING_TAIL(0),             0,      0, "RING_TAIL"},
>+	COMMON_GEN12BASE_ENGINE_INSTANCE(),
>+};
>+
>+/* XE_LPD - Blitter Per-Engine-Instance */
>+static const struct __guc_mmio_reg_descr xe_lpd_blt_inst_regs[] = {
>+	COMMON_GEN12BASE_ENGINE_INSTANCE(),
>+};
>+
>+/* XE_LPD - Blitter Per-Class */
>+/* XE_LPD - Media Decode/Encode Per-Class */
>+static const struct __guc_mmio_reg_descr empty_regs_list[] = {
> };
>
> #define TO_GCAP_DEF_OWNER(x) (GUC_CAPTURE_LIST_INDEX_##x)
>@@ -74,10 +140,12 @@ static const struct __guc_mmio_reg_descr_group xe_lpd_lists[] = {
> 	MAKE_REGLIST(xe_lpd_global_regs, PF, GLOBAL, 0),
> 	MAKE_REGLIST(xe_lpd_rc_class_regs, PF, ENGINE_CLASS, GUC_RENDER_CLASS),
> 	MAKE_REGLIST(xe_lpd_rc_inst_regs, PF, ENGINE_INSTANCE, GUC_RENDER_CLASS),
>-	MAKE_REGLIST(xe_lpd_vd_class_regs, PF, ENGINE_CLASS, GUC_VIDEO_CLASS),
>+	MAKE_REGLIST(empty_regs_list, PF, ENGINE_CLASS, GUC_VIDEO_CLASS),
> 	MAKE_REGLIST(xe_lpd_vd_inst_regs, PF, ENGINE_INSTANCE, GUC_VIDEO_CLASS),
> 	MAKE_REGLIST(xe_lpd_vec_class_regs, PF, ENGINE_CLASS, GUC_VIDEOENHANCE_CLASS),
> 	MAKE_REGLIST(xe_lpd_vec_inst_regs, PF, ENGINE_INSTANCE, GUC_VIDEOENHANCE_CLASS),
>+	MAKE_REGLIST(empty_regs_list, PF, ENGINE_CLASS, GUC_BLITTER_CLASS),
>+	MAKE_REGLIST(xe_lpd_blt_inst_regs, PF, ENGINE_INSTANCE, GUC_BLITTER_CLASS),
> 	{}
> };
>
>@@ -191,20 +259,24 @@ guc_capture_list_init(struct intel_guc *guc, u32 owner, u32 type, u32 classid,
> 		return -ENODEV;
>
> 	match = guc_capture_get_one_list(reglists, owner, type, classid);
>-	if (match) {
>-		for (i = 0; i < num_entries && i < match->num_regs; ++i) {
>-			ptr[i].offset = match->list[i].reg.reg;
>-			ptr[i].value = 0xDEADF00D;
>-			ptr[i].flags = match->list[i].flags;
>-			ptr[i].mask = match->list[i].mask;
>-		}
>-		return 0;
>+	if (!match) {
>+		guc_capture_warn_with_list_info(i915, "Missing register list init", owner, type,
>+						classid);
>+		return -ENODATA;
>+	}
>+
>+	for (i = 0; i < num_entries && i < match->num_regs; ++i) {
>+		ptr[i].offset = match->list[i].reg.reg;
>+		ptr[i].value = 0xDEADF00D;
>+		ptr[i].flags = match->list[i].flags;
>+		ptr[i].mask = match->list[i].mask;
> 	}
>
>-	guc_capture_warn_with_list_info(i915, "Missing register list init", owner, type,
>-					classid);
>+	if (i < num_entries)
>+		drm_dbg(&i915->drm, "GuC-capture: Init reglist short %d out %d.\n",
>+			(int)i, (int)num_entries);
>
>-	return -ENODATA;
>+	return 0;
> }
>
> static int
>diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_fwif.h b/drivers/gpu/drm/i915/gt/uc/intel_guc_fwif.h
>index b5f59c6a2424..14ab4c9588ae 100644
>--- a/drivers/gpu/drm/i915/gt/uc/intel_guc_fwif.h
>+++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_fwif.h
>@@ -271,10 +271,12 @@ 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)
>+#define GUC_REGSET_STEERING_GROUP       GENMASK(15, 12)
>+#define GUC_REGSET_STEERING_INSTANCE    GENMASK(23, 20)
>+	u32 mask;
> } __packed;
>
> /* GuC register sets */
>-- 
>2.25.1
>

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [Intel-gfx] [PATCH v7 03/13] drm/i915/guc: Add XE_LP steered register lists support
  2022-02-26  9:55 ` [Intel-gfx] [PATCH v7 03/13] drm/i915/guc: Add XE_LP steered register lists support Alan Previn
@ 2022-03-10  0:56   ` Umesh Nerlige Ramappa
  0 siblings, 0 replies; 32+ messages in thread
From: Umesh Nerlige Ramappa @ 2022-03-10  0:56 UTC (permalink / raw)
  To: Alan Previn; +Cc: intel-gfx

On Sat, Feb 26, 2022 at 01:55:31AM -0800, Alan Previn wrote:
>Add the ability for runtime allocation and freeing of
>steered register list extentions that depend on the
>detected HW config fuses.
>
>Signed-off-by: Alan Previn <alan.previn.teres.alexis@intel.com>
>---
> drivers/gpu/drm/i915/gt/uc/guc_capture_fwif.h |   9 +
> .../gpu/drm/i915/gt/uc/intel_guc_capture.c    | 175 ++++++++++++++++--
> 2 files changed, 173 insertions(+), 11 deletions(-)
>
>diff --git a/drivers/gpu/drm/i915/gt/uc/guc_capture_fwif.h b/drivers/gpu/drm/i915/gt/uc/guc_capture_fwif.h
>index 858f85478636..27b89539d0d5 100644
>--- a/drivers/gpu/drm/i915/gt/uc/guc_capture_fwif.h
>+++ b/drivers/gpu/drm/i915/gt/uc/guc_capture_fwif.h
>@@ -51,6 +51,7 @@ struct __guc_mmio_reg_descr_group {
> 	u32 owner; /* see enum guc_capture_owner */
> 	u32 type; /* see enum guc_capture_type */
> 	u32 engine; /* as per MAX_ENGINE_CLASS */
>+	struct __guc_mmio_reg_descr *extlist; /* only used for steered registers */
> };
>
> /**
>@@ -78,6 +79,14 @@ struct __guc_state_capture_priv {
> 	 */
> 	const struct __guc_mmio_reg_descr_group *reglists;
>
>+	/**
>+	 * @extlists: allocated table of steered register lists used for error-capture state.
>+	 *
>+	 * NOTE: steered registers have multiple instances depending on the HW configuration
>+	 * (slices or dual-sub-slices) and thus depends on HW fuses discovered at startup
>+	 */
>+	struct __guc_mmio_reg_descr_group *extlists;
>+
> 	/**
> 	 * @ads_cache: cached register lists that is ADS format ready
> 	 */
>diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c
>index fb3ca734ef97..6370943ea300 100644
>--- a/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c
>+++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c
>@@ -133,6 +133,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 */
>@@ -150,28 +151,33 @@ static const struct __guc_mmio_reg_descr_group xe_lpd_lists[] = {
> };
>
> static const struct __guc_mmio_reg_descr_group *
>-guc_capture_get_device_reglist(struct intel_guc *guc)
>+guc_capture_get_one_list(const struct __guc_mmio_reg_descr_group *reglists,
>+			 u32 owner, u32 type, u32 id)
> {
>-	struct drm_i915_private *i915 = guc_to_gt(guc)->i915;
>+	int i;
>
>-	if (IS_TIGERLAKE(i915) || IS_ROCKETLAKE(i915) ||
>-	    IS_ALDERLAKE_S(i915) || IS_ALDERLAKE_P(i915)) {
>-		return xe_lpd_lists;
>+	if (!reglists)
>+		return NULL;
>+
>+	for (i = 0; reglists[i].list; ++i) {
>+		if (reglists[i].owner == owner && reglists[i].type == type &&
>+		    (reglists[i].engine == id || reglists[i].type == GUC_CAPTURE_LIST_TYPE_GLOBAL))
>+		return &reglists[i];
> 	}
>
> 	return NULL;
> }
>
>-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, u32 id)
>+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].list; ++i) {
>+	for (i = 0; reglists[i].extlist; ++i) {
> 		if (reglists[i].owner == owner && reglists[i].type == type &&
> 		    (reglists[i].engine == id || reglists[i].type == GUC_CAPTURE_LIST_TYPE_GLOBAL))
> 		return &reglists[i];
>@@ -180,6 +186,127 @@ guc_capture_get_one_list(const struct __guc_mmio_reg_descr_group *reglists,
> 	return NULL;
> }
>
>+static void guc_capture_free_extlists(struct __guc_mmio_reg_descr_group *reglists)
>+{
>+	int i = 0;
>+
>+	if (!reglists)
>+		return;
>+
>+	while (reglists[i].extlist)
>+		kfree(reglists[i++].extlist);
>+}
>+
>+struct __ext_steer_reg {
>+	const char *name;
>+	i915_reg_t reg;
>+};
>+
>+static const struct __ext_steer_reg xe_extregs[] = {
>+	{"GEN7_SAMPLER_INSTDONE", GEN7_SAMPLER_INSTDONE},
>+	{"GEN7_ROW_INSTDONE", GEN7_ROW_INSTDONE}
>+};
>+
>+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 = extlist->reg;
>+	ext->flags = FIELD_PREP(GUC_REGSET_STEERING_GROUP, slice_id);
>+	ext->flags |= FIELD_PREP(GUC_REGSET_STEERING_INSTANCE, subslice_id);
>+	ext->regname = extlist->name;
>+}
>+
>+static int
>+__alloc_ext_regs(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 = kcalloc(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_xe_lpd(struct intel_guc *guc,
>+				       const struct __guc_mmio_reg_descr_group *lists)
>+{
>+	struct intel_gt *gt = guc_to_gt(guc);
>+	struct drm_i915_private *i915 = guc_to_gt(guc)->i915;
>+	int slice, subslice, 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;
>+	struct sseu_dev_info *sseu;
>+
>+	/* In XE_LPD we only have steered registers for the render-class */
>+	list = guc_capture_get_one_list(lists, GUC_CAPTURE_LIST_INDEX_PF,
>+					GUC_CAPTURE_LIST_TYPE_ENGINE_CLASS, GUC_RENDER_CLASS);
>+	/* skip if extlists was previously allocated */
>+	if (!list || guc->capture.priv->extlists)
>+		return;
>+
>+	num_steer_regs = ARRAY_SIZE(xe_extregs);
>+
>+	sseu = &gt->info.sseu;
>+	for_each_instdone_slice_subslice(i915, sseu, slice, subslice)
>+		num_tot_regs += num_steer_regs;
>+
>+	if (!num_tot_regs)
>+		return;
>+
>+	/* allocate an extra for an end marker */
>+	extlists = kcalloc(2, sizeof(struct __guc_mmio_reg_descr_group), GFP_KERNEL);
>+	if (!extlists)
>+		return;
>+
>+	if (__alloc_ext_regs(&extlists[0], list, num_tot_regs)) {
>+		kfree(extlists);
>+		return;
>+	}
>+
>+	extarray = extlists[0].extlist;
>+	for_each_instdone_slice_subslice(i915, sseu, slice, subslice) {
>+		for (i = 0; i < num_steer_regs; ++i) {
>+			__fill_ext_reg(extarray, &xe_extregs[i], slice, subslice);
>+			++extarray;
>+		}
>+	}
>+
>+	guc->capture.priv->extlists = extlists;
>+}
>+
>+static const struct __guc_mmio_reg_descr_group *
>+guc_capture_get_device_reglist(struct intel_guc *guc)
>+{
>+	struct drm_i915_private *i915 = guc_to_gt(guc)->i915;
>+
>+	if (IS_TIGERLAKE(i915) || IS_ROCKETLAKE(i915) ||
>+	    IS_ALDERLAKE_S(i915) || IS_ALDERLAKE_P(i915)) {
>+		/*
>+		 * 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_xe_lpd(guc, xe_lpd_lists);
>+		return xe_lpd_lists;
>+	}
>+
>+	drm_warn(&i915->drm, "No GuC-capture register lists\n");
>+
>+	return NULL;
>+}
>+
> static const char *
> __stringify_owner(u32 owner)
> {
>@@ -250,10 +377,12 @@ static int
> guc_capture_list_init(struct intel_guc *guc, u32 owner, u32 type, u32 classid,
> 		      struct guc_mmio_reg *ptr, u16 num_entries)
> {
>-	u32 i = 0;
>+	u32 i = 0, j = 0;
> 	struct drm_i915_private *i915 = guc_to_gt(guc)->i915;
> 	const struct __guc_mmio_reg_descr_group *reglists = guc->capture.priv->reglists;
>+	struct __guc_mmio_reg_descr_group *extlists = guc->capture.priv->extlists;
> 	const struct __guc_mmio_reg_descr_group *match;
>+	struct __guc_mmio_reg_descr_group *matchext;
>
> 	if (!reglists)
> 		return -ENODEV;
>@@ -272,6 +401,17 @@ guc_capture_list_init(struct intel_guc *guc, u32 owner, u32 type, u32 classid,
> 		ptr[i].mask = match->list[i].mask;
> 	}
>
>+	matchext = guc_capture_get_one_ext_list(extlists, owner, type, classid);
>+	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.reg;
>+			ptr[i].value = 0xDEADF00D;
>+			ptr[i].flags = matchext->extlist[j].flags;
>+			ptr[i].mask = matchext->extlist[j].mask;
>+		}
>+	}
> 	if (i < num_entries)
> 		drm_dbg(&i915->drm, "GuC-capture: Init reglist short %d out %d.\n",
> 			(int)i, (int)num_entries);
>@@ -283,12 +423,20 @@ static int
> guc_cap_list_num_regs(struct __guc_state_capture_priv *gc, u32 owner, u32 type, u32 classid)
> {
> 	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, classid);
> 	if (!match)
> 		return 0;
>
>-	return match->num_regs;
>+	num_regs = match->num_regs;
>+
>+	matchext = guc_capture_get_one_ext_list(gc->extlists, owner, type, classid);
>+	if (matchext)
>+		num_regs += matchext->num_regs;
>+
>+	return num_regs;
> }
>
> int
>@@ -408,6 +556,11 @@ void intel_guc_capture_destroy(struct intel_guc *guc)
>
> 	guc_capture_free_ads_cache(guc->capture.priv);
>
>+	if (guc->capture.priv->extlists) {

>+		guc_capture_free_extlists(guc->capture.priv->extlists);
>+		kfree(guc->capture.priv->extlists);

nit: If checking for NULL reglist inside guc_capture_free_extlists, then 
the if condition here can be dropped. Also kfree should be unaffected 
either ways.

As is, this is

Reviewed-by: Umesh Nerlige Ramappa <umesh.nerlige.ramappa@intel.com>

Umesh



>+	}
>+
> 	kfree(guc->capture.priv);
> 	guc->capture.priv = NULL;
> }
>-- 
>2.25.1
>

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [Intel-gfx] [PATCH v7 04/13] drm/i915/guc: Add DG2 registers for GuC error state capture.
  2022-02-26  9:55 ` [Intel-gfx] [PATCH v7 04/13] drm/i915/guc: Add DG2 registers for GuC error state capture Alan Previn
@ 2022-03-10  1:53   ` Umesh Nerlige Ramappa
  0 siblings, 0 replies; 32+ messages in thread
From: Umesh Nerlige Ramappa @ 2022-03-10  1:53 UTC (permalink / raw)
  To: Alan Previn; +Cc: intel-gfx

On Sat, Feb 26, 2022 at 01:55:32AM -0800, Alan Previn wrote:
>Add additional DG2 registers for GuC error state capture.
>
>Signed-off-by: Alan Previn <alan.previn.teres.alexis@intel.com>
>---
> .../gpu/drm/i915/gt/uc/intel_guc_capture.c    | 80 ++++++++++++++++++-
> 1 file changed, 78 insertions(+), 2 deletions(-)
>
>diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c
>index 6370943ea300..c8441ca1566b 100644
>--- a/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c
>+++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c
>@@ -285,20 +285,96 @@ guc_capture_alloc_steered_lists_xe_lpd(struct intel_guc *guc,
> 	guc->capture.priv->extlists = extlists;
> }
>
>+static const struct __ext_steer_reg xehpg_extregs[] = {
>+	{"XEHPG_INSTDONE_GEOM_SVG", XEHPG_INSTDONE_GEOM_SVG}
>+};
>+
>+static bool __has_xehpg_extregs(u32 ipver)

What I meant was to make has_xehpg_extregs part of platform feature, but 
this is fine too.

>+{
>+	return (ipver >= IP_VER(12, 55));
>+}
>+
>+static void
>+guc_capture_alloc_steered_lists_xe_hpg(struct intel_guc *guc,
>+				       const struct __guc_mmio_reg_descr_group *lists,
>+				       u32 ipver)
>+{
>+	struct intel_gt *gt = guc_to_gt(guc);
>+	struct drm_i915_private *i915 = guc_to_gt(guc)->i915;
>+	struct sseu_dev_info *sseu;
>+	int slice, subslice, i, iter, 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;
>+
>+	/* In XE_LP / HPG we only have render-class steering registers during error-capture */
>+	list = guc_capture_get_one_list(lists, GUC_CAPTURE_LIST_INDEX_PF,
>+					GUC_CAPTURE_LIST_TYPE_ENGINE_CLASS, GUC_RENDER_CLASS);
>+	/* skip if extlists was previously allocated */
>+	if (!list || guc->capture.priv->extlists)
>+		return;
>+
>+	num_steer_regs = ARRAY_SIZE(xe_extregs);
>+	if (__has_xehpg_extregs(ipver))
>+		num_steer_regs += ARRAY_SIZE(xehpg_extregs);
>+
>+	sseu = &gt->info.sseu;
>+	for_each_instdone_gslice_dss_xehp(i915, sseu, iter, slice, subslice) {
>+		num_tot_regs += num_steer_regs;
>+	}
>+
>+	if (!num_tot_regs)
>+		return;
>+
>+	/* allocate an extra for an end marker */
>+	extlists = kcalloc(2, sizeof(struct __guc_mmio_reg_descr_group), GFP_KERNEL);
>+	if (!extlists)
>+		return;
>+
>+	if (__alloc_ext_regs(&extlists[0], list, num_tot_regs)) {
>+		kfree(extlists);
>+		return;
>+	}
>+
>+	extarray = extlists[0].extlist;
>+	for_each_instdone_gslice_dss_xehp(i915, sseu, iter, 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(ipver)) {
>+			for (i = 0; i < ARRAY_SIZE(xehpg_extregs); ++i) {
>+				__fill_ext_reg(extarray, &xehpg_extregs[i], slice, subslice);
>+				++extarray;
>+			}
>+		}
>+	}
>+
>+	drm_dbg(&i915->drm, "GuC-capture found %d-ext-regs.\n", num_tot_regs);
>+	guc->capture.priv->extlists = extlists;
>+}
>+
> static const struct __guc_mmio_reg_descr_group *
> guc_capture_get_device_reglist(struct intel_guc *guc)
> {
> 	struct drm_i915_private *i915 = guc_to_gt(guc)->i915;
>
> 	if (IS_TIGERLAKE(i915) || IS_ROCKETLAKE(i915) ||
>-	    IS_ALDERLAKE_S(i915) || IS_ALDERLAKE_P(i915)) {
>+	    IS_ALDERLAKE_S(i915) || IS_ALDERLAKE_P(i915) ||
>+	    IS_DG2(i915) || IS_XEHPSDV(i915)) {
> 		/*
> 		 * 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_xe_lpd(guc, xe_lpd_lists);
>+		if (IS_DG2(i915))
>+			guc_capture_alloc_steered_lists_xe_hpg(guc, xe_lpd_lists, IP_VER(12, 55));
>+		else if (IS_XEHPSDV(i915))
>+			guc_capture_alloc_steered_lists_xe_hpg(guc, xe_lpd_lists, IP_VER(12, 50));
>+		else
>+			guc_capture_alloc_steered_lists_xe_lpd(guc, xe_lpd_lists);
>+
> 		return xe_lpd_lists;
> 	}

If you know that this applies to gen12 platforms, then you could do away with 
the ORed platform checks. I think it is cumbersome to maintain an OR of 
platforms that need this. The other pattern I see in the driver is like this:

i.e, If you return xe_lpd_lists back from 
guc_capture_alloc_steered_lists_xe_hpg, then this functions simplifies to 

struct __guc_mmio_reg_descr_group *ret;

if (IS_DG2(i915))
	ret = guc_capture_alloc_steered_lists_xe_hpg(guc, xe_lpd_lists, IP_VER(12, 55));
else if (IS_XEHPSDV(i915))
	ret = guc_capture_alloc_steered_lists_xe_hpg(guc, xe_lpd_lists, IP_VER(12, 50));
else if (GRAPHICS_VER(i915) > 11) /* OR maybe == 12 */
	ret = guc_capture_alloc_steered_lists_xe_lpd(guc, xe_lpd_lists);
else
	ret = NULL;

if !(ret)
	drm_...

return ret;

Irrespective of that:

Reviewed-by: Umesh Nerlige Ramappa <umesh.nerlige.ramappa@intel.com>

Umesh


>
>-- 
>2.25.1
>

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [Intel-gfx] [PATCH v7 01/13] drm/i915/guc: Update GuC ADS size for error capture lists
  2022-02-26  9:55 ` [Intel-gfx] [PATCH v7 01/13] drm/i915/guc: Update GuC ADS size for error capture lists Alan Previn
  2022-02-26 20:22   ` Teres Alexis, Alan Previn
  2022-02-27 21:02   ` kernel test robot
@ 2022-03-10  5:30   ` Matthew Brost
  2022-03-11  9:59     ` Teres Alexis, Alan Previn
  2022-03-11 10:18     ` Teres Alexis, Alan Previn
  2 siblings, 2 replies; 32+ messages in thread
From: Matthew Brost @ 2022-03-10  5:30 UTC (permalink / raw)
  To: Alan Previn; +Cc: intel-gfx

On Sat, Feb 26, 2022 at 01:55:29AM -0800, Alan Previn wrote:
> Update GuC ADS size allocation to include space for
> the lists of error state capture register descriptors.
> 
> Also, populate 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.
> 
> NOTE: Start with a sample table of register lists to layout the
> framework before adding real registers in subsequent patch.
> 
> Signed-off-by: Alan Previn <alan.previn.teres.alexis@intel.com>
> ---
>  drivers/gpu/drm/i915/Makefile                 |   1 +
>  drivers/gpu/drm/i915/gt/uc/guc_capture_fwif.h |  89 +++++
>  drivers/gpu/drm/i915/gt/uc/intel_guc.c        |  13 +-
>  drivers/gpu/drm/i915/gt/uc/intel_guc.h        |  11 +-
>  drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c    | 132 ++++++-
>  .../gpu/drm/i915/gt/uc/intel_guc_capture.c    | 352 ++++++++++++++++++
>  .../gpu/drm/i915/gt/uc/intel_guc_capture.h    |  22 ++
>  drivers/gpu/drm/i915/gt/uc/intel_guc_fwif.h   |   8 +
>  8 files changed, 611 insertions(+), 17 deletions(-)
>  create mode 100644 drivers/gpu/drm/i915/gt/uc/guc_capture_fwif.h
>  create mode 100644 drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c
>  create mode 100644 drivers/gpu/drm/i915/gt/uc/intel_guc_capture.h
> 
> diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
> index 9d588d936e3d..547adc36d4e9 100644
> --- a/drivers/gpu/drm/i915/Makefile
> +++ b/drivers/gpu/drm/i915/Makefile
> @@ -184,6 +184,7 @@ i915-y += gt/uc/intel_uc.o \
>  	  gt/uc/intel_uc_fw.o \
>  	  gt/uc/intel_guc.o \
>  	  gt/uc/intel_guc_ads.o \
> +	  gt/uc/intel_guc_capture.o \
>  	  gt/uc/intel_guc_ct.o \
>  	  gt/uc/intel_guc_debugfs.o \
>  	  gt/uc/intel_guc_fw.o \
> diff --git a/drivers/gpu/drm/i915/gt/uc/guc_capture_fwif.h b/drivers/gpu/drm/i915/gt/uc/guc_capture_fwif.h
> new file mode 100644
> index 000000000000..858f85478636
> --- /dev/null
> +++ b/drivers/gpu/drm/i915/gt/uc/guc_capture_fwif.h
> @@ -0,0 +1,89 @@
> +/* SPDX-License-Identifier: MIT */
> +/*
> + * Copyright © 2021-2022 Intel Corporation
> + */
> +
> +#ifndef _INTEL_GUC_CAPTURE_FWIF_H
> +#define _INTEL_GUC_CAPTURE_FWIF_H
> +
> +#include <linux/types.h>
> +#include "intel_guc_fwif.h"
> +
> +struct intel_guc;
> +struct file;
> +
> +/**
> + * struct guc_debug_capture_list_header / struct guc_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 {
> +	u32 info;
> +#define GUC_CAPTURELISTHDR_NUMDESCR GENMASK(15, 0)
> +} __packed;
> +
> +struct guc_debug_capture_list {
> +	struct guc_debug_capture_list_header header;

struct guc_mmio_reg regs[0];

This is the convention when allocating dynamically sized structures.

This allows:
list.header
list.regs[some index]

Also by looking at the structure definition, it implies that the end of
struct is dynamically allocated array.

> +} __packed;
> +
> +/**
> + * struct __guc_mmio_reg_descr / struct __guc_mmio_reg_descr_group
> + *
> + * intel_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 {
> +	i915_reg_t reg;
> +	u32 flags;
> +	u32 mask;
> +	const char *regname;
> +};
> +
> +struct __guc_mmio_reg_descr_group {
> +	const struct __guc_mmio_reg_descr *list;
> +	u32 num_regs;
> +	u32 owner; /* see enum guc_capture_owner */
> +	u32 type; /* see enum guc_capture_type */
> +	u32 engine; /* as per MAX_ENGINE_CLASS */
> +};
> +
> +/**
> + * struct __guc_capture_ads_cache
> + *
> + * A structure to cache register lists that were populated and registered
> + * with GuC at startup during ADS registration. This allows much quicker
> + * GuC resets without re-parsing all the tables for the given gt.

It also stores a one time allocated file too which I don't think you
need rather just a pointer. More on this below. 

> + */
> +struct __guc_capture_ads_cache {
> +	bool is_valid;
> +	struct file *file;
> +	size_t size;
> +	int status;
> +};
> +
> +/**
> + * struct __guc_state_capture_priv
> + *
> + * Internal context of the intel_guc_capture module.
> + */
> +struct __guc_state_capture_priv {
> +	/**
> +	 * @reglists: static table of register lists used for error-capture state.
> +	 */
> +	const struct __guc_mmio_reg_descr_group *reglists;
> +
> +	/**
> +	 * @ads_cache: cached register lists that is ADS format ready
> +	 */
> +	struct __guc_capture_ads_cache ads_cache[GUC_CAPTURE_LIST_INDEX_MAX]
> +						[GUC_CAPTURE_LIST_TYPE_MAX]
> +						[GUC_MAX_ENGINE_CLASSES];
> +};
> +
> +#endif /* _INTEL_GUC_CAPTURE_FWIF_H */
> diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc.c b/drivers/gpu/drm/i915/gt/uc/intel_guc.c
> index 447a976c9f25..cda7e4bb8bac 100644
> --- a/drivers/gpu/drm/i915/gt/uc/intel_guc.c
> +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc.c
> @@ -9,8 +9,9 @@
>  #include "gt/intel_gt_pm_irq.h"
>  #include "gt/intel_gt_regs.h"
>  #include "intel_guc.h"
> -#include "intel_guc_slpc.h"
>  #include "intel_guc_ads.h"
> +#include "intel_guc_capture.h"
> +#include "intel_guc_slpc.h"
>  #include "intel_guc_submission.h"
>  #include "i915_drv.h"
>  #include "i915_irq.h"
> @@ -362,9 +363,14 @@ int intel_guc_init(struct intel_guc *guc)
>  	if (ret)
>  		goto err_fw;
>  
> -	ret = intel_guc_ads_create(guc);
> +	ret = intel_guc_capture_init(guc);
>  	if (ret)
>  		goto err_log;
> +
> +	ret = intel_guc_ads_create(guc);
> +	if (ret)
> +		goto err_capture;
> +
>  	GEM_BUG_ON(!guc->ads_vma);
>  
>  	ret = intel_guc_ct_init(&guc->ct);
> @@ -403,6 +409,8 @@ int intel_guc_init(struct intel_guc *guc)
>  	intel_guc_ct_fini(&guc->ct);
>  err_ads:
>  	intel_guc_ads_destroy(guc);
> +err_capture:
> +	intel_guc_capture_destroy(guc);
>  err_log:
>  	intel_guc_log_destroy(&guc->log);
>  err_fw:
> @@ -430,6 +438,7 @@ void intel_guc_fini(struct intel_guc *guc)
>  	intel_guc_ct_fini(&guc->ct);
>  
>  	intel_guc_ads_destroy(guc);
> +	intel_guc_capture_destroy(guc);
>  	intel_guc_log_destroy(&guc->log);
>  	intel_uc_fw_fini(&guc->fw);
>  }
> diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc.h b/drivers/gpu/drm/i915/gt/uc/intel_guc.h
> index bf7079480d47..908c6b1dd51a 100644
> --- a/drivers/gpu/drm/i915/gt/uc/intel_guc.h
> +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc.h
> @@ -10,18 +10,19 @@
>  #include <linux/iosys-map.h>
>  #include <linux/xarray.h>
>  
> -#include "intel_uncore.h"
> +#include "intel_guc_ct.h"
>  #include "intel_guc_fw.h"
>  #include "intel_guc_fwif.h"
> -#include "intel_guc_ct.h"
>  #include "intel_guc_log.h"
>  #include "intel_guc_reg.h"
>  #include "intel_guc_slpc_types.h"
>  #include "intel_uc_fw.h"
> +#include "intel_uncore.h"
>  #include "i915_utils.h"
>  #include "i915_vma.h"
>  
>  struct __guc_ads_blob;
> +struct __guc_state_capture_priv;
>  
>  /**
>   * struct intel_guc - Top level structure of GuC.
> @@ -38,6 +39,10 @@ struct intel_guc {
>  	struct intel_guc_ct ct;
>  	/** @slpc: sub-structure containing SLPC related data and objects */
>  	struct intel_guc_slpc slpc;
> +	/** @capture: the error-state-capture module's data and objects */
> +	struct intel_guc_state_capture {
> +		struct __guc_state_capture_priv *priv;
> +	} capture;

The capture + priv is a weird convention. I'd just do it like:

struct intel_guc_state_capture *capture;

Where __guc_state_capture_priv -> intel_guc_state_capture
Forward declare intel_guc_state_capture in this file
Define intel_guc_state_capture in guc_capture_fwif.h
Only import guc_capture_fwif.h where you have know about intel_guc_state_capture contexts

>  
>  	/** @sched_engine: Global engine used to submit requests to GuC */
>  	struct i915_sched_engine *sched_engine;
> @@ -160,6 +165,8 @@ struct intel_guc {
>  	struct guc_mmio_reg *ads_regset;
>  	/** @ads_golden_ctxt_size: size of the golden contexts in the ADS */
>  	u32 ads_golden_ctxt_size;
> +	/** @ads_capture_size: size of register lists in the ADS used for error capture */
> +	u32 ads_capture_size;
>  	/** @ads_engine_usage_size: size of engine usage in the ADS */
>  	u32 ads_engine_usage_size;
>  
> diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c
> index 847e00390b00..8bed68299bba 100644
> --- a/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c
> +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_ads.c
> @@ -11,6 +11,7 @@
>  #include "gt/intel_lrc.h"
>  #include "gt/shmem_utils.h"
>  #include "intel_guc_ads.h"
> +#include "intel_guc_capture.h"
>  #include "intel_guc_fwif.h"
>  #include "intel_uc.h"
>  #include "i915_drv.h"
> @@ -86,8 +87,7 @@ static u32 guc_ads_golden_ctxt_size(struct intel_guc *guc)
>  
>  static u32 guc_ads_capture_size(struct intel_guc *guc)
>  {
> -	/* FIXME: Allocate a proper capture list */
> -	return PAGE_ALIGN(PAGE_SIZE);
> +	return PAGE_ALIGN(guc->ads_capture_size);
>  }
>  
>  static u32 guc_ads_private_data_size(struct intel_guc *guc)
> @@ -584,24 +584,124 @@ static void guc_init_golden_context(struct intel_guc *guc)
>  	GEM_BUG_ON(guc->ads_golden_ctxt_size != total_size);
>  }
>  
> -static void guc_capture_list_init(struct intel_guc *guc)
> +static int
> +guc_capture_prep_lists(struct intel_guc *guc)
>  {
> +	struct intel_gt *gt = guc_to_gt(guc);
> +	struct drm_i915_private *i915 = guc_to_gt(guc)->i915;
> +	u32 ggtt, capture_offset, null_ggtt, total_size = 0;
> +	struct guc_gt_system_info local_info;
> +	struct iosys_map info_map;
> +	u32 null_header[2]={0};
> +	struct file *file;
> +	size_t size = 0;
>  	int i, j;
> -	u32 addr_ggtt, offset;
>  
> -	offset = guc_ads_capture_offset(guc);
> -	addr_ggtt = intel_guc_ggtt_offset(guc, guc->ads_vma) + offset;
> +	if (!iosys_map_is_null(&guc->ads_map)) {
> +		capture_offset = guc_ads_capture_offset(guc);
> +		ggtt = intel_guc_ggtt_offset(guc, guc->ads_vma) + capture_offset;

This should just be capture_offset, right? ads_map is CPU mapped
address that has nothing do with the GGTT address, it is just a pointer
to the base of the ADS structure that can be accessed through the
iosys_map* macros. 

> +		info_map = IOSYS_MAP_INIT_OFFSET(&guc->ads_map,
> +						 offsetof(struct __guc_ads_blob, system_info));
> +	} else {
> +		memset(&local_info, 0, sizeof(local_info));
> +		iosys_map_set_vaddr(&info_map, &local_info);
> +		fill_engine_enable_masks(gt, &info_map);
> +	}
>  
> -	/* FIXME: Populate a proper capture list */
> +	/* first, set aside the first page for a capture_list with zero descriptors */
> +	total_size = PAGE_SIZE;
> +	if (!iosys_map_is_null(&guc->ads_map)) {
> +		file = shmem_create_from_data("guc-err-cap", null_header, sizeof(null_header));

You don't need to create file a here. There are IOSYS map calls to just
write to the memory.

iosys_map_memset(<map>, <offset>, <val>, <size>)
 
> +		if (!IS_ERR(file)) {
> +			shmem_read_to_iosys_map(file, 0, &guc->ads_map,
> +						ggtt, sizeof(null_header));
> +			fput(file);
> +		} else {
> +			drm_dbg(&i915->drm, "GuC-capture: failed shmem for nulllist = 0x%016lx",
> +				PTR_ERR(file));
> +		}
> +		null_ggtt = ggtt;
> +		ggtt += PAGE_SIZE;
> +	}
>  
>  	for (i = 0; i < GUC_CAPTURE_LIST_INDEX_MAX; i++) {
>  		for (j = 0; j < GUC_MAX_ENGINE_CLASSES; j++) {
> -			ads_blob_write(guc, ads.capture_instance[i][j], addr_ggtt);
> -			ads_blob_write(guc, ads.capture_class[i][j], addr_ggtt);
> -		}
>  
> -		ads_blob_write(guc, ads.capture_global[i], addr_ggtt);
> +			/* null list if we dont have said engine or list */
> +			if (!info_map_read(&info_map, engine_enabled_masks[j])) {
> +
> +				if (!iosys_map_is_null(&guc->ads_map)) {
> +					ads_blob_write(guc, ads.capture_class[i][j], null_ggtt);
> +					ads_blob_write(guc, ads.capture_instance[i][j], null_ggtt);
> +				}
> +				continue;
> +			}
> +			if (intel_guc_capture_getlistsize(guc, i,
> +							  GUC_CAPTURE_LIST_TYPE_ENGINE_CLASS,
> +							  j, &size)) {
> +				if (!iosys_map_is_null(&guc->ads_map))
> +					ads_blob_write(guc, ads.capture_class[i][j], null_ggtt);
> +				goto engine_instance_list;
> +			}
> +			total_size += size;
> +			if (!iosys_map_is_null(&guc->ads_map)) {
> +				if (total_size > guc->ads_capture_size ||
> +				    intel_guc_capture_getlist(guc, i,
> +							      GUC_CAPTURE_LIST_TYPE_ENGINE_CLASS,
> +							      j, &file)) {
> +					ads_blob_write(guc, ads.capture_class[i][j], null_ggtt);
> +					continue;
> +				}
> +				ads_blob_write(guc, ads.capture_class[i][j], ggtt);
> +				shmem_read_to_iosys_map(file, 0, &guc->ads_map, ggtt, size);
> +				ggtt += size;
> +			}
> +engine_instance_list:
> +			if (intel_guc_capture_getlistsize(guc, i,
> +							  GUC_CAPTURE_LIST_TYPE_ENGINE_INSTANCE,
> +							  j, &size)) {
> +				if (!iosys_map_is_null(&guc->ads_map))
> +					ads_blob_write(guc, ads.capture_instance[i][j], null_ggtt);
> +				continue;
> +			}
> +			total_size += size;
> +			if (!iosys_map_is_null(&guc->ads_map)) {
> +				if (total_size > guc->ads_capture_size ||
> +				    intel_guc_capture_getlist(guc, i,
> +							      GUC_CAPTURE_LIST_TYPE_ENGINE_INSTANCE,
> +							      j, &file)) {
> +					ads_blob_write(guc, ads.capture_instance[i][j], null_ggtt);
> +					continue;
> +				}
> +				ads_blob_write(guc, ads.capture_instance[i][j], ggtt);
> +				shmem_read_to_iosys_map(file, 0, &guc->ads_map, ggtt, size);
> +				ggtt += size;
> +			}
> +		}
> +		if (intel_guc_capture_getlistsize(guc, i, GUC_CAPTURE_LIST_TYPE_GLOBAL, 0, &size)) {
> +			if (!iosys_map_is_null(&guc->ads_map))
> +				ads_blob_write(guc, ads.capture_global[i], null_ggtt);
> +			continue;
> +		}
> +		total_size += size;
> +		if (!iosys_map_is_null(&guc->ads_map)) {
> +			if (total_size > guc->ads_capture_size ||
> +			    intel_guc_capture_getlist(guc, i, GUC_CAPTURE_LIST_TYPE_GLOBAL, 0,
> +						      &file)) {
> +				ads_blob_write(guc, ads.capture_global[i], null_ggtt);
> +				continue;
> +			}
> +			ads_blob_write(guc, ads.capture_global[i], ggtt);
> +			shmem_read_to_iosys_map(file, 0, &guc->ads_map, ggtt, size);
> +			ggtt += size;
> +		}
>  	}
> +
> +	if (guc->ads_capture_size && guc->ads_capture_size != PAGE_ALIGN(total_size))
> +		drm_warn(&i915->drm, "GuC->ADS->Capture alloc size changed from %d to %d\n",
> +			 guc->ads_capture_size, PAGE_ALIGN(total_size));
> +
> +	return PAGE_ALIGN(total_size);
>  }
>  
>  static void __guc_ads_init(struct intel_guc *guc)
> @@ -639,8 +739,8 @@ static void __guc_ads_init(struct intel_guc *guc)
>  
>  	base = intel_guc_ggtt_offset(guc, guc->ads_vma);
>  
> -	/* Capture list for hang debug */
> -	guc_capture_list_init(guc);
> +	/* Lists for error capture debug */
> +	guc_capture_prep_lists(guc);
>  
>  	/* ADS */
>  	ads_blob_write(guc, ads.scheduler_policies, base +
> @@ -688,6 +788,12 @@ int intel_guc_ads_create(struct intel_guc *guc)
>  		return ret;
>  	guc->ads_golden_ctxt_size = ret;
>  
> +	/* Likewise the capture lists: */
> +	ret = guc_capture_prep_lists(guc);
> +	if (ret < 0)
> +		return ret;
> +	guc->ads_capture_size = ret;
> +
>  	/* Now the total size can be determined: */
>  	size = guc_ads_blob_size(guc);
>  
> diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c
> new file mode 100644
> index 000000000000..647a118aa3c3
> --- /dev/null
> +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c
> @@ -0,0 +1,352 @@
> +// SPDX-License-Identifier: MIT
> +/*
> + * Copyright © 2021-2022 Intel Corporation
> + */
> +
> +#include <linux/types.h>
> +
> +#include <drm/drm_print.h>
> +
> +#include "gt/intel_engine_regs.h"
> +#include "gt/intel_gt.h"
> +#include "gt/intel_gt_regs.h"
> +#include "gt/shmem_utils.h"
> +#include "guc_capture_fwif.h"
> +#include "intel_guc_fwif.h"
> +#include "i915_drv.h"
> +#include "i915_memcpy.h"
> +#include "i915_reg.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
> + */
> +/* XE_LPD - Global */
> +static const struct __guc_mmio_reg_descr xe_lpd_global_regs[] = {
> +	{GEN12_RING_FAULT_REG,     0,      0, "GEN12_RING_FAULT_REG"}
> +};
> +
> +/* XE_LPD - Render / Compute Per-Class */
> +static const struct __guc_mmio_reg_descr xe_lpd_rc_class_regs[] = {
> +	{EIR,                      0,      0, "EIR"}
> +};
> +
> +/* XE_LPD - Render / Compute Per-Engine-Instance */
> +static const struct __guc_mmio_reg_descr xe_lpd_rc_inst_regs[] = {
> +	{RING_HEAD(0),             0,      0, "RING_HEAD"},
> +	{RING_TAIL(0),             0,      0, "RING_TAIL"},
> +};
> +
> +/* XE_LPD - Media Decode/Encode Per-Class */
> +static const struct __guc_mmio_reg_descr xe_lpd_vd_class_regs[] = {
> +};
> +
> +/* XE_LPD - Media Decode/Encode Per-Engine-Instance */
> +static const struct __guc_mmio_reg_descr xe_lpd_vd_inst_regs[] = {
> +	{RING_HEAD(0),             0,      0, "RING_HEAD"},
> +	{RING_TAIL(0),             0,      0, "RING_TAIL"},
> +};
> +
> +/* XE_LPD - Video Enhancement Per-Class */
> +static const struct __guc_mmio_reg_descr xe_lpd_vec_class_regs[] = {
> +};
> +
> +/* XE_LPD - Video Enhancement Per-Engine-Instance */
> +static const struct __guc_mmio_reg_descr xe_lpd_vec_inst_regs[] = {
> +	{RING_HEAD(0),             0,      0, "RING_HEAD"},
> +	{RING_TAIL(0),             0,      0, "RING_TAIL"},
> +};
> +
> +#define TO_GCAP_DEF_OWNER(x) (GUC_CAPTURE_LIST_INDEX_##x)
> +#define TO_GCAP_DEF_TYPE(x) (GUC_CAPTURE_LIST_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_lpd_lists[] = {
> +	MAKE_REGLIST(xe_lpd_global_regs, PF, GLOBAL, 0),
> +	MAKE_REGLIST(xe_lpd_rc_class_regs, PF, ENGINE_CLASS, GUC_RENDER_CLASS),
> +	MAKE_REGLIST(xe_lpd_rc_inst_regs, PF, ENGINE_INSTANCE, GUC_RENDER_CLASS),
> +	MAKE_REGLIST(xe_lpd_vd_class_regs, PF, ENGINE_CLASS, GUC_VIDEO_CLASS),
> +	MAKE_REGLIST(xe_lpd_vd_inst_regs, PF, ENGINE_INSTANCE, GUC_VIDEO_CLASS),
> +	MAKE_REGLIST(xe_lpd_vec_class_regs, PF, ENGINE_CLASS, GUC_VIDEOENHANCE_CLASS),
> +	MAKE_REGLIST(xe_lpd_vec_inst_regs, PF, ENGINE_INSTANCE, GUC_VIDEOENHANCE_CLASS),
> +	{}
> +};
> +
> +static const struct __guc_mmio_reg_descr_group *
> +guc_capture_get_device_reglist(struct intel_guc *guc)
> +{
> +	struct drm_i915_private *i915 = guc_to_gt(guc)->i915;
> +
> +	if (IS_TIGERLAKE(i915) || IS_ROCKETLAKE(i915) ||
> +	    IS_ALDERLAKE_S(i915) || IS_ALDERLAKE_P(i915)) {
> +		return xe_lpd_lists;
> +	}
> +
> +	return NULL;
> +}
> +
> +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, u32 id)
> +{
> +	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 == id || reglists[i].type == GUC_CAPTURE_LIST_TYPE_GLOBAL))
> +		return &reglists[i];
> +	}
> +
> +	return NULL;
> +}
> +
> +static const char *
> +__stringify_owner(u32 owner)
> +{
> +	switch (owner) {
> +	case GUC_CAPTURE_LIST_INDEX_PF:
> +		return "PF";
> +	case GUC_CAPTURE_LIST_INDEX_VF:
> +		return "VF";
> +	default:
> +		return "unknown";
> +	}
> +
> +	return "";
> +}
> +
> +static const char *
> +__stringify_type(u32 type)
> +{
> +	switch (type) {
> +	case GUC_CAPTURE_LIST_TYPE_GLOBAL:
> +		return "Global";
> +	case GUC_CAPTURE_LIST_TYPE_ENGINE_CLASS:
> +		return "Class";
> +	case GUC_CAPTURE_LIST_TYPE_ENGINE_INSTANCE:
> +		return "Instance";
> +	default:
> +		return "unknown";
> +	}
> +
> +	return "";
> +}
> +
> +static const char *
> +__stringify_engclass(u32 class)
> +{
> +	switch (class) {
> +	case GUC_RENDER_CLASS:
> +		return "Render";
> +	case GUC_VIDEO_CLASS:
> +		return "Video";
> +	case GUC_VIDEOENHANCE_CLASS:
> +		return "VideoEnhance";
> +	case GUC_BLITTER_CLASS:
> +		return "Blitter";
> +	case GUC_RESERVED_CLASS:
> +		return "Reserved";
> +	default:
> +		return "unknown";
> +	}
> +
> +	return "";
> +}

Nit: I would better we have common helpers for these somewhere. Not a
blocker.

> +
> +static void
> +guc_capture_warn_with_list_info(struct drm_i915_private *i915, char *msg,
> +				u32 owner, u32 type, u32 classid)
> +{
> +	if (type == GUC_CAPTURE_LIST_TYPE_GLOBAL)
> +		drm_dbg(&i915->drm, "GuC-capture: %s for %s %s-Registers.\n", msg,
> +			__stringify_owner(owner), __stringify_type(type));
> +	else
> +		drm_dbg(&i915->drm, "GuC-capture: %s for %s %s-Registers on %s-Engine\n", msg,
> +			__stringify_owner(owner), __stringify_type(type),
> +			__stringify_engclass(classid));
> +}
> +
> +static int
> +guc_capture_list_init(struct intel_guc *guc, u32 owner, u32 type, u32 classid,
> +		      struct guc_mmio_reg *ptr, u16 num_entries)
> +{
> +	u32 i = 0;
> +	struct drm_i915_private *i915 = guc_to_gt(guc)->i915;
> +	const struct __guc_mmio_reg_descr_group *reglists = guc->capture.priv->reglists;
> +	const struct __guc_mmio_reg_descr_group *match;
> +
> +	if (!reglists)
> +		return -ENODEV;
> +
> +	match = guc_capture_get_one_list(reglists, owner, type, classid);
> +	if (match) {
> +		for (i = 0; i < num_entries && i < match->num_regs; ++i) {
> +			ptr[i].offset = match->list[i].reg.reg;
> +			ptr[i].value = 0xDEADF00D;
> +			ptr[i].flags = match->list[i].flags;
> +			ptr[i].mask = match->list[i].mask;
> +		}
> +		return 0;
> +	}
> +
> +	guc_capture_warn_with_list_info(i915, "Missing register list init", owner, type,
> +					classid);
> +
> +	return -ENODATA;
> +}
> +
> +static int
> +guc_cap_list_num_regs(struct __guc_state_capture_priv *gc, u32 owner, u32 type, u32 classid)
> +{
> +	const struct __guc_mmio_reg_descr_group *match;
> +
> +	match = guc_capture_get_one_list(gc->reglists, owner, type, classid);
> +	if (!match)
> +		return 0;
> +
> +	return match->num_regs;
> +}
> +
> +int
> +intel_guc_capture_getlistsize(struct intel_guc *guc, u32 owner, u32 type, u32 classid,
> +			      size_t *size)
> +{
> +	struct drm_i915_private *i915 = guc_to_gt(guc)->i915;
> +	struct __guc_state_capture_priv *gc = guc->capture.priv;
> +	struct __guc_capture_ads_cache *cache = &gc->ads_cache[owner][type][classid];
> +	int num_regs;
> +
> +	if (!gc->reglists)
> +		return -ENODEV;
> +
> +	if (cache->is_valid) {
> +		*size = cache->size;
> +		return cache->status;
> +	}
> +
> +	num_regs = guc_cap_list_num_regs(gc, owner, type, classid);
> +	if (!num_regs) {
> +		guc_capture_warn_with_list_info(i915, "Missing register list size",
> +						owner, type, classid);
> +		return -ENODATA;
> +	}
> +
> +	*size = PAGE_ALIGN((sizeof(struct guc_debug_capture_list)) +
> +			   (num_regs * sizeof(struct guc_mmio_reg)));
> +
> +	return 0;
> +}
> +
> +int
> +intel_guc_capture_getlist(struct intel_guc *guc, u32 owner, u32 type, u32 classid,
> +			  struct file **fileoutptr)
> +{
> +	struct __guc_state_capture_priv *gc = guc->capture.priv;
> +	struct __guc_capture_ads_cache *cache = &gc->ads_cache[owner][type][classid];
> +	struct drm_i915_private *i915 = guc_to_gt(guc)->i915;
> +	struct guc_debug_capture_list *listnode;
> +	struct file *file;
> +	u8 *caplist, *tmp;
> +	size_t size = 0;
> +	int ret, num_regs;
> +
> +	if (!gc->reglists)
> +		return -ENODEV;
> +
> +	if (cache->is_valid) {
> +		*fileoutptr = cache->file;
> +		return cache->status;
> +	}
> +
> +	ret = intel_guc_capture_getlistsize(guc, owner, type, classid, &size);
> +	if (ret) {
> +		cache->is_valid = true;
> +		cache->file = NULL;
> +		cache->size = 0;
> +		cache->status = ret;
> +		return ret;
> +	}
> +
> +	caplist = kzalloc(size, GFP_KERNEL);
> +	if (!caplist)
> +		return -ENOMEM;
> +
> +	/* populate capture list header */
> +	tmp = caplist;
> +	num_regs = guc_cap_list_num_regs(guc->capture.priv, owner, type, classid);
> +	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, classid, (struct guc_mmio_reg *)tmp, num_regs);
> +
> +	/* ADS only takes file handles, so we keep that as our cache */
> +	file = shmem_create_from_data("guc-err-cap", caplist, size);

I don't think you need a file here, why can't this just be a peice of
kalloc'd memory that is populated and copied via IOSYS map calls? 

The memory will need to be persistent (allocated at driver load, free'd
during unload) as can't allocate memory in the reset path tho.

Beyond that, you are parsing a static table to populate memory. That
almost certainly means you could just have static memory which we
directly copy into the ADS.

Matt

> +	kfree(caplist);
> +	if (IS_ERR(file)) {
> +		drm_warn(&i915->drm, "GuC-capture: cant create shmem for caplist = 0x%016lx", PTR_ERR(file));
> +		return PTR_ERR(file);
> +	}
> +
> +	/* cache this list */
> +	cache->file = file;
> +	cache->size = size;
> +	cache->status = 0;
> +	cache->is_valid = true;
> +
> +	*fileoutptr = file;
> +
> +	return 0;
> +}
> +
> +static void
> +guc_capture_free_ads_cache(struct __guc_state_capture_priv *gc)
> +{
> +	int i, j, k;
> +	struct __guc_capture_ads_cache *cache;
> +
> +	for (i = 0; i < GUC_CAPTURE_LIST_INDEX_MAX; ++i) {
> +		for (j = 0; j < GUC_CAPTURE_LIST_TYPE_MAX; ++j) {
> +			for (k = 0; k < GUC_MAX_ENGINE_CLASSES; ++k) {
> +				cache = &gc->ads_cache[i][j][k];
> +				if (cache->is_valid && cache->file)
> +					fput(cache->file);
> +			}
> +		}
> +	}
> +}
> +
> +void intel_guc_capture_destroy(struct intel_guc *guc)
> +{
> +	if (!guc->capture.priv)
> +		return;
> +
> +	guc_capture_free_ads_cache(guc->capture.priv);
> +
> +	kfree(guc->capture.priv);
> +	guc->capture.priv = NULL;
> +}
> +
> +int intel_guc_capture_init(struct intel_guc *guc)
> +{
> +	guc->capture.priv = kzalloc(sizeof(*guc->capture.priv), GFP_KERNEL);
> +	if (!guc->capture.priv)
> +		return -ENOMEM;
> +
> +	guc->capture.priv->reglists = guc_capture_get_device_reglist(guc);
> +
> +	return 0;
> +}
> diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.h b/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.h
> new file mode 100644
> index 000000000000..f05365239a2f
> --- /dev/null
> +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.h
> @@ -0,0 +1,22 @@
> +/* SPDX-License-Identifier: MIT */
> +/*
> + * Copyright © 2021-2021 Intel Corporation
> + */
> +
> +#ifndef _INTEL_GUC_CAPTURE_H
> +#define _INTEL_GUC_CAPTURE_H
> +
> +#include <linux/types.h>
> +
> +struct file;
> +struct guc_gt_system_info;
> +struct intel_guc;
> +
> +int intel_guc_capture_getlist(struct intel_guc *guc, u32 owner, u32 type, u32 classid,
> +			      struct file **fileptr);
> +int intel_guc_capture_getlistsize(struct intel_guc *guc, u32 owner, u32 type, u32 classid,
> +				  size_t *size);
> +void intel_guc_capture_destroy(struct intel_guc *guc);
> +int intel_guc_capture_init(struct intel_guc *guc);
> +
> +#endif /* _INTEL_GUC_CAPTURE_H */
> diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_fwif.h b/drivers/gpu/drm/i915/gt/uc/intel_guc_fwif.h
> index 6a4612a852e2..b5f59c6a2424 100644
> --- a/drivers/gpu/drm/i915/gt/uc/intel_guc_fwif.h
> +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_fwif.h
> @@ -297,6 +297,14 @@ enum {
>  	GUC_CAPTURE_LIST_INDEX_MAX = 2,
>  };
>  
> +/*Register-types of GuC capture register lists */
> +enum guc_capture_type {
> +	GUC_CAPTURE_LIST_TYPE_GLOBAL = 0,
> +	GUC_CAPTURE_LIST_TYPE_ENGINE_CLASS,
> +	GUC_CAPTURE_LIST_TYPE_ENGINE_INSTANCE,
> +	GUC_CAPTURE_LIST_TYPE_MAX,
> +};
> +
>  /* GuC Additional Data Struct */
>  struct guc_ads {
>  	struct guc_mmio_reg_set reg_state_list[GUC_MAX_ENGINE_CLASSES][GUC_MAX_INSTANCES_PER_CLASS];
> -- 
> 2.25.1
> 

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [Intel-gfx] [PATCH v7 13/13] drm/i915/guc: Print the GuC error capture output register list.
  2022-02-26  9:55 ` [Intel-gfx] [PATCH v7 13/13] drm/i915/guc: Print the GuC error capture output register list Alan Previn
@ 2022-03-11  5:26   ` Umesh Nerlige Ramappa
  0 siblings, 0 replies; 32+ messages in thread
From: Umesh Nerlige Ramappa @ 2022-03-11  5:26 UTC (permalink / raw)
  To: Alan Previn; +Cc: intel-gfx

On Sat, Feb 26, 2022 at 01:55:41AM -0800, Alan Previn wrote:
>Print the GuC captured error state register list (string names
>and values) when gpu_coredump_state printout is invoked via
>the i915 debugfs for flushing the gpu error-state that was
>captured prior.
>
>Since GuC could have reported multiple engine register dumps
>in a single notification event, parse the captured data
>(appearing as a stream of structures) to identify each dump as
>a different 'engine-capture-group-output'.
>
>Finally, for each 'engine-capture-group-output' that is found,
>verify if the engine register dump corresponds to the
>engine_coredump content that was previously populated by the
>i915_gpu_coredump function. That function would have copied
>the context's vma's including the bacth buffer during the
>G2H-context-reset notification that occurred earlier. Perform
>this verification check by comparing guc_id, lrca and engine-
>instance obtained from the 'engine-capture-group-output' vs a
>copy of that same info taken during i915_gpu_coredump. If
>they match, then print those vma's as well (such as the batch
>buffers).
>
>Signed-off-by: Alan Previn <alan.previn.teres.alexis@intel.com>

Looks intricate since you are plugging this in to existing code. This 
looks fine. Can you also please share the igt tests that you are using 
for this?

Reviewed-by: Umesh Nerlige Ramappa <umesh.nerlige.ramappa@intel.com>

Thanks,
Umesh
>---
> drivers/gpu/drm/i915/gt/intel_engine_cs.c     |   4 +-
> drivers/gpu/drm/i915/gt/uc/intel_guc.h        |   3 +
> .../gpu/drm/i915/gt/uc/intel_guc_capture.c    | 162 ++++++++++++++++++
> .../gpu/drm/i915/gt/uc/intel_guc_capture.h    |   2 +-
> .../gpu/drm/i915/gt/uc/intel_guc_submission.c |   6 +-
> drivers/gpu/drm/i915/i915_debugfs.c           |   1 +
> drivers/gpu/drm/i915/i915_gpu_error.c         |  16 +-
> drivers/gpu/drm/i915/i915_gpu_error.h         |   5 +
> 8 files changed, 184 insertions(+), 15 deletions(-)
>
>diff --git a/drivers/gpu/drm/i915/gt/intel_engine_cs.c b/drivers/gpu/drm/i915/gt/intel_engine_cs.c
>index e855c801ba28..4643745e5d09 100644
>--- a/drivers/gpu/drm/i915/gt/intel_engine_cs.c
>+++ b/drivers/gpu/drm/i915/gt/intel_engine_cs.c
>@@ -1629,9 +1629,7 @@ static void intel_engine_print_registers(struct intel_engine_cs *engine,
> 		drm_printf(m, "\tIPEHR: 0x%08x\n", ENGINE_READ(engine, IPEHR));
> 	}
>
>-	if (intel_engine_uses_guc(engine)) {
>-		/* nothing to print yet */
>-	} else if (HAS_EXECLISTS(dev_priv)) {
>+	if (HAS_EXECLISTS(dev_priv) && !intel_engine_uses_guc(engine)) {
> 		struct i915_request * const *port, *rq;
> 		const u32 *hws =
> 			&engine->status_page.addr[I915_HWS_CSB_BUF0_INDEX];
>diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc.h b/drivers/gpu/drm/i915/gt/uc/intel_guc.h
>index 908c6b1dd51a..08327294c1e3 100644
>--- a/drivers/gpu/drm/i915/gt/uc/intel_guc.h
>+++ b/drivers/gpu/drm/i915/gt/uc/intel_guc.h
>@@ -438,6 +438,9 @@ int intel_guc_engine_failure_process_msg(struct intel_guc *guc,
> int intel_guc_error_capture_process_msg(struct intel_guc *guc,
> 					const u32 *msg, u32 len);
>
>+struct intel_engine_cs *
>+intel_guc_lookup_engine(struct intel_guc *guc, u8 guc_class, u8 instance);
>+
> void intel_guc_find_hung_context(struct intel_engine_cs *engine);
>
> int intel_guc_global_policies_update(struct intel_guc *guc);
>diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c
>index 621c0b4537a9..2f96bdf2b90f 100644
>--- a/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c
>+++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c
>@@ -746,6 +746,21 @@ int intel_guc_capture_output_min_size_est(struct intel_guc *guc)
>  *                                    intel_engine_coredump struct (if the context and
>  *                                    engine of the event notification matches a node
>  *                                    in the link list).
>+ *
>+ * User Sysfs / Debugfs
>+ * --------------------
>+ *      --> i915_gpu_coredump_copy_to_buffer->
>+ *                   L--> err_print_to_sgl --> err_print_gt
>+ *                        L--> error_print_guc_captures
>+ *                             L--> intel_guc_capture_print_node prints the
>+ *                                  register lists values of the attached node
>+ *                                  on the error-engine-dump being reported.
>+ *                   L--> i915_reset_error_state ... -->__i915_gpu_coredump_free
>+ *                        L--> ... cleanup_gt -->
>+ *                             L--> intel_guc_capture_free_node returns the
>+ *                                  capture-output-node back to the internal
>+ *                                  cachelist for reuse.
>+ *
>  */
>
> static int guc_capture_buf_cnt(struct __guc_capture_bufstate *buf)
>@@ -1346,9 +1361,156 @@ static void __guc_capture_process_output(struct intel_guc *guc)
>
> #if IS_ENABLED(CONFIG_DRM_I915_CAPTURE_ERROR)
>
>+static const char *
>+guc_capture_reg_to_str(const struct intel_guc *guc, u32 owner, u32 type,
>+		       u32 class, u32 id, u32 offset, u32 *is_ext)
>+{
>+	const struct __guc_mmio_reg_descr_group *reglists = guc->capture.priv->reglists;
>+	struct __guc_mmio_reg_descr_group *extlists = guc->capture.priv->extlists;
>+	const struct __guc_mmio_reg_descr_group *match;
>+	struct __guc_mmio_reg_descr_group *matchext;
>+	int j;
>+
>+	*is_ext = 0;
>+	if (!reglists)
>+		return NULL;
>+
>+	match = guc_capture_get_one_list(reglists, owner, type, id);
>+	if (!match)
>+		return NULL;
>+
>+	for (j = 0; j < match->num_regs; ++j) {
>+		if (offset == match->list[j].reg.reg)
>+			return match->list[j].regname;
>+	}
>+	if (extlists) {
>+		matchext = guc_capture_get_one_ext_list(extlists, owner, type, id);
>+		if (!matchext)
>+			return NULL;
>+		for (j = 0; j < matchext->num_regs; ++j) {
>+			if (offset == matchext->extlist[j].reg.reg) {
>+				*is_ext = 1;
>+				return matchext->extlist[j].regname;
>+			}
>+		}
>+	}
>+
>+	return NULL;
>+}
>+
>+#ifdef CONFIG_DRM_I915_DEBUG_GUC
>+#define __out(a, ...) \
>+	do { \
>+		drm_warn((&(a)->drm), __VA_ARGS__); \
>+		i915_error_printf((a), __VA_ARGS__); \
>+	} while (0)
>+#else
>+#define __out(a, ...) \
>+	i915_error_printf(a, __VA_ARGS__)
>+#endif
>+
>+#define GCAP_PRINT_INTEL_ENG_INFO(ebuf, eng) \
>+	do { \
>+		__out(ebuf, "    i915-Eng-Name: %s command stream\n", \
>+		      (eng)->name); \
>+		__out(ebuf, "    i915-Eng-Inst-Class: 0x%02x\n", (eng)->class); \
>+		__out(ebuf, "    i915-Eng-Inst-Id: 0x%02x\n", (eng)->instance); \
>+		__out(ebuf, "    i915-Eng-LogicalMask: 0x%08x\n", \
>+		      (eng)->logical_mask); \
>+	} while (0)
>+
>+#define GCAP_PRINT_GUC_INST_INFO(ebuf, node) \
>+	do { \
>+		__out(ebuf, "    GuC-Engine-Inst-Id: 0x%08x\n", \
>+		      (node)->eng_inst); \
>+		__out(ebuf, "    GuC-Context-Id: 0x%08x\n", (node)->guc_id); \
>+		__out(ebuf, "    LRCA: 0x%08x\n", (node)->lrca); \
>+	} while (0)
>+
> int intel_guc_capture_print_engine_node(struct drm_i915_error_state_buf *ebuf,
> 					const struct intel_engine_coredump *ee)
> {
>+	const char *grptype[GUC_STATE_CAPTURE_GROUP_TYPE_MAX] = {
>+		"full-capture",
>+		"partial-capture"
>+	};
>+	const char *datatype[GUC_CAPTURE_LIST_TYPE_MAX] = {
>+		"Global",
>+		"Engine-Class",
>+		"Engine-Instance"
>+	};
>+	struct intel_guc_state_capture *cap;
>+	struct __guc_capture_parsed_output *node;
>+	struct drm_i915_private *i915;
>+	struct guc_mmio_reg *regs;
>+	struct intel_guc *guc;
>+	struct intel_engine_cs *eng;
>+	const char *str;
>+	int numregs, i, j;
>+	u32 is_ext;
>+
>+	if (!ebuf || !ee)
>+		return -EINVAL;
>+	cap = ee->capture;
>+	if (!cap->priv || !ee->engine)
>+		return -ENODEV;
>+
>+	guc = container_of(cap, struct intel_guc, capture);
>+	i915 = (container_of(guc, struct intel_gt, uc.guc))->i915;
>+	__out(ebuf, "global --- GuC Error Capture on %s command stream:\n",
>+	      ee->engine->name);
>+
>+	node = ee->guc_capture_node;
>+	if (!node) {
>+		__out(ebuf, "  No matching ee-node\n");
>+		return 0;
>+	}
>+
>+	__out(ebuf, "Coverage:  %s\n", grptype[node->is_partial]);
>+
>+	for (i = GUC_CAPTURE_LIST_TYPE_GLOBAL; i < GUC_CAPTURE_LIST_TYPE_MAX; ++i) {
>+		__out(ebuf, "  RegListType: %s\n",
>+		      datatype[i % GUC_CAPTURE_LIST_TYPE_MAX]);
>+		__out(ebuf, "    Owner-Id: %d\n", node->reginfo[i].vfid);
>+
>+		switch (i) {
>+		case GUC_CAPTURE_LIST_TYPE_GLOBAL:
>+		default:
>+			break;
>+		case GUC_CAPTURE_LIST_TYPE_ENGINE_CLASS:
>+			__out(ebuf, "    GuC-Eng-Class: %d\n", node->eng_class);
>+			__out(ebuf, "    i915-Eng-Class: %d\n",
>+			      guc_class_to_engine_class(node->eng_class));
>+			break;
>+		case GUC_CAPTURE_LIST_TYPE_ENGINE_INSTANCE:
>+			eng = intel_guc_lookup_engine(guc, node->eng_class, node->eng_inst);
>+			if (eng)
>+				GCAP_PRINT_INTEL_ENG_INFO(ebuf, eng);
>+			else
>+				__out(ebuf, "    i915-Eng-Lookup Fail!\n");
>+			GCAP_PRINT_GUC_INST_INFO(ebuf, node);
>+			break;
>+		}
>+
>+		numregs = node->reginfo[i].num_regs;
>+		__out(ebuf, "    NumRegs: %d\n", numregs);
>+		j = 0;
>+		while (numregs--) {
>+			regs = node->reginfo[i].regs;
>+			str = guc_capture_reg_to_str(guc, GUC_CAPTURE_LIST_INDEX_PF, i,
>+						     node->eng_class, 0, regs[j].offset, &is_ext);
>+			if (!str)
>+				__out(ebuf, "      REG-0x%08x", regs[j].offset);
>+			else
>+				__out(ebuf, "      %s", str);
>+			if (is_ext)
>+				__out(ebuf, "[%ld][%ld]",
>+				      FIELD_GET(GUC_REGSET_STEERING_GROUP, regs[j].flags),
>+				      FIELD_GET(GUC_REGSET_STEERING_INSTANCE, regs[j].flags));
>+			__out(ebuf, ":  0x%08x\n", regs[j].value);
>+			++j;
>+		}
>+	}
> 	return 0;
> }
>
>diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.h b/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.h
>index ae527c98e2f6..d544efcf0e9f 100644
>--- a/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.h
>+++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.h
>@@ -12,8 +12,8 @@ struct drm_i915_error_state_buf;
> struct file;
> struct guc_ads;
> struct guc_gt_system_info;
>-struct intel_context;
> struct intel_engine_coredump;
>+struct intel_context;
> struct intel_gt;
> struct intel_guc;
>
>diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c
>index 5d41dc95f066..29f35f9f3c6d 100644
>--- a/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c
>+++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c
>@@ -4087,8 +4087,8 @@ int intel_guc_error_capture_process_msg(struct intel_guc *guc,
> 	return 0;
> }
>
>-static struct intel_engine_cs *
>-guc_lookup_engine(struct intel_guc *guc, u8 guc_class, u8 instance)
>+struct intel_engine_cs *
>+intel_guc_lookup_engine(struct intel_guc *guc, u8 guc_class, u8 instance)
> {
> 	struct intel_gt *gt = guc_to_gt(guc);
> 	u8 engine_class = guc_class_to_engine_class(guc_class);
>@@ -4137,7 +4137,7 @@ int intel_guc_engine_failure_process_msg(struct intel_guc *guc,
> 	instance = msg[1];
> 	reason = msg[2];
>
>-	engine = guc_lookup_engine(guc, guc_class, instance);
>+	engine = intel_guc_lookup_engine(guc, guc_class, instance);
> 	if (unlikely(!engine)) {
> 		drm_err(&gt->i915->drm,
> 			"Invalid engine %d:%d", guc_class, instance);
>diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
>index 274abf267f35..71489ce52746 100644
>--- a/drivers/gpu/drm/i915/i915_debugfs.c
>+++ b/drivers/gpu/drm/i915/i915_debugfs.c
>@@ -308,6 +308,7 @@ static int i915_gpu_info_open(struct inode *inode, struct file *file)
> 	gpu = NULL;
> 	with_intel_runtime_pm(&i915->runtime_pm, wakeref)
> 		gpu = i915_gpu_coredump(to_gt(i915), ALL_ENGINES, CORE_DUMP_FLAG_NONE);
>+
> 	if (IS_ERR(gpu))
> 		return PTR_ERR(gpu);
>
>diff --git a/drivers/gpu/drm/i915/i915_gpu_error.c b/drivers/gpu/drm/i915/i915_gpu_error.c
>index 3d566bbe228d..3c7b9d0e8627 100644
>--- a/drivers/gpu/drm/i915/i915_gpu_error.c
>+++ b/drivers/gpu/drm/i915/i915_gpu_error.c
>@@ -530,8 +530,8 @@ __find_vma(struct i915_vma_coredump *vma, const char *name)
> 	return NULL;
> }
>
>-static struct i915_vma_coredump *
>-find_batch(const struct intel_engine_coredump *ee)
>+struct i915_vma_coredump *
>+intel_gpu_error_find_batch(const struct intel_engine_coredump *ee)
> {
> 	return __find_vma(ee->vma, "batch");
> }
>@@ -559,7 +559,7 @@ static void error_print_engine(struct drm_i915_error_state_buf *m,
>
> 	error_print_instdone(m, ee);
>
>-	batch = find_batch(ee);
>+	batch = intel_gpu_error_find_batch(ee);
> 	if (batch) {
> 		u64 start = batch->gtt_offset;
> 		u64 end = start + batch->gtt_size;
>@@ -610,9 +610,9 @@ void i915_error_printf(struct drm_i915_error_state_buf *e, const char *f, ...)
> 	va_end(args);
> }
>
>-static void print_error_vma(struct drm_i915_error_state_buf *m,
>-			    const struct intel_engine_cs *engine,
>-			    const struct i915_vma_coredump *vma)
>+void intel_gpu_error_print_vma(struct drm_i915_error_state_buf *m,
>+			       const struct intel_engine_cs *engine,
>+			       const struct i915_vma_coredump *vma)
> {
> 	char out[ASCII85_BUFSZ];
> 	struct page *page;
>@@ -681,7 +681,7 @@ static void err_print_uc(struct drm_i915_error_state_buf *m,
>
> 	intel_uc_fw_dump(&error_uc->guc_fw, &p);
> 	intel_uc_fw_dump(&error_uc->huc_fw, &p);
>-	print_error_vma(m, NULL, error_uc->guc_log);
>+	intel_gpu_error_print_vma(m, NULL, error_uc->guc_log);
> }
>
> static void err_free_sgl(struct scatterlist *sgl)
>@@ -801,7 +801,7 @@ static void err_print_gt_engines(struct drm_i915_error_state_buf *m,
> 		error_print_context(m, "  Active context: ", &ee->context);
>
> 		for (vma = ee->vma; vma; vma = vma->next)
>-			print_error_vma(m, ee->engine, vma);
>+			intel_gpu_error_print_vma(m, ee->engine, vma);
> 	}
>
> }
>diff --git a/drivers/gpu/drm/i915/i915_gpu_error.h b/drivers/gpu/drm/i915/i915_gpu_error.h
>index 64b999d8ac27..9554ce7a71ef 100644
>--- a/drivers/gpu/drm/i915/i915_gpu_error.h
>+++ b/drivers/gpu/drm/i915/i915_gpu_error.h
>@@ -237,6 +237,11 @@ static inline u32 i915_reset_engine_count(struct i915_gpu_error *error,
>
> __printf(2, 3)
> void i915_error_printf(struct drm_i915_error_state_buf *e, const char *f, ...);
>+void intel_gpu_error_print_vma(struct drm_i915_error_state_buf *m,
>+			       const struct intel_engine_cs *engine,
>+			       const struct i915_vma_coredump *vma);
>+struct i915_vma_coredump *
>+intel_gpu_error_find_batch(const struct intel_engine_coredump *ee);
>
> struct i915_gpu_coredump *i915_gpu_coredump(struct intel_gt *gt,
> 					    intel_engine_mask_t engine_mask, u32 dump_flags);
>-- 
>2.25.1
>

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [Intel-gfx] [PATCH v7 01/13] drm/i915/guc: Update GuC ADS size for error capture lists
  2022-03-10  5:30   ` Matthew Brost
@ 2022-03-11  9:59     ` Teres Alexis, Alan Previn
  2022-03-11 10:18     ` Teres Alexis, Alan Previn
  1 sibling, 0 replies; 32+ messages in thread
From: Teres Alexis, Alan Previn @ 2022-03-11  9:59 UTC (permalink / raw)
  To: Matthew Brost; +Cc: intel-gfx

Thanks for reviewing Matt.. On one specific open - have replied below. 
Will fix the others.

...alan

On 3/9/2022 9:30 PM, Matthew Brost wrote:
> On Sat, Feb 26, 2022 at 01:55:29AM -0800, Alan Previn wrote:
> +static int
>> +guc_capture_prep_lists(struct intel_guc *guc)
>>   {
>> +	struct intel_gt *gt = guc_to_gt(guc);
>> +	struct drm_i915_private *i915 = guc_to_gt(guc)->i915;
>> +	u32 ggtt, capture_offset, null_ggtt, total_size = 0;
>> +	struct guc_gt_system_info local_info;
>> +	struct iosys_map info_map;
>> +	u32 null_header[2]={0};
>> +	struct file *file;
>> +	size_t size = 0;
>>   	int i, j;
>> -	u32 addr_ggtt, offset;
>>   
>> -	offset = guc_ads_capture_offset(guc);
>> -	addr_ggtt = intel_guc_ggtt_offset(guc, guc->ads_vma) + offset;
>> +	if (!iosys_map_is_null(&guc->ads_map)) {
>> +		capture_offset = guc_ads_capture_offset(guc);
>> +		ggtt = intel_guc_ggtt_offset(guc, guc->ads_vma) + capture_offset;
> This should just be capture_offset, right? ads_map is CPU mapped
> address that has nothing do with the GGTT address, it is just a pointer
> to the base of the ADS structure that can be accessed through the
> iosys_map* macros.

Good catch Matt...

note that "ggtt" is being used two ways in this function: [1] to memcpy 
content (register lists) and [2] to write a GGTT offset into members of 
ADS structure (the ptr to #1).

For the usage of [1] such as above, you are right, we shouldnt be 
including the vma's offset offsetting from ads_map. But for [2] we need 
both ads-offset + capture-offset.

for clarity, i can change the variable  name from "ggtt" to "ads_offset" 
and then reuse "capture_ggtt" with the rolling lists' ggtt offset.


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [Intel-gfx] [PATCH v7 01/13] drm/i915/guc: Update GuC ADS size for error capture lists
  2022-03-10  5:30   ` Matthew Brost
  2022-03-11  9:59     ` Teres Alexis, Alan Previn
@ 2022-03-11 10:18     ` Teres Alexis, Alan Previn
  1 sibling, 0 replies; 32+ messages in thread
From: Teres Alexis, Alan Previn @ 2022-03-11 10:18 UTC (permalink / raw)
  To: Matthew Brost; +Cc: intel-gfx


On 3/9/2022 9:30 PM, Matthew Brost wrote:
> On Sat, Feb 26, 2022 at 01:55:29AM -0800, Alan Previn wrote:
> +intel_guc_capture_getlist(struct intel_guc *guc, u32 owner, u32 type, 
> u32 classid,
>> +			  struct file **fileoutptr)
>> +{
>> +	struct __guc_state_capture_priv *gc = guc->capture.priv;
>> +	struct __guc_capture_ads_cache *cache = &gc->ads_cache[owner][type][classid];
>> +	struct drm_i915_private *i915 = guc_to_gt(guc)->i915;
>> +	struct guc_debug_capture_list *listnode;
>> +	struct file *file;
>> +	u8 *caplist, *tmp;
>> +	size_t size = 0;
>> +	int ret, num_regs;
>> +
>> +	if (!gc->reglists)
>> +		return -ENODEV;
>> +
>> +	if (cache->is_valid) {
>> +		*fileoutptr = cache->file;
>> +		return cache->status;
>> +	}
>> +
>> +	ret = intel_guc_capture_getlistsize(guc, owner, type, classid, &size);
>> +	if (ret) {
>> +		cache->is_valid = true;
>> +		cache->file = NULL;
>> +		cache->size = 0;
>> +		cache->status = ret;
>> +		return ret;
>> +	}
>> +
>> +	caplist = kzalloc(size, GFP_KERNEL);
>> +	if (!caplist)
>> +		return -ENOMEM;
>> +
>> +	/* populate capture list header */
>> +	tmp = caplist;
>> +	num_regs = guc_cap_list_num_regs(guc->capture.priv, owner, type, classid);
>> +	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, classid, (struct guc_mmio_reg *)tmp, num_regs);
>> +
>> +	/* ADS only takes file handles, so we keep that as our cache */
>> +	file = shmem_create_from_data("guc-err-cap", caplist, size);
> I don't think you need a file here, why can't this just be a peice of
> kalloc'd memory that is populated and copied via IOSYS map calls?
Agreed on above.
> The memory will need to be persistent (allocated at driver load, free'd
> during unload) as can't allocate memory in the reset path tho.
Of course (as per prior rev comments)
> Beyond that, you are parsing a static table to populate memory. That
> almost certainly means you could just have static memory which we
> directly copy into the ADS.

On this, we dont actually copy the static tables from the top of this 
src file as-is directly into ADS because the format of the static tables 
is different from what ADS expects - i need both formats since the 
latter is lean (just register info) but the former also contains the 
name of registers (strings) to be used at printout time.

> Matt
>

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [Intel-gfx] [PATCH v7 10/13] drm/i915/guc: Extract GuC error capture lists on G2H notification.
  2022-02-26  9:55 ` [Intel-gfx] [PATCH v7 10/13] drm/i915/guc: Extract GuC error capture lists on G2H notification Alan Previn
@ 2022-03-11 18:16   ` Umesh Nerlige Ramappa
  2022-03-11 22:27     ` Teres Alexis, Alan Previn
  0 siblings, 1 reply; 32+ messages in thread
From: Umesh Nerlige Ramappa @ 2022-03-11 18:16 UTC (permalink / raw)
  To: Alan Previn; +Cc: intel-gfx

On Sat, Feb 26, 2022 at 01:55:38AM -0800, Alan Previn wrote:
>- 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 i915_gpu_codedump, duplicate global and engine-class register
>  lists for each engine-instance register dump if we find
>  dependent-engine resets in a engine-capture-group.
>- When i915_gpu_coredump calls into capture_engine, (in a
>  subsequent patch) we detach the matching node (guc-id,
>  LRCA, etc) from the link list above and attach it to
>  i915_gpu_coredump's intel_engine_coredump structure when have
>  matching LRCA/guc-id/engine-instance.
>
>Additional notes to be aware of:
>- GuC generates the error capture dump into the GuC log buffer but
>  this buffer is one big log buffer with 3 independent subregions
>  within it. Each subregion is populated with different content
>  and used in different ways and timings but all regions operate
>  behave as independent ring buffers. Each guc-log subregion
>  (general-logs, crash-dump and error- capture) has it's own
>  guc_log_buffer_state that contain independent read and write
>  pointers.
>
>Signed-off-by: Alan Previn <alan.previn.teres.alexis@intel.com>
>---
> .../gpu/drm/i915/gt/uc/abi/guc_actions_abi.h  |   7 +
> drivers/gpu/drm/i915/gt/uc/guc_capture_fwif.h |  56 ++
> .../gpu/drm/i915/gt/uc/intel_guc_capture.c    | 546 +++++++++++++++++-
> .../gpu/drm/i915/gt/uc/intel_guc_capture.h    |   1 +
> drivers/gpu/drm/i915/gt/uc/intel_guc_log.c    |  26 +-
> drivers/gpu/drm/i915/gt/uc/intel_guc_log.h    |   4 +
> .../gpu/drm/i915/gt/uc/intel_guc_submission.c |  10 +-
> 7 files changed, 639 insertions(+), 11 deletions(-)
>
>diff --git a/drivers/gpu/drm/i915/gt/uc/abi/guc_actions_abi.h b/drivers/gpu/drm/i915/gt/uc/abi/guc_actions_abi.h
>index 7afdadc7656f..ae6448fcaf90 100644
>--- a/drivers/gpu/drm/i915/gt/uc/abi/guc_actions_abi.h
>+++ b/drivers/gpu/drm/i915/gt/uc/abi/guc_actions_abi.h
>@@ -173,4 +173,11 @@ enum intel_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 intel_guc_state_capture_event_status {
>+	INTEL_GUC_STATE_CAPTURE_EVENT_STATUS_SUCCESS = 0x0,
>+	INTEL_GUC_STATE_CAPTURE_EVENT_STATUS_NOSPACE = 0x1,
>+};
>+
>+#define INTEL_GUC_STATE_CAPTURE_EVENT_STATUS_MASK      0x000000FF
>+
> #endif /* _ABI_GUC_ACTIONS_ABI_H */
>diff --git a/drivers/gpu/drm/i915/gt/uc/guc_capture_fwif.h b/drivers/gpu/drm/i915/gt/uc/guc_capture_fwif.h
>index d5cb7d5d4ca7..2b09aa05d8b7 100644
>--- a/drivers/gpu/drm/i915/gt/uc/guc_capture_fwif.h
>+++ b/drivers/gpu/drm/i915/gt/uc/guc_capture_fwif.h
>@@ -12,6 +12,52 @@
> struct intel_guc;
> struct file;
>
>+/**
>+ * 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;
>+	void *data;
>+	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 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
>+	 * i915_gpu_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_CAPTURE_LIST_TYPE_MAX];
>+#define GCAP_PARSED_REGLIST_INDEX_GLOBAL   BIT(GUC_CAPTURE_LIST_TYPE_GLOBAL)
>+#define GCAP_PARSED_REGLIST_INDEX_ENGCLASS BIT(GUC_CAPTURE_LIST_TYPE_ENGINE_CLASS)
>+#define GCAP_PARSED_REGLIST_INDEX_ENGINST  BIT(GUC_CAPTURE_LIST_TYPE_ENGINE_INSTANCE)
>+};
>+
> /**
>  * struct guc_debug_capture_list_header / struct guc_debug_capture_list
>  *
>@@ -140,6 +186,16 @@ struct __guc_state_capture_priv {
> 	struct __guc_capture_ads_cache ads_cache[GUC_CAPTURE_LIST_INDEX_MAX]
> 						[GUC_CAPTURE_LIST_TYPE_MAX]
> 						[GUC_MAX_ENGINE_CLASSES];
>+
>+	/**
>+	 * @outlist: allocated nodes with parsed engine-instance error capture data
>+	 *
>+	 * A linked list of parsed GuC error-capture output data before
>+	 * reporting with formatting via i915_gpu_coredump. Each node in this linked list shall
>+	 * contain a single engine-capture including global, engine-class and
>+	 * engine-instance register dumps as per guc_capture_parsed_output_node
>+	 */
>+	struct list_head outlist;
> };
>
> #endif /* _INTEL_GUC_CAPTURE_FWIF_H */
>diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c
>index ed78995bcc35..9308157d9a74 100644
>--- a/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c
>+++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c
>@@ -14,6 +14,8 @@
> #include "guc_capture_fwif.h"
> #include "intel_guc_fwif.h"
> #include "i915_drv.h"
>+#include "i915_gpu_error.h"
>+#include "i915_irq.h"
> #include "i915_memcpy.h"
> #include "i915_reg.h"
>
>@@ -653,6 +655,9 @@ int intel_guc_capture_output_min_size_est(struct intel_guc *guc)
> 	int worst_min_size = 0, num_regs = 0;
> 	size_t tmp = 0;
>
>+	if (!guc->capture.priv)
>+		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
>@@ -671,7 +676,7 @@ int intel_guc_capture_output_min_size_est(struct intel_guc *guc)
>
> 	for_each_engine(engine, gt, id) {
> 		worst_min_size += sizeof(struct guc_state_capture_group_header_t) +
>-				  (3 * sizeof(struct guc_state_capture_header_t));
>+					 (3 * sizeof(struct guc_state_capture_header_t));
>
> 		if (!intel_guc_capture_getlistsize(guc, 0, GUC_CAPTURE_LIST_TYPE_GLOBAL, 0, &tmp))
> 			num_regs += tmp;
>@@ -691,6 +696,541 @@ int intel_guc_capture_output_min_size_est(struct intel_guc *guc)
> 	return (worst_min_size * GUC_CAPTURE_OVERBUFFER_MULTIPLIER);
> }
>
>+/*
>+ * KMD Init time flows:
>+ * --------------------
>+ *     --> alloc A: GuC input capture regs lists (registered to GuC via ADS).
>+ *                  intel_guc_ads acquires the register lists by calling
>+ *                  intel_guc_capture_list_size and intel_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--> intel_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 dynamically allocated and populated with the error-capture
>+ *                   data from GuC and then it's added into guc->capture->priv->outlist linked
>+ *                   list. This list is used for matchup and printout by i915_gpu_coredump
>+ *                   and err_print_gt, (when user invokes the error capture sysfs).
>+ */
>+
>+static int guc_capture_buf_cnt(struct __guc_capture_bufstate *buf)
>+{
>+	if (buf->rd == buf->wr)
>+		return 0;
>+	if (buf->wr > buf->rd)
>+		return (buf->wr - buf->rd);

nit: For the above 2 conditions:

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);
>+}
>+
>+static int
>+guc_capture_log_remove_dw(struct intel_guc *guc, struct __guc_capture_bufstate *buf,
>+			  u32 *dw)
>+{
>+	struct drm_i915_private *i915 = guc_to_gt(guc)->i915;
>+	int tries = 2;
>+	int avail = 0;
>+	u32 *src_data;
>+
>+	if (!guc_capture_buf_cnt(buf))
>+		return 0;
>+
>+	while (tries--) {
>+		avail = guc_capture_buf_cnt_to_end(buf);
>+		if (avail >= sizeof(u32)) {
>+			src_data = (u32 *)(buf->data + buf->rd);
>+			*dw = *src_data;
>+			buf->rd += 4;
>+			return 4;
>+		}
>+		if (avail)
>+			drm_dbg(&i915->drm, "GuC-Cap-Logs not dword aligned, skipping.\n");
>+		buf->rd = 0;

(1) This looks like an understanding between GuC and KMD that GuC will 
not write partial headers at the end of the log buffer if there is no 
space to fit the header. If you encounter such a scenario, you go back 
to the beginning. Right? If so, please add a comment indicating the same 
here.

>+	}
>+
>+	return 0;
>+}
>+
>+static bool
>+guc_capture_data_extracted(struct __guc_capture_bufstate *b,
>+			   int size, void *dest)
>+{
>+	if (guc_capture_buf_cnt_to_end(b) >= size) {
>+		memcpy(dest, (b->data + b->rd), size);
>+		b->rd += size;
>+		return true;
>+	}
>+	return false;
>+}
>+
>+static int
>+guc_capture_log_get_group_hdr(struct intel_guc *guc, struct __guc_capture_bufstate *buf,
>+			      struct guc_state_capture_group_header_t *ghdr)
>+{
>+	int read = 0;
>+	int fullsize = sizeof(struct guc_state_capture_group_header_t);
>+
>+	if (fullsize > guc_capture_buf_cnt(buf))
>+		return -1;
>+
>+	if (guc_capture_data_extracted(buf, fullsize, (void *)ghdr))
>+		return 0;
>+
>+	read += guc_capture_log_remove_dw(guc, buf, &ghdr->owner);
>+	read += guc_capture_log_remove_dw(guc, buf, &ghdr->info);
>+	if (read != fullsize)
>+		return -1;
>+
>+	return 0;
>+}
>+
>+static int
>+guc_capture_log_get_data_hdr(struct intel_guc *guc, struct __guc_capture_bufstate *buf,
>+			     struct guc_state_capture_header_t *hdr)
>+{
>+	int read = 0;
>+	int fullsize = sizeof(struct guc_state_capture_header_t);
>+
>+	if (fullsize > guc_capture_buf_cnt(buf))
>+		return -1;
>+
>+	if (guc_capture_data_extracted(buf, fullsize, (void *)hdr))
>+		return 0;
>+
>+	read += guc_capture_log_remove_dw(guc, buf, &hdr->owner);
>+	read += guc_capture_log_remove_dw(guc, buf, &hdr->info);
>+	read += guc_capture_log_remove_dw(guc, buf, &hdr->lrca);
>+	read += guc_capture_log_remove_dw(guc, buf, &hdr->guc_id);
>+	read += guc_capture_log_remove_dw(guc, buf, &hdr->num_mmios);
>+	if (read != fullsize)
>+		return -1;
>+
>+	return 0;
>+}
>+
>+static int
>+guc_capture_log_get_register(struct intel_guc *guc, struct __guc_capture_bufstate *buf,
>+			     struct guc_mmio_reg *reg)
>+{
>+	int read = 0;
>+	int fullsize = sizeof(struct guc_mmio_reg);
>+
>+	if (fullsize > guc_capture_buf_cnt(buf))
>+		return -1;
>+
>+	if (guc_capture_data_extracted(buf, fullsize, (void *)reg))
>+		return 0;
>+
>+	read += guc_capture_log_remove_dw(guc, buf, &reg->offset);
>+	read += guc_capture_log_remove_dw(guc, buf, &reg->value);
>+	read += guc_capture_log_remove_dw(guc, buf, &reg->flags);
>+	read += guc_capture_log_remove_dw(guc, buf, &reg->mask);
>+	if (read != fullsize)
>+		return -1;
>+
>+	return 0;
>+}
>+
>+static void
>+guc_capture_delete_one_node(struct intel_guc *guc, struct __guc_capture_parsed_output *node)
>+{
>+	int i;
>+
>+	for (i = 0; i < GUC_CAPTURE_LIST_TYPE_MAX; ++i) {
>+		if (node->reginfo[i].regs)
>+			kfree(node->reginfo[i].regs);

(2) Check before kfree is not needed here and other places that you use 
kfree.

Ref: https://www.kernel.org/doc/htmldocs/kernel-api/API-kfree.html

>+	}
>+	list_del(&node->link);
>+	kfree(node);
>+}
>+
>+static void
>+guc_capture_delete_nodes(struct intel_guc *guc)
>+{
>+	/*
>+	 * NOTE: At the end of driver operation, we must assume that we
>+	 * have nodes in outlist from unclaimed error capture events
>+	 * that occurred prior to shutdown.
>+	 */
>+	if (!list_empty(&guc->capture.priv->outlist)) {
>+		struct __guc_capture_parsed_output *n, *ntmp;
>+
>+		list_for_each_entry_safe(n, ntmp, &guc->capture.priv->outlist, link)
>+			guc_capture_delete_one_node(guc, n);
>+	}
>+}
>+
>+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 __guc_state_capture_priv *gc,
>+				struct __guc_capture_parsed_output *node)
>+{
>+	guc_capture_add_node_to_list(node, &gc->outlist);
>+}
>+
>+static void
>+guc_capture_init_node(struct intel_guc *guc, struct __guc_capture_parsed_output *node)
>+{
>+	INIT_LIST_HEAD(&node->link);
>+}
>+
>+static struct __guc_capture_parsed_output *
>+guc_capture_alloc_one_node(struct intel_guc *guc)
>+{
>+	struct __guc_capture_parsed_output *new;
>+
>+	new = kzalloc(sizeof(*new), GFP_KERNEL);
>+	if (!new)
>+		return NULL;
>+
>+	guc_capture_init_node(guc, new);
>+
>+	return new;
>+}
>+
>+static struct __guc_capture_parsed_output *
>+guc_capture_clone_node(struct intel_guc *guc, struct __guc_capture_parsed_output *ori,
>+		       u32 keep_reglist_mask)
>+{
>+	struct __guc_capture_parsed_output *new;
>+	int i;
>+
>+	new = guc_capture_alloc_one_node(guc);
>+	if (!new)
>+		return NULL;
>+	if (!ori)
>+		return new;

For readability, I would s/ori/orignal/ if that's what you mean.

>+
>+	new->is_partial = ori->is_partial;
>+
>+	/* copy reg-lists that we want to clone */
>+	for (i = 0; i < GUC_CAPTURE_LIST_TYPE_MAX; ++i) {
>+		if (keep_reglist_mask & BIT(i)) {
>+			new->reginfo[i].regs = kcalloc(ori->reginfo[i].num_regs,
>+						       sizeof(struct guc_mmio_reg), GFP_KERNEL);
>+			if (!new->reginfo[i].regs)
>+				goto bail_clone;
>+
>+			memcpy(new->reginfo[i].regs, ori->reginfo[i].regs,
>+			       ori->reginfo[i].num_regs * sizeof(struct guc_mmio_reg));
>+			new->reginfo[i].num_regs = ori->reginfo[i].num_regs;
>+			new->reginfo[i].vfid  = ori->reginfo[i].vfid;
>+
>+			if (i == GUC_CAPTURE_LIST_TYPE_ENGINE_CLASS) {
>+				new->eng_class = ori->eng_class;
>+			} else if (i == GUC_CAPTURE_LIST_TYPE_ENGINE_INSTANCE) {
>+				new->eng_inst = ori->eng_inst;
>+				new->guc_id = ori->guc_id;
>+				new->lrca = ori->lrca;
>+			}
>+		}
>+	}
>+
>+	return new;
>+
>+bail_clone:
>+	for (i = 0; i < GUC_CAPTURE_LIST_TYPE_MAX; ++i) {
>+		if (new->reginfo[i].regs)
>+			kfree(new->reginfo[i].regs);
>+	}
>+	kfree(new);
>+	return NULL;
>+}
>+
>+static int
>+guc_capture_extract_reglists(struct intel_guc *guc, struct __guc_capture_bufstate *buf)
>+{
>+	struct drm_i915_private *i915 = guc_to_gt(guc)->i915;
>+	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_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)) {
>+		drm_warn(&i915->drm, "GuC Capture new entries unaligned\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(CAP_GRP_HDR_CAPTURE_TYPE, ghdr.info);
>+	numlists = FIELD_GET(CAP_GRP_HDR_NUM_CAPTURES, ghdr.info);
>+
>+	while (numlists--) {
>+		if (guc_capture_log_get_data_hdr(guc, buf, &hdr)) {
>+			ret = -EIO;
>+			break;
>+		}
>+
>+		datatype = FIELD_GET(CAP_HDR_CAPTURE_TYPE, hdr.info);
>+		if (datatype > GUC_CAPTURE_LIST_TYPE_ENGINE_INSTANCE) {
>+			/* unknown capture type - skip over to next capture set */
>+			numregs = FIELD_GET(CAP_HDR_NUM_MMIOS, hdr.num_mmios);
>+			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 i915_gpu_coredump 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_CAPTURE_LIST_TYPE_GLOBAL) {
>+				guc_capture_add_node_to_outlist(guc->capture.priv, node);
>+				node = NULL;
>+			} else if (datatype == GUC_CAPTURE_LIST_TYPE_ENGINE_CLASS &&
>+				   node->reginfo[GUC_CAPTURE_LIST_TYPE_ENGINE_CLASS].regs) {
>+				/* Add to list, clone node and duplicate global list */
>+				guc_capture_add_node_to_outlist(guc->capture.priv, node);
>+				node = guc_capture_clone_node(guc, node,
>+							      GCAP_PARSED_REGLIST_INDEX_GLOBAL);
>+			} else if (datatype == GUC_CAPTURE_LIST_TYPE_ENGINE_INSTANCE &&
>+				   node->reginfo[GUC_CAPTURE_LIST_TYPE_ENGINE_INSTANCE].regs) {
>+				/* Add to list, clone node and duplicate global + class lists */
>+				guc_capture_add_node_to_outlist(guc->capture.priv, node);
>+				node = guc_capture_clone_node(guc, node,
>+							      (GCAP_PARSED_REGLIST_INDEX_GLOBAL |
>+							      GCAP_PARSED_REGLIST_INDEX_ENGCLASS));
>+			}
>+		}
>+
>+		if (!node) {
>+			node = guc_capture_alloc_one_node(guc);
>+			if (!node) {
>+				ret = -ENOMEM;
>+				break;
>+			}
>+			if (datatype != GUC_CAPTURE_LIST_TYPE_GLOBAL)
>+				drm_dbg(&i915->drm, "GuC Capture missing global dump: %08x!\n",
>+					datatype);
>+		}
>+		node->is_partial = is_partial;
>+		node->reginfo[datatype].vfid = FIELD_GET(CAP_HDR_CAPTURE_VFID, hdr.owner);
>+		switch (datatype) {
>+		case GUC_CAPTURE_LIST_TYPE_ENGINE_INSTANCE:
>+			node->eng_class = FIELD_GET(CAP_HDR_ENGINE_CLASS, hdr.info);
>+			node->eng_inst = FIELD_GET(CAP_HDR_ENGINE_INSTANCE, hdr.info);
>+			node->lrca = hdr.lrca;
>+			node->guc_id = hdr.guc_id;
>+			break;
>+		case GUC_CAPTURE_LIST_TYPE_ENGINE_CLASS:
>+			node->eng_class = FIELD_GET(CAP_HDR_ENGINE_CLASS, hdr.info);
>+			break;
>+		default:
>+			break;
>+		}
>+
>+		regs = NULL;
>+		numregs = FIELD_GET(CAP_HDR_NUM_MMIOS, hdr.num_mmios);
>+		if (numregs) {
>+			regs = kcalloc(numregs, sizeof(struct guc_mmio_reg), GFP_KERNEL);
>+			if (!regs) {
>+				ret = -ENOMEM;
>+				break;
>+			}
>+		}
>+		node->reginfo[datatype].num_regs = numregs;
>+		node->reginfo[datatype].regs = regs;
>+		i = 0;
>+		while (numregs--) {
>+			if (guc_capture_log_get_register(guc, buf, &regs[i++])) {
>+				ret = -EIO;
>+				break;
>+			}
>+		}
>+	}
>+
>+bailout:
>+	if (node) {
>+		/* If we have data, add to linked list for match-up when i915_gpu_coredump calls */
>+		for (i = GUC_CAPTURE_LIST_TYPE_GLOBAL; i < GUC_CAPTURE_LIST_TYPE_MAX; ++i) {
>+			if (node->reginfo[i].regs) {
>+				guc_capture_add_node_to_outlist(guc->capture.priv, node);
>+				node = NULL;
>+				break;
>+			}
>+		}
>+		if (node) /* else free it */
>+			kfree(node);
>+	}
>+	return ret;
>+}
>+
>+static int __guc_capture_flushlog_complete(struct intel_guc *guc)
>+{
>+	u32 action[] = {
>+		INTEL_GUC_ACTION_LOG_BUFFER_FILE_FLUSH_COMPLETE,
>+		2
>+	};
>+
>+	return intel_guc_send(guc, action, ARRAY_SIZE(action));
>+}
>+
>+static void __guc_capture_process_output(struct intel_guc *guc)
>+{
>+	unsigned int buffer_size, read_offset, write_offset, full_count;
>+	struct intel_uc *uc = container_of(guc, typeof(*uc), guc);
>+	struct drm_i915_private *i915 = guc_to_gt(guc)->i915;
>+	struct guc_log_buffer_state log_buf_state_local;
>+	struct guc_log_buffer_state *log_buf_state;
>+	struct __guc_capture_bufstate buf;
>+	void *src_data = NULL;
>+	bool new_overflow;
>+	int ret;
>+
>+	log_buf_state = guc->log.buf_addr +
>+			(sizeof(struct guc_log_buffer_state) * GUC_CAPTURE_LOG_BUFFER);
>+	src_data = guc->log.buf_addr + intel_guc_get_log_buffer_offset(GUC_CAPTURE_LOG_BUFFER);
>+
>+	/*
>+	 * 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.
>+	 */
>+	memcpy(&log_buf_state_local, log_buf_state, sizeof(struct guc_log_buffer_state));
>+	buffer_size = intel_guc_get_log_buffer_size(GUC_CAPTURE_LOG_BUFFER);
>+	read_offset = log_buf_state_local.read_ptr;
>+	write_offset = log_buf_state_local.sampled_write_ptr;
>+	full_count = log_buf_state_local.buffer_full_cnt;
>+
>+	/* Bookkeeping stuff */
>+	guc->log.stats[GUC_CAPTURE_LOG_BUFFER].flush += log_buf_state_local.flush_to_file;
>+	new_overflow = intel_guc_check_log_buf_overflow(&guc->log, GUC_CAPTURE_LOG_BUFFER,
>+							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))) {
>+		drm_err(&i915->drm, "invalid GuC log capture buffer state!\n");
>+		/* 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 = src_data;
>+
>+	if (!uc->reset_in_progress) {
>+		do {
>+			ret = guc_capture_extract_reglists(guc, &buf);
>+		} while (ret >= 0);
>+	}
>+
>+	/* Update the state of log buffer err-cap state */
>+	log_buf_state->read_ptr = write_offset;
>+	log_buf_state->flush_to_file = 0;
>+	__guc_capture_flushlog_complete(guc);
>+}
>+
>+void intel_guc_capture_process(struct intel_guc *guc)
>+{
>+	if (guc->capture.priv)
>+		__guc_capture_process_output(guc);
>+}
>+
> static void
> guc_capture_free_ads_cache(struct __guc_state_capture_priv *gc)
> {
>@@ -715,6 +1255,8 @@ void intel_guc_capture_destroy(struct intel_guc *guc)
>
> 	guc_capture_free_ads_cache(guc->capture.priv);
>
>+	guc_capture_delete_nodes(guc);
>+
> 	if (guc->capture.priv->extlists) {
> 		guc_capture_free_extlists(guc->capture.priv->extlists);
> 		kfree(guc->capture.priv->extlists);
>@@ -732,5 +1274,7 @@ int intel_guc_capture_init(struct intel_guc *guc)
>
> 	guc->capture.priv->reglists = guc_capture_get_device_reglist(guc);
>
>+	INIT_LIST_HEAD(&guc->capture.priv->outlist);
>+
> 	return 0;
> }
>diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.h b/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.h
>index 24a11f33f7d9..3c79460c7ca5 100644
>--- a/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.h
>+++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.h
>@@ -12,6 +12,7 @@ struct file;
> struct guc_gt_system_info;
> struct intel_guc;
>
>+void intel_guc_capture_process(struct intel_guc *guc);
> int intel_guc_capture_output_min_size_est(struct intel_guc *guc);
> int intel_guc_capture_getlist(struct intel_guc *guc, u32 owner, u32 type, u32 classid,
> 			      struct file **fileptr);
>diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_log.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_log.c
>index e9a865c2f4cb..8d59a11ec595 100644
>--- a/drivers/gpu/drm/i915/gt/uc/intel_guc_log.c
>+++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_log.c
>@@ -158,9 +158,9 @@ static void *guc_get_write_buffer(struct intel_guc_log *log)
> 	return relay_reserve(log->relay.channel, 0);
> }
>
>-static bool guc_check_log_buf_overflow(struct intel_guc_log *log,
>-				       enum guc_log_buffer_type type,
>-				       unsigned int full_cnt)
>+bool intel_guc_check_log_buf_overflow(struct intel_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;
>@@ -183,7 +183,7 @@ static bool guc_check_log_buf_overflow(struct intel_guc_log *log,
> 	return overflow;
> }
>
>-static unsigned int guc_get_log_buffer_size(enum guc_log_buffer_type type)
>+unsigned int intel_guc_get_log_buffer_size(enum guc_log_buffer_type type)
> {
> 	switch (type) {
> 	case GUC_DEBUG_LOG_BUFFER:
>@@ -199,6 +199,20 @@ static unsigned int guc_get_log_buffer_size(enum guc_log_buffer_type type)
> 	return 0;
> }
>
>+size_t intel_guc_get_log_buffer_offset(enum guc_log_buffer_type type)
>+{
>+	enum guc_log_buffer_type i;
>+	size_t offset = PAGE_SIZE;/* for the log_buffer_states */
>+
>+	for (i = GUC_DEBUG_LOG_BUFFER; i < GUC_MAX_LOG_BUFFER; ++i) {
>+		if (i == type)
>+			break;
>+		offset += intel_guc_get_log_buffer_size(i);
>+	}
>+
>+	return offset;
>+}
>+
> static void _guc_log_copy_debuglogs_for_relay(struct intel_guc_log *log)
> {
> 	unsigned int buffer_size, read_offset, write_offset, bytes_to_copy, full_cnt;
>@@ -243,14 +257,14 @@ static void _guc_log_copy_debuglogs_for_relay(struct intel_guc_log *log)
> 		 */
> 		memcpy(&log_buf_state_local, log_buf_state,
> 		       sizeof(struct guc_log_buffer_state));
>-		buffer_size = guc_get_log_buffer_size(type);
>+		buffer_size = intel_guc_get_log_buffer_size(type);
> 		read_offset = log_buf_state_local.read_ptr;
> 		write_offset = log_buf_state_local.sampled_write_ptr;
> 		full_cnt = log_buf_state_local.buffer_full_cnt;
>
> 		/* Bookkeeping stuff */
> 		log->stats[type].flush += log_buf_state_local.flush_to_file;
>-		new_overflow = guc_check_log_buf_overflow(log, type, full_cnt);
>+		new_overflow = intel_guc_check_log_buf_overflow(log, type, full_cnt);
>
> 		/* Update the state of shared log buffer */
> 		log_buf_state->read_ptr = write_offset;
>diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_log.h b/drivers/gpu/drm/i915/gt/uc/intel_guc_log.h
>index e1345fca7729..18007e639be9 100644
>--- a/drivers/gpu/drm/i915/gt/uc/intel_guc_log.h
>+++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_log.h
>@@ -67,6 +67,10 @@ struct intel_guc_log {
> };
>
> void intel_guc_log_init_early(struct intel_guc_log *log);
>+bool intel_guc_check_log_buf_overflow(struct intel_guc_log *log, enum guc_log_buffer_type type,
>+				      unsigned int full_cnt);
>+unsigned int intel_guc_get_log_buffer_size(enum guc_log_buffer_type type);
>+size_t intel_guc_get_log_buffer_offset(enum guc_log_buffer_type type);
> int intel_guc_log_create(struct intel_guc_log *log);
> void intel_guc_log_destroy(struct intel_guc_log *log);
>
>diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c
>index ab3cea352fb3..870b48456e9c 100644
>--- a/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c
>+++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c
>@@ -25,6 +25,7 @@
> #include "gt/intel_ring.h"
>
> #include "intel_guc_ads.h"
>+#include "intel_guc_capture.h"
> #include "intel_guc_submission.h"
>
> #include "i915_drv.h"
>@@ -4070,17 +4071,18 @@ int intel_guc_context_reset_process_msg(struct intel_guc *guc,
> int intel_guc_error_capture_process_msg(struct intel_guc *guc,
> 					const u32 *msg, u32 len)
> {
>-	int status;
>+	u32 status;
>
> 	if (unlikely(len != 1)) {
> 		drm_dbg(&guc_to_gt(guc)->i915->drm, "Invalid length %u", len);
> 		return -EPROTO;
> 	}
>
>-	status = msg[0];
>-	drm_info(&guc_to_gt(guc)->i915->drm, "Got error capture: status = %d", status);
>+	status = msg[0] & INTEL_GUC_STATE_CAPTURE_EVENT_STATUS_MASK;
>+	if (status == INTEL_GUC_STATE_CAPTURE_EVENT_STATUS_NOSPACE)
>+		drm_warn(&guc_to_gt(guc)->i915->drm, "G2H-Error capture no space");
>
>-	/* FIXME: Do something with the capture */
>+	intel_guc_capture_process(guc);
>
> 	return 0;
> }

With (1) and (2) taken care of, this is:

Reviewed-by: Umesh Nerlige Ramappa <umesh.nerlige.ramappa@intel.com>

Regards,
Umesh

>-- 
>2.25.1
>

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [Intel-gfx] [PATCH v7 11/13] drm/i915/guc: Pre-allocate output nodes for extraction
  2022-02-26  9:55 ` [Intel-gfx] [PATCH v7 11/13] drm/i915/guc: Pre-allocate output nodes for extraction Alan Previn
@ 2022-03-11 19:40   ` Umesh Nerlige Ramappa
  2022-03-11 19:46     ` Teres Alexis, Alan Previn
  0 siblings, 1 reply; 32+ messages in thread
From: Umesh Nerlige Ramappa @ 2022-03-11 19:40 UTC (permalink / raw)
  To: Alan Previn; +Cc: intel-gfx

On Sat, Feb 26, 2022 at 01:55:39AM -0800, Alan Previn wrote:
>In the rare but possible scenario where we are in the midst of
>multiple GuC error-capture (and engine reset) events and the
>user also triggers a forced full GT reset or the internal watchdog
>triggers the same, intel_guc_submission_reset_prepare's call
>to flush_work(&guc->ct.requests.worker) can cause the G2H message
>handler to trigger intel_guc_capture_store_snapshot upon
>receiving new G2H error-capture notifications. This can happen
>despite the prior call to disable_submission(guc);. However,
>there's no race-free way for intel_guc_capture_store_snapshot to
>know that we are in the midst of a reset. That said, we can never
>dynamically allocate the output nodes in this handler. Thus, we
>shall 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: Alan Previn <alan.previn.teres.alexis@intel.com>
>---
> drivers/gpu/drm/i915/gt/uc/guc_capture_fwif.h |  19 +-
> .../gpu/drm/i915/gt/uc/intel_guc_capture.c    | 178 ++++++++++++++----
> 2 files changed, 160 insertions(+), 37 deletions(-)
>
>diff --git a/drivers/gpu/drm/i915/gt/uc/guc_capture_fwif.h b/drivers/gpu/drm/i915/gt/uc/guc_capture_fwif.h
>index 2b09aa05d8b7..a77a6274e0b0 100644
>--- a/drivers/gpu/drm/i915/gt/uc/guc_capture_fwif.h
>+++ b/drivers/gpu/drm/i915/gt/uc/guc_capture_fwif.h
>@@ -31,7 +31,7 @@ struct __guc_capture_bufstate {
>  *
>  * 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 outlist below.
>+ * See cachelist and outlist below.
>  */
> struct __guc_capture_parsed_output {
> 	/*
>@@ -188,7 +188,22 @@ struct __guc_state_capture_priv {
> 						[GUC_MAX_ENGINE_CLASSES];
>
> 	/**
>-	 * @outlist: allocated nodes with parsed engine-instance error capture data
>+	 * @cachelist: Pool of pre-allocated nodes for error capture output
>+	 *
>+	 * We need this pool of pre-allocated nodes because we cannot
>+	 * dynamically allocate new nodes when receiving the G2H notification
>+	 * because the event handlers for all G2H event-processing is called
>+	 * by the ct processing worker queue and when that queue is being
>+	 * processed, there is no absoluate guarantee that we are not in the
>+	 * midst of a GT reset operation (which doesn't allow allocations).
>+	 */
>+	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;
>+
>+	/**
>+	 * @outlist: Pool of pre-allocated nodes for error capture output
> 	 *
> 	 * A linked list of parsed GuC error-capture output data before
> 	 * reporting with formatting via i915_gpu_coredump. Each node in this linked list shall
>diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c
>index 9308157d9a74..7bd297515504 100644
>--- a/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c
>+++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c
>@@ -583,6 +583,8 @@ intel_guc_capture_getlistsize(struct intel_guc *guc, u32 owner, u32 type, u32 cl
> 	return 0;
> }
>
>+static void guc_capture_create_prealloc_nodes(struct intel_guc *guc);
>+
> int
> intel_guc_capture_getlist(struct intel_guc *guc, u32 owner, u32 type, u32 classid,
> 			  struct file **fileoutptr)
>@@ -604,6 +606,12 @@ intel_guc_capture_getlist(struct intel_guc *guc, u32 owner, u32 type, u32 classi
> 		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 = intel_guc_capture_getlistsize(guc, owner, type, classid, &size);
> 	if (ret) {
> 		cache->is_valid = true;
>@@ -721,7 +729,8 @@ int intel_guc_capture_output_min_size_est(struct intel_guc *guc)
>  *                                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 dynamically allocated and populated with the error-capture
>+ *                   This node is created from a pre-allocated list of blank nodes in
>+ *                   guc->capture->priv->cachelist and populated with the error-capture
>  *                   data from GuC and then it's added into guc->capture->priv->outlist linked
>  *                   list. This list is used for matchup and printout by i915_gpu_coredump
>  *                   and err_print_gt, (when user invokes the error capture sysfs).
>@@ -865,19 +874,20 @@ guc_capture_delete_one_node(struct intel_guc *guc, struct __guc_capture_parsed_o
> }
>
> static void
>-guc_capture_delete_nodes(struct intel_guc *guc)
>+guc_capture_delete_prealloc_nodes(struct intel_guc *guc)
> {
>+	struct __guc_capture_parsed_output *n, *ntmp;
>+
> 	/*
> 	 * NOTE: At the end of driver operation, we must assume that we
>-	 * have nodes in outlist from unclaimed error capture events
>-	 * that occurred prior to shutdown.
>+	 * have prealloc nodes in both the cachelist as well as outlist
>+	 * if unclaimed error capture events occurred prior to shutdown.
> 	 */
>-	if (!list_empty(&guc->capture.priv->outlist)) {
>-		struct __guc_capture_parsed_output *n, *ntmp;
>+	list_for_each_entry_safe(n, ntmp, &guc->capture.priv->outlist, link)
>+		guc_capture_delete_one_node(guc, n);
>
>-		list_for_each_entry_safe(n, ntmp, &guc->capture.priv->outlist, link)
>-			guc_capture_delete_one_node(guc, n);
>-	}
>+	list_for_each_entry_safe(n, ntmp, &guc->capture.priv->cachelist, link)
>+		guc_capture_delete_one_node(guc, n);
> }
>
> static void
>@@ -894,21 +904,80 @@ guc_capture_add_node_to_outlist(struct __guc_state_capture_priv *gc,
> 	guc_capture_add_node_to_list(node, &gc->outlist);
> }
>
>+static void
>+guc_capture_add_node_to_cachelist(struct __guc_state_capture_priv *gc,
>+				  struct __guc_capture_parsed_output *node)
>+{
>+	guc_capture_add_node_to_list(node, &gc->cachelist);
>+}
>+
> static void
> guc_capture_init_node(struct intel_guc *guc, struct __guc_capture_parsed_output *node)
> {
>+	struct guc_mmio_reg *tmp[GUC_CAPTURE_LIST_TYPE_MAX];
>+	int i;
>+
>+	for (i = 0; i < GUC_CAPTURE_LIST_TYPE_MAX; ++i) {
>+		tmp[i] = node->reginfo[i].regs;
>+		memset(tmp[i], 0, sizeof(struct guc_mmio_reg) *
>+		       guc->capture.priv->max_mmio_per_node);
>+	}
>+	memset(node, 0, sizeof(*node));
>+	for (i = 0; i < GUC_CAPTURE_LIST_TYPE_MAX; ++i)
>+		node->reginfo[i].regs = tmp[i];
>+
> 	INIT_LIST_HEAD(&node->link);
> }
>
>+static struct __guc_capture_parsed_output *
>+guc_capture_get_prealloc_node(struct intel_guc *guc)
>+{
>+	struct __guc_capture_parsed_output *found = NULL;
>+
>+	if (!list_empty(&guc->capture.priv->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.priv->cachelist, link) {
>+			found = n;
>+			list_del(&n->link);
>+			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.priv->outlist, link) {
>+			found = n;
>+		}
>+		if (found)
>+			list_del(&found->link);
>+	}
>+	if (found)
>+		guc_capture_init_node(guc, found);
>+
>+	return found;
>+}
>+
> static struct __guc_capture_parsed_output *
> guc_capture_alloc_one_node(struct intel_guc *guc)
> {
> 	struct __guc_capture_parsed_output *new;
>+	int i;
>
> 	new = kzalloc(sizeof(*new), GFP_KERNEL);
> 	if (!new)
> 		return NULL;
>
>+	for (i = 0; i < GUC_CAPTURE_LIST_TYPE_MAX; ++i) {
>+		new->reginfo[i].regs = kcalloc(guc->capture.priv->max_mmio_per_node,
>+					       sizeof(struct guc_mmio_reg), GFP_KERNEL);
>+		if (!new->reginfo[i].regs) {
>+			while (i)
>+				kfree(new->reginfo[--i].regs);
>+			return NULL;
>+		}
>+	}
> 	guc_capture_init_node(guc, new);

In guc_capture_init_node, looks like you are just saving the .regs 
pointer and then restoring it back. If so, you don't need to call 
guc_capture_init_node here because kzalloc already zeroes out the new 
node.

With that, this is

Reviewed-by: Umesh Nerlige Ramappa <umesh.nerlige.ramappa@intel.com>
Umesh
>
> 	return new;
>@@ -921,7 +990,7 @@ guc_capture_clone_node(struct intel_guc *guc, struct __guc_capture_parsed_output
> 	struct __guc_capture_parsed_output *new;
> 	int i;
>
>-	new = guc_capture_alloc_one_node(guc);
>+	new = guc_capture_get_prealloc_node(guc);
> 	if (!new)
> 		return NULL;
> 	if (!ori)
>@@ -932,16 +1001,14 @@ guc_capture_clone_node(struct intel_guc *guc, struct __guc_capture_parsed_output
> 	/* copy reg-lists that we want to clone */
> 	for (i = 0; i < GUC_CAPTURE_LIST_TYPE_MAX; ++i) {
> 		if (keep_reglist_mask & BIT(i)) {
>-			new->reginfo[i].regs = kcalloc(ori->reginfo[i].num_regs,
>-						       sizeof(struct guc_mmio_reg), GFP_KERNEL);
>-			if (!new->reginfo[i].regs)
>-				goto bail_clone;
>+			GEM_BUG_ON(ori->reginfo[i].num_regs  >
>+				   guc->capture.priv->max_mmio_per_node);
>
> 			memcpy(new->reginfo[i].regs, ori->reginfo[i].regs,
> 			       ori->reginfo[i].num_regs * sizeof(struct guc_mmio_reg));
>+
> 			new->reginfo[i].num_regs = ori->reginfo[i].num_regs;
> 			new->reginfo[i].vfid  = ori->reginfo[i].vfid;
>-
> 			if (i == GUC_CAPTURE_LIST_TYPE_ENGINE_CLASS) {
> 				new->eng_class = ori->eng_class;
> 			} else if (i == GUC_CAPTURE_LIST_TYPE_ENGINE_INSTANCE) {
>@@ -953,14 +1020,58 @@ guc_capture_clone_node(struct intel_guc *guc, struct __guc_capture_parsed_output
> 	}
>
> 	return new;
>+}
>
>-bail_clone:
>-	for (i = 0; i < GUC_CAPTURE_LIST_TYPE_MAX; ++i) {
>-		if (new->reginfo[i].regs)
>-			kfree(new->reginfo[i].regs);
>+static void
>+__guc_capture_create_prealloc_nodes(struct intel_guc *guc)
>+{
>+	struct __guc_capture_parsed_output *node = NULL;
>+	struct drm_i915_private *i915 = guc_to_gt(guc)->i915;
>+	int i;
>+
>+	for (i = 0; i < PREALLOC_NODES_MAX_COUNT; ++i) {
>+		node = guc_capture_alloc_one_node(guc);
>+		if (!node) {
>+			drm_warn(&i915->drm, "GuC 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.priv, node);
> 	}
>-	kfree(new);
>-	return NULL;
>+}
>+
>+static int
>+guc_get_max_reglist_count(struct intel_guc *guc)
>+{
>+	int i, j, k, tmp, maxregcount = 0;
>+
>+	for (i = 0; i < GUC_CAPTURE_LIST_INDEX_MAX; ++i) {
>+		for (j = 0; j < GUC_CAPTURE_LIST_TYPE_MAX; ++j) {
>+			for (k = 0; k < GUC_MAX_ENGINE_CLASSES; ++k) {
>+				if (j == GUC_CAPTURE_LIST_TYPE_GLOBAL && k > 0)
>+					continue;
>+
>+				tmp = guc_cap_list_num_regs(guc->capture.priv, 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 intel_guc *guc)
>+{
>+	/* skip if we've already done the pre-alloc */
>+	if (guc->capture.priv->max_mmio_per_node)
>+		return;
>+
>+	guc->capture.priv->max_mmio_per_node = guc_get_max_reglist_count(guc);
>+	__guc_capture_create_prealloc_nodes(guc);
> }
>
> static int
>@@ -1076,13 +1187,13 @@ guc_capture_extract_reglists(struct intel_guc *guc, struct __guc_capture_bufstat
> 				guc_capture_add_node_to_outlist(guc->capture.priv, node);
> 				node = NULL;
> 			} else if (datatype == GUC_CAPTURE_LIST_TYPE_ENGINE_CLASS &&
>-				   node->reginfo[GUC_CAPTURE_LIST_TYPE_ENGINE_CLASS].regs) {
>+				   node->reginfo[GUC_CAPTURE_LIST_TYPE_ENGINE_CLASS].num_regs) {
> 				/* Add to list, clone node and duplicate global list */
> 				guc_capture_add_node_to_outlist(guc->capture.priv, node);
> 				node = guc_capture_clone_node(guc, node,
> 							      GCAP_PARSED_REGLIST_INDEX_GLOBAL);
> 			} else if (datatype == GUC_CAPTURE_LIST_TYPE_ENGINE_INSTANCE &&
>-				   node->reginfo[GUC_CAPTURE_LIST_TYPE_ENGINE_INSTANCE].regs) {
>+				   node->reginfo[GUC_CAPTURE_LIST_TYPE_ENGINE_INSTANCE].num_regs) {
> 				/* Add to list, clone node and duplicate global + class lists */
> 				guc_capture_add_node_to_outlist(guc->capture.priv, node);
> 				node = guc_capture_clone_node(guc, node,
>@@ -1092,7 +1203,7 @@ guc_capture_extract_reglists(struct intel_guc *guc, struct __guc_capture_bufstat
> 		}
>
> 		if (!node) {
>-			node = guc_capture_alloc_one_node(guc);
>+			node = guc_capture_get_prealloc_node(guc);
> 			if (!node) {
> 				ret = -ENOMEM;
> 				break;
>@@ -1117,17 +1228,13 @@ guc_capture_extract_reglists(struct intel_guc *guc, struct __guc_capture_bufstat
> 			break;
> 		}
>
>-		regs = NULL;
> 		numregs = FIELD_GET(CAP_HDR_NUM_MMIOS, hdr.num_mmios);
>-		if (numregs) {
>-			regs = kcalloc(numregs, sizeof(struct guc_mmio_reg), GFP_KERNEL);
>-			if (!regs) {
>-				ret = -ENOMEM;
>-				break;
>-			}
>+		if (numregs > guc->capture.priv->max_mmio_per_node) {
>+			drm_dbg(&i915->drm, "GuC Capture list extraction clipped by prealloc!\n");
>+			numregs = guc->capture.priv->max_mmio_per_node;
> 		}
> 		node->reginfo[datatype].num_regs = numregs;
>-		node->reginfo[datatype].regs = regs;
>+		regs = node->reginfo[datatype].regs;
> 		i = 0;
> 		while (numregs--) {
> 			if (guc_capture_log_get_register(guc, buf, &regs[i++])) {
>@@ -1147,8 +1254,8 @@ guc_capture_extract_reglists(struct intel_guc *guc, struct __guc_capture_bufstat
> 				break;
> 			}
> 		}
>-		if (node) /* else free it */
>-			kfree(node);
>+		if (node) /* else return it back to cache list */
>+			guc_capture_add_node_to_cachelist(guc->capture.priv, node);
> 	}
> 	return ret;
> }
>@@ -1255,7 +1362,7 @@ void intel_guc_capture_destroy(struct intel_guc *guc)
>
> 	guc_capture_free_ads_cache(guc->capture.priv);
>
>-	guc_capture_delete_nodes(guc);
>+	guc_capture_delete_prealloc_nodes(guc);
>
> 	if (guc->capture.priv->extlists) {
> 		guc_capture_free_extlists(guc->capture.priv->extlists);
>@@ -1275,6 +1382,7 @@ int intel_guc_capture_init(struct intel_guc *guc)
> 	guc->capture.priv->reglists = guc_capture_get_device_reglist(guc);
>
> 	INIT_LIST_HEAD(&guc->capture.priv->outlist);
>+	INIT_LIST_HEAD(&guc->capture.priv->cachelist);
>
> 	return 0;
> }
>-- 
>2.25.1
>

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [Intel-gfx] [PATCH v7 11/13] drm/i915/guc: Pre-allocate output nodes for extraction
  2022-03-11 19:40   ` Umesh Nerlige Ramappa
@ 2022-03-11 19:46     ` Teres Alexis, Alan Previn
  0 siblings, 0 replies; 32+ messages in thread
From: Teres Alexis, Alan Previn @ 2022-03-11 19:46 UTC (permalink / raw)
  To: Umesh Nerlige Ramappa; +Cc: intel-gfx

Thanks for reviewing and for the Rvb Umesh. ... will fix 
guc_capture_alloc_one_node as per below.

...alan

On 3/11/2022 11:40 AM, Umesh Nerlige Ramappa wrote:
> On Sat, Feb 26, 2022 at 01:55:39AM -0800, Alan Previn wrote:
>> In the rare but possible scenario where we are in the midst of
>> multiple GuC error-capture (and engine reset) events and the
>> user also triggers a forced full GT reset or the internal watchdog
>> triggers the same, intel_guc_submission_reset_prepare's call
>> to flush_work(&guc->ct.requests.worker) can cause the G2H message
>> handler to trigger intel_guc_capture_store_snapshot upon
>> receiving new G2H error-capture notifications. This can happen
>> despite the prior call to disable_submission(guc);. However,
>> there's no race-free way for intel_guc_capture_store_snapshot to
>> know that we are in the midst of a reset. That said, we can never
>> dynamically allocate the output nodes in this handler. Thus, we
>> shall 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: Alan Previn <alan.previn.teres.alexis@intel.com>
>> ---
>> drivers/gpu/drm/i915/gt/uc/guc_capture_fwif.h |  19 +-
>> .../gpu/drm/i915/gt/uc/intel_guc_capture.c    | 178 ++++++++++++++----
>> 2 files changed, 160 insertions(+), 37 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/i915/gt/uc/guc_capture_fwif.h 
>> b/drivers/gpu/drm/i915/gt/uc/guc_capture_fwif.h
>> index 2b09aa05d8b7..a77a6274e0b0 100644
>> --- a/drivers/gpu/drm/i915/gt/uc/guc_capture_fwif.h
>> +++ b/drivers/gpu/drm/i915/gt/uc/guc_capture_fwif.h
>> @@ -31,7 +31,7 @@ struct __guc_capture_bufstate {
>>  *
>>  * 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 outlist below.
>> + * See cachelist and outlist below.
>>  */
>> struct __guc_capture_parsed_output {
>>     /*
>> @@ -188,7 +188,22 @@ struct __guc_state_capture_priv {
>>                         [GUC_MAX_ENGINE_CLASSES];
>>
>>     /**
>> -     * @outlist: allocated nodes with parsed engine-instance error 
>> capture data
>> +     * @cachelist: Pool of pre-allocated nodes for error capture output
>> +     *
>> +     * We need this pool of pre-allocated nodes because we cannot
>> +     * dynamically allocate new nodes when receiving the G2H 
>> notification
>> +     * because the event handlers for all G2H event-processing is 
>> called
>> +     * by the ct processing worker queue and when that queue is being
>> +     * processed, there is no absoluate guarantee that we are not in 
>> the
>> +     * midst of a GT reset operation (which doesn't allow allocations).
>> +     */
>> +    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;
>> +
>> +    /**
>> +     * @outlist: Pool of pre-allocated nodes for error capture output
>>      *
>>      * A linked list of parsed GuC error-capture output data before
>>      * reporting with formatting via i915_gpu_coredump. Each node in 
>> this linked list shall
>> diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c 
>> b/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c
>> index 9308157d9a74..7bd297515504 100644
>> --- a/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c
>> +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.c
>> @@ -583,6 +583,8 @@ intel_guc_capture_getlistsize(struct intel_guc 
>> *guc, u32 owner, u32 type, u32 cl
>>     return 0;
>> }
>>
>> +static void guc_capture_create_prealloc_nodes(struct intel_guc *guc);
>> +
>> int
>> intel_guc_capture_getlist(struct intel_guc *guc, u32 owner, u32 type, 
>> u32 classid,
>>               struct file **fileoutptr)
>> @@ -604,6 +606,12 @@ intel_guc_capture_getlist(struct intel_guc *guc, 
>> u32 owner, u32 type, u32 classi
>>         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 = intel_guc_capture_getlistsize(guc, owner, type, classid, 
>> &size);
>>     if (ret) {
>>         cache->is_valid = true;
>> @@ -721,7 +729,8 @@ int intel_guc_capture_output_min_size_est(struct 
>> intel_guc *guc)
>>  *                                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 dynamically allocated and 
>> populated with the error-capture
>> + *                   This node is created from a pre-allocated list 
>> of blank nodes in
>> + *                   guc->capture->priv->cachelist and populated 
>> with the error-capture
>>  *                   data from GuC and then it's added into 
>> guc->capture->priv->outlist linked
>>  *                   list. This list is used for matchup and printout 
>> by i915_gpu_coredump
>>  *                   and err_print_gt, (when user invokes the error 
>> capture sysfs).
>> @@ -865,19 +874,20 @@ guc_capture_delete_one_node(struct intel_guc 
>> *guc, struct __guc_capture_parsed_o
>> }
>>
>> static void
>> -guc_capture_delete_nodes(struct intel_guc *guc)
>> +guc_capture_delete_prealloc_nodes(struct intel_guc *guc)
>> {
>> +    struct __guc_capture_parsed_output *n, *ntmp;
>> +
>>     /*
>>      * NOTE: At the end of driver operation, we must assume that we
>> -     * have nodes in outlist from unclaimed error capture events
>> -     * that occurred prior to shutdown.
>> +     * have prealloc nodes in both the cachelist as well as outlist
>> +     * if unclaimed error capture events occurred prior to shutdown.
>>      */
>> -    if (!list_empty(&guc->capture.priv->outlist)) {
>> -        struct __guc_capture_parsed_output *n, *ntmp;
>> +    list_for_each_entry_safe(n, ntmp, &guc->capture.priv->outlist, 
>> link)
>> +        guc_capture_delete_one_node(guc, n);
>>
>> -        list_for_each_entry_safe(n, ntmp, 
>> &guc->capture.priv->outlist, link)
>> -            guc_capture_delete_one_node(guc, n);
>> -    }
>> +    list_for_each_entry_safe(n, ntmp, &guc->capture.priv->cachelist, 
>> link)
>> +        guc_capture_delete_one_node(guc, n);
>> }
>>
>> static void
>> @@ -894,21 +904,80 @@ guc_capture_add_node_to_outlist(struct 
>> __guc_state_capture_priv *gc,
>>     guc_capture_add_node_to_list(node, &gc->outlist);
>> }
>>
>> +static void
>> +guc_capture_add_node_to_cachelist(struct __guc_state_capture_priv *gc,
>> +                  struct __guc_capture_parsed_output *node)
>> +{
>> +    guc_capture_add_node_to_list(node, &gc->cachelist);
>> +}
>> +
>> static void
>> guc_capture_init_node(struct intel_guc *guc, struct 
>> __guc_capture_parsed_output *node)
>> {
>> +    struct guc_mmio_reg *tmp[GUC_CAPTURE_LIST_TYPE_MAX];
>> +    int i;
>> +
>> +    for (i = 0; i < GUC_CAPTURE_LIST_TYPE_MAX; ++i) {
>> +        tmp[i] = node->reginfo[i].regs;
>> +        memset(tmp[i], 0, sizeof(struct guc_mmio_reg) *
>> +               guc->capture.priv->max_mmio_per_node);
>> +    }
>> +    memset(node, 0, sizeof(*node));
>> +    for (i = 0; i < GUC_CAPTURE_LIST_TYPE_MAX; ++i)
>> +        node->reginfo[i].regs = tmp[i];
>> +
>>     INIT_LIST_HEAD(&node->link);
>> }
>>
>> +static struct __guc_capture_parsed_output *
>> +guc_capture_get_prealloc_node(struct intel_guc *guc)
>> +{
>> +    struct __guc_capture_parsed_output *found = NULL;
>> +
>> +    if (!list_empty(&guc->capture.priv->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.priv->cachelist, link) {
>> +            found = n;
>> +            list_del(&n->link);
>> +            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.priv->outlist, link) {
>> +            found = n;
>> +        }
>> +        if (found)
>> +            list_del(&found->link);
>> +    }
>> +    if (found)
>> +        guc_capture_init_node(guc, found);
>> +
>> +    return found;
>> +}
>> +
>> static struct __guc_capture_parsed_output *
>> guc_capture_alloc_one_node(struct intel_guc *guc)
>> {
>>     struct __guc_capture_parsed_output *new;
>> +    int i;
>>
>>     new = kzalloc(sizeof(*new), GFP_KERNEL);
>>     if (!new)
>>         return NULL;
>>
>> +    for (i = 0; i < GUC_CAPTURE_LIST_TYPE_MAX; ++i) {
>> +        new->reginfo[i].regs = 
>> kcalloc(guc->capture.priv->max_mmio_per_node,
>> +                           sizeof(struct guc_mmio_reg), GFP_KERNEL);
>> +        if (!new->reginfo[i].regs) {
>> +            while (i)
>> +                kfree(new->reginfo[--i].regs);
>> +            return NULL;
>> +        }
>> +    }
>>     guc_capture_init_node(guc, new);
>
> In guc_capture_init_node, looks like you are just saving the .regs 
> pointer and then restoring it back. If so, you don't need to call 
> guc_capture_init_node here because kzalloc already zeroes out the new 
> node.
>
> With that, this is
>
> Reviewed-by: Umesh Nerlige Ramappa <umesh.nerlige.ramappa@intel.com>
> Umesh
>>
>>     return new;
>> @@ -921,7 +990,7 @@ guc_capture_clone_node(struct intel_guc *guc, 
>> struct __guc_capture_parsed_output
>>     struct __guc_capture_parsed_output *new;
>>     int i;
>>
>> -    new = guc_capture_alloc_one_node(guc);
>> +    new = guc_capture_get_prealloc_node(guc);
>>     if (!new)
>>         return NULL;
>>     if (!ori)
>> @@ -932,16 +1001,14 @@ guc_capture_clone_node(struct intel_guc *guc, 
>> struct __guc_capture_parsed_output
>>     /* copy reg-lists that we want to clone */
>>     for (i = 0; i < GUC_CAPTURE_LIST_TYPE_MAX; ++i) {
>>         if (keep_reglist_mask & BIT(i)) {
>> -            new->reginfo[i].regs = kcalloc(ori->reginfo[i].num_regs,
>> -                               sizeof(struct guc_mmio_reg), 
>> GFP_KERNEL);
>> -            if (!new->reginfo[i].regs)
>> -                goto bail_clone;
>> +            GEM_BUG_ON(ori->reginfo[i].num_regs  >
>> +                   guc->capture.priv->max_mmio_per_node);
>>
>>             memcpy(new->reginfo[i].regs, ori->reginfo[i].regs,
>>                    ori->reginfo[i].num_regs * sizeof(struct 
>> guc_mmio_reg));
>> +
>>             new->reginfo[i].num_regs = ori->reginfo[i].num_regs;
>>             new->reginfo[i].vfid  = ori->reginfo[i].vfid;
>> -
>>             if (i == GUC_CAPTURE_LIST_TYPE_ENGINE_CLASS) {
>>                 new->eng_class = ori->eng_class;
>>             } else if (i == GUC_CAPTURE_LIST_TYPE_ENGINE_INSTANCE) {
>> @@ -953,14 +1020,58 @@ guc_capture_clone_node(struct intel_guc *guc, 
>> struct __guc_capture_parsed_output
>>     }
>>
>>     return new;
>> +}
>>
>> -bail_clone:
>> -    for (i = 0; i < GUC_CAPTURE_LIST_TYPE_MAX; ++i) {
>> -        if (new->reginfo[i].regs)
>> -            kfree(new->reginfo[i].regs);
>> +static void
>> +__guc_capture_create_prealloc_nodes(struct intel_guc *guc)
>> +{
>> +    struct __guc_capture_parsed_output *node = NULL;
>> +    struct drm_i915_private *i915 = guc_to_gt(guc)->i915;
>> +    int i;
>> +
>> +    for (i = 0; i < PREALLOC_NODES_MAX_COUNT; ++i) {
>> +        node = guc_capture_alloc_one_node(guc);
>> +        if (!node) {
>> +            drm_warn(&i915->drm, "GuC 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.priv, node);
>>     }
>> -    kfree(new);
>> -    return NULL;
>> +}
>> +
>> +static int
>> +guc_get_max_reglist_count(struct intel_guc *guc)
>> +{
>> +    int i, j, k, tmp, maxregcount = 0;
>> +
>> +    for (i = 0; i < GUC_CAPTURE_LIST_INDEX_MAX; ++i) {
>> +        for (j = 0; j < GUC_CAPTURE_LIST_TYPE_MAX; ++j) {
>> +            for (k = 0; k < GUC_MAX_ENGINE_CLASSES; ++k) {
>> +                if (j == GUC_CAPTURE_LIST_TYPE_GLOBAL && k > 0)
>> +                    continue;
>> +
>> +                tmp = guc_cap_list_num_regs(guc->capture.priv, 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 intel_guc *guc)
>> +{
>> +    /* skip if we've already done the pre-alloc */
>> +    if (guc->capture.priv->max_mmio_per_node)
>> +        return;
>> +
>> +    guc->capture.priv->max_mmio_per_node = 
>> guc_get_max_reglist_count(guc);
>> +    __guc_capture_create_prealloc_nodes(guc);
>> }
>>
>> static int
>> @@ -1076,13 +1187,13 @@ guc_capture_extract_reglists(struct intel_guc 
>> *guc, struct __guc_capture_bufstat
>> guc_capture_add_node_to_outlist(guc->capture.priv, node);
>>                 node = NULL;
>>             } else if (datatype == GUC_CAPTURE_LIST_TYPE_ENGINE_CLASS &&
>> - node->reginfo[GUC_CAPTURE_LIST_TYPE_ENGINE_CLASS].regs) {
>> + node->reginfo[GUC_CAPTURE_LIST_TYPE_ENGINE_CLASS].num_regs) {
>>                 /* Add to list, clone node and duplicate global list */
>> guc_capture_add_node_to_outlist(guc->capture.priv, node);
>>                 node = guc_capture_clone_node(guc, node,
>> GCAP_PARSED_REGLIST_INDEX_GLOBAL);
>>             } else if (datatype == 
>> GUC_CAPTURE_LIST_TYPE_ENGINE_INSTANCE &&
>> - node->reginfo[GUC_CAPTURE_LIST_TYPE_ENGINE_INSTANCE].regs) {
>> + node->reginfo[GUC_CAPTURE_LIST_TYPE_ENGINE_INSTANCE].num_regs) {
>>                 /* Add to list, clone node and duplicate global + 
>> class lists */
>> guc_capture_add_node_to_outlist(guc->capture.priv, node);
>>                 node = guc_capture_clone_node(guc, node,
>> @@ -1092,7 +1203,7 @@ guc_capture_extract_reglists(struct intel_guc 
>> *guc, struct __guc_capture_bufstat
>>         }
>>
>>         if (!node) {
>> -            node = guc_capture_alloc_one_node(guc);
>> +            node = guc_capture_get_prealloc_node(guc);
>>             if (!node) {
>>                 ret = -ENOMEM;
>>                 break;
>> @@ -1117,17 +1228,13 @@ guc_capture_extract_reglists(struct intel_guc 
>> *guc, struct __guc_capture_bufstat
>>             break;
>>         }
>>
>> -        regs = NULL;
>>         numregs = FIELD_GET(CAP_HDR_NUM_MMIOS, hdr.num_mmios);
>> -        if (numregs) {
>> -            regs = kcalloc(numregs, sizeof(struct guc_mmio_reg), 
>> GFP_KERNEL);
>> -            if (!regs) {
>> -                ret = -ENOMEM;
>> -                break;
>> -            }
>> +        if (numregs > guc->capture.priv->max_mmio_per_node) {
>> +            drm_dbg(&i915->drm, "GuC Capture list extraction clipped 
>> by prealloc!\n");
>> +            numregs = guc->capture.priv->max_mmio_per_node;
>>         }
>>         node->reginfo[datatype].num_regs = numregs;
>> -        node->reginfo[datatype].regs = regs;
>> +        regs = node->reginfo[datatype].regs;
>>         i = 0;
>>         while (numregs--) {
>>             if (guc_capture_log_get_register(guc, buf, &regs[i++])) {
>> @@ -1147,8 +1254,8 @@ guc_capture_extract_reglists(struct intel_guc 
>> *guc, struct __guc_capture_bufstat
>>                 break;
>>             }
>>         }
>> -        if (node) /* else free it */
>> -            kfree(node);
>> +        if (node) /* else return it back to cache list */
>> + guc_capture_add_node_to_cachelist(guc->capture.priv, node);
>>     }
>>     return ret;
>> }
>> @@ -1255,7 +1362,7 @@ void intel_guc_capture_destroy(struct intel_guc 
>> *guc)
>>
>>     guc_capture_free_ads_cache(guc->capture.priv);
>>
>> -    guc_capture_delete_nodes(guc);
>> +    guc_capture_delete_prealloc_nodes(guc);
>>
>>     if (guc->capture.priv->extlists) {
>> guc_capture_free_extlists(guc->capture.priv->extlists);
>> @@ -1275,6 +1382,7 @@ int intel_guc_capture_init(struct intel_guc *guc)
>>     guc->capture.priv->reglists = guc_capture_get_device_reglist(guc);
>>
>>     INIT_LIST_HEAD(&guc->capture.priv->outlist);
>> +    INIT_LIST_HEAD(&guc->capture.priv->cachelist);
>>
>>     return 0;
>> }
>> -- 
>> 2.25.1
>>

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [Intel-gfx] [PATCH v7 10/13] drm/i915/guc: Extract GuC error capture lists on G2H notification.
  2022-03-11 18:16   ` Umesh Nerlige Ramappa
@ 2022-03-11 22:27     ` Teres Alexis, Alan Previn
  0 siblings, 0 replies; 32+ messages in thread
From: Teres Alexis, Alan Previn @ 2022-03-11 22:27 UTC (permalink / raw)
  To: intel-gfx

Thanks Umesh for reviewing and for the conditional Rb ...  a follow up 
on the conditions for #1 below (as per our offline conversation)... and 
i will fix #2 as expected.

On 3/11/2022 10:16 AM, Umesh Nerlige Ramappa wrote:
> On Sat, Feb 26, 2022 at 01:55:38AM -0800, Alan Previn wrote: +static int
>> +guc_capture_log_remove_dw(struct intel_guc *guc, struct 
>> __guc_capture_bufstate *buf,
>> +              u32 *dw)
>> +{
>> +    struct drm_i915_private *i915 = guc_to_gt(guc)->i915;
>> +    int tries = 2;
>> +    int avail = 0;
>> +    u32 *src_data;
>> +
>> +    if (!guc_capture_buf_cnt(buf))
>> +        return 0;
>> +
>> +    while (tries--) {
>> +        avail = guc_capture_buf_cnt_to_end(buf);
>> +        if (avail >= sizeof(u32)) {
>> +            src_data = (u32 *)(buf->data + buf->rd);
>> +            *dw = *src_data;
>> +            buf->rd += 4;
>> +            return 4;
>> +        }
>> +        if (avail)
>> +            drm_dbg(&i915->drm, "GuC-Cap-Logs not dword aligned, 
>> skipping.\n");
>> +        buf->rd = 0;
>
> (1) This looks like an understanding between GuC and KMD that GuC will 
> not write partial headers at the end of the log buffer if there is no 
> space to fit the header. If you encounter such a scenario, you go back 
> to the beginning. Right? If so, please add a comment indicating the 
> same here.
>
Thanks Umesh for reviewing, and as per the offline conversation, we 
resolved a bit of a misunderstanding in intent of the function above.

As per what we agreed, we should furnish some additional comments -thus, 
this will go into the code as a comment:

      The GuC Log buffer region for error-capture gets populated in a 
byte-stream flow. However, right now, 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 across the end of the ring buffer and onto 
the start. The above function, guc_capture_log_remove_dw is responsible 
for that. All callers of this function would usually do a straight-up 
memcpy and only only call above function if their structure-extraction 
is straddling across the end of the region. GuC firmware does not add 
padding. Once above function has extracted a structure that stradles 
across the end, all extraction functions would return to the norm of 
straight up memcpy's. 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.

An additional note wrt above function, .. the while loop always attempts 
up to two tries because if we have exhausted all the dwords, i.e. we've 
this the end, we will reset buf->rd = zero and try again from the 
beginning (this wont be in the comment).


>> +    }
>> +
>> +    return 0;
>> +}
>> +
>> +static bool
>> +guc_capture_data_extracted(struct __guc_capture_bufstate *b,
>> +               int size, void *dest)
>> +{
>> +    if (guc_capture_buf_cnt_to_end(b) >= size) {
>> +        memcpy(dest, (b->data + b->rd), size);
>> +        b->rd += size;
>> +        return true;
>> +    }
>> +    return false;
>> +}
>> +
>> +static int
>> +guc_capture_log_get_group_hdr(struct intel_guc *guc, struct 
>> __guc_capture_bufstate *buf,
>> +                  struct guc_state_capture_group_header_t *ghdr)
>> +{
>> +    int read = 0;
>> +    int fullsize = sizeof(struct guc_state_capture_group_header_t);
>> +
>> +    if (fullsize > guc_capture_buf_cnt(buf))
>> +        return -1;
>> +
>> +    if (guc_capture_data_extracted(buf, fullsize, (void *)ghdr))
>> +        return 0;
>> +
>> +    read += guc_capture_log_remove_dw(guc, buf, &ghdr->owner);
>> +    read += guc_capture_log_remove_dw(guc, buf, &ghdr->info);
>> +    if (read != fullsize)
>> +        return -1;
>> +
>> +    return 0;
>> +}
>> +
>> +static int
>> +guc_capture_log_get_data_hdr(struct intel_guc *guc, struct 
>> __guc_capture_bufstate *buf,
>> +                 struct guc_state_capture_header_t *hdr)
>> +{
>> +    int read = 0;
>> +    int fullsize = sizeof(struct guc_state_capture_header_t);
>> +
>> +    if (fullsize > guc_capture_buf_cnt(buf))
>> +        return -1;
>> +
>> +    if (guc_capture_data_extracted(buf, fullsize, (void *)hdr))
>> +        return 0;
>> +
>> +    read += guc_capture_log_remove_dw(guc, buf, &hdr->owner);
>> +    read += guc_capture_log_remove_dw(guc, buf, &hdr->info);
>> +    read += guc_capture_log_remove_dw(guc, buf, &hdr->lrca);
>> +    read += guc_capture_log_remove_dw(guc, buf, &hdr->guc_id);
>> +    read += guc_capture_log_remove_dw(guc, buf, &hdr->num_mmios);
>> +    if (read != fullsize)
>> +        return -1;
>> +
>> +    return 0;
>> +}
>> +
>> +static int
>> +guc_capture_log_get_register(struct intel_guc *guc, struct 
>> __guc_capture_bufstate *buf,
>> +                 struct guc_mmio_reg *reg)
>> +{
>> +    int read = 0;
>> +    int fullsize = sizeof(struct guc_mmio_reg);
>> +
>> +    if (fullsize > guc_capture_buf_cnt(buf))
>> +        return -1;
>> +
>> +    if (guc_capture_data_extracted(buf, fullsize, (void *)reg))
>> +        return 0;
>> +
>> +    read += guc_capture_log_remove_dw(guc, buf, &reg->offset);
>> +    read += guc_capture_log_remove_dw(guc, buf, &reg->value);
>> +    read += guc_capture_log_remove_dw(guc, buf, &reg->flags);
>> +    read += guc_capture_log_remove_dw(guc, buf, &reg->mask);
>> +    if (read != fullsize)
>> +        return -1;
>> +
>> +    return 0;
>> +}
>> +
>> +static void
>> +guc_capture_delete_one_node(struct intel_guc *guc, struct 
>> __guc_capture_parsed_output *node)
>> +{
>> +    int i;
>> +
>> +    for (i = 0; i < GUC_CAPTURE_LIST_TYPE_MAX; ++i) {
>> +        if (node->reginfo[i].regs)
>> +            kfree(node->reginfo[i].regs);
>
> (2) Check before kfree is not needed here and other places that you 
> use kfree.
>
> Ref: https://www.kernel.org/doc/htmldocs/kernel-api/API-kfree.html
>
Yes, i realize i have been holding back against changing my coding style 
(not used to freeing memory without checking).

However, as per offline chat, I shall change this here and in other patches.


>> +    }
>> +    list_del(&node->link);
>> +    kfree(node);
>> +}
>> +
>> +static void
>> +guc_capture_delete_nodes(struct intel_guc *guc)
>> +{
>> +    /*
>> +     * NOTE: At the end of driver operation, we must assume that we
>> +     * have nodes in outlist from unclaimed error capture events
>> +     * that occurred prior to shutdown.
>> +     */
>> +    if (!list_empty(&guc->capture.priv->outlist)) {
>> +        struct __guc_capture_parsed_output *n, *ntmp;
>> +
>> +        list_for_each_entry_safe(n, ntmp, 
>> &guc->capture.priv->outlist, link)
>> +            guc_capture_delete_one_node(guc, n);
>> +    }
>> +}
>> +
>> +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 __guc_state_capture_priv *gc,
>> +                struct __guc_capture_parsed_output *node)
>> +{
>> +    guc_capture_add_node_to_list(node, &gc->outlist);
>> +}
>> +
>> +static void
>> +guc_capture_init_node(struct intel_guc *guc, struct 
>> __guc_capture_parsed_output *node)
>> +{
>> +    INIT_LIST_HEAD(&node->link);
>> +}
>> +
>> +static struct __guc_capture_parsed_output *
>> +guc_capture_alloc_one_node(struct intel_guc *guc)
>> +{
>> +    struct __guc_capture_parsed_output *new;
>> +
>> +    new = kzalloc(sizeof(*new), GFP_KERNEL);
>> +    if (!new)
>> +        return NULL;
>> +
>> +    guc_capture_init_node(guc, new);
>> +
>> +    return new;
>> +}
>> +
>> +static struct __guc_capture_parsed_output *
>> +guc_capture_clone_node(struct intel_guc *guc, struct 
>> __guc_capture_parsed_output *ori,
>> +               u32 keep_reglist_mask)
>> +{
>> +    struct __guc_capture_parsed_output *new;
>> +    int i;
>> +
>> +    new = guc_capture_alloc_one_node(guc);
>> +    if (!new)
>> +        return NULL;
>> +    if (!ori)
>> +        return new;
>
> For readability, I would s/ori/orignal/ if that's what you mean.
>
>> +
>> +    new->is_partial = ori->is_partial;
>> +
>> +    /* copy reg-lists that we want to clone */
>> +    for (i = 0; i < GUC_CAPTURE_LIST_TYPE_MAX; ++i) {
>> +        if (keep_reglist_mask & BIT(i)) {
>> +            new->reginfo[i].regs = kcalloc(ori->reginfo[i].num_regs,
>> +                               sizeof(struct guc_mmio_reg), 
>> GFP_KERNEL);
>> +            if (!new->reginfo[i].regs)
>> +                goto bail_clone;
>> +
>> +            memcpy(new->reginfo[i].regs, ori->reginfo[i].regs,
>> +                   ori->reginfo[i].num_regs * sizeof(struct 
>> guc_mmio_reg));
>> +            new->reginfo[i].num_regs = ori->reginfo[i].num_regs;
>> +            new->reginfo[i].vfid  = ori->reginfo[i].vfid;
>> +
>> +            if (i == GUC_CAPTURE_LIST_TYPE_ENGINE_CLASS) {
>> +                new->eng_class = ori->eng_class;
>> +            } else if (i == GUC_CAPTURE_LIST_TYPE_ENGINE_INSTANCE) {
>> +                new->eng_inst = ori->eng_inst;
>> +                new->guc_id = ori->guc_id;
>> +                new->lrca = ori->lrca;
>> +            }
>> +        }
>> +    }
>> +
>> +    return new;
>> +
>> +bail_clone:
>> +    for (i = 0; i < GUC_CAPTURE_LIST_TYPE_MAX; ++i) {
>> +        if (new->reginfo[i].regs)
>> +            kfree(new->reginfo[i].regs);
>> +    }
>> +    kfree(new);
>> +    return NULL;
>> +}
>> +
>> +static int
>> +guc_capture_extract_reglists(struct intel_guc *guc, struct 
>> __guc_capture_bufstate *buf)
>> +{
>> +    struct drm_i915_private *i915 = guc_to_gt(guc)->i915;
>> +    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_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)) {
>> +        drm_warn(&i915->drm, "GuC Capture new entries unaligned\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(CAP_GRP_HDR_CAPTURE_TYPE, ghdr.info);
>> +    numlists = FIELD_GET(CAP_GRP_HDR_NUM_CAPTURES, ghdr.info);
>> +
>> +    while (numlists--) {
>> +        if (guc_capture_log_get_data_hdr(guc, buf, &hdr)) {
>> +            ret = -EIO;
>> +            break;
>> +        }
>> +
>> +        datatype = FIELD_GET(CAP_HDR_CAPTURE_TYPE, hdr.info);
>> +        if (datatype > GUC_CAPTURE_LIST_TYPE_ENGINE_INSTANCE) {
>> +            /* unknown capture type - skip over to next capture set */
>> +            numregs = FIELD_GET(CAP_HDR_NUM_MMIOS, hdr.num_mmios);
>> +            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 i915_gpu_coredump 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_CAPTURE_LIST_TYPE_GLOBAL) {
>> + guc_capture_add_node_to_outlist(guc->capture.priv, node);
>> +                node = NULL;
>> +            } else if (datatype == 
>> GUC_CAPTURE_LIST_TYPE_ENGINE_CLASS &&
>> + node->reginfo[GUC_CAPTURE_LIST_TYPE_ENGINE_CLASS].regs) {
>> +                /* Add to list, clone node and duplicate global list */
>> + guc_capture_add_node_to_outlist(guc->capture.priv, node);
>> +                node = guc_capture_clone_node(guc, node,
>> + GCAP_PARSED_REGLIST_INDEX_GLOBAL);
>> +            } else if (datatype == 
>> GUC_CAPTURE_LIST_TYPE_ENGINE_INSTANCE &&
>> + node->reginfo[GUC_CAPTURE_LIST_TYPE_ENGINE_INSTANCE].regs) {
>> +                /* Add to list, clone node and duplicate global + 
>> class lists */
>> + guc_capture_add_node_to_outlist(guc->capture.priv, node);
>> +                node = guc_capture_clone_node(guc, node,
>> + (GCAP_PARSED_REGLIST_INDEX_GLOBAL |
>> + GCAP_PARSED_REGLIST_INDEX_ENGCLASS));
>> +            }
>> +        }
>> +
>> +        if (!node) {
>> +            node = guc_capture_alloc_one_node(guc);
>> +            if (!node) {
>> +                ret = -ENOMEM;
>> +                break;
>> +            }
>> +            if (datatype != GUC_CAPTURE_LIST_TYPE_GLOBAL)
>> +                drm_dbg(&i915->drm, "GuC Capture missing global 
>> dump: %08x!\n",
>> +                    datatype);
>> +        }
>> +        node->is_partial = is_partial;
>> +        node->reginfo[datatype].vfid = 
>> FIELD_GET(CAP_HDR_CAPTURE_VFID, hdr.owner);
>> +        switch (datatype) {
>> +        case GUC_CAPTURE_LIST_TYPE_ENGINE_INSTANCE:
>> +            node->eng_class = FIELD_GET(CAP_HDR_ENGINE_CLASS, 
>> hdr.info);
>> +            node->eng_inst = FIELD_GET(CAP_HDR_ENGINE_INSTANCE, 
>> hdr.info);
>> +            node->lrca = hdr.lrca;
>> +            node->guc_id = hdr.guc_id;
>> +            break;
>> +        case GUC_CAPTURE_LIST_TYPE_ENGINE_CLASS:
>> +            node->eng_class = FIELD_GET(CAP_HDR_ENGINE_CLASS, 
>> hdr.info);
>> +            break;
>> +        default:
>> +            break;
>> +        }
>> +
>> +        regs = NULL;
>> +        numregs = FIELD_GET(CAP_HDR_NUM_MMIOS, hdr.num_mmios);
>> +        if (numregs) {
>> +            regs = kcalloc(numregs, sizeof(struct guc_mmio_reg), 
>> GFP_KERNEL);
>> +            if (!regs) {
>> +                ret = -ENOMEM;
>> +                break;
>> +            }
>> +        }
>> +        node->reginfo[datatype].num_regs = numregs;
>> +        node->reginfo[datatype].regs = regs;
>> +        i = 0;
>> +        while (numregs--) {
>> +            if (guc_capture_log_get_register(guc, buf, &regs[i++])) {
>> +                ret = -EIO;
>> +                break;
>> +            }
>> +        }
>> +    }
>> +
>> +bailout:
>> +    if (node) {
>> +        /* If we have data, add to linked list for match-up when 
>> i915_gpu_coredump calls */
>> +        for (i = GUC_CAPTURE_LIST_TYPE_GLOBAL; i < 
>> GUC_CAPTURE_LIST_TYPE_MAX; ++i) {
>> +            if (node->reginfo[i].regs) {
>> + guc_capture_add_node_to_outlist(guc->capture.priv, node);
>> +                node = NULL;
>> +                break;
>> +            }
>> +        }
>> +        if (node) /* else free it */
>> +            kfree(node);
>> +    }
>> +    return ret;
>> +}
>> +
>> +static int __guc_capture_flushlog_complete(struct intel_guc *guc)
>> +{
>> +    u32 action[] = {
>> +        INTEL_GUC_ACTION_LOG_BUFFER_FILE_FLUSH_COMPLETE,
>> +        2
>> +    };
>> +
>> +    return intel_guc_send(guc, action, ARRAY_SIZE(action));
>> +}
>> +
>> +static void __guc_capture_process_output(struct intel_guc *guc)
>> +{
>> +    unsigned int buffer_size, read_offset, write_offset, full_count;
>> +    struct intel_uc *uc = container_of(guc, typeof(*uc), guc);
>> +    struct drm_i915_private *i915 = guc_to_gt(guc)->i915;
>> +    struct guc_log_buffer_state log_buf_state_local;
>> +    struct guc_log_buffer_state *log_buf_state;
>> +    struct __guc_capture_bufstate buf;
>> +    void *src_data = NULL;
>> +    bool new_overflow;
>> +    int ret;
>> +
>> +    log_buf_state = guc->log.buf_addr +
>> +            (sizeof(struct guc_log_buffer_state) * 
>> GUC_CAPTURE_LOG_BUFFER);
>> +    src_data = guc->log.buf_addr + 
>> intel_guc_get_log_buffer_offset(GUC_CAPTURE_LOG_BUFFER);
>> +
>> +    /*
>> +     * 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.
>> +     */
>> +    memcpy(&log_buf_state_local, log_buf_state, sizeof(struct 
>> guc_log_buffer_state));
>> +    buffer_size = 
>> intel_guc_get_log_buffer_size(GUC_CAPTURE_LOG_BUFFER);
>> +    read_offset = log_buf_state_local.read_ptr;
>> +    write_offset = log_buf_state_local.sampled_write_ptr;
>> +    full_count = log_buf_state_local.buffer_full_cnt;
>> +
>> +    /* Bookkeeping stuff */
>> +    guc->log.stats[GUC_CAPTURE_LOG_BUFFER].flush += 
>> log_buf_state_local.flush_to_file;
>> +    new_overflow = intel_guc_check_log_buf_overflow(&guc->log, 
>> GUC_CAPTURE_LOG_BUFFER,
>> +                            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))) {
>> +        drm_err(&i915->drm, "invalid GuC log capture buffer state!\n");
>> +        /* 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 = src_data;
>> +
>> +    if (!uc->reset_in_progress) {
>> +        do {
>> +            ret = guc_capture_extract_reglists(guc, &buf);
>> +        } while (ret >= 0);
>> +    }
>> +
>> +    /* Update the state of log buffer err-cap state */
>> +    log_buf_state->read_ptr = write_offset;
>> +    log_buf_state->flush_to_file = 0;
>> +    __guc_capture_flushlog_complete(guc);
>> +}
>> +
>> +void intel_guc_capture_process(struct intel_guc *guc)
>> +{
>> +    if (guc->capture.priv)
>> +        __guc_capture_process_output(guc);
>> +}
>> +
>> static void
>> guc_capture_free_ads_cache(struct __guc_state_capture_priv *gc)
>> {
>> @@ -715,6 +1255,8 @@ void intel_guc_capture_destroy(struct intel_guc 
>> *guc)
>>
>>     guc_capture_free_ads_cache(guc->capture.priv);
>>
>> +    guc_capture_delete_nodes(guc);
>> +
>>     if (guc->capture.priv->extlists) {
>> guc_capture_free_extlists(guc->capture.priv->extlists);
>>         kfree(guc->capture.priv->extlists);
>> @@ -732,5 +1274,7 @@ int intel_guc_capture_init(struct intel_guc *guc)
>>
>>     guc->capture.priv->reglists = guc_capture_get_device_reglist(guc);
>>
>> +    INIT_LIST_HEAD(&guc->capture.priv->outlist);
>> +
>>     return 0;
>> }
>> diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.h 
>> b/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.h
>> index 24a11f33f7d9..3c79460c7ca5 100644
>> --- a/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.h
>> +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_capture.h
>> @@ -12,6 +12,7 @@ struct file;
>> struct guc_gt_system_info;
>> struct intel_guc;
>>
>> +void intel_guc_capture_process(struct intel_guc *guc);
>> int intel_guc_capture_output_min_size_est(struct intel_guc *guc);
>> int intel_guc_capture_getlist(struct intel_guc *guc, u32 owner, u32 
>> type, u32 classid,
>>                   struct file **fileptr);
>> diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_log.c 
>> b/drivers/gpu/drm/i915/gt/uc/intel_guc_log.c
>> index e9a865c2f4cb..8d59a11ec595 100644
>> --- a/drivers/gpu/drm/i915/gt/uc/intel_guc_log.c
>> +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_log.c
>> @@ -158,9 +158,9 @@ static void *guc_get_write_buffer(struct 
>> intel_guc_log *log)
>>     return relay_reserve(log->relay.channel, 0);
>> }
>>
>> -static bool guc_check_log_buf_overflow(struct intel_guc_log *log,
>> -                       enum guc_log_buffer_type type,
>> -                       unsigned int full_cnt)
>> +bool intel_guc_check_log_buf_overflow(struct intel_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;
>> @@ -183,7 +183,7 @@ static bool guc_check_log_buf_overflow(struct 
>> intel_guc_log *log,
>>     return overflow;
>> }
>>
>> -static unsigned int guc_get_log_buffer_size(enum guc_log_buffer_type 
>> type)
>> +unsigned int intel_guc_get_log_buffer_size(enum guc_log_buffer_type 
>> type)
>> {
>>     switch (type) {
>>     case GUC_DEBUG_LOG_BUFFER:
>> @@ -199,6 +199,20 @@ static unsigned int guc_get_log_buffer_size(enum 
>> guc_log_buffer_type type)
>>     return 0;
>> }
>>
>> +size_t intel_guc_get_log_buffer_offset(enum guc_log_buffer_type type)
>> +{
>> +    enum guc_log_buffer_type i;
>> +    size_t offset = PAGE_SIZE;/* for the log_buffer_states */
>> +
>> +    for (i = GUC_DEBUG_LOG_BUFFER; i < GUC_MAX_LOG_BUFFER; ++i) {
>> +        if (i == type)
>> +            break;
>> +        offset += intel_guc_get_log_buffer_size(i);
>> +    }
>> +
>> +    return offset;
>> +}
>> +
>> static void _guc_log_copy_debuglogs_for_relay(struct intel_guc_log *log)
>> {
>>     unsigned int buffer_size, read_offset, write_offset, 
>> bytes_to_copy, full_cnt;
>> @@ -243,14 +257,14 @@ static void 
>> _guc_log_copy_debuglogs_for_relay(struct intel_guc_log *log)
>>          */
>>         memcpy(&log_buf_state_local, log_buf_state,
>>                sizeof(struct guc_log_buffer_state));
>> -        buffer_size = guc_get_log_buffer_size(type);
>> +        buffer_size = intel_guc_get_log_buffer_size(type);
>>         read_offset = log_buf_state_local.read_ptr;
>>         write_offset = log_buf_state_local.sampled_write_ptr;
>>         full_cnt = log_buf_state_local.buffer_full_cnt;
>>
>>         /* Bookkeeping stuff */
>>         log->stats[type].flush += log_buf_state_local.flush_to_file;
>> -        new_overflow = guc_check_log_buf_overflow(log, type, full_cnt);
>> +        new_overflow = intel_guc_check_log_buf_overflow(log, type, 
>> full_cnt);
>>
>>         /* Update the state of shared log buffer */
>>         log_buf_state->read_ptr = write_offset;
>> diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_log.h 
>> b/drivers/gpu/drm/i915/gt/uc/intel_guc_log.h
>> index e1345fca7729..18007e639be9 100644
>> --- a/drivers/gpu/drm/i915/gt/uc/intel_guc_log.h
>> +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_log.h
>> @@ -67,6 +67,10 @@ struct intel_guc_log {
>> };
>>
>> void intel_guc_log_init_early(struct intel_guc_log *log);
>> +bool intel_guc_check_log_buf_overflow(struct intel_guc_log *log, 
>> enum guc_log_buffer_type type,
>> +                      unsigned int full_cnt);
>> +unsigned int intel_guc_get_log_buffer_size(enum guc_log_buffer_type 
>> type);
>> +size_t intel_guc_get_log_buffer_offset(enum guc_log_buffer_type type);
>> int intel_guc_log_create(struct intel_guc_log *log);
>> void intel_guc_log_destroy(struct intel_guc_log *log);
>>
>> diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c 
>> b/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c
>> index ab3cea352fb3..870b48456e9c 100644
>> --- a/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c
>> +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c
>> @@ -25,6 +25,7 @@
>> #include "gt/intel_ring.h"
>>
>> #include "intel_guc_ads.h"
>> +#include "intel_guc_capture.h"
>> #include "intel_guc_submission.h"
>>
>> #include "i915_drv.h"
>> @@ -4070,17 +4071,18 @@ int 
>> intel_guc_context_reset_process_msg(struct intel_guc *guc,
>> int intel_guc_error_capture_process_msg(struct intel_guc *guc,
>>                     const u32 *msg, u32 len)
>> {
>> -    int status;
>> +    u32 status;
>>
>>     if (unlikely(len != 1)) {
>>         drm_dbg(&guc_to_gt(guc)->i915->drm, "Invalid length %u", len);
>>         return -EPROTO;
>>     }
>>
>> -    status = msg[0];
>> -    drm_info(&guc_to_gt(guc)->i915->drm, "Got error capture: status 
>> = %d", status);
>> +    status = msg[0] & INTEL_GUC_STATE_CAPTURE_EVENT_STATUS_MASK;
>> +    if (status == INTEL_GUC_STATE_CAPTURE_EVENT_STATUS_NOSPACE)
>> +        drm_warn(&guc_to_gt(guc)->i915->drm, "G2H-Error capture no 
>> space");
>>
>> -    /* FIXME: Do something with the capture */
>> +    intel_guc_capture_process(guc);
>>
>>     return 0;
>> }
>
> With (1) and (2) taken care of, this is:
>
> Reviewed-by: Umesh Nerlige Ramappa <umesh.nerlige.ramappa@intel.com>
>
> Regards,
> Umesh
>
>> -- 
>> 2.25.1
>>

^ permalink raw reply	[flat|nested] 32+ messages in thread

end of thread, other threads:[~2022-03-11 22:27 UTC | newest]

Thread overview: 32+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-02-26  9:55 [PATCH v7 00/13] Add GuC Error Capture Support Alan Previn
2022-02-26  9:55 ` [Intel-gfx] " Alan Previn
2022-02-26  9:55 ` [Intel-gfx] [PATCH v7 01/13] drm/i915/guc: Update GuC ADS size for error capture lists Alan Previn
2022-02-26 20:22   ` Teres Alexis, Alan Previn
2022-02-27 21:02   ` kernel test robot
2022-03-10  5:30   ` Matthew Brost
2022-03-11  9:59     ` Teres Alexis, Alan Previn
2022-03-11 10:18     ` Teres Alexis, Alan Previn
2022-02-26  9:55 ` [Intel-gfx] [PATCH v7 02/13] drm/i915/guc: Add XE_LP static registers for GuC error capture Alan Previn
2022-03-10  0:05   ` Umesh Nerlige Ramappa
2022-02-26  9:55 ` [Intel-gfx] [PATCH v7 03/13] drm/i915/guc: Add XE_LP steered register lists support Alan Previn
2022-03-10  0:56   ` Umesh Nerlige Ramappa
2022-02-26  9:55 ` [Intel-gfx] [PATCH v7 04/13] drm/i915/guc: Add DG2 registers for GuC error state capture Alan Previn
2022-03-10  1:53   ` Umesh Nerlige Ramappa
2022-02-26  9:55 ` [Intel-gfx] [PATCH v7 05/13] drm/i915/guc: Add Gen9 " Alan Previn
2022-02-26  9:55 ` [Intel-gfx] [PATCH v7 06/13] drm/i915/guc: Add GuC's error state capture output structures Alan Previn
2022-02-26  9:55 ` [Intel-gfx] [PATCH v7 07/13] drm/i915/guc: Update GuC-log relay function names Alan Previn
2022-02-26  9:55 ` [Intel-gfx] [PATCH v7 08/13] drm/i915/guc: Add capture region into intel_guc_log Alan Previn
2022-02-26  9:55 ` [Intel-gfx] [PATCH v7 09/13] drm/i915/guc: Check sizing of guc_capture output Alan Previn
2022-02-28 22:32   ` kernel test robot
2022-02-26  9:55 ` [Intel-gfx] [PATCH v7 10/13] drm/i915/guc: Extract GuC error capture lists on G2H notification Alan Previn
2022-03-11 18:16   ` Umesh Nerlige Ramappa
2022-03-11 22:27     ` Teres Alexis, Alan Previn
2022-02-26  9:55 ` [Intel-gfx] [PATCH v7 11/13] drm/i915/guc: Pre-allocate output nodes for extraction Alan Previn
2022-03-11 19:40   ` Umesh Nerlige Ramappa
2022-03-11 19:46     ` Teres Alexis, Alan Previn
2022-02-26  9:55 ` [Intel-gfx] [PATCH v7 12/13] drm/i915/guc: Plumb GuC-capture into gpu_coredump Alan Previn
2022-02-26  9:55 ` [Intel-gfx] [PATCH v7 13/13] drm/i915/guc: Print the GuC error capture output register list Alan Previn
2022-03-11  5:26   ` Umesh Nerlige Ramappa
2022-02-26 10:11 ` [Intel-gfx] ✗ Fi.CI.CHECKPATCH: warning for Add GuC Error Capture Support (rev7) Patchwork
2022-02-26 10:12 ` [Intel-gfx] ✗ Fi.CI.SPARSE: " Patchwork
2022-02-26 10:43 ` [Intel-gfx] ✗ Fi.CI.BAT: failure " Patchwork

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.