trace_recursion_record.c 6.0 KB

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