summaryrefslogtreecommitdiff
path: root/kernel/trace
diff options
context:
space:
mode:
authorMatt Wagantall <mattw@codeaurora.org>2014-09-23 23:45:33 -0700
committerRohit Vaswani <rvaswani@codeaurora.org>2016-03-01 12:22:25 -0800
commit6a0fccbd29ff07dca1a25d5ff1ee15be0e221bd2 (patch)
tree7c896f2ad1baca6a37041a24397341de58bfc4b0 /kernel/trace
parentf973afee85ad998191bfbaeece2610788648f981 (diff)
trace: rtb: add msm_rtb register tracing feature snapshot
This snapshot is taken as of msm-3.10 commit: 78c36fa0ef (Merge "msm: mdss: Prevent backlight update during continuous splash") RTB support captures system events such as register writes to a small uncached region. This is designed to aid in debugging, where it may be useful to know the last events that occurred prior to a device reset. Change-Id: Idc51e618380f58a6803f40c47f2b3d29033b3196 Signed-off-by: Matt Wagantall <mattw@codeaurora.org> [spjoshi@codeaurora.org: fix merge conflict] Signed-off-by: Sarangdhar Joshi <spjoshi@codeaurora.org>
Diffstat (limited to 'kernel/trace')
-rw-r--r--kernel/trace/Kconfig17
-rw-r--r--kernel/trace/Makefile1
-rw-r--r--kernel/trace/msm_rtb.c320
3 files changed, 338 insertions, 0 deletions
diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig
index 5f5b66a2f156..fd8b1a2de934 100644
--- a/kernel/trace/Kconfig
+++ b/kernel/trace/Kconfig
@@ -89,6 +89,23 @@ config RING_BUFFER_ALLOW_SWAP
Allow the use of ring_buffer_swap_cpu.
Adds a very slight overhead to tracing when enabled.
+config QCOM_RTB
+ bool "Register tracing"
+ help
+ Add support for logging different events to a small uncached
+ region. This is designed to aid in debugging reset cases where the
+ caches may not be flushed before the target resets.
+
+config QCOM_RTB_SEPARATE_CPUS
+ bool "Separate entries for each cpu"
+ depends on QCOM_RTB
+ depends on SMP
+ help
+ Under some circumstances, it may be beneficial to give dedicated space
+ for each cpu to log accesses. Selecting this option will log each cpu
+ separately. This will guarantee that the last acesses for each cpu
+ will be logged but there will be fewer entries per cpu
+
# All tracer options should select GENERIC_TRACER. For those options that are
# enabled by all tracers (context switch and event tracer) they select TRACING.
# This allows those options to appear when no other tracer is selected. But the
diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile
index ba04bf0c2653..9f2c8d0672c7 100644
--- a/kernel/trace/Makefile
+++ b/kernel/trace/Makefile
@@ -65,6 +65,7 @@ endif
obj-$(CONFIG_PROBE_EVENTS) += trace_probe.o
obj-$(CONFIG_UPROBE_EVENT) += trace_uprobe.o
obj-$(CONFIG_GPU_TRACEPOINTS) += gpu-traces.o
+obj-$(CONFIG_QCOM_RTB) += msm_rtb.o
obj-$(CONFIG_TRACEPOINT_BENCHMARK) += trace_benchmark.o
diff --git a/kernel/trace/msm_rtb.c b/kernel/trace/msm_rtb.c
new file mode 100644
index 000000000000..2bbb909a20d5
--- /dev/null
+++ b/kernel/trace/msm_rtb.c
@@ -0,0 +1,320 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/atomic.h>
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/atomic.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/io.h>
+#include <asm-generic/sizes.h>
+#include <linux/msm_rtb.h>
+
+#define SENTINEL_BYTE_1 0xFF
+#define SENTINEL_BYTE_2 0xAA
+#define SENTINEL_BYTE_3 0xFF
+
+#define RTB_COMPAT_STR "qcom,msm-rtb"
+
+/* Write
+ * 1) 11 bytes sentinel
+ * 2) 1 bytes of log type
+ * 3) 8 bytes of where the caller came from
+ * 4) 4 bytes index
+ * 4) 8 bytes extra data from the caller
+ *
+ * Total = 32 bytes.
+ */
+struct msm_rtb_layout {
+ unsigned char sentinel[11];
+ unsigned char log_type;
+ uint32_t idx;
+ uint64_t caller;
+ uint64_t data;
+} __attribute__ ((__packed__));
+
+
+struct msm_rtb_state {
+ struct msm_rtb_layout *rtb;
+ phys_addr_t phys;
+ int nentries;
+ int size;
+ int enabled;
+ int initialized;
+ uint32_t filter;
+ int step_size;
+};
+
+#if defined(CONFIG_QCOM_RTB_SEPARATE_CPUS)
+DEFINE_PER_CPU(atomic_t, msm_rtb_idx_cpu);
+#else
+static atomic_t msm_rtb_idx;
+#endif
+
+static struct msm_rtb_state msm_rtb = {
+ .filter = 1 << LOGK_LOGBUF,
+ .enabled = 1,
+};
+
+module_param_named(filter, msm_rtb.filter, uint, 0644);
+module_param_named(enable, msm_rtb.enabled, int, 0644);
+
+static int msm_rtb_panic_notifier(struct notifier_block *this,
+ unsigned long event, void *ptr)
+{
+ msm_rtb.enabled = 0;
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block msm_rtb_panic_blk = {
+ .notifier_call = msm_rtb_panic_notifier,
+};
+
+int notrace msm_rtb_event_should_log(enum logk_event_type log_type)
+{
+ return msm_rtb.initialized && msm_rtb.enabled &&
+ ((1 << (log_type & ~LOGTYPE_NOPC)) & msm_rtb.filter);
+}
+EXPORT_SYMBOL(msm_rtb_event_should_log);
+
+static void msm_rtb_emit_sentinel(struct msm_rtb_layout *start)
+{
+ start->sentinel[0] = SENTINEL_BYTE_1;
+ start->sentinel[1] = SENTINEL_BYTE_2;
+ start->sentinel[2] = SENTINEL_BYTE_3;
+}
+
+static void msm_rtb_write_type(enum logk_event_type log_type,
+ struct msm_rtb_layout *start)
+{
+ start->log_type = (char)log_type;
+}
+
+static void msm_rtb_write_caller(uint64_t caller, struct msm_rtb_layout *start)
+{
+ start->caller = caller;
+}
+
+static void msm_rtb_write_idx(uint32_t idx,
+ struct msm_rtb_layout *start)
+{
+ start->idx = idx;
+}
+
+static void msm_rtb_write_data(uint64_t data, struct msm_rtb_layout *start)
+{
+ start->data = data;
+}
+
+static void uncached_logk_pc_idx(enum logk_event_type log_type, uint64_t caller,
+ uint64_t data, int idx)
+{
+ struct msm_rtb_layout *start;
+
+ start = &msm_rtb.rtb[idx & (msm_rtb.nentries - 1)];
+
+ msm_rtb_emit_sentinel(start);
+ msm_rtb_write_type(log_type, start);
+ msm_rtb_write_caller(caller, start);
+ msm_rtb_write_idx(idx, start);
+ msm_rtb_write_data(data, start);
+ mb();
+
+ return;
+}
+
+static void uncached_logk_timestamp(int idx)
+{
+ unsigned long long timestamp;
+
+ timestamp = sched_clock();
+ uncached_logk_pc_idx(LOGK_TIMESTAMP|LOGTYPE_NOPC,
+ (uint64_t)lower_32_bits(timestamp),
+ (uint64_t)upper_32_bits(timestamp), idx);
+}
+
+#if defined(CONFIG_QCOM_RTB_SEPARATE_CPUS)
+static int msm_rtb_get_idx(void)
+{
+ int cpu, i, offset;
+ atomic_t *index;
+
+ /*
+ * ideally we would use get_cpu but this is a close enough
+ * approximation for our purposes.
+ */
+ cpu = raw_smp_processor_id();
+
+ index = &per_cpu(msm_rtb_idx_cpu, cpu);
+
+ i = atomic_add_return(msm_rtb.step_size, index);
+ i -= msm_rtb.step_size;
+
+ /* Check if index has wrapped around */
+ offset = (i & (msm_rtb.nentries - 1)) -
+ ((i - msm_rtb.step_size) & (msm_rtb.nentries - 1));
+ if (offset < 0) {
+ uncached_logk_timestamp(i);
+ i = atomic_add_return(msm_rtb.step_size, index);
+ i -= msm_rtb.step_size;
+ }
+
+ return i;
+}
+#else
+static int msm_rtb_get_idx(void)
+{
+ int i, offset;
+
+ i = atomic_inc_return(&msm_rtb_idx);
+ i--;
+
+ /* Check if index has wrapped around */
+ offset = (i & (msm_rtb.nentries - 1)) -
+ ((i - 1) & (msm_rtb.nentries - 1));
+ if (offset < 0) {
+ uncached_logk_timestamp(i);
+ i = atomic_inc_return(&msm_rtb_idx);
+ i--;
+ }
+
+ return i;
+}
+#endif
+
+int notrace uncached_logk_pc(enum logk_event_type log_type, void *caller,
+ void *data)
+{
+ int i;
+
+ if (!msm_rtb_event_should_log(log_type))
+ return 0;
+
+ i = msm_rtb_get_idx();
+ uncached_logk_pc_idx(log_type, (uint64_t)((unsigned long) caller),
+ (uint64_t)((unsigned long) data), i);
+
+ return 1;
+}
+EXPORT_SYMBOL(uncached_logk_pc);
+
+noinline int notrace uncached_logk(enum logk_event_type log_type, void *data)
+{
+ return uncached_logk_pc(log_type, __builtin_return_address(0), data);
+}
+EXPORT_SYMBOL(uncached_logk);
+
+static int msm_rtb_probe(struct platform_device *pdev)
+{
+ struct msm_rtb_platform_data *d = pdev->dev.platform_data;
+#if defined(CONFIG_QCOM_RTB_SEPARATE_CPUS)
+ unsigned int cpu;
+#endif
+ int ret;
+
+ if (!pdev->dev.of_node) {
+ msm_rtb.size = d->size;
+ } else {
+ u64 size;
+ struct device_node *pnode;
+
+ pnode = of_parse_phandle(pdev->dev.of_node,
+ "linux,contiguous-region", 0);
+ if (pnode != NULL) {
+ const u32 *addr;
+
+ addr = of_get_address(pnode, 0, &size, NULL);
+ if (!addr) {
+ of_node_put(pnode);
+ return -EINVAL;
+ }
+ of_node_put(pnode);
+ } else {
+ ret = of_property_read_u32(pdev->dev.of_node,
+ "qcom,rtb-size",
+ (u32 *)&size);
+ if (ret < 0)
+ return ret;
+
+ }
+
+ msm_rtb.size = size;
+ }
+
+ if (msm_rtb.size <= 0 || msm_rtb.size > SZ_1M)
+ return -EINVAL;
+
+ msm_rtb.rtb = dma_alloc_coherent(&pdev->dev, msm_rtb.size,
+ &msm_rtb.phys,
+ GFP_KERNEL);
+
+ if (!msm_rtb.rtb)
+ return -ENOMEM;
+
+ msm_rtb.nentries = msm_rtb.size / sizeof(struct msm_rtb_layout);
+
+ /* Round this down to a power of 2 */
+ msm_rtb.nentries = __rounddown_pow_of_two(msm_rtb.nentries);
+
+ memset(msm_rtb.rtb, 0, msm_rtb.size);
+
+
+#if defined(CONFIG_QCOM_RTB_SEPARATE_CPUS)
+ for_each_possible_cpu(cpu) {
+ atomic_t *a = &per_cpu(msm_rtb_idx_cpu, cpu);
+ atomic_set(a, cpu);
+ }
+ msm_rtb.step_size = num_possible_cpus();
+#else
+ atomic_set(&msm_rtb_idx, 0);
+ msm_rtb.step_size = 1;
+#endif
+
+ atomic_notifier_chain_register(&panic_notifier_list,
+ &msm_rtb_panic_blk);
+ msm_rtb.initialized = 1;
+ return 0;
+}
+
+static struct of_device_id msm_match_table[] = {
+ {.compatible = RTB_COMPAT_STR},
+ {},
+};
+
+static struct platform_driver msm_rtb_driver = {
+ .driver = {
+ .name = "msm_rtb",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_match_table
+ },
+};
+
+static int __init msm_rtb_init(void)
+{
+ return platform_driver_probe(&msm_rtb_driver, msm_rtb_probe);
+}
+
+static void __exit msm_rtb_exit(void)
+{
+ platform_driver_unregister(&msm_rtb_driver);
+}
+module_init(msm_rtb_init)
+module_exit(msm_rtb_exit)