summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorSubramaniam Chanderashekarapuram <subramaniam.ca@ti.com>2012-06-25 17:43:49 -0500
committerAndy Green <andy.green@linaro.org>2012-09-07 13:05:52 +0800
commit4560dd7b8f6028c98f33920ed5f14fa96d972dcf (patch)
treed91c6e15b2c64f24ab2e9585ec2079cd742be2ad /drivers
parente59d076a77e700513a6f26a12ff41071e202c227 (diff)
remoteproc: implement last trace for remoteproc
The last trace is a way of preserving the remoteproc traces past remoteproc recovery. This is achieved by creating a new last_trace debugfs entry during a crash for each trace entry, and copying the trace buffer contents into the corresponding last trace entry. This copy can then be read out using a debugfs entry. The trace entries themselves are cleaned up after the copy and are recreated during recovery. Something like, cat <debugfs root>/remoteproc/remoteprocX/traceX_last should give the traces that were printed out just before the recovery happened. Change-Id: I2d6879421022a895dc34dfcc5c003ff33779128c Signed-off-by: Subramaniam Chanderashekarapuram <subramaniam.ca@ti.com> Signed-off-by: Suman Anna <s-anna@ti.com> Signed-off-by: Fernando Guzman Lugo <fernando.lugo@ti.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/remoteproc/remoteproc_core.c138
1 files changed, 137 insertions, 1 deletions
diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c
index 4e4c4807f5e7..2fc457bebe90 100644
--- a/drivers/remoteproc/remoteproc_core.c
+++ b/drivers/remoteproc/remoteproc_core.c
@@ -43,6 +43,7 @@
#include <linux/pm_runtime.h>
#include <linux/kthread.h>
#include <linux/delay.h>
+#include <linux/vmalloc.h>
#include "remoteproc_internal.h"
@@ -710,6 +711,103 @@ free_rvdev:
}
/**
+ * rproc_handle_last_trace() - setup a buffer to capture the trace snapshot
+ * before recovery
+ * @rproc: the remote processor
+ * @trace: the trace resource descriptor
+ * @count: the index of the trace under process
+ *
+ * The last trace is allocated and the contents of the trace buffer are
+ * copied during a recovery cleanup. Once, the contents get copied, the
+ * trace buffers are cleaned up for re-use.
+ *
+ * It might also happen that the remoteproc binary changes between the
+ * time that it was loaded and the time that it crashed. In this case,
+ * the trace descriptors might have changed too. The last traces are
+ * re-built as required in this case.
+ *
+ * Returns 0 on success, or an appropriate error code otherwise
+ */
+static int rproc_handle_last_trace(struct rproc *rproc,
+ struct rproc_mem_entry *trace, int count)
+{
+ struct rproc_mem_entry *trace_last, *tmp_trace;
+ struct device *dev = &rproc->dev;
+ char name[15];
+ int i = 0;
+ bool new_trace = false;
+
+ if (!rproc || !trace)
+ return -EINVAL;
+
+ /* we need a new trace in this case */
+ if (count > rproc->num_last_traces) {
+ new_trace = true;
+ /*
+ * make sure snprintf always null terminates, even if truncating
+ */
+ snprintf(name, sizeof(name), "trace%d_last", (count - 1));
+ trace_last = kzalloc(sizeof *trace_last, GFP_KERNEL);
+ if (!trace_last) {
+ dev_err(dev, "kzalloc failed for trace%d_last\n",
+ count);
+ return -ENOMEM;
+ }
+ } else {
+ /* try to reuse buffers here */
+ list_for_each_entry_safe(trace_last, tmp_trace,
+ &rproc->last_traces, node) {
+ if (++i == count)
+ break;
+ }
+
+ /* if we can reuse the trace, copy buffer and exit */
+ if (trace_last->len == trace->len)
+ goto copy_and_exit;
+
+ /* can reuse the trace struct but not the buffer */
+ vfree(trace_last->va);
+ trace_last->va = NULL;
+ trace_last->len = 0;
+ }
+
+ trace_last->len = trace->len;
+ trace_last->va = vmalloc(sizeof(u32) * trace_last->len);
+ if (!trace_last->va) {
+ dev_err(dev, "vmalloc failed for trace%d_last\n", count);
+ if (!new_trace) {
+ list_del(&trace_last->node);
+ rproc->num_last_traces--;
+ }
+ kfree(trace_last);
+ return -ENOMEM;
+ }
+
+ /* create the debugfs entry */
+ if (new_trace) {
+ trace_last->priv = rproc_create_trace_file(name, rproc,
+ trace_last);
+ if (!trace_last->priv) {
+ dev_err(dev, "trace%d_last create debugfs failed\n",
+ count);
+ vfree(trace_last->va);
+ kfree(trace_last);
+ return -EINVAL;
+ }
+
+ /* add it to the trace list */
+ list_add_tail(&trace_last->node, &rproc->last_traces);
+ rproc->num_last_traces++;
+ }
+
+copy_and_exit:
+ /* copy the trace to last trace */
+ memcpy(trace_last->va, trace->va, trace->len);
+
+ return 0;
+}
+
+/**
* rproc_handle_trace() - handle a shared trace buffer resource
* @rproc: the remote processor
* @rsc: the trace resource descriptor
@@ -1253,6 +1351,18 @@ rproc_find_fw_version_section(struct rproc *rproc, const u8 *elf_data,
}
/**
+ * rproc_free_last_trace() - helper function to cleanup a last trace entry
+ * @trace: the last trace element to be cleaned up
+ */
+static void rproc_free_last_trace(struct rproc_mem_entry *trace)
+{
+ rproc_remove_trace_file(trace->priv);
+ list_del(&trace->node);
+ vfree(trace->va);
+ kfree(trace);
+}
+
+/**
* rproc_resource_cleanup() - clean up and free all acquired resources
* @rproc: rproc handle
*
@@ -1263,14 +1373,32 @@ static void rproc_resource_cleanup(struct rproc *rproc)
{
struct rproc_mem_entry *entry, *tmp;
struct device *dev = &rproc->dev;
+ int count = 0, i = rproc->num_traces;
/* clean up debugfs trace entries */
list_for_each_entry_safe(entry, tmp, &rproc->traces, node) {
+ /* handle last trace here */
+ if (rproc->state == RPROC_CRASHED)
+ rproc_handle_last_trace(rproc, entry, ++count);
+
rproc_remove_trace_file(entry->priv);
- rproc->num_traces--;
list_del(&entry->node);
kfree(entry);
}
+ rproc->num_traces = 0;
+
+ /*
+ * clean up debugfs last trace entries. This either deletes all last
+ * trace entries during cleanup or just the remaining entries, if any,
+ * in case of a crash.
+ */
+ list_for_each_entry_safe(entry, tmp, &rproc->last_traces, node) {
+ /* skip the valid traces */
+ if ((i--) && (rproc->state == RPROC_CRASHED))
+ continue;
+ rproc_free_last_trace(entry);
+ rproc->num_last_traces--;
+ }
/* clean up carveout allocations */
list_for_each_entry_safe(entry, tmp, &rproc->carveouts, node) {
@@ -1662,6 +1790,13 @@ EXPORT_SYMBOL(rproc_shutdown);
void rproc_release(struct kref *kref)
{
struct rproc *rproc = container_of(kref, struct rproc, refcount);
+ struct rproc_mem_entry *entry, *tmp;
+
+ /* clean up debugfs last trace entries */
+ list_for_each_entry_safe(entry, tmp, &rproc->last_traces, node) {
+ rproc_free_last_trace(entry);
+ rproc->num_last_traces--;
+ }
dev_info(&rproc->dev, "removing %s\n", rproc->name);
@@ -2041,6 +2176,7 @@ struct rproc *rproc_alloc(struct device *dev, const char *name,
INIT_LIST_HEAD(&rproc->carveouts);
INIT_LIST_HEAD(&rproc->mappings);
INIT_LIST_HEAD(&rproc->traces);
+ INIT_LIST_HEAD(&rproc->last_traces);
INIT_LIST_HEAD(&rproc->rvdevs);
INIT_WORK(&rproc->error_handler, rproc_error_handler_work);