Skip to contentSkip to conversation

drm/msm: Capture and dump the GPU crash state

PATCH In review Jordan Crouse 3 participants 15 messages Jul 24, 2018 1 message not in the archive

Subsystems (7)
lore
Share & tools
lore
#0 lore raw

v8 cover drm/msm: Capture and dump the GPU crash state

This is revision 8 implementing a GPU crash state for drm/msm
(https://patchwork.freedesktop.org/series/36097/). This patchset adds better documentation and reflects comments from the mailing lists. I know we will miss 4.19 at this point, but I think this is ready to soak in msm-next for a while.

The object of this code is to store and provide enough information to debug software and hardware issues on the Adreno hardware in a semi human-readable format that can also be parsed by scripts.

THe full set of changes here capture basic information about the GPU, the status and contents of the ringbuffers, a snapshot of the current register state and the active buffers from the hanging submit.

The data is printed with devcoredump. For example, after a hang you can get the data from /sys/class/devcoredump/devcdX/data where X is a unique number.

v8: Add documentation and consolidate puts/printf code from code comments
v7: Add EXPORT_SYMBOL for __drm_puts_coredump and use %zd to print a size_t variable for the bo dump thanks to the ever vigilant zero one bot.
v6: Add drm_puts() and use it in the appropriate place. Clean up a few minor bugs here and there.
v5: Fix symbol error in i915_gpu_error.c thanks to 01 dot org bot. Added open/release functions for the show debugfs file to get the state per Chris Wilson. Slightly modified the register output format to be more YAML friendly also per Chris.
v4: Add buffer dump for the active submit. Fix refcount issue with devcoredump. Change header for a5xx registers to registers-hlsq because I'm told YAML requires unique tags.
v3: Make recommended changes to ascii85 per Chris Wilson. Use devcoredump to dump crash states as suggested by Bjorn Andersson and add a new drm_print facility to facilitate that. Remove the now obsolete 'crash' debugfs node. Add documentation for the crash dump output.
v2: Convert output to yaml, use ascii85 to dump ringbuffer contents.

Jordan Crouse (13):
include: Move ascii85 functions from i915 to linux/ascii85.h
drm: drm_printer: Add printer for devcoredump
drm: Add drm_puts() to complement drm_printf()
drm: Add a -puts() function for the seq_file printer
drm: Add puts callback for the coredump printer drm/msm/gpu: Capture the state of the GPU
drm/msm/gpu: Convert the GPU show function to use the GPU state drm/msm/gpu: Rearrange the code that collects the task during a hang drm/msm/gpu: Capture the GPU state on a GPU hang drm/msm/adreno: Convert the show/crash file format drm/msm/adreno: Add ringbuffer data to the GPU state drm/msm/adreno: Add a5xx specific registers for the GPU state drm/msm/gpu: Add the buffer objects from the submit to the crash dump

 Documentation/gpu/msm-crash-dump.rst    |  96 ++++++++++
 drivers/gpu/drm/drm_print.c             | 111 +++++++++++
 drivers/gpu/drm/i915/i915_gpu_error.c   |  34 +---
 drivers/gpu/drm/msm/Kconfig             |   1 +
 drivers/gpu/drm/msm/adreno/a3xx_gpu.c   |  30 +--
 drivers/gpu/drm/msm/adreno/a4xx_gpu.c   |  22 ++-
 drivers/gpu/drm/msm/adreno/a5xx_gpu.c   | 242 ++++++++++++++++++++++--
 drivers/gpu/drm/msm/adreno/adreno_gpu.c | 184 ++++++++++++++++--
 drivers/gpu/drm/msm/adreno/adreno_gpu.h |  10 +-
 drivers/gpu/drm/msm/msm_debugfs.c       |  93 ++++++++-
 drivers/gpu/drm/msm/msm_gpu.c           | 145 +++++++++++++-
 drivers/gpu/drm/msm/msm_gpu.h           |  68 ++++++-
 include/drm/drm_print.h                 |  71 +++++++
 include/linux/ascii85.h                 |  38 ++++
 14 files changed, 1044 insertions(+), 101 deletions(-)
 create mode 100644 Documentation/gpu/msm-crash-dump.rst
 create mode 100644 include/linux/ascii85.h
Signature · 6 lines
2.18.0

_______________________________________________
Freedreno mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/freedreno
#1 lore raw

PATCH 1/13 include: Move ascii85 functions from i915 to linux/ascii85.h

The i915 DRM driver very cleverly used ascii85 encoding for their GPU state file. Move the encode functions to a general header file to support other drivers that might be interested in the same functionality.

v4: Make the return value const char * as suggested by Chris Wilson
v3: Fix error_puts -> err_puts pointed out by the 01.org bot
v2: Update API to be cleaner for the caller as suggested by Chris Wilson

---
 drivers/gpu/drm/i915/i915_gpu_error.c | 34 +++---------------------
 include/linux/ascii85.h               | 38 +++++++++++++++++++++++++++
 2 files changed, 42 insertions(+), 30 deletions(-)
 create mode 100644 include/linux/ascii85.h
drivers/gpu/drm/i915/i915_gpu_error.c +4 -30
index df234dc23274..284e899ca8ff 100644
@@ -31,6 +31,7 @@
 #include <linux/stop_machine.h>
 #include <linux/zlib.h>
 #include <drm/drm_print.h>
+#include <linux/ascii85.h>
 
 #include "i915_gpu_error.h"
 #include "i915_drv.h"
@@ -522,35 +523,12 @@ void i915_error_printf(struct drm_i915_error_state_buf *e, const char *f, ...)
 	va_end(args);
 }
 
-static int
-ascii85_encode_len(int len)
-{
-	return DIV_ROUND_UP(len, 4);
-}
-
-static bool
-ascii85_encode(u32 in, char *out)
-{
-	int i;
-
-	if (in == 0)
-		return false;
-
-	out[5] = '\0';
-	for (i = 5; i--; ) {
-		out[i] = '!' + in % 85;
-		in /= 85;
-	}
-
-	return true;
-}
-
 static void print_error_obj(struct drm_i915_error_state_buf *m,
 			    struct intel_engine_cs *engine,
 			    const char *name,
 			    struct drm_i915_error_object *obj)
 {
-	char out[6];
+	char out[ASCII85_BUFSZ];
 	int page;
 
 	if (!obj)
@@ -572,12 +550,8 @@ static void print_error_obj(struct drm_i915_error_state_buf *m,
 			len -= obj->unused;
 		len = ascii85_encode_len(len);
 
-		for (i = 0; i < len; i++) {
-			if (ascii85_encode(obj->pages[page][i], out))
-				err_puts(m, out);
-			else
-				err_puts(m, "z");
-		}
+		for (i = 0; i < len; i++)
+			err_puts(m, ascii85_encode(obj->pages[page][i], out));
 	}
 	err_puts(m, "\n");
 }
include/linux/ascii85.h new file +38 -0
new file mode 100644
index 000000000000..4cc40201273e
@@ -0,0 +1,38 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (c) 2008 Intel Corporation
+ * Copyright (c) 2018 The Linux Foundation. All rights reserved.
+ */
+
+#ifndef _ASCII85_H_
+#define _ASCII85_H_
+
+#include <linux/kernel.h>
+
+#define ASCII85_BUFSZ 6
+
+static inline long
+ascii85_encode_len(long len)
+{
+	return DIV_ROUND_UP(len, 4);
+}
+
+static inline const char *
+ascii85_encode(u32 in, char *out)
+{
+	int i;
+
+	if (in == 0)
+		return "z";
+
+	out[5] = '\0';
+	for (i = 5; i--; ) {
+		out[i] = '!' + in % 85;
+		in /= 85;
+	}
+
+	return out;
+}
+
+#endif
Signature · 6 lines
2.18.0

_______________________________________________
Intel-gfx mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

A message that is not in the archive (sent off-list or not yet received): 20180724163331.18250-1-jcrouse-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org

#3 lore raw

PATCH v8 2/13 drm: drm_printer: Add printer for devcoredump

Add a drm printer suitable for use with the read callback for devcoredump or other suitable buffer based output format that isn't otherwise covered by seq_file.

v2: Add improved documentation per Daniel Vetter

---
 drivers/gpu/drm/drm_print.c | 74 +++++++++++++++++++++++++++++++++++++
 include/drm/drm_print.h     | 65 ++++++++++++++++++++++++++++++++
 2 files changed, 139 insertions(+)
drivers/gpu/drm/drm_print.c +74 -0
index b25f98f33f6c..03d1f98e5ac7 100644
@@ -30,6 +30,80 @@
 #include <drm/drmP.h>
 #include <drm/drm_print.h>
 
