pstore: Add ftrace timestamp counter
authorJoel Fernandes <joelaf@google.com>
Thu, 20 Oct 2016 07:34:05 +0000 (00:34 -0700)
committerKees Cook <keescook@chromium.org>
Wed, 16 Nov 2016 00:34:27 +0000 (16:34 -0800)
In preparation for merging the per CPU buffers into one buffer when
we retrieve the pstore ftrace data, we store the timestamp as a
counter in the ftrace pstore record.  We store the CPU number as well
if !PSTORE_CPU_IN_IP, in this case we shift the counter and may lose
ordering there but we preserve the same record size. The timestamp counter
is also racy, and not doing any locking or synchronization here results
in the benefit of lower overhead. Since we don't care much here for exact
ordering of function traces across CPUs, we don't synchronize and may lose
some counter updates but I'm ok with that.

Using trace_clock() results in much lower performance so avoid using it
since we don't want accuracy in timestamp and need a rough ordering to
perform merge.

Signed-off-by: Joel Fernandes <joelaf@google.com>
[kees: updated commit message, added comments]
Signed-off-by: Kees Cook <keescook@chromium.org>
fs/pstore/ftrace.c
fs/pstore/inode.c
fs/pstore/internal.h
include/linux/pstore.h

index d4887705bb61bb768f9c9a8e3a5fbb3c677b87a9..31548cc09e7b6f8ef60d323a7b62fc99cedeb3e0 100644 (file)
@@ -27,6 +27,9 @@
 #include <asm/barrier.h>
 #include "internal.h"
 
