162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright 2012 Google, Inc. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/kernel.h> 762306a36Sopenharmony_ci#include <linux/compiler.h> 862306a36Sopenharmony_ci#include <linux/irqflags.h> 962306a36Sopenharmony_ci#include <linux/percpu.h> 1062306a36Sopenharmony_ci#include <linux/smp.h> 1162306a36Sopenharmony_ci#include <linux/atomic.h> 1262306a36Sopenharmony_ci#include <linux/types.h> 1362306a36Sopenharmony_ci#include <linux/mutex.h> 1462306a36Sopenharmony_ci#include <linux/ftrace.h> 1562306a36Sopenharmony_ci#include <linux/fs.h> 1662306a36Sopenharmony_ci#include <linux/debugfs.h> 1762306a36Sopenharmony_ci#include <linux/err.h> 1862306a36Sopenharmony_ci#include <linux/cache.h> 1962306a36Sopenharmony_ci#include <linux/slab.h> 2062306a36Sopenharmony_ci#include <asm/barrier.h> 2162306a36Sopenharmony_ci#include "internal.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* This doesn't need to be atomic: speed is chosen over correctness here. */ 2462306a36Sopenharmony_cistatic u64 pstore_ftrace_stamp; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic void notrace pstore_ftrace_call(unsigned long ip, 2762306a36Sopenharmony_ci unsigned long parent_ip, 2862306a36Sopenharmony_ci struct ftrace_ops *op, 2962306a36Sopenharmony_ci struct ftrace_regs *fregs) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci int bit; 3262306a36Sopenharmony_ci unsigned long flags; 3362306a36Sopenharmony_ci struct pstore_ftrace_record rec = {}; 3462306a36Sopenharmony_ci struct pstore_record record = { 3562306a36Sopenharmony_ci .type = PSTORE_TYPE_FTRACE, 3662306a36Sopenharmony_ci .buf = (char *)&rec, 3762306a36Sopenharmony_ci .size = sizeof(rec), 3862306a36Sopenharmony_ci .psi = psinfo, 3962306a36Sopenharmony_ci }; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci if (unlikely(oops_in_progress)) 4262306a36Sopenharmony_ci return; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci bit = ftrace_test_recursion_trylock(ip, parent_ip); 4562306a36Sopenharmony_ci if (bit < 0) 4662306a36Sopenharmony_ci return; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci local_irq_save(flags); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci rec.ip = ip; 5162306a36Sopenharmony_ci rec.parent_ip = parent_ip; 5262306a36Sopenharmony_ci pstore_ftrace_write_timestamp(&rec, pstore_ftrace_stamp++); 5362306a36Sopenharmony_ci pstore_ftrace_encode_cpu(&rec, raw_smp_processor_id()); 5462306a36Sopenharmony_ci psinfo->write(&record); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci local_irq_restore(flags); 5762306a36Sopenharmony_ci ftrace_test_recursion_unlock(bit); 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic struct ftrace_ops pstore_ftrace_ops __read_mostly = { 6162306a36Sopenharmony_ci .func = pstore_ftrace_call, 6262306a36Sopenharmony_ci}; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic DEFINE_MUTEX(pstore_ftrace_lock); 6562306a36Sopenharmony_cistatic bool pstore_ftrace_enabled; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic int pstore_set_ftrace_enabled(bool on) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci ssize_t ret; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci if (on == pstore_ftrace_enabled) 7262306a36Sopenharmony_ci return 0; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci if (on) { 7562306a36Sopenharmony_ci ftrace_ops_set_global_filter(&pstore_ftrace_ops); 7662306a36Sopenharmony_ci ret = register_ftrace_function(&pstore_ftrace_ops); 7762306a36Sopenharmony_ci } else { 7862306a36Sopenharmony_ci ret = unregister_ftrace_function(&pstore_ftrace_ops); 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci if (ret) { 8262306a36Sopenharmony_ci pr_err("%s: unable to %sregister ftrace ops: %zd\n", 8362306a36Sopenharmony_ci __func__, on ? "" : "un", ret); 8462306a36Sopenharmony_ci } else { 8562306a36Sopenharmony_ci pstore_ftrace_enabled = on; 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci return ret; 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic ssize_t pstore_ftrace_knob_write(struct file *f, const char __user *buf, 9262306a36Sopenharmony_ci size_t count, loff_t *ppos) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci u8 on; 9562306a36Sopenharmony_ci ssize_t ret; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci ret = kstrtou8_from_user(buf, count, 2, &on); 9862306a36Sopenharmony_ci if (ret) 9962306a36Sopenharmony_ci return ret; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci mutex_lock(&pstore_ftrace_lock); 10262306a36Sopenharmony_ci ret = pstore_set_ftrace_enabled(on); 10362306a36Sopenharmony_ci mutex_unlock(&pstore_ftrace_lock); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if (ret == 0) 10662306a36Sopenharmony_ci ret = count; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci return ret; 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic ssize_t pstore_ftrace_knob_read(struct file *f, char __user *buf, 11262306a36Sopenharmony_ci size_t count, loff_t *ppos) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci char val[] = { '0' + pstore_ftrace_enabled, '\n' }; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci return simple_read_from_buffer(buf, count, ppos, val, sizeof(val)); 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic const struct file_operations pstore_knob_fops = { 12062306a36Sopenharmony_ci .open = simple_open, 12162306a36Sopenharmony_ci .read = pstore_ftrace_knob_read, 12262306a36Sopenharmony_ci .write = pstore_ftrace_knob_write, 12362306a36Sopenharmony_ci}; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic struct dentry *pstore_ftrace_dir; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic bool record_ftrace; 12862306a36Sopenharmony_cimodule_param(record_ftrace, bool, 0400); 12962306a36Sopenharmony_ciMODULE_PARM_DESC(record_ftrace, 13062306a36Sopenharmony_ci "enable ftrace recording immediately (default: off)"); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_civoid pstore_register_ftrace(void) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci if (!psinfo->write) 13562306a36Sopenharmony_ci return; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci pstore_ftrace_dir = debugfs_create_dir("pstore", NULL); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci pstore_set_ftrace_enabled(record_ftrace); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci debugfs_create_file("record_ftrace", 0600, pstore_ftrace_dir, NULL, 14262306a36Sopenharmony_ci &pstore_knob_fops); 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_civoid pstore_unregister_ftrace(void) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci mutex_lock(&pstore_ftrace_lock); 14862306a36Sopenharmony_ci if (pstore_ftrace_enabled) { 14962306a36Sopenharmony_ci unregister_ftrace_function(&pstore_ftrace_ops); 15062306a36Sopenharmony_ci pstore_ftrace_enabled = false; 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci mutex_unlock(&pstore_ftrace_lock); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci debugfs_remove_recursive(pstore_ftrace_dir); 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cissize_t pstore_ftrace_combine_log(char **dest_log, size_t *dest_log_size, 15862306a36Sopenharmony_ci const char *src_log, size_t src_log_size) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci size_t dest_size, src_size, total, dest_off, src_off; 16162306a36Sopenharmony_ci size_t dest_idx = 0, src_idx = 0, merged_idx = 0; 16262306a36Sopenharmony_ci void *merged_buf; 16362306a36Sopenharmony_ci struct pstore_ftrace_record *drec, *srec, *mrec; 16462306a36Sopenharmony_ci size_t record_size = sizeof(struct pstore_ftrace_record); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci dest_off = *dest_log_size % record_size; 16762306a36Sopenharmony_ci dest_size = *dest_log_size - dest_off; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci src_off = src_log_size % record_size; 17062306a36Sopenharmony_ci src_size = src_log_size - src_off; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci total = dest_size + src_size; 17362306a36Sopenharmony_ci merged_buf = kmalloc(total, GFP_KERNEL); 17462306a36Sopenharmony_ci if (!merged_buf) 17562306a36Sopenharmony_ci return -ENOMEM; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci drec = (struct pstore_ftrace_record *)(*dest_log + dest_off); 17862306a36Sopenharmony_ci srec = (struct pstore_ftrace_record *)(src_log + src_off); 17962306a36Sopenharmony_ci mrec = (struct pstore_ftrace_record *)(merged_buf); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci while (dest_size > 0 && src_size > 0) { 18262306a36Sopenharmony_ci if (pstore_ftrace_read_timestamp(&drec[dest_idx]) < 18362306a36Sopenharmony_ci pstore_ftrace_read_timestamp(&srec[src_idx])) { 18462306a36Sopenharmony_ci mrec[merged_idx++] = drec[dest_idx++]; 18562306a36Sopenharmony_ci dest_size -= record_size; 18662306a36Sopenharmony_ci } else { 18762306a36Sopenharmony_ci mrec[merged_idx++] = srec[src_idx++]; 18862306a36Sopenharmony_ci src_size -= record_size; 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci while (dest_size > 0) { 19362306a36Sopenharmony_ci mrec[merged_idx++] = drec[dest_idx++]; 19462306a36Sopenharmony_ci dest_size -= record_size; 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci while (src_size > 0) { 19862306a36Sopenharmony_ci mrec[merged_idx++] = srec[src_idx++]; 19962306a36Sopenharmony_ci src_size -= record_size; 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci kfree(*dest_log); 20362306a36Sopenharmony_ci *dest_log = merged_buf; 20462306a36Sopenharmony_ci *dest_log_size = total; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci return 0; 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pstore_ftrace_combine_log); 209