Signed-off-by: Sharat Masetty <smasetty@xxxxxxxxxxxxxx>
---
drivers/gpu/drm/msm/adreno/adreno_gpu.c | 35 +++----------
drivers/gpu/drm/msm/msm_gpu.c | 93 +++++++++++++++++++++++++++++++--
drivers/gpu/drm/msm/msm_gpu.h | 1 +
3 files changed, 98 insertions(+), 31 deletions(-)
diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c
index 141062f..7441571 100644
--- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c
@@ -406,7 +406,7 @@ int adreno_gpu_state_get(struct msm_gpu *gpu, struct msm_gpu_state *state)
size = j + 1;
if (size) {
- state->ring[i].data = kmalloc(size << 2, GFP_KERNEL);
+ state->ring[i].data = kvmalloc(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;
This is a good fix, but unrelated to this change as detailed above. Should be
separate.
@@ -445,7 +445,7 @@ void adreno_gpu_state_destroy(struct msm_gpu_state *state)
int i;
for (i = 0; i < ARRAY_SIZE(state->ring); i++)
- kfree(state->ring[i].data);
+ kvfree(state->ring[i].data);
As above.
for (i = 0; state->bos && i < state->nr_bos; i++)
kvfree(state->bos[i].data);
@@ -475,34 +475,15 @@ int adreno_gpu_state_put(struct msm_gpu_state *state)
#if defined(CONFIG_DEBUG_FS) || defined(CONFIG_DEV_COREDUMP)
-static void adreno_show_object(struct drm_printer *p, u32 *ptr, int len)
+static void adreno_show_object(struct drm_printer *p, void *ptr)
{
- char out[ASCII85_BUFSZ];
- long l, datalen, i;
-
- if (!ptr || !len)
- return;
-
- /*
- * Only dump the non-zero part of the buffer - rarely will any data
- * completely fill the entire allocated size of the buffer
- */
- for (datalen = 0, i = 0; i < len >> 2; i++) {
- if (ptr[i])
- datalen = (i << 2) + 1;
- }
-
- /* Skip printing the object if it is empty */
- if (datalen == 0)
+ if (!ptr)
return;
- l = ascii85_encode_len(datalen);
-
drm_puts(p, " data: !!ascii85 |\n");
drm_puts(p, " ");
- for (i = 0; i < l; i++)
- drm_puts(p, ascii85_encode(ptr[i], out));
+ drm_puts(p, ptr);
drm_puts(p, "\n");
}
@@ -534,8 +515,7 @@ void adreno_show(struct msm_gpu *gpu, struct msm_gpu_state *state,
drm_printf(p, " wptr: %d\n", state->ring[i].wptr);
drm_printf(p, " size: %d\n", MSM_GPU_RINGBUFFER_SZ);
- adreno_show_object(p, state->ring[i].data,
- state->ring[i].data_size);
+ adreno_show_object(p, state->ring[i].data);
}
if (state->bos) {
@@ -546,8 +526,7 @@ void adreno_show(struct msm_gpu *gpu, struct msm_gpu_state *state,
state->bos[i].iova);
drm_printf(p, " size: %zd\n", state->bos[i].size);
- adreno_show_object(p, state->bos[i].data,
- state->bos[i].size);
+ adreno_show_object(p, state->bos[i].data);
}
}
diff --git a/drivers/gpu/drm/msm/msm_gpu.c b/drivers/gpu/drm/msm/msm_gpu.c
index ff95848..d5533f1 100644
--- a/drivers/gpu/drm/msm/msm_gpu.c
+++ b/drivers/gpu/drm/msm/msm_gpu.c
@@ -21,6 +21,7 @@
#include "msm_fence.h"
#include <generated/utsrelease.h>
+#include <linux/ascii85.h>
#include <linux/string_helpers.h>
#include <linux/pm_opp.h>
#include <linux/devfreq.h>
@@ -375,9 +376,7 @@ static void msm_gpu_crashstate_capture(struct msm_gpu *gpu,
/* Set the active crash state to be dumped on failure */
gpu->crashstate = state;
- /* FIXME: Release the crashstate if this errors out? */
- dev_coredumpm(gpu->dev->dev, THIS_MODULE, gpu, 0, GFP_KERNEL,
- msm_gpu_devcoredump_read, msm_gpu_devcoredump_free);
+ schedule_work(&gpu->crashstate_work);
I'm super hesitant to ever add anything having to do with scheduling because we
already abuse it so badly but this seems like it might be okay since it isn't
high priority and not related to timing. I would be curious what others think.
}
#else
static void msm_gpu_crashstate_capture(struct msm_gpu *gpu,
@@ -420,6 +419,93 @@ static void update_fences(struct msm_gpu *gpu, struct msm_ringbuffer *ring,
static void retire_submits(struct msm_gpu *gpu);
+static char *msm_gpu_ascii85_encode_buf(u32 *src, size_t len)
+{
+ void *buf;
+ size_t buf_itr = 0, buffer_size;
+ char out[ASCII85_BUFSZ];
+ long l;
+ int i;
+
+ if (!src || !len)
+ return NULL;
I don't think src() will ever legitimately be NULL so you can remove the check.
+ l = ascii85_encode_len(len);
+
+ /*
+ * Ascii85 outputs either a 5 byte string or a 1 byte string. So we
+ * account for the worst case of 5 bytes per dword plus the 1 for '\0'
+ */
+ buffer_size = (l * 5) + 1;
buffer_size is't needed - you can do the math inline in the kvmalloc() function.
+
+ buf = kvmalloc(buffer_size, GFP_KERNEL);
+ if (!buf)
+ return NULL;
+
+ for (i = 0; i < l; i++)
+ buf_itr += snprintf(buf + buf_itr, buffer_size - buf_itr, "%s",
This specific algorithm should use scnprintf() here but on a broader note
sprintf() and friends is a bad choice if performance is important because it
jumps through more hoops to eventually do what amounts to a mempcy (this is the
reason why seq_printf() and seq_puts() are different things and checkpatch
insists that you use seq_puts() for constant strings).
Inf you are planning on outputting into a buffer you should just make a custom
ascii85 function to output directly and avoid copying in the first place.
If you wanted to get fancy you could add it to include/linux/ascii85.h -
ascii85_encode_to_buf() or some such.
+ ascii85_encode(src[i], out));
+
+ return buf;
+}
+
+/*
+ * Convert the binary data in the gpu crash state capture(like bos and
+ * ringbuffer data) to ascii85 encoded strings. Note that the encoded
+ * string is NULL terminated.
This is technically not true because you are specifically accounting for a NULL
terminator in the function above.
+ */
+static void crashstate_worker(struct work_struct *work)
+{
+ struct msm_gpu_state *state;
+ struct msm_gpu *gpu = container_of(work, struct msm_gpu,
+ crashstate_work);
+ int i;
+
+ state = msm_gpu_crashstate_get(gpu);
+ if (!state)
+ return;
+
+ for (i = 0; i < MSM_GPU_MAX_RINGS; i++) {
+ void *buf = NULL;
You don't needed to initialize buf.
+
+ if (!state->ring[i].data)
+ continue;
+
A comment here describing the variable switch-a-roo you are doing would be
helpful.
+ buf = state->ring[i].data;
+
+ state->ring[i].data = msm_gpu_ascii85_encode_buf(buf,
+ state->ring[i].data_size);
+ kvfree(buf);
+ }
+
+ for (i = 0; i < state->nr_bos; i++) {
+ u32 *buf = NULL;
buf does not need to be initialized.
+ long datalen, j;
+
+ if (!state->bos[i].data)
+ continue;
+
+ buf = state->bos[i].data;
+
+ /*
+ * Only dump the non-zero part of the buffer - rarely will
+ * any data completely fill the entire allocated size of the
+ * buffer
+ */
+ for (datalen = 0, j = 0; j < state->bos[i].size >> 2; j++)
+ if (buf[j])
+ datalen = ((j + 1) << 2);
+
+ state->bos[i].data = msm_gpu_ascii85_encode_buf(buf, datalen);
+ kvfree(buf);
+ }
+
+ msm_gpu_crashstate_put(gpu);
+
+ dev_coredumpm(gpu->dev->dev, THIS_MODULE, gpu, 0, GFP_KERNEL,
+ msm_gpu_devcoredump_read, msm_gpu_devcoredump_free);
+}
+
static void recover_worker(struct work_struct *work)
{
struct msm_gpu *gpu = container_of(work, struct msm_gpu, recover_work);
@@ -856,6 +942,7 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev,
INIT_LIST_HEAD(&gpu->active_list);
INIT_WORK(&gpu->retire_work, retire_worker);
INIT_WORK(&gpu->recover_work, recover_worker);
+ INIT_WORK(&gpu->crashstate_work, crashstate_worker);
timer_setup(&gpu->hangcheck_timer, hangcheck_handler, 0);
diff --git a/drivers/gpu/drm/msm/msm_gpu.h b/drivers/gpu/drm/msm/msm_gpu.h
index f82bac0..8825069 100644
--- a/drivers/gpu/drm/msm/msm_gpu.h
+++ b/drivers/gpu/drm/msm/msm_gpu.h
@@ -137,6 +137,7 @@ struct msm_gpu {
} devfreq;
struct msm_gpu_state *crashstate;
+ struct work_struct crashstate_work;
};
/* It turns out that all targets use the same ringbuffer size */
--
1.9.1