summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZhanjun Dong <zhanjun.dong@intel.com>2026-10-13 15:28:46 -0700
committerRodrigo Vivi <rodrigo.vivi@intel.com>2026-10-14 14:18:11 -0400
commit149f81d8a73ea69ebd65e13369ae6d9af3165cd7 (patch)
tree65c3fa83c6a4c786875bb724c85e7660c4d3924a
parentc69ffc53f360e49eaa931b6b28cdebc678dacb85 (diff)
downloadlinux-next-149f81d8a73ea69ebd65e13369ae6d9af3165cd7.tar.gz
linux-next-149f81d8a73ea69ebd65e13369ae6d9af3165cd7.zip
drm/xe/guc: Compress GuC log and CTB dump with zstd
With default settings, GuC log is 8MB in size and dumping the log is time-consuming. In debugging scenarios, dump-to-dmesg suffers truncation loss because CI's size limit is 2MB. Additionally, if also forwarded to serial console, it takes several minutes. In use-case of continuous error+log collection over extended period, we pressure CI's allocated storage. Thus, compress the GuC log data dump to address these issues and multiply any benefits from a future upgrade in CI's size limits. Replace the raw ascii85 dumps of GuC log snapshots with a zstd-compressed ascii85 stream and reuse the same helper for CTB snapshot output. Use zstd streaming with the default compression level of 3 and an estimated source size of 0 so the compression workspace stays bounded even for multi-megabyte logs. Add a new Kconfig option DRM_XE_COMPRESS_DUMP to control whether GuC log and CTB dumps are compressed with ZSTD. This allows users to choose between reduced dump sizes and lower memory usage. Signed-off-by: Zhanjun Dong <zhanjun.dong@intel.com> Signed-off-by: Alan Previn <alan.previn.teres.alexis@intel.com> Acked-by: José Roberto de Souza <jose.souza@intel.com> Acked-by: Rodrigo Vivi <rodrigo.vivi@intel.com> Reviewed-by: Julia Filipchuk <julia.filipchuk@intel.com> Link: https://patch.msgid.link/20261013222846.308259-7-alan.previn.teres.alexis@intel.com Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
-rw-r--r--drivers/gpu/drm/xe/Kconfig15
-rw-r--r--drivers/gpu/drm/xe/xe_devcoredump.c4
-rw-r--r--drivers/gpu/drm/xe/xe_guc_ct.c19
-rw-r--r--drivers/gpu/drm/xe/xe_guc_ct.h4
-rw-r--r--drivers/gpu/drm/xe/xe_guc_log.c168
-rw-r--r--drivers/gpu/drm/xe/xe_guc_log.h9
6 files changed, 190 insertions, 29 deletions
diff --git a/drivers/gpu/drm/xe/Kconfig b/drivers/gpu/drm/xe/Kconfig
index 4d7dcaff2b91..0d87251c5d7c 100644
--- a/drivers/gpu/drm/xe/Kconfig
+++ b/drivers/gpu/drm/xe/Kconfig
@@ -101,6 +101,21 @@ config DRM_XE_PAGEMAP
If in doubt say "Y".
+config DRM_XE_COMPRESS_DUMP
+ bool "Enable ZSTD compression for error dumps"
+ depends on DRM_XE
+ default y
+ select ZSTD_COMPRESS
+ help
+ Enable this option to compress GuC log and CTB dumps with ZSTD
+ compression. This can significantly reduce the size of error dumps
+ but requires additional memory for compression workspace.
+
+ Disable this option if you want to save memory or prefer uncompressed
+ error dumps for debugging.
+
+ If in doubt say "Y".
+
config DRM_XE_FORCE_PROBE
string "Force probe xe for selected Intel hardware IDs"
depends on DRM_XE
diff --git a/drivers/gpu/drm/xe/xe_devcoredump.c b/drivers/gpu/drm/xe/xe_devcoredump.c
index ab681b148869..133cbe196d66 100644
--- a/drivers/gpu/drm/xe/xe_devcoredump.c
+++ b/drivers/gpu/drm/xe/xe_devcoredump.c
@@ -110,9 +110,9 @@ static ssize_t __xe_devcoredump_read(char *buffer, ssize_t count,
drm_printf(&p, "\tTile: %d\n", ss->gt->tile->id);
drm_puts(&p, "\n**** GuC Log ****\n");
- xe_guc_log_snapshot_print(ss->guc.log, &p);
+ xe_guc_log_snapshot_print(ss->guc.log, &p, ss->gt);
drm_puts(&p, "\n**** GuC CT ****\n");
- xe_guc_ct_snapshot_print(ss->guc.ct, &p);
+ xe_guc_ct_snapshot_print(ss->guc.ct, &p, ss->gt);
drm_puts(&p, "\n**** Contexts ****\n");
xe_guc_exec_queue_snapshot_print(ss->ge, &p);
diff --git a/drivers/gpu/drm/xe/xe_guc_ct.c b/drivers/gpu/drm/xe/xe_guc_ct.c
index da5ce97484b8..0d2464c72277 100644
--- a/drivers/gpu/drm/xe/xe_guc_ct.c
+++ b/drivers/gpu/drm/xe/xe_guc_ct.c
@@ -2201,11 +2201,12 @@ struct xe_guc_ct_snapshot *xe_guc_ct_snapshot_capture(struct xe_guc_ct *ct)
* xe_guc_ct_snapshot_print - Print out a given GuC CT snapshot.
* @snapshot: GuC CT snapshot object.
* @p: drm_printer where it will be printed out.
+ * @gt: GT structure for error reporting
*
* This function prints out a given GuC CT snapshot object.
*/
void xe_guc_ct_snapshot_print(struct xe_guc_ct_snapshot *snapshot,
- struct drm_printer *p)
+ struct drm_printer *p, struct xe_gt *gt)
{
if (!snapshot)
return;
@@ -2219,11 +2220,10 @@ void xe_guc_ct_snapshot_print(struct xe_guc_ct_snapshot *snapshot,
drm_printf(p, "\tg2h outstanding: %d\n",
snapshot->g2h_outstanding);
- if (snapshot->ctb) {
- drm_printf(p, "[CTB].length: 0x%zx\n", snapshot->ctb_size);
- xe_print_blob_ascii85(p, "[CTB].data", '\n',
- snapshot->ctb, 0, snapshot->ctb_size);
- }
+ if (snapshot->ctb)
+ xe_guc_print_blob(gt, p, "[CTB]", "[CTB].data",
+ &snapshot->ctb, 1,
+ snapshot->ctb_size, snapshot->ctb_size);
} else {
drm_puts(p, "CT disabled\n");
}
@@ -2257,9 +2257,10 @@ void xe_guc_ct_snapshot_free(struct xe_guc_ct_snapshot *snapshot)
void xe_guc_ct_print(struct xe_guc_ct *ct, struct drm_printer *p, bool want_ctb)
{
struct xe_guc_ct_snapshot *snapshot;
+ struct xe_gt *gt = ct_to_gt(ct);
snapshot = guc_ct_snapshot_capture(ct, false, want_ctb);
- xe_guc_ct_snapshot_print(snapshot, p);
+ xe_guc_ct_snapshot_print(snapshot, p, gt);
xe_guc_ct_snapshot_free(snapshot);
}
@@ -2357,10 +2358,10 @@ static void ct_dead_print(struct xe_dead_ct *dead)
drm_printf(&lp, "\tTile: %d\n", gt->tile->id);
drm_puts(&lp, "**** GuC Log ****\n");
- xe_guc_log_snapshot_print(dead->snapshot_log, &lp);
+ xe_guc_log_snapshot_print(dead->snapshot_log, &lp, gt);
drm_puts(&lp, "**** GuC CT ****\n");
- xe_guc_ct_snapshot_print(dead->snapshot_ct, &lp);
+ xe_guc_ct_snapshot_print(dead->snapshot_ct, &lp, gt);
drm_puts(&lp, "Done.\n");
}
diff --git a/drivers/gpu/drm/xe/xe_guc_ct.h b/drivers/gpu/drm/xe/xe_guc_ct.h
index e2a33249d65d..f3af15c95c75 100644
--- a/drivers/gpu/drm/xe/xe_guc_ct.h
+++ b/drivers/gpu/drm/xe/xe_guc_ct.h
@@ -12,6 +12,7 @@
struct drm_printer;
struct xe_device;
+struct xe_gt;
int xe_guc_ct_init_noalloc(struct xe_guc_ct *ct);
int xe_guc_ct_init(struct xe_guc_ct *ct);
@@ -26,7 +27,8 @@ void xe_guc_ct_flush_and_stop(struct xe_guc_ct *ct);
void xe_guc_ct_fast_path(struct xe_guc_ct *ct);
struct xe_guc_ct_snapshot *xe_guc_ct_snapshot_capture(struct xe_guc_ct *ct);
-void xe_guc_ct_snapshot_print(struct xe_guc_ct_snapshot *snapshot, struct drm_printer *p);
+void xe_guc_ct_snapshot_print(struct xe_guc_ct_snapshot *snapshot, struct drm_printer *p,
+ struct xe_gt *gt);
void xe_guc_ct_snapshot_free(struct xe_guc_ct_snapshot *snapshot);
void xe_guc_ct_print(struct xe_guc_ct *ct, struct drm_printer *p, bool want_ctb);
diff --git a/drivers/gpu/drm/xe/xe_guc_log.c b/drivers/gpu/drm/xe/xe_guc_log.c
index 7d006268ce91..47adaf70d5bf 100644
--- a/drivers/gpu/drm/xe/xe_guc_log.c
+++ b/drivers/gpu/drm/xe/xe_guc_log.c
@@ -6,6 +6,9 @@
#include "xe_guc_log.h"
#include <linux/fault-inject.h>
+#if IS_ENABLED(CONFIG_DRM_XE_COMPRESS_DUMP)
+#include <linux/zstd.h>
+#endif
#include <linux/utsname.h>
#include <drm/drm_managed.h>
@@ -226,16 +229,155 @@ struct xe_guc_log_snapshot *xe_guc_log_snapshot_capture(struct xe_guc_log *log,
return snapshot;
}
+#if IS_ENABLED(CONFIG_DRM_XE_COMPRESS_DUMP)
+static void xe_guc_print_blob_compressed(struct xe_gt *gt, struct drm_printer *p,
+ const char *length_label, const char *data_label,
+ void **chunks, int num_chunks,
+ size_t chunk_size, size_t total_size)
+{
+ zstd_parameters params;
+ zstd_cstream *cstream;
+ zstd_out_buffer outbuf;
+ size_t wksp_size, dst_size;
+ size_t remain, ret;
+ void *wksp = NULL;
+ char *dst = NULL;
+ int i;
+
+ /*
+ * Pass 0 as estimated_src_size to use zstd level-3 defaults
+ * (windowLog=18, 256KB window). Passing the full total_size would
+ * cause zstd to inflate windowLog proportionally (e.g. windowLog=24
+ * for a 19MB log), making the workspace tens of MB. With 0, the
+ * workspace stays ~1.5MB, well within kmalloc limits.
+ */
+ params = zstd_get_params(3, 0);
+
+ wksp_size = zstd_cstream_workspace_bound(&params.cParams);
+ wksp = kvzalloc(wksp_size, GFP_KERNEL);
+ if (!wksp)
+ goto err;
+
+ dst_size = zstd_compress_bound(total_size);
+ /*
+ * zstd may only write dst_size bytes, but xe_print_blob_ascii85() reads
+ * the blob a u32 at a time and so touches up to three bytes past the
+ * last compressed byte. Round-up allocation to a u32 boundary rather than
+ * rely on dst_size happening to be aligned. kvzalloc() leaves that
+ * padding zeroed, which also keeps the encoded tail reproducible.
+ */
+ dst = kvzalloc(round_up(dst_size, sizeof(u32)), GFP_KERNEL);
+ if (!dst)
+ goto err;
+
+ cstream = zstd_init_cstream(&params, 0, wksp, wksp_size);
+ if (!cstream) {
+ xe_gt_err(gt, "failed to init zstd cstream\n");
+ goto err;
+ }
+
+ outbuf.dst = dst;
+ outbuf.size = dst_size;
+ outbuf.pos = 0;
+
+ remain = total_size;
+ for (i = 0; i < num_chunks && remain; i++) {
+ size_t size = min(chunk_size, remain);
+ zstd_in_buffer inbuf = { .src = chunks[i], .size = size, .pos = 0 };
+
+ while (inbuf.pos < inbuf.size) {
+ ret = zstd_compress_stream(cstream, &outbuf, &inbuf);
+ if (ZSTD_isError(ret)) {
+ xe_gt_err(gt, "zstd_compress_stream failed: %s\n",
+ ZSTD_getErrorName(ret));
+ goto err;
+ }
+ }
+ remain -= size;
+ }
+
+ do {
+ ret = zstd_end_stream(cstream, &outbuf);
+ if (ZSTD_isError(ret)) {
+ xe_gt_err(gt, "zstd_end_stream failed: %s\n",
+ ZSTD_getErrorName(ret));
+ goto err;
+ }
+ } while (ret > 0 && outbuf.pos < outbuf.size);
+
+ drm_printf(p, "%s.length: 0x%zx -> 0x%zx Algo: ZSTD\n",
+ length_label, total_size, outbuf.pos);
+ xe_print_blob_ascii85(p, data_label, '\n', dst, 0,
+ DIV_ROUND_UP(outbuf.pos, sizeof(u32)) * sizeof(u32));
+ goto out;
+
+err:
+ drm_printf(p, "%s.length: 0x%zx Algo: ERROR (compression failed)\n",
+ length_label, total_size);
+out:
+ kvfree(dst);
+ kvfree(wksp);
+}
+#endif
+
/**
- * xe_guc_log_snapshot_print - dump a previously saved copy of the GuC log to some useful location
- * @snapshot: a snapshot of the GuC log
- * @p: the printer object to output to
+ * xe_guc_print_blob - Print binary data (compressed or uncompressed) as ascii85
+ * @gt: GT structure for error reporting
+ * @p: drm_printer for output
+ * @length_label: label prefix for the length/metadata line (e.g. "[LOG]" or "[CTB]")
+ * @data_label: label for the ascii85 blob (e.g. "[LOG].data" or "[CTB].data")
+ * @chunks: array of pointers to source data chunks
+ * @num_chunks: number of elements in @chunks
+ * @chunk_size: nominal size of each chunk; the last chunk may hold fewer valid bytes
+ * @total_size: total uncompressed byte count across all chunks
+ *
+ * If CONFIG_DRM_XE_COMPRESS_DUMP is enabled, compresses @total_size bytes using
+ * zstd level-3 streaming compression and prints the result via @p using ascii85
+ * encoding. Otherwise, directly prints the uncompressed data.
+ * On failure the function returns without printing the data blob.
*/
-void xe_guc_log_snapshot_print(struct xe_guc_log_snapshot *snapshot, struct drm_printer *p)
+void xe_guc_print_blob(struct xe_gt *gt __maybe_unused, struct drm_printer *p,
+ const char *length_label, const char *data_label,
+ void **chunks, int num_chunks,
+ size_t chunk_size, size_t total_size)
{
+#if IS_ENABLED(CONFIG_DRM_XE_COMPRESS_DUMP)
+ xe_guc_print_blob_compressed(gt, p, length_label, data_label,
+ chunks, num_chunks, chunk_size, total_size);
+#else
size_t remain;
int i;
+ drm_printf(p, "%s.length: 0x%zx Algo: RAW\n", length_label, total_size);
+
+ /*
+ * Print each chunk directly without merging into a single buffer.
+ * xe_print_blob_ascii85() supports multiple calls - use suffix=0
+ * for all but the last chunk to omit the newline between chunks.
+ */
+ remain = total_size;
+ for (i = 0; i < num_chunks && remain; i++) {
+ size_t size = min(chunk_size, remain);
+ const char *prefix = i ? NULL : data_label;
+ /* Add suffix only on the last chunk */
+ char suffix = (remain == size) ? '\n' : 0;
+
+ xe_print_blob_ascii85(p, prefix, suffix, chunks[i], 0,
+ DIV_ROUND_UP(size, sizeof(u32)) * sizeof(u32));
+ remain -= size;
+ }
+#endif
+}
+
+/**
+ * xe_guc_log_snapshot_print - dump a previously saved copy of the GuC log to some useful location
+ * @snapshot: a snapshot of the GuC log
+ * @p: the printer object to output to
+ * @gt: GT structure of the GuC log
+ */
+void xe_guc_log_snapshot_print(struct xe_guc_log_snapshot *snapshot, struct drm_printer *p,
+ struct xe_gt *gt)
+{
if (!snapshot) {
drm_printf(p, "GuC log snapshot not allocated!\n");
return;
@@ -249,16 +391,9 @@ void xe_guc_log_snapshot_print(struct xe_guc_log_snapshot *snapshot, struct drm_
drm_printf(p, "GuC timestamp: 0x%08llX [%llu]\n", snapshot->stamp, snapshot->stamp);
drm_printf(p, "Log level: %u\n", snapshot->level);
- drm_printf(p, "[LOG].length: 0x%zx\n", snapshot->size);
- remain = snapshot->size;
- for (i = 0; i < snapshot->num_chunks; i++) {
- size_t size = min(GUC_LOG_CHUNK_SIZE, remain);
- const char *prefix = i ? NULL : "[LOG].data";
- char suffix = i == snapshot->num_chunks - 1 ? '\n' : 0;
-
- xe_print_blob_ascii85(p, prefix, suffix, snapshot->copy[i], 0, size);
- remain -= size;
- }
+ xe_guc_print_blob(gt, p, "[LOG]", "[LOG].data",
+ snapshot->copy, snapshot->num_chunks,
+ GUC_LOG_CHUNK_SIZE, snapshot->size);
}
static inline void lfd_output_binary(struct drm_printer *p, char *buf, int buf_size)
@@ -599,12 +734,13 @@ void xe_guc_log_print_dmesg(struct xe_guc_log *log)
void xe_guc_log_print(struct xe_guc_log *log, struct drm_printer *p)
{
struct xe_guc_log_snapshot *snapshot;
+ struct xe_gt *gt = log_to_gt(log);
drm_printf(p, "**** GuC Log ****\n");
snapshot = xe_guc_log_snapshot_capture(log, false);
- drm_printf(p, "CS reference clock: %u\n", log_to_gt(log)->info.reference_clock);
- xe_guc_log_snapshot_print(snapshot, p);
+ drm_printf(p, "CS reference clock: %u\n", gt->info.reference_clock);
+ xe_guc_log_snapshot_print(snapshot, p, gt);
xe_guc_log_snapshot_free(snapshot);
}
diff --git a/drivers/gpu/drm/xe/xe_guc_log.h b/drivers/gpu/drm/xe/xe_guc_log.h
index 4649a260755e..3e97647ee8b5 100644
--- a/drivers/gpu/drm/xe/xe_guc_log.h
+++ b/drivers/gpu/drm/xe/xe_guc_log.h
@@ -11,6 +11,7 @@
struct drm_printer;
struct xe_device;
+struct xe_gt;
#if IS_ENABLED(CONFIG_DRM_XE_DEBUG_GUC)
#define XE_GUC_LOG_EVENT_DATA_BUFFER_SIZE SZ_16M
@@ -59,7 +60,13 @@ void xe_guc_log_print(struct xe_guc_log *log, struct drm_printer *p);
void xe_guc_log_print_lfd(struct xe_guc_log *log, struct drm_printer *p);
void xe_guc_log_print_dmesg(struct xe_guc_log *log);
struct xe_guc_log_snapshot *xe_guc_log_snapshot_capture(struct xe_guc_log *log, bool atomic);
-void xe_guc_log_snapshot_print(struct xe_guc_log_snapshot *snapshot, struct drm_printer *p);
+
+void xe_guc_print_blob(struct xe_gt *gt, struct drm_printer *p, const char *length_label,
+ const char *data_label, void **chunks, int num_chunks,
+ size_t chunk_size, size_t total_size);
+
+void xe_guc_log_snapshot_print(struct xe_guc_log_snapshot *snapshot, struct drm_printer *p,
+ struct xe_gt *gt);
void xe_guc_log_snapshot_free(struct xe_guc_log_snapshot *snapshot);
static inline u32