+void __drm_printfn_coredump(struct drm_printer *p, struct va_format *vaf)
+{
+	struct drm_print_iterator *iterator = p->arg;
+	ssize_t len;
+
+	if (!iterator->remain)
+		return;
+
+	/* Figure out how big the string will be */
+	len = snprintf(NULL, 0, "%pV", vaf);
+
+	if (iterator->offset < iterator->start) {
+		char *buf;
+		ssize_t copy;
+
+		if (iterator->offset + len <= iterator->start) {
+			iterator->offset += len;
+			return;
+		}
+
+		/* Print the string into a temporary buffer */
+		buf = kmalloc(len + 1,
+			GFP_KERNEL | __GFP_NOWARN | __GFP_NORETRY);
+		if (!buf)
+			return;
+
+		snprintf(buf, len + 1, "%pV", vaf);
+
+		copy = len - (iterator->start - iterator->offset);
+
+		if (copy > iterator->remain)
+			copy = iterator->remain;
+
+		/* Copy out the bit of the string that we need */
+		memcpy(iterator->data,
+			buf + (iterator->start - iterator->offset), copy);
+
+		iterator->offset = iterator->start + copy;
+		iterator->remain -= copy;
+
+		kfree(buf);
+	} else {
+		char *buf;
+		ssize_t pos = iterator->offset - iterator->start;
+
+		if (len < iterator->remain) {
+			snprintf(((char *) iterator->data) + pos,
+				iterator->remain, "%pV", vaf);
+
+			iterator->offset += len;
+			iterator->remain -= len;
+
+			return;
+		}
+
+		/* Print the string into a temporary buffer */
+		buf = kmalloc(len + 1,
+			GFP_KERNEL | __GFP_NOWARN | __GFP_NORETRY);
+		if (!buf)
+			return;
+
+		snprintf(buf, len + 1, "%pV", vaf);
+
+		/* Copy out the remaining bits */
+		memcpy(iterator->data + pos, buf, iterator->remain);
+
+		iterator->offset += iterator->remain;
+		iterator->remain = 0;
+
+		kfree(buf);
+	}
+}
+EXPORT_SYMBOL(__drm_printfn_coredump);
+
 void __drm_printfn_seq_file(struct drm_printer *p, struct va_format *vaf)
 {
 	seq_printf(p->arg, "%pV", vaf);
include/drm/drm_print.h +65 -0

77 lines not on this page · raw message

Signature · 6 lines
2.18.0

_______________________________________________
Freedreno mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/freedreno
#4 lore raw

PATCH v8 3/13 drm: Add drm_puts() to complement drm_printf()

In reply to a message not in the archive

Add drm_puts() for a much faster path to print constant strings into a drm_printer object with memcpy and friends. This can have seconds off of really large outputs such as GPU dumps.

If the drm_printer object supports a custom puts function then use that otherwise fall back to the slower legacy printf call.

v2: Add documentation for drm_puts() per Daniel Vetter

---
 drivers/gpu/drm/drm_print.c | 17 +++++++++++++++++
 include/drm/drm_print.h     |  2 ++
 2 files changed, 19 insertions(+)
drivers/gpu/drm/drm_print.c +17 -0
index 03d1f98e5ac7..4bcd960788ba 100644
@@ -122,6 +122,23 @@ void __drm_printfn_debug(struct drm_printer *p, struct va_format *vaf)
 }
 EXPORT_SYMBOL(__drm_printfn_debug);
 
+/**
+ * drm_puts - print a const string to a &drm_printer stream
+ * @p: the &drm printer
+ * @f: const string
+ *
+ * Allow &drm_printer types that have a constant string
+ * option to use it.
+ */
+void drm_puts(struct drm_printer *p, const char *str)
+{
+	if (p->puts)
+		p->puts(p, str);
+	else
+		drm_printf(p, "%s", str);
+}
+EXPORT_SYMBOL(drm_puts);
+
 /**
  * drm_printf - print to a &drm_printer stream
  * @p: the &drm_printer
include/drm/drm_print.h +2 -0
index 3213d2896e9c..06167a30f165 100644
@@ -69,6 +69,7 @@
 struct drm_printer {
 	/* private: */
 	void (*printfn)(struct drm_printer *p, struct va_format *vaf);
+	void (*puts)(struct drm_printer *p, const char *str);
 	void *arg;
 	const char *prefix;
 };
@@ -80,6 +81,7 @@ void __drm_printfn_debug(struct drm_printer *p, struct va_format *vaf);
 
 __printf(2, 3)
 void drm_printf(struct drm_printer *p, const char *f, ...);
+void drm_puts(struct drm_printer *p, const char *str);
 
 __printf(2, 0)
 /**
Signature · 6 lines
2.18.0

_______________________________________________
Freedreno mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/freedreno
#5 lore raw

Re: [PATCH 03/13] drm: Add drm_puts() to complement drm_printf()

Hi Jordan,

I might be a bit late for the party, so consider the following jfyi.

Quoted 1 line · Jordan Crouse

+void drm_puts(struct drm_printer *p, const char *str)

One could easily use the compiler to detect if drm_printf or drm_puts should be used. See the trace_printk define in include/linux/kernel.h.

drivers/gpu/drm/drm_print.c void __drm_printfn_debug(struct drm_printer *p, struct va_format *vaf) · line 138

Quoted 5 lines
drivers/gpu/drm/drm_print.c +5 -0
+{
+       if (p->puts)
+               p->puts(p, str);
+       else
+               drm_printf(p, "%s", str);

From a quick look from the existing three printers (seq_file, info and debug) only the first one is updated with this series.
I would imagine that updating the other two and dropping the drm_printf() fallback is a good move.

Otherwise one could easily assume that they have a fast path when they do not.

HTH
Emil
_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel

#6 lore raw

PATCH v8 4/13 drm: Add a -puts() function for the seq_file printer

In reply to a message not in the archive

Add a puts() function to use seq_puts() to help speed up up print time for constant strings.

---
 drivers/gpu/drm/drm_print.c | 6 ++++++
 include/drm/drm_print.h     | 2 ++
 2 files changed, 8 insertions(+)
drivers/gpu/drm/drm_print.c +6 -0
index 4bcd960788ba..8ddeaba4bf9d 100644
@@ -104,6 +104,12 @@ void __drm_printfn_coredump(struct drm_printer *p, struct va_format *vaf)
 }
 EXPORT_SYMBOL(__drm_printfn_coredump);
 
+void __drm_puts_seq_file(struct drm_printer *p, const char *str)
+{
+	seq_puts(p->arg, str);
+}
+EXPORT_SYMBOL(__drm_puts_seq_file);
+
 void __drm_printfn_seq_file(struct drm_printer *p, struct va_format *vaf)
 {
 	seq_printf(p->arg, "%pV", vaf);
include/drm/drm_print.h +2 -0
index 06167a30f165..1834ec6244bf 100644
@@ -76,6 +76,7 @@ struct drm_printer {
 
 void __drm_printfn_coredump(struct drm_printer *p, struct va_format *vaf);
 void __drm_printfn_seq_file(struct drm_printer *p, struct va_format *vaf);
+void __drm_puts_seq_file(struct drm_printer *p, const char *str);
 void __drm_printfn_info(struct drm_printer *p, struct va_format *vaf);
 void __drm_printfn_debug(struct drm_printer *p, struct va_format *vaf);
 
@@ -182,6 +183,7 @@ static inline struct drm_printer drm_seq_file_printer(struct seq_file *f)
 {
 	struct drm_printer p = {
 		.printfn = __drm_printfn_seq_file,
+		.puts = __drm_puts_seq_file,
 		.arg = f,
 	};
 	return p;
Signature · 6 lines
2.18.0

_______________________________________________
Freedreno mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/freedreno
#7 lore raw

PATCH v8 5/13 drm: Add puts callback for the coredump printer

In reply to a message not in the archive

Add a puts function for the coredump printer to bypass printf() for constant strings for a speed boost. Reorganize the coredump printf callback to share as much code as possible.

v2: Try to reuse code between print and puts as suggested by
Chris Wilson

---
 drivers/gpu/drm/drm_print.c | 84 +++++++++++++++++++++----------------
 include/drm/drm_print.h     |  2 +
 2 files changed, 51 insertions(+), 35 deletions(-)
drivers/gpu/drm/drm_print.c +49 -35
index 8ddeaba4bf9d..7631a40389ba 100644
@@ -30,7 +30,7 @@
 #include <drm/drmP.h>
 #include <drm/drm_print.h>
 
-void __drm_printfn_coredump(struct drm_printer *p, struct va_format *vaf)
+void __drm_puts_coredump(struct drm_printer *p, const char *str)
 {
 	struct drm_print_iterator *iterator = p->arg;
 	ssize_t len;
@@ -38,26 +38,16 @@ void __drm_printfn_coredump(struct drm_printer *p, struct va_format *vaf)
 	if (!iterator->remain)
 		return;
 
-	/* Figure out how big the string will be */
-	len = snprintf(NULL, 0, "%pV", vaf);
-
 	if (iterator->offset < iterator->start) {
-		char *buf;
 		ssize_t copy;
 
+		len = strlen(str);
+
 		if (iterator->offset + len <= iterator->start) {
 			iterator->offset += len;
 			return;
 		}
 
-		/* Print the string into a temporary buffer */
-		buf = kmalloc(len + 1,
-			GFP_KERNEL | __GFP_NOWARN | __GFP_NORETRY);
-		if (!buf)
-			return;
-
-		snprintf(buf, len + 1, "%pV", vaf);
-
 		copy = len - (iterator->start - iterator->offset);
 
 		if (copy > iterator->remain)
@@ -65,42 +55,66 @@ void __drm_printfn_coredump(struct drm_printer *p, struct va_format *vaf)
 
 		/* Copy out the bit of the string that we need */
 		memcpy(iterator->data,
-			buf + (iterator->start - iterator->offset), copy);
+			str + (iterator->start - iterator->offset), copy);
 
 		iterator->offset = iterator->start + copy;
 		iterator->remain -= copy;
