209 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright 2012 Google, Inc.4 */5 6#include <linux/kernel.h>7#include <linux/compiler.h>8#include <linux/irqflags.h>9#include <linux/percpu.h>10#include <linux/smp.h>11#include <linux/atomic.h>12#include <linux/types.h>13#include <linux/mutex.h>14#include <linux/ftrace.h>15#include <linux/fs.h>16#include <linux/debugfs.h>17#include <linux/err.h>18#include <linux/cache.h>19#include <linux/slab.h>20#include <asm/barrier.h>21#include "internal.h"22 23/* This doesn't need to be atomic: speed is chosen over correctness here. */24static u64 pstore_ftrace_stamp;25 26static void notrace pstore_ftrace_call(unsigned long ip,27 unsigned long parent_ip,28 struct ftrace_ops *op,29 struct ftrace_regs *fregs)30{31 int bit;32 unsigned long flags;33 struct pstore_ftrace_record rec = {};34 struct pstore_record record = {35 .type = PSTORE_TYPE_FTRACE,36 .buf = (char *)&rec,37 .size = sizeof(rec),38 .psi = psinfo,39 };40 41 if (unlikely(oops_in_progress))42 return;43 44 bit = ftrace_test_recursion_trylock(ip, parent_ip);45 if (bit < 0)46 return;47 48 local_irq_save(flags);49 50 rec.ip = ip;51 rec.parent_ip = parent_ip;52 pstore_ftrace_write_timestamp(&rec, pstore_ftrace_stamp++);53 pstore_ftrace_encode_cpu(&rec, raw_smp_processor_id());54 psinfo->write(&record);55 56 local_irq_restore(flags);57 ftrace_test_recursion_unlock(bit);58}59 60static struct ftrace_ops pstore_ftrace_ops __read_mostly = {61 .func = pstore_ftrace_call,62};63 64static DEFINE_MUTEX(pstore_ftrace_lock);65static bool pstore_ftrace_enabled;66 67static int pstore_set_ftrace_enabled(bool on)68{69 ssize_t ret;70 71 if (on == pstore_ftrace_enabled)72 return 0;73 74 if (on) {75 ftrace_ops_set_global_filter(&pstore_ftrace_ops);76 ret = register_ftrace_function(&pstore_ftrace_ops);77 } else {78 ret = unregister_ftrace_function(&pstore_ftrace_ops);79 }80 81 if (ret) {82 pr_err("%s: unable to %sregister ftrace ops: %zd\n",83 __func__, on ? "" : "un", ret);84 } else {85 pstore_ftrace_enabled = on;86 }87 88 return ret;89}90 91static ssize_t pstore_ftrace_knob_write(struct file *f, const char __user *buf,92 size_t count, loff_t *ppos)93{94 u8 on;95 ssize_t ret;96 97 ret = kstrtou8_from_user(buf, count, 2, &on);98 if (ret)99 return ret;100 101 mutex_lock(&pstore_ftrace_lock);102 ret = pstore_set_ftrace_enabled(on);103 mutex_unlock(&pstore_ftrace_lock);104 105 if (ret == 0)106 ret = count;107 108 return ret;109}110 111static ssize_t pstore_ftrace_knob_read(struct file *f, char __user *buf,112 size_t count, loff_t *ppos)113{114 char val[] = { '0' + pstore_ftrace_enabled, '\n' };115 116 return simple_read_from_buffer(buf, count, ppos, val, sizeof(val));117}118 119static const struct file_operations pstore_knob_fops = {120 .open = simple_open,121 .read = pstore_ftrace_knob_read,122 .write = pstore_ftrace_knob_write,123};124 125static struct dentry *pstore_ftrace_dir;126 127static bool record_ftrace;128module_param(record_ftrace, bool, 0400);129MODULE_PARM_DESC(record_ftrace,130 "enable ftrace recording immediately (default: off)");131 132void pstore_register_ftrace(void)133{134 if (!psinfo->write)135 return;136 137 pstore_ftrace_dir = debugfs_create_dir("pstore", NULL);138 139 pstore_set_ftrace_enabled(record_ftrace);140 141 debugfs_create_file("record_ftrace", 0600, pstore_ftrace_dir, NULL,142 &pstore_knob_fops);143}144 145void pstore_unregister_ftrace(void)146{147 mutex_lock(&pstore_ftrace_lock);148 if (pstore_ftrace_enabled) {149 unregister_ftrace_function(&pstore_ftrace_ops);150 pstore_ftrace_enabled = false;151 }152 mutex_unlock(&pstore_ftrace_lock);153 154 debugfs_remove_recursive(pstore_ftrace_dir);155}156 157ssize_t pstore_ftrace_combine_log(char **dest_log, size_t *dest_log_size,158 const char *src_log, size_t src_log_size)159{160 size_t dest_size, src_size, total, dest_off, src_off;161 size_t dest_idx = 0, src_idx = 0, merged_idx = 0;162 void *merged_buf;163 struct pstore_ftrace_record *drec, *srec, *mrec;164 size_t record_size = sizeof(struct pstore_ftrace_record);165 166 dest_off = *dest_log_size % record_size;167 dest_size = *dest_log_size - dest_off;168 169 src_off = src_log_size % record_size;170 src_size = src_log_size - src_off;171 172 total = dest_size + src_size;173 merged_buf = kmalloc(total, GFP_KERNEL);174 if (!merged_buf)175 return -ENOMEM;176 177 drec = (struct pstore_ftrace_record *)(*dest_log + dest_off);178 srec = (struct pstore_ftrace_record *)(src_log + src_off);179 mrec = (struct pstore_ftrace_record *)(merged_buf);180 181 while (dest_size > 0 && src_size > 0) {182 if (pstore_ftrace_read_timestamp(&drec[dest_idx]) <183 pstore_ftrace_read_timestamp(&srec[src_idx])) {184 mrec[merged_idx++] = drec[dest_idx++];185 dest_size -= record_size;186 } else {187 mrec[merged_idx++] = srec[src_idx++];188 src_size -= record_size;189 }190 }191 192 while (dest_size > 0) {193 mrec[merged_idx++] = drec[dest_idx++];194 dest_size -= record_size;195 }196 197 while (src_size > 0) {198 mrec[merged_idx++] = srec[src_idx++];199 src_size -= record_size;200 }201 202 kfree(*dest_log);203 *dest_log = merged_buf;204 *dest_log_size = total;205 206 return 0;207}208EXPORT_SYMBOL_GPL(pstore_ftrace_combine_log);209