930-kmsg_dump_backport.patch 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. --- /dev/null
  2. +++ b/include/linux/kmsg_dump.h
  3. @@ -0,0 +1,44 @@
  4. +/*
  5. + * linux/include/kmsg_dump.h
  6. + *
  7. + * Copyright (C) 2009 Net Insight AB
  8. + *
  9. + * Author: Simon Kagstrom <[email protected]>
  10. + *
  11. + * This file is subject to the terms and conditions of the GNU General Public
  12. + * License. See the file COPYING in the main directory of this archive
  13. + * for more details.
  14. + */
  15. +#ifndef _LINUX_KMSG_DUMP_H
  16. +#define _LINUX_KMSG_DUMP_H
  17. +
  18. +#include <linux/list.h>
  19. +
  20. +enum kmsg_dump_reason {
  21. + KMSG_DUMP_OOPS,
  22. + KMSG_DUMP_PANIC,
  23. +};
  24. +
  25. +/**
  26. + * struct kmsg_dumper - kernel crash message dumper structure
  27. + * @dump: The callback which gets called on crashes. The buffer is passed
  28. + * as two sections, where s1 (length l1) contains the older
  29. + * messages and s2 (length l2) contains the newer.
  30. + * @list: Entry in the dumper list (private)
  31. + * @registered: Flag that specifies if this is already registered
  32. + */
  33. +struct kmsg_dumper {
  34. + void (*dump)(struct kmsg_dumper *dumper, enum kmsg_dump_reason reason,
  35. + const char *s1, unsigned long l1,
  36. + const char *s2, unsigned long l2);
  37. + struct list_head list;
  38. + int registered;
  39. +};
  40. +
  41. +void kmsg_dump(enum kmsg_dump_reason reason);
  42. +
  43. +int kmsg_dump_register(struct kmsg_dumper *dumper);
  44. +
  45. +int kmsg_dump_unregister(struct kmsg_dumper *dumper);
  46. +
  47. +#endif /* _LINUX_KMSG_DUMP_H */
  48. --- a/kernel/panic.c
  49. +++ b/kernel/panic.c
  50. @@ -10,6 +10,7 @@
  51. */
  52. #include <linux/debug_locks.h>
  53. #include <linux/interrupt.h>
  54. +#include <linux/kmsg_dump.h>
  55. #include <linux/kallsyms.h>
  56. #include <linux/notifier.h>
  57. #include <linux/module.h>
  58. @@ -74,6 +75,7 @@ NORET_TYPE void panic(const char * fmt,
  59. dump_stack();
  60. #endif
  61. + kmsg_dump(KMSG_DUMP_PANIC);
  62. /*
  63. * If we have crashed and we have a crash kernel loaded let it handle
  64. * everything else.
  65. @@ -339,6 +341,7 @@ void oops_exit(void)
  66. {
  67. do_oops_enter_exit();
  68. print_oops_end_marker();
  69. + kmsg_dump(KMSG_DUMP_OOPS);
  70. }
  71. #ifdef WANT_WARN_ON_SLOWPATH
  72. --- a/kernel/printk.c
  73. +++ b/kernel/printk.c
  74. @@ -33,6 +33,7 @@
  75. #include <linux/bootmem.h>
  76. #include <linux/syscalls.h>
  77. #include <linux/kexec.h>
  78. +#include <linux/kmsg_dump.h>
  79. #include <asm/uaccess.h>
  80. @@ -1405,3 +1406,121 @@ bool printk_timed_ratelimit(unsigned lon
  81. }
  82. EXPORT_SYMBOL(printk_timed_ratelimit);
  83. #endif
  84. +
  85. +static DEFINE_SPINLOCK(dump_list_lock);
  86. +static LIST_HEAD(dump_list);
  87. +
  88. +/**
  89. + * kmsg_dump_register - register a kernel log dumper.
  90. + * @dump: pointer to the kmsg_dumper structure
  91. + *
  92. + * Adds a kernel log dumper to the system. The dump callback in the
  93. + * structure will be called when the kernel oopses or panics and must be
  94. + * set. Returns zero on success and %-EINVAL or %-EBUSY otherwise.
  95. + */
  96. +int kmsg_dump_register(struct kmsg_dumper *dumper)
  97. +{
  98. + unsigned long flags;
  99. + int err = -EBUSY;
  100. +
  101. + /* The dump callback needs to be set */
  102. + if (!dumper->dump)
  103. + return -EINVAL;
  104. +
  105. + spin_lock_irqsave(&dump_list_lock, flags);
  106. + /* Don't allow registering multiple times */
  107. + if (!dumper->registered) {
  108. + dumper->registered = 1;
  109. + list_add_tail(&dumper->list, &dump_list);
  110. + err = 0;
  111. + }
  112. + spin_unlock_irqrestore(&dump_list_lock, flags);
  113. +
  114. + return err;
  115. +}
  116. +EXPORT_SYMBOL_GPL(kmsg_dump_register);
  117. +
  118. +/**
  119. + * kmsg_dump_unregister - unregister a kmsg dumper.
  120. + * @dump: pointer to the kmsg_dumper structure
  121. + *
  122. + * Removes a dump device from the system. Returns zero on success and
  123. + * %-EINVAL otherwise.
  124. + */
  125. +int kmsg_dump_unregister(struct kmsg_dumper *dumper)
  126. +{
  127. + unsigned long flags;
  128. + int err = -EINVAL;
  129. +
  130. + spin_lock_irqsave(&dump_list_lock, flags);
  131. + if (dumper->registered) {
  132. + dumper->registered = 0;
  133. + list_del(&dumper->list);
  134. + err = 0;
  135. + }
  136. + spin_unlock_irqrestore(&dump_list_lock, flags);
  137. +
  138. + return err;
  139. +}
  140. +EXPORT_SYMBOL_GPL(kmsg_dump_unregister);
  141. +
  142. +static const char const *kmsg_reasons[] = {
  143. + [KMSG_DUMP_OOPS] = "oops",
  144. + [KMSG_DUMP_PANIC] = "panic",
  145. +};
  146. +
  147. +static const char *kmsg_to_str(enum kmsg_dump_reason reason)
  148. +{
  149. + if (reason >= ARRAY_SIZE(kmsg_reasons) || reason < 0)
  150. + return "unknown";
  151. +
  152. + return kmsg_reasons[reason];
  153. +}
  154. +
  155. +/**
  156. + * kmsg_dump - dump kernel log to kernel message dumpers.
  157. + * @reason: the reason (oops, panic etc) for dumping
  158. + *
  159. + * Iterate through each of the dump devices and call the oops/panic
  160. + * callbacks with the log buffer.
  161. + */
  162. +void kmsg_dump(enum kmsg_dump_reason reason)
  163. +{
  164. + unsigned long end;
  165. + unsigned chars;
  166. + struct kmsg_dumper *dumper;
  167. + const char *s1, *s2;
  168. + unsigned long l1, l2;
  169. + unsigned long flags;
  170. +
  171. + /* Theoretically, the log could move on after we do this, but
  172. + there's not a lot we can do about that. The new messages
  173. + will overwrite the start of what we dump. */
  174. + spin_lock_irqsave(&logbuf_lock, flags);
  175. + end = log_end & LOG_BUF_MASK;
  176. + chars = logged_chars;
  177. + spin_unlock_irqrestore(&logbuf_lock, flags);
  178. +
  179. + if (logged_chars > end) {
  180. + s1 = log_buf + log_buf_len - logged_chars + end;
  181. + l1 = logged_chars - end;
  182. +
  183. + s2 = log_buf;
  184. + l2 = end;
  185. + } else {
  186. + s1 = "";
  187. + l1 = 0;
  188. +
  189. + s2 = log_buf + end - logged_chars;
  190. + l2 = logged_chars;
  191. + }
  192. +
  193. + if (!spin_trylock_irqsave(&dump_list_lock, flags)) {
  194. + printk(KERN_ERR "dump_kmsg: dump list lock is held during %s, skipping dump\n",
  195. + kmsg_to_str(reason));
  196. + return;
  197. + }
  198. + list_for_each_entry(dumper, &dump_list, list)
  199. + dumper->dump(dumper, reason, s1, l1, s2, l2);
  200. + spin_unlock_irqrestore(&dump_list_lock, flags);
  201. +}