-
-		kfree(buf);
 	} else {
-		char *buf;
 		ssize_t pos = iterator->offset - iterator->start;
 
-		if (len < iterator->remain) {
-			snprintf(((char *) iterator->data) + pos,
-				iterator->remain, "%pV", vaf);
+		len = min_t(ssize_t, strlen(str), iterator->remain);
 
-			iterator->offset += len;
-			iterator->remain -= len;
+		memcpy(iterator->data + pos, str, len);
 
-			return;
-		}
+		iterator->offset += len;
+		iterator->remain -= len;
+	}
+}
+EXPORT_SYMBOL(__drm_puts_coredump);
 
-		/* Print the string into a temporary buffer */
-		buf = kmalloc(len + 1,
-			GFP_KERNEL | __GFP_NOWARN | __GFP_NORETRY);
-		if (!buf)
-			return;
+void __drm_printfn_coredump(struct drm_printer *p, struct va_format *vaf)
+{
+	struct drm_print_iterator *iterator = p->arg;
+	size_t len;
+	char *buf;
+
+	if (!iterator->remain)
+		return;
+
+	/* Figure out how big the string will be */
+	len = snprintf(NULL, 0, "%pV", vaf);
+
+	/* This is the easiest path, we've already advanced beyond the offset */
+	if (iterator->offset + len <= iterator->start) {
+		iterator->offset += len;
+		return;
+	}
 
-		snprintf(buf, len + 1, "%pV", vaf);
+	/* Then check if we can directly copy into the target buffer */
+	if ((iterator->offset >= iterator->start) && (len < iterator->remain)) {
+		ssize_t pos = iterator->offset - iterator->start;
 
-		/* Copy out the remaining bits */
-		memcpy(iterator->data + pos, buf, iterator->remain);
+		snprintf(((char *) iterator->data) + pos,
+			iterator->remain, "%pV", vaf);
 
-		iterator->offset += iterator->remain;
-		iterator->remain = 0;
+		iterator->offset += len;
+		iterator->remain -= len;
 
-		kfree(buf);
+		return;
 	}
+
+	/*
+	 * Finally, hit the slow path and make a temporary string to copy over
+	 * using _drm_puts_coredump
+	 */
+	buf = kmalloc(len + 1, GFP_KERNEL | __GFP_NOWARN | __GFP_NORETRY);
+	if (!buf)
+		return;
+
+	snprintf(buf, len + 1, "%pV", vaf);
+	__drm_puts_coredump(p, (const char *) buf);
+
+	kfree(buf);
 }
 EXPORT_SYMBOL(__drm_printfn_coredump);
 
include/drm/drm_print.h +2 -0
index 1834ec6244bf..e0e791a094b9 100644
@@ -75,6 +75,7 @@ struct drm_printer {
 };
 
 void __drm_printfn_coredump(struct drm_printer *p, struct va_format *vaf);
+void __drm_puts_coredump(struct drm_printer *p, const char *str);
 void __drm_printfn_seq_file(struct drm_printer *p, struct va_format *vaf);
 void __drm_puts_seq_file(struct drm_printer *p, const char *str);
 void __drm_printfn_info(struct drm_printer *p, struct va_format *vaf);
@@ -163,6 +164,7 @@ drm_coredump_printer(struct drm_print_iterator *iter)
 {
 	struct drm_printer p = {
 		.printfn = __drm_printfn_coredump,
+		.puts = __drm_puts_coredump,
 		.arg = iter,
 	};
 
Signature · 6 lines
2.18.0

_______________________________________________
Freedreno mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/freedreno
#8 lore raw

PATCH v8 6/13 drm/msm/gpu: Capture the state of the GPU

In reply to a message not in the archive

Add the infrastructure to capture the current state of the GPU and store it in memory so that it can be dumped later.

For now grab the same basic ringbuffer information and registers that are provided by the debugfs 'gpu' node but obviously this should be extended to capture a much larger set of GPU information.

---
 drivers/gpu/drm/msm/adreno/a3xx_gpu.c   | 15 +++++++
 drivers/gpu/drm/msm/adreno/a4xx_gpu.c   | 14 +++++++
 drivers/gpu/drm/msm/adreno/a5xx_gpu.c   | 22 ++++++++++
 drivers/gpu/drm/msm/adreno/adreno_gpu.c | 55 +++++++++++++++++++++++++
 drivers/gpu/drm/msm/adreno/adreno_gpu.h |  3 ++
 drivers/gpu/drm/msm/msm_gpu.h           | 20 +++++++++
 6 files changed, 129 insertions(+)
drivers/gpu/drm/msm/adreno/a3xx_gpu.c +15 -0
index 3ebbeb3a9b68..b707b5bca9ab 100644
@@ -427,6 +427,19 @@ static void a3xx_dump(struct msm_gpu *gpu)
 			gpu_read(gpu, REG_A3XX_RBBM_STATUS));
 	adreno_dump(gpu);
 }
+
+static struct msm_gpu_state *a3xx_gpu_state_get(struct msm_gpu *gpu)
+{
+	struct msm_gpu_state *state = adreno_gpu_state_get(gpu);
+
+	if (IS_ERR(state))
+		return state;
+
+	state->rbbm_status = gpu_read(gpu, REG_A3XX_RBBM_STATUS);
+
+	return state;
+}
+
 /* Register offset defines for A3XX */
 static const unsigned int a3xx_register_offsets[REG_ADRENO_REGISTER_MAX] = {
 	REG_ADRENO_DEFINE(REG_ADRENO_CP_RB_BASE, REG_AXXX_CP_RB_BASE),
@@ -453,6 +466,8 @@ static const struct adreno_gpu_funcs funcs = {
 #ifdef CONFIG_DEBUG_FS
 		.show = a3xx_show,
 #endif
+		.gpu_state_get = a3xx_gpu_state_get,
+		.gpu_state_put = adreno_gpu_state_put,
 	},
 };
 
drivers/gpu/drm/msm/adreno/a4xx_gpu.c +14 -0
index 16d3d596638e..17e97ebc1077 100644
@@ -465,6 +465,18 @@ static void a4xx_show(struct msm_gpu *gpu, struct seq_file *m)
 }
 #endif
 
+static struct msm_gpu_state *a4xx_gpu_state_get(struct msm_gpu *gpu)
+{
+	struct msm_gpu_state *state = adreno_gpu_state_get(gpu);
+
+	if (IS_ERR(state))
+		return state;
+
+	state->rbbm_status = gpu_read(gpu, REG_A4XX_RBBM_STATUS);
+
+	return state;
+}
+
 /* Register offset defines for A4XX, in order of enum adreno_regs */
 static const unsigned int a4xx_register_offsets[REG_ADRENO_REGISTER_MAX] = {
 	REG_ADRENO_DEFINE(REG_ADRENO_CP_RB_BASE, REG_A4XX_CP_RB_BASE),
@@ -541,6 +553,8 @@ static const struct adreno_gpu_funcs funcs = {
 #ifdef CONFIG_DEBUG_FS
 		.show = a4xx_show,
 #endif
+		.gpu_state_get = a4xx_gpu_state_get,
+		.gpu_state_put = adreno_gpu_state_put,
 	},
 	.get_timestamp = a4xx_get_timestamp,
 };
drivers/gpu/drm/msm/adreno/a5xx_gpu.c +22 -0
index d39400e5bc42..9e85e4f7016d 100644
@@ -1195,6 +1195,26 @@ static int a5xx_get_timestamp(struct msm_gpu *gpu, uint64_t *value)
 	return 0;
 }
 
+static struct msm_gpu_state *a5xx_gpu_state_get(struct msm_gpu *gpu)
+{
+	struct msm_gpu_state *state;
+
+	/*
+	 * Temporarily disable hardware clock gating before going into
+	 * adreno_show to avoid issues while reading the registers
+	 */
+	a5xx_set_hwcg(gpu, false);
+
+	state = adreno_gpu_state_get(gpu);
+
+	if (!IS_ERR(state))
+		state->rbbm_status = gpu_read(gpu, REG_A5XX_RBBM_STATUS);
+
+	a5xx_set_hwcg(gpu, true);
+
+	return state;
+}
+
 #ifdef CONFIG_DEBUG_FS
 static void a5xx_show(struct msm_gpu *gpu, struct seq_file *m)
 {
@@ -1244,6 +1264,8 @@ static const struct adreno_gpu_funcs funcs = {
 		.debugfs_init = a5xx_debugfs_init,
 #endif
 		.gpu_busy = a5xx_gpu_busy,
+		.gpu_state_get = a5xx_gpu_state_get,
+		.gpu_state_put = adreno_gpu_state_put,
 	},
 	.get_timestamp = a5xx_get_timestamp,
 };
