]> www.infradead.org Git - linux.git/commitdiff
drm/xe/guc: Add capture size check in GuC log buffer
authorZhanjun Dong <zhanjun.dong@intel.com>
Fri, 4 Oct 2024 19:34:25 +0000 (12:34 -0700)
committerMatt Roper <matthew.d.roper@intel.com>
Tue, 8 Oct 2024 16:34:28 +0000 (09:34 -0700)
Capture-nodes generated by GuC are placed in the GuC capture ring
buffer which is a sub-region of the larger Guc-Log-buffer.
Add capture output size check before allocating the shared buffer.

Signed-off-by: Zhanjun Dong <zhanjun.dong@intel.com>
Reviewed-by: Alan Previn <alan.previn.teres.alexis@intel.com>
Signed-off-by: Matt Roper <matthew.d.roper@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20241004193428.3311145-4-zhanjun.dong@intel.com
drivers/gpu/drm/xe/abi/guc_log_abi.h [new file with mode: 0644]
drivers/gpu/drm/xe/xe_guc_capture.c
drivers/gpu/drm/xe/xe_guc_log.c
drivers/gpu/drm/xe/xe_guc_log.h

diff --git a/drivers/gpu/drm/xe/abi/guc_log_abi.h b/drivers/gpu/drm/xe/abi/guc_log_abi.h
new file mode 100644 (file)
index 0000000..10db4ff
--- /dev/null
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2024 Intel Corporation
+ */
+
+#ifndef _ABI_GUC_LOG_ABI_H
+#define _ABI_GUC_LOG_ABI_H
+
+#include <linux/types.h>
+
+/* GuC logging buffer types */
+enum guc_log_buffer_type {
+       GUC_LOG_BUFFER_CRASH_DUMP,
+       GUC_LOG_BUFFER_DEBUG,
+       GUC_LOG_BUFFER_CAPTURE,
+};
+
+#define GUC_LOG_BUFFER_TYPE_MAX                3
+
+#endif
index 4256051a04b6109504349641907f998f38e3446d..f1b9ddcb2d896ed9aaaceca8818fc853e3e01b2e 100644 (file)
@@ -22,6 +22,7 @@
 #include "xe_gt_mcr.h"
 #include "xe_gt_printk.h"
 #include "xe_guc.h"
+#include "xe_guc_ads.h"
 #include "xe_guc_capture.h"
 #include "xe_guc_capture_types.h"
 #include "xe_guc_ct.h"
@@ -669,6 +670,85 @@ size_t xe_guc_capture_ads_input_worst_size(struct xe_guc *guc)
        return PAGE_ALIGN(total_size);
 }
 
+static int guc_capture_output_size_est(struct xe_guc *guc)
+{
+       struct xe_gt *gt = guc_to_gt(guc);
+       struct xe_hw_engine *hwe;
+       enum xe_hw_engine_id id;
+
+       int capture_size = 0;
+       size_t tmp = 0;
+
+       if (!guc->capture)
+               return -ENODEV;
+
+       /*
+        * If every single engine-instance suffered a failure in quick succession but
+        * were all unrelated, then a burst of multiple error-capture events would dump
+        * registers for every one engine instance, one at a time. In this case, GuC
+        * would even dump the global-registers repeatedly.
+        *
+        * For each engine instance, there would be 1 x guc_state_capture_group_t output
+        * followed by 3 x guc_state_capture_t lists. The latter is how the register
+        * dumps are split across different register types (where the '3' are global vs class
+        * vs instance).
+        */
+       for_each_hw_engine(hwe, gt, id) {
+               enum guc_capture_list_class_type capture_class;
+
+               capture_class = xe_engine_class_to_guc_capture_class(hwe->class);
+               capture_size += sizeof(struct guc_state_capture_group_header_t) +
+                                        (3 * sizeof(struct guc_state_capture_header_t));
+
+               if (!guc_capture_getlistsize(guc, 0, GUC_STATE_CAPTURE_TYPE_GLOBAL,
+                                            0, &tmp, true))
+                       capture_size += tmp;
+               if (!guc_capture_getlistsize(guc, 0, GUC_STATE_CAPTURE_TYPE_ENGINE_CLASS,
+                                            capture_class, &tmp, true))
+                       capture_size += tmp;
+               if (!guc_capture_getlistsize(guc, 0, GUC_STATE_CAPTURE_TYPE_ENGINE_INSTANCE,
+                                            capture_class, &tmp, true))
+                       capture_size += tmp;
+       }
+
+       return capture_size;
+}
+
+/*
+ * Add on a 3x multiplier to allow for multiple back-to-back captures occurring
+ * before the Xe can read the data out and process it
+ */
+#define GUC_CAPTURE_OVERBUFFER_MULTIPLIER 3
+
+static void check_guc_capture_size(struct xe_guc *guc)
+{
+       int capture_size = guc_capture_output_size_est(guc);
+       int spare_size = capture_size * GUC_CAPTURE_OVERBUFFER_MULTIPLIER;
+       u32 buffer_size = xe_guc_log_section_size_capture(&guc->log);
+
+       /*
+        * NOTE: capture_size is much smaller than the capture region
+        * allocation (DG2: <80K vs 1MB).
+        * Additionally, its based on space needed to fit all engines getting
+        * reset at once within the same G2H handler task slot. This is very
+        * unlikely. However, if GuC really does run out of space for whatever
+        * reason, we will see an separate warning message when processing the
+        * G2H event capture-notification, search for:
+        * xe_guc_STATE_CAPTURE_EVENT_STATUS_NOSPACE.
+        */
+       if (capture_size < 0)
+               xe_gt_dbg(guc_to_gt(guc),
+                         "Failed to calculate error state capture buffer minimum size: %d!\n",
+                         capture_size);
+       if (capture_size > buffer_size)
+               xe_gt_dbg(guc_to_gt(guc), "Error state capture buffer maybe small: %d < %d\n",
+                         buffer_size, capture_size);
+       else if (spare_size > buffer_size)
+               xe_gt_dbg(guc_to_gt(guc),
+                         "Error state capture buffer lacks spare size: %d < %d (min = %d)\n",
+                         buffer_size, spare_size, capture_size);
+}
+
 /*
  * xe_guc_capture_steered_list_init - Init steering register list
  * @guc: The GuC object
@@ -684,9 +764,10 @@ void xe_guc_capture_steered_list_init(struct xe_guc *guc)
         * the end of the pre-populated render list.
         */
        guc_capture_alloc_steered_lists(guc);