+/* This doesn't need to be atomic: speed is chosen over correctness here. */
+static u64 pstore_ftrace_stamp;
+
 static void notrace pstore_ftrace_call(unsigned long ip,
                                       unsigned long parent_ip,
                                       struct ftrace_ops *op,
@@ -42,6 +45,7 @@ static void notrace pstore_ftrace_call(unsigned long ip,
 
        rec.ip = ip;
        rec.parent_ip = parent_ip;
+       pstore_ftrace_write_timestamp(&rec, pstore_ftrace_stamp++);
        pstore_ftrace_encode_cpu(&rec, raw_smp_processor_id());
        psinfo->write_buf(PSTORE_TYPE_FTRACE, 0, NULL, 0, (void *)&rec,
                          0, sizeof(rec), psinfo);
index 1781dc50762e2fe03391e8efa8c0ff5577cd7cf6..0d6bbcf47d5243ede507f44383565c1f9df4e486 100644 (file)
@@ -107,9 +107,11 @@ static int pstore_ftrace_seq_show(struct seq_file *s, void *v)
        struct pstore_ftrace_seq_data *data = v;
        struct pstore_ftrace_record *rec = (void *)(ps->data + data->off);
 
-       seq_printf(s, "%d %08lx  %08lx  %pf <- %pF\n",
-               pstore_ftrace_decode_cpu(rec), rec->ip, rec->parent_ip,
-               (void *)rec->ip, (void *)rec->parent_ip);
+       seq_printf(s, "CPU:%d ts:%llu %08lx  %08lx  %pf <- %pF\n",
+                  pstore_ftrace_decode_cpu(rec),
+                  pstore_ftrace_read_timestamp(rec),
+                  rec->ip, rec->parent_ip, (void *)rec->ip,
+                  (void *)rec->parent_ip);
 
        return 0;
 }
index e38a22b31282e18c4aa24d37a420c8130089dcc6..da416e6591c9d91cd937b36438e31a51bde6bfa4 100644 (file)
@@ -5,40 +5,6 @@
 #include <linux/time.h>
 #include <linux/pstore.h>
 
-#if NR_CPUS <= 2 && defined(CONFIG_ARM_THUMB)
-#define PSTORE_CPU_IN_IP 0x1
-#elif NR_CPUS <= 4 && defined(CONFIG_ARM)
-#define PSTORE_CPU_IN_IP 0x3
-#endif
-
-struct pstore_ftrace_record {
-       unsigned long ip;
-       unsigned long parent_ip;
-#ifndef PSTORE_CPU_IN_IP
-       unsigned int cpu;
-#endif
-};
-
-static inline void
-pstore_ftrace_encode_cpu(struct pstore_ftrace_record *rec, unsigned int cpu)
-{
-#ifndef PSTORE_CPU_IN_IP
-       rec->cpu = cpu;
-#else
-       rec->ip |= cpu;
-#endif
-}
-
-static inline unsigned int
-pstore_ftrace_decode_cpu(struct pstore_ftrace_record *rec)
-{
-#ifndef PSTORE_CPU_IN_IP
-       return rec->cpu;
-#else
-       return rec->ip & PSTORE_CPU_IN_IP;
-#endif
-}
-
 #ifdef CONFIG_PSTORE_FTRACE
 extern void pstore_register_ftrace(void);
 extern void pstore_unregister_ftrace(void);
index 92013cc9cc8cfe3e73be83c0edd4f5e05a192dae..0da29cae009b186a9010736b2c9b84210ff7b3a2 100644 (file)
@@ -89,4 +89,80 @@ extern int pstore_register(struct pstore_info *);
 extern void pstore_unregister(struct pstore_info *);
 extern bool pstore_cannot_block_path(enum kmsg_dump_reason reason);
 
+struct pstore_ftrace_record {
+       unsigned long ip;
+       unsigned long parent_ip;
+       u64 ts;
+};
+
+/*
+ * ftrace related stuff: Both backends and frontends need these so expose
+ * them here.
+ */
+
+#if NR_CPUS <= 2 && defined(CONFIG_ARM_THUMB)
+#define PSTORE_CPU_IN_IP 0x1
+#elif NR_CPUS <= 4 && defined(CONFIG_ARM)
+#define PSTORE_CPU_IN_IP 0x3
+#endif
+
+#define TS_CPU_SHIFT 8
+#define TS_CPU_MASK (BIT(TS_CPU_SHIFT) - 1)
+
+/*
+ * If CPU number can be stored in IP, store it there, otherwise store it in
+ * the time stamp. This means more timestamp resolution is available when
+ * the CPU can be stored in the IP.
+ */
+#ifdef PSTORE_CPU_IN_IP
+static inline void
+pstore_ftrace_encode_cpu(struct pstore_ftrace_record *rec, unsigned int cpu)
+{
+       rec->ip |= cpu;
+}
+
+static inline unsigned int
+pstore_ftrace_decode_cpu(struct pstore_ftrace_record *rec)
+{
+       return rec->ip & PSTORE_CPU_IN_IP;
+}
+
+static inline u64
+pstore_ftrace_read_timestamp(struct pstore_ftrace_record *rec)
+{
+       return rec->ts;
+}
+
+static inline void
+pstore_ftrace_write_timestamp(struct pstore_ftrace_record *rec, u64 val)
+{
+       rec->ts = val;
+}
+#else
+static inline void
+pstore_ftrace_encode_cpu(struct pstore_ftrace_record *rec, unsigned int cpu)
+{
+       rec->ts &= ~(TS_CPU_MASK);
+       rec->ts |= cpu;
+}
+
+static inline unsigned int
+pstore_ftrace_decode_cpu(struct pstore_ftrace_record *rec)
+{
+       return rec->ts & TS_CPU_MASK;
+}
+
+static inline u64
+pstore_ftrace_read_timestamp(struct pstore_ftrace_record *rec)
+{
+       return rec->ts >> TS_CPU_SHIFT;
+}
+
+static inline void
+pstore_ftrace_write_timestamp(struct pstore_ftrace_record *rec, u64 val)
+{
+       rec->ts = (rec->ts & TS_CPU_MASK) | (val << TS_CPU_SHIFT);
+}
+#endif
+
 #endif /*_LINUX_PSTORE_H*/