drivers/gpu/drm/msm/adreno/adreno_gpu.c +55 -0

61 lines not on this page · raw message

drivers/gpu/drm/msm/adreno/adreno_gpu.h +3 -0
index bc9ec27e9ed8..734e31a9631f 100644
@@ -229,6 +229,9 @@ int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev,
 void adreno_gpu_cleanup(struct adreno_gpu *gpu);
 
 
+struct msm_gpu_state *adreno_gpu_state_get(struct msm_gpu *gpu);
+void adreno_gpu_state_put(struct msm_gpu_state *state);
+
 /* ringbuffer helpers (the parts that are adreno specific) */
 
 static inline void
drivers/gpu/drm/msm/msm_gpu.h +20 -0
index b8241179175a..de71cc04ecf3 100644
@@ -27,6 +27,7 @@
 
 struct msm_gem_submit;
 struct msm_gpu_perfcntr;
+struct msm_gpu_state;
 
 struct msm_gpu_config {
 	const char *ioname;
@@ -69,6 +70,8 @@ struct msm_gpu_funcs {
 	int (*debugfs_init)(struct msm_gpu *gpu, struct drm_minor *minor);
 #endif
 	int (*gpu_busy)(struct msm_gpu *gpu, uint64_t *value);
+	struct msm_gpu_state *(*gpu_state_get)(struct msm_gpu *gpu);
+	void (*gpu_state_put)(struct msm_gpu_state *state);
 };
 
 struct msm_gpu {
@@ -175,6 +178,23 @@ struct msm_gpu_submitqueue {
 	struct kref ref;
 };
 
+struct msm_gpu_state {
+	struct timeval time;
+
+	struct {
+		u64 iova;
+		u32 fence;
+		u32 seqno;
+		u32 rptr;
+		u32 wptr;
+	} ring[MSM_GPU_MAX_RINGS];
+
+	int nr_registers;
+	u32 *registers;
+
+	u32 rbbm_status;
+};
+
 static inline void gpu_write(struct msm_gpu *gpu, u32 reg, u32 data)
 {
 	msm_writel(data, gpu->mmio + (reg << 2));
Signature · 6 lines
2.18.0

_______________________________________________
Freedreno mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/freedreno
#9 lore raw

PATCH v8 7/13 drm/msm/gpu: Convert the GPU show function to use the GPU state

In reply to a message not in the archive

Convert the existing GPU show function to use the GPU state to dump the information rather than reading it directly from the hardware. This will require an additional step to capture the state before dumping it for the existing nodes but it will greatly facilitate reusing the same code for dumping a previously captured state from a GPU hang.

---
 drivers/gpu/drm/msm/adreno/a3xx_gpu.c   | 11 +--
 drivers/gpu/drm/msm/adreno/a4xx_gpu.c   | 12 +---
 drivers/gpu/drm/msm/adreno/a5xx_gpu.c   | 18 +----
 drivers/gpu/drm/msm/adreno/adreno_gpu.c | 30 ++++----
 drivers/gpu/drm/msm/adreno/adreno_gpu.h |  3 +-
 drivers/gpu/drm/msm/msm_debugfs.c       | 92 ++++++++++++++++++++++---
 drivers/gpu/drm/msm/msm_gpu.h           |  3 +-
 7 files changed, 104 insertions(+), 65 deletions(-)
drivers/gpu/drm/msm/adreno/a3xx_gpu.c +1 -10
index b707b5bca9ab..4cffec2b6adc 100644
@@ -411,15 +411,6 @@ static const unsigned int a3xx_registers[] = {
 	~0   /* sentinel */
 };
 
-#ifdef CONFIG_DEBUG_FS
-static void a3xx_show(struct msm_gpu *gpu, struct seq_file *m)
-{
-	seq_printf(m, "status:   %08x\n",
-			gpu_read(gpu, REG_A3XX_RBBM_STATUS));
-	adreno_show(gpu, m);
-}
-#endif
-
 /* would be nice to not have to duplicate the _show() stuff with printk(): */
 static void a3xx_dump(struct msm_gpu *gpu)
 {
@@ -464,7 +455,7 @@ static const struct adreno_gpu_funcs funcs = {
 		.irq = a3xx_irq,
 		.destroy = a3xx_destroy,
 #ifdef CONFIG_DEBUG_FS
-		.show = a3xx_show,
+		.show = adreno_show,
 #endif
 		.gpu_state_get = a3xx_gpu_state_get,
 		.gpu_state_put = adreno_gpu_state_put,
drivers/gpu/drm/msm/adreno/a4xx_gpu.c +1 -11
index 17e97ebc1077..95f08c22e8d7 100644
@@ -455,16 +455,6 @@ static const unsigned int a4xx_registers[] = {
 	~0 /* sentinel */
 };
 
-#ifdef CONFIG_DEBUG_FS
-static void a4xx_show(struct msm_gpu *gpu, struct seq_file *m)
-{
-	seq_printf(m, "status:   %08x\n",
-			gpu_read(gpu, REG_A4XX_RBBM_STATUS));
-	adreno_show(gpu, m);
-
-}
-#endif
-
 static struct msm_gpu_state *a4xx_gpu_state_get(struct msm_gpu *gpu)
 {
 	struct msm_gpu_state *state = adreno_gpu_state_get(gpu);
@@ -551,7 +541,7 @@ static const struct adreno_gpu_funcs funcs = {
 		.irq = a4xx_irq,
 		.destroy = a4xx_destroy,
 #ifdef CONFIG_DEBUG_FS
-		.show = a4xx_show,
+		.show = adreno_show,
 #endif
 		.gpu_state_get = a4xx_gpu_state_get,
 		.gpu_state_put = adreno_gpu_state_put,
drivers/gpu/drm/msm/adreno/a5xx_gpu.c +1 -17
index 9e85e4f7016d..5f1aab3c1cb1 100644
@@ -1215,22 +1215,6 @@ static struct msm_gpu_state *a5xx_gpu_state_get(struct msm_gpu *gpu)
 	return state;
 }
 
-#ifdef CONFIG_DEBUG_FS
-static void a5xx_show(struct msm_gpu *gpu, struct seq_file *m)
-{
-	seq_printf(m, "status:   %08x\n",
-			gpu_read(gpu, REG_A5XX_RBBM_STATUS));
-
-	/*
-	 * Temporarily disable hardware clock gating before going into
-	 * adreno_show to avoid issues while reading the registers
-	 */
-	a5xx_set_hwcg(gpu, false);
-	adreno_show(gpu, m);
-	a5xx_set_hwcg(gpu, true);
-}
-#endif
-
 static struct msm_ringbuffer *a5xx_active_ring(struct msm_gpu *gpu)
 {
 	struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
@@ -1260,7 +1244,7 @@ static const struct adreno_gpu_funcs funcs = {
 		.irq = a5xx_irq,
 		.destroy = a5xx_destroy,
 #ifdef CONFIG_DEBUG_FS
-		.show = a5xx_show,
+		.show = adreno_show,
 		.debugfs_init = a5xx_debugfs_init,
 #endif
 		.gpu_busy = a5xx_gpu_busy,
drivers/gpu/drm/msm/adreno/adreno_gpu.c +13 -17
index 4286a539f40a..42828253ede5 100644
@@ -424,38 +424,34 @@ void adreno_gpu_state_put(struct msm_gpu_state *state)
 }
 
 #ifdef CONFIG_DEBUG_FS
-void adreno_show(struct msm_gpu *gpu, struct seq_file *m)
+void adreno_show(struct msm_gpu *gpu, struct msm_gpu_state *state,
+		struct seq_file *m)
 {
 	struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
 	int i;
 
+	if (IS_ERR_OR_NULL(state))
+		return;
+
+	seq_printf(m, "status:   %08x\n", state->rbbm_status);
 	seq_printf(m, "revision: %d (%d.%d.%d.%d)\n",
 			adreno_gpu->info->revn, adreno_gpu->rev.core,
 			adreno_gpu->rev.major, adreno_gpu->rev.minor,
 			adreno_gpu->rev.patchid);
 
 	for (i = 0; i < gpu->nr_rings; i++) {
-		struct msm_ringbuffer *ring = gpu->rb[i];
-
 		seq_printf(m, "rb %d: fence:    %d/%d\n", i,
-			ring->memptrs->fence, ring->seqno);
+			state->ring[i].fence, state->ring[i].seqno);
 
-		seq_printf(m, "      rptr:     %d\n",
-			get_rptr(adreno_gpu, ring));
-		seq_printf(m, "rb wptr:  %d\n", get_wptr(ring));
+		seq_printf(m, "      rptr:     %d\n", state->ring[i].rptr);
+		seq_printf(m, "rb wptr:  %d\n", state->ring[i].wptr);
 	}
 
-	/* dump these out in a form that can be parsed by demsm: */
 	seq_printf(m, "IO:region %s 00000000 00020000\n", gpu->name);
-	for (i = 0; adreno_gpu->registers[i] != ~0; i += 2) {
-		uint32_t start = adreno_gpu->registers[i];
-		uint32_t end   = adreno_gpu->registers[i+1];
-		uint32_t addr;
-
-		for (addr = start; addr <= end; addr++) {
-			uint32_t val = gpu_read(gpu, addr);
-			seq_printf(m, "IO:R %08x %08x\n", addr<<2, val);
-		}
+	for (i = 0; i < state->nr_registers; i++) {
+		seq_printf(m, "IO:R %08x %08x\n",
+			state->registers[i * 2] << 2,
+			state->registers[(i * 2) + 1]);
 	}
 }
 #endif
drivers/gpu/drm/msm/adreno/adreno_gpu.h +2 -1
index 734e31a9631f..90b6b59252af 100644
@@ -216,7 +216,8 @@ void adreno_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit,
 void adreno_flush(struct msm_gpu *gpu, struct msm_ringbuffer *ring);
 bool adreno_idle(struct msm_gpu *gpu, struct msm_ringbuffer *ring);
 #ifdef CONFIG_DEBUG_FS
-void adreno_show(struct msm_gpu *gpu, struct seq_file *m);
+void adreno_show(struct msm_gpu *gpu, struct msm_gpu_state *state,
+		struct seq_file *m);
 #endif
 void adreno_dump_info(struct msm_gpu *gpu);
 void adreno_dump(struct msm_gpu *gpu);
drivers/gpu/drm/msm/msm_debugfs.c +84 -8

123 lines not on this page · raw message

drivers/gpu/drm/msm/msm_gpu.h +2 -1
index de71cc04ecf3..d204eca8518e 100644
@@ -65,7 +65,8 @@ struct msm_gpu_funcs {
 	void (*destroy)(struct msm_gpu *gpu);
 #ifdef CONFIG_DEBUG_FS
 	/* show GPU status in debugfs: */
-	void (*show)(struct msm_gpu *gpu, struct seq_file *m);
+	void (*show)(struct msm_gpu *gpu, struct msm_gpu_state *state,
+			struct seq_file *m);
 	/* for generation specific debugfs: */
 	int (*debugfs_init)(struct msm_gpu *gpu, struct drm_minor *minor);
 #endif
Signature · 6 lines
2.18.0

_______________________________________________
Freedreno mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/freedreno
#10 lore raw

PATCH v8 8/13 drm/msm/gpu: Rearrange the code that collects the task during a hang

In reply to a message not in the archive

Do a bit of cleanup to prepare for upcoming changes to pass the hanging task comm and cmdline to the crash dump function.

v2: Use GFP_ATOMIC while holding the rcu lock per Chris Wilson

---
 drivers/gpu/drm/msm/msm_gpu.c | 20 +++++++++++---------
 1 file changed, 11 insertions(+), 9 deletions(-)
drivers/gpu/drm/msm/msm_gpu.c +11 -9
index 1c09acfb4028..03ba8872cc99 100644
@@ -314,6 +314,7 @@ static void recover_worker(struct work_struct *work)
 	struct msm_drm_private *priv = dev->dev_private;
 	struct msm_gem_submit *submit;
 	struct msm_ringbuffer *cur_ring = gpu->funcs->active_ring(gpu);
+	char *comm = NULL, *cmd = NULL;
 	int i;
 
 	mutex_lock(&dev->struct_mutex);
@@ -327,7 +328,7 @@ static void recover_worker(struct work_struct *work)
 		rcu_read_lock();
 		task = pid_task(submit->pid, PIDTYPE_PID);
 		if (task) {
-			char *cmd;
+			comm = kstrdup(task->comm, GFP_ATOMIC);
 
 			/*
 			 * So slightly annoying, in other paths like
@@ -340,22 +341,23 @@ static void recover_worker(struct work_struct *work)
 			 * about the submit going away.
 			 */
 			mutex_unlock(&dev->struct_mutex);
-			cmd = kstrdup_quotable_cmdline(task, GFP_KERNEL);
+			cmd = kstrdup_quotable_cmdline(task, GFP_ATOMIC);
 			mutex_lock(&dev->struct_mutex);
+		}
+		rcu_read_unlock();
 
+		if (comm && cmd) {
 			dev_err(dev->dev, "%s: offending task: %s (%s)\n",
-				gpu->name, task->comm, cmd);
+				gpu->name, comm, cmd);
 
 			msm_rd_dump_submit(priv->hangrd, submit,
-				"offending task: %s (%s)", task->comm, cmd);
-
-			kfree(cmd);
-		} else {
+				"offending task: %s (%s)", comm, cmd);
+		} else
 			msm_rd_dump_submit(priv->hangrd, submit, NULL);
-		}
-		rcu_read_unlock();
 	}
 
+	kfree(cmd);
+	kfree(comm);
 
 	/*
 	 * Update all the rings with the latest and greatest fence.. this
Signature · 6 lines
2.18.0

_______________________________________________
Freedreno mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/freedreno
#11 lore raw

PATCH v8 9/13 drm/msm/gpu: Capture the GPU state on a GPU hang

In reply to a message not in the archive

Capture the GPU state on a GPU hang and store it for later playback via the devcoredump facility. Only one crash state is stored at a time on the assumption that the first hang is usually the most interesting. The existing crash state can be cleared after capturing it and then a new one will be captured on the next hang.

---
 drivers/gpu/drm/msm/Kconfig             |  1 +
 drivers/gpu/drm/msm/adreno/a3xx_gpu.c   |  2 +-
 drivers/gpu/drm/msm/adreno/a4xx_gpu.c   |  2 +-
 drivers/gpu/drm/msm/adreno/a5xx_gpu.c   |  4 +-
 drivers/gpu/drm/msm/adreno/adreno_gpu.c | 36 +++++++----
 drivers/gpu/drm/msm/adreno/adreno_gpu.h |  6 +-
 drivers/gpu/drm/msm/msm_debugfs.c       |  5 +-
 drivers/gpu/drm/msm/msm_gpu.c           | 83 ++++++++++++++++++++++++-
 drivers/gpu/drm/msm/msm_gpu.h           | 38 ++++++++++-
 9 files changed, 154 insertions(+), 23 deletions(-)
drivers/gpu/drm/msm/Kconfig +1 -0
index 38cbde971b48..843a9d40c05e 100644
@@ -12,6 +12,7 @@ config DRM_MSM
 	select SHMEM
 	select TMPFS
 	select QCOM_SCM
+	select WANT_DEV_COREDUMP
 	select SND_SOC_HDMI_CODEC if SND_SOC
 	select SYNC_FILE
 	select PM_OPP
drivers/gpu/drm/msm/adreno/a3xx_gpu.c +1 -1
index 4cffec2b6adc..fc502e412132 100644
@@ -454,7 +454,7 @@ static const struct adreno_gpu_funcs funcs = {
 		.active_ring = adreno_active_ring,
 		.irq = a3xx_irq,
 		.destroy = a3xx_destroy,
-#ifdef CONFIG_DEBUG_FS
+#if defined(CONFIG_DEBUG_FS) || defined(CONFIG_DEV_COREDUMP)
 		.show = adreno_show,
 #endif
 		.gpu_state_get = a3xx_gpu_state_get,
drivers/gpu/drm/msm/adreno/a4xx_gpu.c +1 -1
index 95f08c22e8d7..8129cf037db1 100644
@@ -540,7 +540,7 @@ static const struct adreno_gpu_funcs funcs = {
 		.active_ring = adreno_active_ring,
 		.irq = a4xx_irq,
 		.destroy = a4xx_destroy,
-#ifdef CONFIG_DEBUG_FS
+#if defined(CONFIG_DEBUG_FS) || defined(CONFIG_DEV_COREDUMP)
 		.show = adreno_show,
 #endif
 		.gpu_state_get = a4xx_gpu_state_get,
drivers/gpu/drm/msm/adreno/a5xx_gpu.c +3 -1
index 5f1aab3c1cb1..16074fa6bf1e 100644
@@ -1243,8 +1243,10 @@ static const struct adreno_gpu_funcs funcs = {
 		.active_ring = a5xx_active_ring,
 		.irq = a5xx_irq,
 		.destroy = a5xx_destroy,
-#ifdef CONFIG_DEBUG_FS
+#if defined(CONFIG_DEBUG_FS) || defined(CONFIG_DEV_COREDUMP)
 		.show = adreno_show,
+#endif
+#if defined(CONFIG_DEBUG_FS)
 		.debugfs_init = a5xx_debugfs_init,
 #endif
 		.gpu_busy = a5xx_gpu_busy,
drivers/gpu/drm/msm/adreno/adreno_gpu.c +24 -12
index 42828253ede5..c72e3afc43a8 100644
@@ -378,6 +378,8 @@ struct msm_gpu_state *adreno_gpu_state_get(struct msm_gpu *gpu)
 	if (!state)
 		return ERR_PTR(-ENOMEM);
 
+	kref_init(&state->ref);
+
 	do_gettimeofday(&state->time);
 
 	for (i = 0; i < gpu->nr_rings; i++) {
@@ -414,18 +416,28 @@ struct msm_gpu_state *adreno_gpu_state_get(struct msm_gpu *gpu)
 	return state;
 }
 
-void adreno_gpu_state_put(struct msm_gpu_state *state)
+static void adreno_gpu_state_destroy(struct kref *kref)
 {
-	if (IS_ERR_OR_NULL(state))
-		return;
+	struct msm_gpu_state *state = container_of(kref,
+		struct msm_gpu_state, ref);
 
+	kfree(state->comm);
+	kfree(state->cmd);
 	kfree(state->registers);
 	kfree(state);
 }
 
-#ifdef CONFIG_DEBUG_FS
+int adreno_gpu_state_put(struct msm_gpu_state *state)
+{
+	if (IS_ERR_OR_NULL(state))
+		return 1;
+
+	return kref_put(&state->ref, adreno_gpu_state_destroy);
+}
+
+#if defined(CONFIG_DEBUG_FS) || defined(CONFIG_DEV_COREDUMP)
 void adreno_show(struct msm_gpu *gpu, struct msm_gpu_state *state,
-		struct seq_file *m)
+		struct drm_printer *p)
 {
 	struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
 	int i;
@@ -433,23 +445,23 @@ void adreno_show(struct msm_gpu *gpu, struct msm_gpu_state *state,
 	if (IS_ERR_OR_NULL(state))
 		return;
 
-	seq_printf(m, "status:   %08x\n", state->rbbm_status);
-	seq_printf(m, "revision: %d (%d.%d.%d.%d)\n",
+	drm_printf(p, "status:   %08x\n", state->rbbm_status);
+	drm_printf(p, "revision: %d (%d.%d.%d.%d)\n",
 			adreno_gpu->info->revn, adreno_gpu->rev.core,
 			adreno_gpu->rev.major, adreno_gpu->rev.minor,
 			adreno_gpu->rev.patchid);
 
 	for (i = 0; i < gpu->nr_rings; i++) {
-		seq_printf(m, "rb %d: fence:    %d/%d\n", i,
+		drm_printf(p, "rb %d: fence:    %d/%d\n", i,
 			state->ring[i].fence, state->ring[i].seqno);
 
-		seq_printf(m, "      rptr:     %d\n", state->ring[i].rptr);
-		seq_printf(m, "rb wptr:  %d\n", state->ring[i].wptr);
+		drm_printf(p, "      rptr:     %d\n", state->ring[i].rptr);
+		drm_printf(p, "rb wptr:  %d\n", state->ring[i].wptr);
 	}
 
-	seq_printf(m, "IO:region %s 00000000 00020000\n", gpu->name);
+	drm_printf(p, "IO:region %s 00000000 00020000\n", gpu->name);
 	for (i = 0; i < state->nr_registers; i++) {
-		seq_printf(m, "IO:R %08x %08x\n",
+		drm_printf(p, "IO:R %08x %08x\n",
 			state->registers[i * 2] << 2,
 			state->registers[(i * 2) + 1]);
 	}
drivers/gpu/drm/msm/adreno/adreno_gpu.h +3 -3
index 90b6b59252af..4a868aaf1a70 100644
@@ -215,9 +215,9 @@ void adreno_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit,
 		struct msm_file_private *ctx);
 void adreno_flush(struct msm_gpu *gpu, struct msm_ringbuffer *ring);
 bool adreno_idle(struct msm_gpu *gpu, struct msm_ringbuffer *ring);
-#ifdef CONFIG_DEBUG_FS
+#if defined(CONFIG_DEBUG_FS) || defined(CONFIG_DEV_COREDUMP)
 void adreno_show(struct msm_gpu *gpu, struct msm_gpu_state *state,
-		struct seq_file *m);
+		struct drm_printer *p);
 #endif
 void adreno_dump_info(struct msm_gpu *gpu);
 void adreno_dump(struct msm_gpu *gpu);
@@ -231,7 +231,7 @@ void adreno_gpu_cleanup(struct adreno_gpu *gpu);
 
 
 struct msm_gpu_state *adreno_gpu_state_get(struct msm_gpu *gpu);
-void adreno_gpu_state_put(struct msm_gpu_state *state);
+int adreno_gpu_state_put(struct msm_gpu_state *state);
 
 /* ringbuffer helpers (the parts that are adreno specific) */
 
drivers/gpu/drm/msm/msm_debugfs.c +3 -2
index c3da12179888..f0da0d3c8a80 100644
@@ -29,6 +29,7 @@ struct msm_gpu_show_priv {
 
 static int msm_gpu_show(struct seq_file *m, void *arg)
 {
+	struct drm_printer p = drm_seq_file_printer(m);
 	struct msm_gpu_show_priv *show_priv = m->private;
 	struct msm_drm_private *priv = show_priv->dev->dev_private;
 	struct msm_gpu *gpu = priv->gpu;
@@ -38,8 +39,8 @@ static int msm_gpu_show(struct seq_file *m, void *arg)
 	if (ret)
 		return ret;
 
-	seq_printf(m, "%s Status:\n", gpu->name);
-	gpu->funcs->show(gpu, show_priv->state, m);
+	drm_printf(&p, "%s Status:\n", gpu->name);
+	gpu->funcs->show(gpu, show_priv->state, &p);
 
 	mutex_unlock(&show_priv->dev->struct_mutex);
 
drivers/gpu/drm/msm/msm_gpu.c +82 -1

104 lines not on this page · raw message

drivers/gpu/drm/msm/msm_gpu.h +36 -2
index d204eca8518e..b1cb44922441 100644
@@ -66,13 +66,13 @@ struct msm_gpu_funcs {
 #ifdef CONFIG_DEBUG_FS
 	/* show GPU status in debugfs: */
 	void (*show)(struct msm_gpu *gpu, struct msm_gpu_state *state,
-			struct seq_file *m);
+			struct drm_printer *p);
 	/* for generation specific debugfs: */
 	int (*debugfs_init)(struct msm_gpu *gpu, struct drm_minor *minor);
 #endif
 	int (*gpu_busy)(struct msm_gpu *gpu, uint64_t *value);
 	struct msm_gpu_state *(*gpu_state_get)(struct msm_gpu *gpu);
-	void (*gpu_state_put)(struct msm_gpu_state *state);
+	int (*gpu_state_put)(struct msm_gpu_state *state);
 };
 
 struct msm_gpu {
@@ -133,6 +133,8 @@ struct msm_gpu {
 		u64 busy_cycles;
 		ktime_t time;
 	} devfreq;
+
+	struct msm_gpu_state *crashstate;
 };
 
 /* It turns out that all targets use the same ringbuffer size */
@@ -180,6 +182,7 @@ struct msm_gpu_submitqueue {
 };
 
 struct msm_gpu_state {
+	struct kref ref;
 	struct timeval time;
 
 	struct {
@@ -194,6 +197,9 @@ struct msm_gpu_state {
 	u32 *registers;
 
 	u32 rbbm_status;
+
+	char *comm;
+	char *cmd;
 };
 
 static inline void gpu_write(struct msm_gpu *gpu, u32 reg, u32 data)
@@ -275,4 +281,32 @@ static inline void msm_submitqueue_put(struct msm_gpu_submitqueue *queue)
 		kref_put(&queue->ref, msm_submitqueue_destroy);
 }
 
+static inline struct msm_gpu_state *msm_gpu_crashstate_get(struct msm_gpu *gpu)
+{
+	struct msm_gpu_state *state = NULL;
+
+	mutex_lock(&gpu->dev->struct_mutex);
+
+	if (gpu->crashstate) {
+		kref_get(&gpu->crashstate->ref);
+		state = gpu->crashstate;
+	}
+
+	mutex_unlock(&gpu->dev->struct_mutex);
+
+	return state;
+}
+
+static inline void msm_gpu_crashstate_put(struct msm_gpu *gpu)
+{
+	mutex_lock(&gpu->dev->struct_mutex);
+
+	if (gpu->crashstate) {
+		if (gpu->funcs->gpu_state_put(gpu->crashstate))
+			gpu->crashstate = NULL;
+	}
+
+	mutex_unlock(&gpu->dev->struct_mutex);
+}
+
 #endif /* __MSM_GPU_H__ */
Signature · 6 lines
2.18.0

_______________________________________________
Freedreno mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/freedreno
#12 lore raw

PATCH v8 10/13 drm/msm/adreno: Convert the show/crash file format

In reply to a message not in the archive

Convert the format of the 'show' debugfs file and the crash dump to a format resembling YAML. This should be easier to parse and be more flexible for future changes and expansions.

v2: Use a standard .rst for the msm crashdump documentation

---
 Documentation/gpu/msm-crash-dump.rst    | 71 +++++++++++++++++++++++++
 drivers/gpu/drm/msm/adreno/adreno_gpu.c | 21 +++++---
 2 files changed, 84 insertions(+), 8 deletions(-)
 create mode 100644 Documentation/gpu/msm-crash-dump.rst
Documentation/gpu/msm-crash-dump.rst new file +71 -0
new file mode 100644
index 000000000000..75ab1d541c03
@@ -0,0 +1,71 @@
+=====================
+MSM Crash Dump Format
+=====================
+
+Following a GPU hang the MSM driver outputs debugging information via
+/sys/kernel/dri/X/show or via devcoredump (/sys/class/devcoredump/dcdX/data).
+This document describes how the output is formatted.
+
+Each entry is in the form key: value. Sections headers will not have a value
+and all the contents of a section will be indented two spaces from the header.
+Each section might have multiple array entries the start of which is designated
+by a (-).
+
+Mappings
+--------
+
+kernel
+	The kernel version that generated the dump (UTS_RELEASE).
+
+module
+	The module that generated the crashdump.
+
+time
+	The kernel time at crash formated as seconds.microseconds.
+
+comm
+	Comm string for the binary that generated the fault.
+
+cmdline
+	Command line for the binary that generated the fault.
+
+revision
+	ID of the GPU that generated the crash formatted as
+	core.major.minor.patchlevel separated by dots.
+
+rbbm-status
+	The current value of RBBM_STATUS which shows what top level GPU
+	components are in use at the time of crash.
+
+ringbuffer
+	Section containing the contents of each ringbuffer. Each ringbuffer is
+	identified with an id number.
+
+	id
+		Ringbuffer ID (0 based index).  Each ringbuffer in the section
+		will have its own unique id.
+	iova
+		GPU address of the ringbuffer.
+
+	last-fence
+		The last fence that was issued on the ringbuffer
+
+	retired-fence
+		The last fence retired on the ringbuffer.
+
+	rptr
+		The current read pointer (rptr) for the ringbuffer.
+
+	wptr
+		The current write pointer (wptr) for the ringbuffer.
+
+registers
+	Set of registers values. Each entry is on its own line enclosed
+	by brackets { }.
+
+	offset
+		Byte offset of the register from the start of the
+		GPU memory region.
+
+	value
+		Hexadecimal value of the register.
drivers/gpu/drm/msm/adreno/adreno_gpu.c +13 -8
index c72e3afc43a8..c7a998d9dc85 100644
@@ -445,23 +445,28 @@ void adreno_show(struct msm_gpu *gpu, struct msm_gpu_state *state,
 	if (IS_ERR_OR_NULL(state))
 		return;
 
-	drm_printf(p, "status:   %08x\n", state->rbbm_status);
 	drm_printf(p, "revision: %d (%d.%d.%d.%d)\n",
 			adreno_gpu->info->revn, adreno_gpu->rev.core,
 			adreno_gpu->rev.major, adreno_gpu->rev.minor,
 			adreno_gpu->rev.patchid);
 
-	for (i = 0; i < gpu->nr_rings; i++) {
-		drm_printf(p, "rb %d: fence:    %d/%d\n", i,
-			state->ring[i].fence, state->ring[i].seqno);
+	drm_printf(p, "rbbm-status: 0x%08x\n", state->rbbm_status);
+
+	drm_puts(p, "ringbuffer:\n");
 
-		drm_printf(p, "      rptr:     %d\n", state->ring[i].rptr);
-		drm_printf(p, "rb wptr:  %d\n", state->ring[i].wptr);
+	for (i = 0; i < gpu->nr_rings; i++) {
+		drm_printf(p, "  - id: %d\n", i);
+		drm_printf(p, "    iova: 0x%016llx\n", state->ring[i].iova);
+		drm_printf(p, "    last-fence: %d\n", state->ring[i].seqno);
+		drm_printf(p, "    retired-fence: %d\n", state->ring[i].fence);
+		drm_printf(p, "    rptr: %d\n", state->ring[i].rptr);
+		drm_printf(p, "    wptr: %d\n", state->ring[i].wptr);
 	}
 
-	drm_printf(p, "IO:region %s 00000000 00020000\n", gpu->name);
+	drm_puts(p, "registers:\n");
+
 	for (i = 0; i < state->nr_registers; i++) {
-		drm_printf(p, "IO:R %08x %08x\n",
+		drm_printf(p, "  - { offset: 0x%04x, value: 0x%08x }\n",
 			state->registers[i * 2] << 2,
 			state->registers[(i * 2) + 1]);
 	}
Signature · 6 lines
2.18.0

_______________________________________________
Freedreno mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/freedreno
#13 lore raw

PATCH v8 11/13 drm/msm/adreno: Add ringbuffer data to the GPU state

In reply to a message not in the archive

Add the contents of each ringbuffer to the GPU state and dump the data in the crash file encoded with ascii85. To save space only the used portions of the ringbuffer are dumped.

---
 Documentation/gpu/msm-crash-dump.rst    |  7 +++++
 drivers/gpu/drm/msm/adreno/adreno_gpu.c | 39 +++++++++++++++++++++++++
 drivers/gpu/drm/msm/msm_gpu.h           |  2 ++
 3 files changed, 48 insertions(+)
Documentation/gpu/msm-crash-dump.rst +7 -0
index 75ab1d541c03..35e87004e006 100644
@@ -59,6 +59,13 @@ ringbuffer
 	wptr
 		The current write pointer (wptr) for the ringbuffer.
 
+	size
+		Maximum size of the ringbuffer programmed in the hardware.
+
+	data
+		The contents of the ring encoded as ascii85.  Only the used
+		portions of the ring will be printed.
+
 registers
 	Set of registers values. Each entry is on its own line enclosed
 	by brackets { }.
drivers/gpu/drm/msm/adreno/adreno_gpu.c +39 -0
index c7a998d9dc85..808d4fc9c4a1 100644
@@ -17,6 +17,7 @@
  * this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <linux/ascii85.h>
 #include <linux/pm_opp.h>
 #include "adreno_gpu.h"
 #include "msm_gem.h"
@@ -383,11 +384,29 @@ struct msm_gpu_state *adreno_gpu_state_get(struct msm_gpu *gpu)
 	do_gettimeofday(&state->time);
 
 	for (i = 0; i < gpu->nr_rings; i++) {
+		int size = 0, j;
+
 		state->ring[i].fence = gpu->rb[i]->memptrs->fence;
 		state->ring[i].iova = gpu->rb[i]->iova;
 		state->ring[i].seqno = gpu->rb[i]->seqno;
 		state->ring[i].rptr = get_rptr(adreno_gpu, gpu->rb[i]);
 		state->ring[i].wptr = get_wptr(gpu->rb[i]);
+
+		/* Copy at least 'wptr' dwords of the data */
+		size = state->ring[i].wptr;
+
+		/* After wptr find the last non zero dword to save space */
+		for (j = state->ring[i].wptr; j < MSM_GPU_RINGBUFFER_SZ >> 2; j++)
+			if (gpu->rb[i]->start[j])
+				size = j + 1;
+
+		if (size) {
+			state->ring[i].data = kmalloc(size << 2, GFP_KERNEL);
+			if (state->ring[i].data) {
+				memcpy(state->ring[i].data, gpu->rb[i]->start, size << 2);
+				state->ring[i].data_size = size << 2;
+			}
+		}
 	}
 
 	/* Count the number of registers */
@@ -418,9 +437,13 @@ struct msm_gpu_state *adreno_gpu_state_get(struct msm_gpu *gpu)
 
 static void adreno_gpu_state_destroy(struct kref *kref)
 {
+	int i;
 	struct msm_gpu_state *state = container_of(kref,
 		struct msm_gpu_state, ref);
 
+	for (i = 0; i < ARRAY_SIZE(state->ring); i++)
+		kfree(state->ring[i].data);
+
 	kfree(state->comm);
 	kfree(state->cmd);
 	kfree(state->registers);
@@ -461,6 +484,22 @@ void adreno_show(struct msm_gpu *gpu, struct msm_gpu_state *state,
 		drm_printf(p, "    retired-fence: %d\n", state->ring[i].fence);
 		drm_printf(p, "    rptr: %d\n", state->ring[i].rptr);
 		drm_printf(p, "    wptr: %d\n", state->ring[i].wptr);
+		drm_printf(p, "    size: %d\n", MSM_GPU_RINGBUFFER_SZ);
+
+		if (state->ring[i].data && state->ring[i].data_size) {
+			u32 *ptr = (u32 *) state->ring[i].data;
+			char out[ASCII85_BUFSZ];
+			long len = ascii85_encode_len(state->ring[i].data_size);
+			int j;
+
+			drm_printf(p, "    data: !!ascii85 |\n");
+			drm_printf(p, "     ");
+
+			for (j = 0; j < len; j++)
+				drm_printf(p, ascii85_encode(ptr[j], out));
+
+			drm_printf(p, "\n");
+		}
 	}
 
 	drm_puts(p, "registers:\n");
drivers/gpu/drm/msm/msm_gpu.h +2 -0
index b1cb44922441..878090b57e90 100644
@@ -191,6 +191,8 @@ struct msm_gpu_state {
 		u32 seqno;
 		u32 rptr;
 		u32 wptr;
+		void *data;
+		int data_size;
 	} ring[MSM_GPU_MAX_RINGS];
 
 	int nr_registers;
Signature · 6 lines
2.18.0

_______________________________________________
Freedreno mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/freedreno
#14 lore raw

PATCH v8 12/13 drm/msm/adreno: Add a5xx specific registers for the GPU state

In reply to a message not in the archive

HLSQ, SP and TP registers are only accessible from a special aperture and to make matters worse the aperture is blocked from the CPU on targets that can support secure rendering. Luckily the GPU hardware has its own purpose built register dumper that can access the registers from the aperture. Add a5xx specific code to program the crashdumper and retrieve the wayward registers and dump them for the crash state.

Also, remove a block of registers the regular CPU accessible list that aren't useful for debug which helps reduce the size of the crash state file by a goodly amount.

---
 Documentation/gpu/msm-crash-dump.rst    |   4 +
 drivers/gpu/drm/msm/adreno/a3xx_gpu.c   |   8 +-
 drivers/gpu/drm/msm/adreno/a4xx_gpu.c   |   8 +-
 drivers/gpu/drm/msm/adreno/a5xx_gpu.c   | 236 ++++++++++++++++++++++--
 drivers/gpu/drm/msm/adreno/adreno_gpu.c |  23 +--
 drivers/gpu/drm/msm/adreno/adreno_gpu.h |   4 +-
 6 files changed, 252 insertions(+), 31 deletions(-)
Documentation/gpu/msm-crash-dump.rst +4 -0
index 35e87004e006..7943f43f70d6 100644
@@ -76,3 +76,7 @@ registers
 
 	value
 		Hexadecimal value of the register.
+
+registers-hlsq
+		(5xx only) Register values from the HLSQ aperture.
+		Same format as the register section.
drivers/gpu/drm/msm/adreno/a3xx_gpu.c +5 -3
index fc502e412132..669c2d4b070d 100644
@@ -421,10 +421,12 @@ static void a3xx_dump(struct msm_gpu *gpu)
 
 static struct msm_gpu_state *a3xx_gpu_state_get(struct msm_gpu *gpu)
 {
-	struct msm_gpu_state *state = adreno_gpu_state_get(gpu);
+	struct msm_gpu_state *state = kzalloc(sizeof(*state), GFP_KERNEL);
 
-	if (IS_ERR(state))
-		return state;
+	if (!state)
+		return ERR_PTR(-ENOMEM);
+
+	adreno_gpu_state_get(gpu, state);
 
 	state->rbbm_status = gpu_read(gpu, REG_A3XX_RBBM_STATUS);
 
drivers/gpu/drm/msm/adreno/a4xx_gpu.c +5 -3
index 8129cf037db1..7c4e6dc1ed59 100644
@@ -457,10 +457,12 @@ static const unsigned int a4xx_registers[] = {
 
 static struct msm_gpu_state *a4xx_gpu_state_get(struct msm_gpu *gpu)
 {
-	struct msm_gpu_state *state = adreno_gpu_state_get(gpu);
+	struct msm_gpu_state *state = kzalloc(sizeof(*state), GFP_KERNEL);
 
-	if (IS_ERR(state))
-		return state;
+	if (!state)
+		return ERR_PTR(-ENOMEM);
+
+	adreno_gpu_state_get(gpu, state);
 
 	state->rbbm_status = gpu_read(gpu, REG_A4XX_RBBM_STATUS);
 
drivers/gpu/drm/msm/adreno/a5xx_gpu.c +223 -13

276 lines not on this page · raw message

drivers/gpu/drm/msm/adreno/adreno_gpu.c +12 -11
index 808d4fc9c4a1..cd418dab7a62 100644
@@ -369,16 +369,11 @@ bool adreno_idle(struct msm_gpu *gpu, struct msm_ringbuffer *ring)
 	return false;
 }
 
-struct msm_gpu_state *adreno_gpu_state_get(struct msm_gpu *gpu)
+int adreno_gpu_state_get(struct msm_gpu *gpu, struct msm_gpu_state *state)
 {
 	struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
-	struct msm_gpu_state *state;
 	int i, count = 0;
 
-	state = kzalloc(sizeof(*state), GFP_KERNEL);
-	if (!state)
-		return ERR_PTR(-ENOMEM);
-
 	kref_init(&state->ref);
 
 	do_gettimeofday(&state->time);
@@ -432,14 +427,12 @@ struct msm_gpu_state *adreno_gpu_state_get(struct msm_gpu *gpu)
 		state->nr_registers = count;
 	}
 
-	return state;
+	return 0;
 }
 
-static void adreno_gpu_state_destroy(struct kref *kref)
+void adreno_gpu_state_destroy(struct msm_gpu_state *state)
 {
 	int i;
-	struct msm_gpu_state *state = container_of(kref,
-		struct msm_gpu_state, ref);
 
 	for (i = 0; i < ARRAY_SIZE(state->ring); i++)
 		kfree(state->ring[i].data);
@@ -447,6 +440,14 @@ static void adreno_gpu_state_destroy(struct kref *kref)
 	kfree(state->comm);
 	kfree(state->cmd);
 	kfree(state->registers);
+}
+
+static void adreno_gpu_state_kref_destroy(struct kref *kref)
+{
+	struct msm_gpu_state *state = container_of(kref,
+		struct msm_gpu_state, ref);
+
+	adreno_gpu_state_destroy(state);
 	kfree(state);
 }
 
@@ -455,7 +456,7 @@ int adreno_gpu_state_put(struct msm_gpu_state *state)
 	if (IS_ERR_OR_NULL(state))
 		return 1;
 
-	return kref_put(&state->ref, adreno_gpu_state_destroy);
+	return kref_put(&state->ref, adreno_gpu_state_kref_destroy);
 }
 
 #if defined(CONFIG_DEBUG_FS) || defined(CONFIG_DEV_COREDUMP)
drivers/gpu/drm/msm/adreno/adreno_gpu.h +3 -1
index 4a868aaf1a70..4406776597fd 100644
@@ -230,7 +230,9 @@ int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev,
 void adreno_gpu_cleanup(struct adreno_gpu *gpu);
 
 
-struct msm_gpu_state *adreno_gpu_state_get(struct msm_gpu *gpu);
+void adreno_gpu_state_destroy(struct msm_gpu_state *state);
+
+int adreno_gpu_state_get(struct msm_gpu *gpu, struct msm_gpu_state *state);
 int adreno_gpu_state_put(struct msm_gpu_state *state);
 
 /* ringbuffer helpers (the parts that are adreno specific) */
Signature · 6 lines
2.18.0

_______________________________________________
Freedreno mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/freedreno
#15 lore raw

PATCH v8 13/13 drm/msm/gpu: Add the buffer objects from the submit to the crash dump

In reply to a message not in the archive

For hangs, dump copy out the contents of the buffer objects attached to the guilty submission and print them in the crash dump report.

---
 Documentation/gpu/msm-crash-dump.rst    | 14 ++++++
 drivers/gpu/drm/msm/adreno/adreno_gpu.c | 58 ++++++++++++++++++++-----
 drivers/gpu/drm/msm/msm_gpu.c           | 48 ++++++++++++++++++--
 drivers/gpu/drm/msm/msm_gpu.h           |  9 ++++
 4 files changed, 116 insertions(+), 13 deletions(-)
Documentation/gpu/msm-crash-dump.rst +14 -0
index 7943f43f70d6..757cd257e0d8 100644
@@ -66,6 +66,20 @@ ringbuffer
 		The contents of the ring encoded as ascii85.  Only the used
 		portions of the ring will be printed.
 
+bo
+	List of buffers from the hanging submission if available.
+	Each buffer object will have a uinque iova.
+
+	iova
+		GPU address of the buffer object.
+
+	size
+		Allocated size of the buffer object.
+
+	data
+		The contents of the buffer object encoded with ascii85.  Only
+		Trailing zeros at the end of the buffer will be skipped.
+
 registers
 	Set of registers values. Each entry is on its own line enclosed
 	by brackets { }.
drivers/gpu/drm/msm/adreno/adreno_gpu.c +48 -10

79 lines not on this page · raw message

drivers/gpu/drm/msm/msm_gpu.c +45 -3

66 lines not on this page · raw message

drivers/gpu/drm/msm/msm_gpu.h +9 -0
index 878090b57e90..57380ef8d1f7 100644
@@ -181,6 +181,12 @@ struct msm_gpu_submitqueue {
 	struct kref ref;
 };
 
+struct msm_gpu_state_bo {
+	u64 iova;
+	size_t size;
+	void *data;
+};
+
 struct msm_gpu_state {
 	struct kref ref;
 	struct timeval time;
@@ -202,6 +208,9 @@ struct msm_gpu_state {
 
 	char *comm;
 	char *cmd;
+
+	int nr_bos;
+	struct msm_gpu_state_bo *bos;
 };
 
 static inline void gpu_write(struct msm_gpu *gpu, u32 reg, u32 data)
Signature · 6 lines
2.18.0

_______________________________________________
Freedreno mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/freedreno