+       check_guc_capture_size(guc);
 }
 
-/**
+/*
  * xe_guc_capture_init - Init for GuC register capture
  * @guc: The GuC object
  *
index 7fbc56cceabac0dc0799fb864fdc60c7672e6bc3..5bab5a3948cc393b03bb415e0b28b5173bec45a4 100644 (file)
@@ -270,4 +270,70 @@ int xe_guc_log_init(struct xe_guc_log *log)
 
        return 0;
 }
+
 ALLOW_ERROR_INJECTION(xe_guc_log_init, ERRNO); /* See xe_pci_probe() */
+
+static u32 xe_guc_log_section_size_crash(struct xe_guc_log *log)
+{
+       return CRASH_BUFFER_SIZE;
+}
+
+static u32 xe_guc_log_section_size_debug(struct xe_guc_log *log)
+{
+       return DEBUG_BUFFER_SIZE;
+}
+
+/**
+ * xe_guc_log_section_size_capture - Get capture buffer size within log sections.
+ * @log: The log object.
+ *
+ * This function will return the capture buffer size within log sections.
+ *
+ * Return: capture buffer size.
+ */
+u32 xe_guc_log_section_size_capture(struct xe_guc_log *log)
+{
+       return CAPTURE_BUFFER_SIZE;
+}
+
+/**
+ * xe_guc_get_log_buffer_size - Get log buffer size for a type.
+ * @log: The log object.
+ * @type: The log buffer type
+ *
+ * Return: buffer size.
+ */
+u32 xe_guc_get_log_buffer_size(struct xe_guc_log *log, enum guc_log_buffer_type type)
+{
+       switch (type) {
+       case GUC_LOG_BUFFER_CRASH_DUMP:
+               return xe_guc_log_section_size_crash(log);
+       case GUC_LOG_BUFFER_DEBUG:
+               return xe_guc_log_section_size_debug(log);
+       case GUC_LOG_BUFFER_CAPTURE:
+               return xe_guc_log_section_size_capture(log);
+       }
+       return 0;
+}
+
+/**
+ * xe_guc_get_log_buffer_offset - Get offset in log buffer for a type.
+ * @log: The log object.
+ * @type: The log buffer type
+ *
+ * This function will return the offset in the log buffer for a type.
+ * Return: buffer offset.
+ */
+u32 xe_guc_get_log_buffer_offset(struct xe_guc_log *log, enum guc_log_buffer_type type)
+{
+       enum guc_log_buffer_type i;
+       u32 offset = PAGE_SIZE;/* for the log_buffer_states */
+
+       for (i = GUC_LOG_BUFFER_CRASH_DUMP; i < GUC_LOG_BUFFER_TYPE_MAX; ++i) {
+               if (i == type)
+                       break;
+               offset += xe_guc_get_log_buffer_size(log, i);
+       }
+
+       return offset;
+}
index 1fb2fae1f4e1a580c1550e0b040b7cf5294b3e56..86501643431b93f899a58840324fe0a6a820703e 100644 (file)
@@ -7,6 +7,7 @@
 #define _XE_GUC_LOG_H_
 
 #include "xe_guc_log_types.h"
+#include "abi/guc_log_abi.h"
 
 struct drm_printer;
 struct xe_device;
@@ -18,7 +19,7 @@ struct xe_device;
 #else
 #define CRASH_BUFFER_SIZE      SZ_8K
 #define DEBUG_BUFFER_SIZE      SZ_64K
-#define CAPTURE_BUFFER_SIZE    SZ_16K
+#define CAPTURE_BUFFER_SIZE    SZ_1M
 #endif
 /*
  * While we're using plain log level in i915, GuC controls are much more...
@@ -50,4 +51,8 @@ xe_guc_log_get_level(struct xe_guc_log *log)
        return log->level;
 }
 
+u32 xe_guc_log_section_size_capture(struct xe_guc_log *log);
+u32 xe_guc_get_log_buffer_size(struct xe_guc_log *log, enum guc_log_buffer_type type);
+u32 xe_guc_get_log_buffer_offset(struct xe_guc_log *log, enum guc_log_buffer_type type);
+
 #endif