Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.15.
  1// SPDX-License-Identifier: GPL-2.0
  2
  3#include <linux/seq_file.h>
  4#include <linux/kallsyms.h>
  5#include <linux/module.h>
  6#include <linux/ftrace.h>
  7#include <linux/fs.h>
  8
  9#include "trace_output.h"
 10
 11struct recursed_functions {
 12	unsigned long		ip;
 13	unsigned long		parent_ip;
 14};
 15
 16static struct recursed_functions recursed_functions[CONFIG_FTRACE_RECORD_RECURSION_SIZE];
 17static atomic_t nr_records;
 18
 19/*
 20 * Cache the last found function. Yes, updates to this is racey, but
 21 * so is memory cache ;-)
 22 */
 23static unsigned long cached_function;
 24
 25void ftrace_record_recursion(unsigned long ip, unsigned long parent_ip)
 26{
 27	int index = 0;
 28	int i;
 29	unsigned long old;
 30
 31 again:
 32	/* First check the last one recorded */
 33	if (ip == cached_function)
 34		return;
 35
 36	i = atomic_read(&nr_records);
 37	/* nr_records is -1 when clearing records */
 38	smp_mb__after_atomic();
 39	if (i < 0)
 40		return;
 41
 42	/*
 43	 * If there's two writers and this writer comes in second,
 44	 * the cmpxchg() below to update the ip will fail. Then this
 45	 * writer will try again. It is possible that index will now
 46	 * be greater than nr_records. This is because the writer
 47	 * that succeeded has not updated the nr_records yet.
 48	 * This writer could keep trying again until the other writer
 49	 * updates nr_records. But if the other writer takes an
 50	 * interrupt, and that interrupt locks up that CPU, we do
 51	 * not want this CPU to lock up due to the recursion protection,
 52	 * and have a bug report showing this CPU as the cause of
 53	 * locking up the computer. To not lose this record, this
 54	 * writer will simply use the next position to update the
 55	 * recursed_functions, and it will update the nr_records
 56	 * accordingly.
 57	 */
 58	if (index < i)
 59		index = i;
 60	if (index >= CONFIG_FTRACE_RECORD_RECURSION_SIZE)
 61		return;
 62
 63	for (i = index - 1; i >= 0; i--) {
 64		if (recursed_functions[i].ip == ip) {
 65			cached_function = ip;
 66			return;
 67		}
 68	}
 69
 70	cached_function = ip;
 71
 72	/*
 73	 * We only want to add a function if it hasn't been added before.
 74	 * Add to the current location before incrementing the count.
 75	 * If it fails to add, then increment the index (save in i)
 76	 * and try again.
 77	 */
 78	old = cmpxchg(&recursed_functions[index].ip, 0, ip);
 79	if (old != 0) {
 80		/* Did something else already added this for us? */
 81		if (old == ip)
 82			return;
 83		/* Try the next location (use i for the next index) */
 84		index++;
 85		goto again;
 86	}
 87
 88	recursed_functions[index].parent_ip = parent_ip;
 89
 90	/*
 91	 * It's still possible that we could race with the clearing
 92	 *    CPU0                                    CPU1
 93	 *    ----                                    ----
 94	 *                                       ip = func
 95	 *  nr_records = -1;
 96	 *  recursed_functions[0] = 0;
 97	 *                                       i = -1
 98	 *                                       if (i < 0)
 99	 *  nr_records = 0;
100	 *  (new recursion detected)
101	 *      recursed_functions[0] = func
102	 *                                            cmpxchg(recursed_functions[0],
103	 *                                                    func, 0)
104	 *
105	 * But the worse that could happen is that we get a zero in
106	 * the recursed_functions array, and it's likely that "func" will
107	 * be recorded again.
108	 */
109	i = atomic_read(&nr_records);
110	smp_mb__after_atomic();
111	if (i < 0)
112		cmpxchg(&recursed_functions[index].ip, ip, 0);
113	else if (i <= index)
114		atomic_cmpxchg(&nr_records, i, index + 1);
115}
116EXPORT_SYMBOL_GPL(ftrace_record_recursion);
117
118static DEFINE_MUTEX(recursed_function_lock);
119static struct trace_seq *tseq;
120
121static void *recursed_function_seq_start(struct seq_file *m, loff_t *pos)
122{
123	void *ret = NULL;
124	int index;
125
126	mutex_lock(&recursed_function_lock);
127	index = atomic_read(&nr_records);
128	if (*pos < index) {
129		ret = &recursed_functions[*pos];
130	}
131
132	tseq = kzalloc(sizeof(*tseq), GFP_KERNEL);
133	if (!tseq)
134		return ERR_PTR(-ENOMEM);
135
136	trace_seq_init(tseq);
137
138	return ret;
139}
140
141static void *recursed_function_seq_next(struct seq_file *m, void *v, loff_t *pos)
142{
143	int index;
144	int p;
145
146	index = atomic_read(&nr_records);
147	p = ++(*pos);
148
149	return p < index ? &recursed_functions[p] : NULL;
150}
151
152static void recursed_function_seq_stop(struct seq_file *m, void *v)
153{
154	kfree(tseq);
155	mutex_unlock(&recursed_function_lock);
156}
157
158static int recursed_function_seq_show(struct seq_file *m, void *v)
159{
160	struct recursed_functions *record = v;
161	int ret = 0;
162
163	if (record) {
164		trace_seq_print_sym(tseq, record->parent_ip, true);
165		trace_seq_puts(tseq, ":\t");
166		trace_seq_print_sym(tseq, record->ip, true);
167		trace_seq_putc(tseq, '\n');
168		ret = trace_print_seq(m, tseq);
169	}
170
171	return ret;
172}
173
174static const struct seq_operations recursed_function_seq_ops = {
175	.start  = recursed_function_seq_start,
176	.next   = recursed_function_seq_next,
177	.stop   = recursed_function_seq_stop,
178	.show   = recursed_function_seq_show
179};
180
181static int recursed_function_open(struct inode *inode, struct file *file)
182{
183	int ret = 0;
184
185	mutex_lock(&recursed_function_lock);
186	/* If this file was opened for write, then erase contents */
187	if ((file->f_mode & FMODE_WRITE) && (file->f_flags & O_TRUNC)) {
188		/* disable updating records */
189		atomic_set(&nr_records, -1);
190		smp_mb__after_atomic();
191		memset(recursed_functions, 0, sizeof(recursed_functions));
192		smp_wmb();
193		/* enable them again */
194		atomic_set(&nr_records, 0);
195	}
196	if (file->f_mode & FMODE_READ)
197		ret = seq_open(file, &recursed_function_seq_ops);
198	mutex_unlock(&recursed_function_lock);
199
200	return ret;
201}
202
203static ssize_t recursed_function_write(struct file *file,
204				       const char __user *buffer,
205				       size_t count, loff_t *ppos)
206{
207	return count;
208}
209
210static int recursed_function_release(struct inode *inode, struct file *file)
211{
212	if (file->f_mode & FMODE_READ)
213		seq_release(inode, file);
214	return 0;
215}
216
217static const struct file_operations recursed_functions_fops = {
218	.open           = recursed_function_open,
219	.write		= recursed_function_write,
220	.read           = seq_read,
221	.llseek         = seq_lseek,
222	.release        = recursed_function_release,
223};
224
225__init static int create_recursed_functions(void)
226{
227
228	trace_create_file("recursed_functions", TRACE_MODE_WRITE,
229			  NULL, NULL, &recursed_functions_fops);
230	return 0;
231}
232
233fs_initcall(create_recursed_functions);