Kernel panic messages

It is sometime useful to be able to store the text of kernel panic messages in non volatile memory for postmortem fault analysis, in lieu of big enough storage device or network medium to store or send these message in the field.

The following code examples shows how this can be accomplished.

Some notes and caveats are due: the code does not deal with initializing the non volatile memory, if applicable. To use this example with NOR flash, as an example, code handling flash erasing will need to be added. In addition, this example does not display or clear saved messages and implementing this is left as an exercise to the reader.

Here is the Makefile:

default:
   $(MAKE) -C /lib/modules/`uname -r`/build \
      SUBDIRS=`pwd` modules
 
clean:
  $(MAKE) -C /lib/modules/`uname -r`/build \
      SUBDIRS=`pwd` clean

The simple Kbuild file is:

obj-m := panic_msg.o

And the panic_msg.c is:

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/notifier.h>
#include <linux/ioport.h>
#include <asm/io.h>
 
#define LOG_PREFIX "panic_msg: "
 
static unsigned long buf_address = 0;
module_param(buf_address, ulong, S_IRUSR);
MODULE_PARM_DESC(buf_address, 
  "The physical address of the panic message buffer.");
 
static unsigned long buf_size = 0;
module_param(buf_size, ulong, S_IRUSR);
MODULE_PARM_DESC(buf_size, 
  "The size in bytes of the panic message buffer.");
 
static unsigned int notifier_priority = INT_MAX;
module_param(notifier_priority, uint, S_IRUSR);
MODULE_PARM_DESC(notifier_priority, 
  "Notifier priority: INT_MAX &gt;= x &gt;= 0.");
 
static char * buf;
 
static int panic_msg_handler(
  struct notifier_block *this, 
  unsigned long event,void *msg)
{
  static unsigned int panic_event_handled = 0;
  unsigned int msg_size = min(strlen(msg), buf_size);
 
  if(!panic_event_handled) {
     panic_event_handled = 1;
     memcpy_toio((char *)msg, buf, msg_size);
  }
  return NOTIFY_OK;
}
 
static struct notifier_block panic_msg_notifier = {
  .notifier_call  = panic_msg_handler,
  .next           = NULL,
  .priority       = INT_MAX
};
 
static int __init panic_msg_init(void)
{
  int ret = 0;
  struct resource * res = NULL;
 
  if((!buf_address) || (!buf_size)) {
    printk(KERN_ERR LOG_PREFIX 
      "Address and size parameters are mandatory.\n", 
      buf_address, buf_size);
    ret = -EINVAL;
    goto error;
  }
 
  panic_msg_notifier.priority = notifier_priority;
 
  res = request_mem_region(buf_address, buf_size, 
    "Panic message handler");
 
  if(!res) {
    printk(KERN_ERR LOG_PREFIX 
      "Failed requesting panic message buffer " \
      "at address  0x%lx of size %ld.\n",
      buf_address, buf_size);
    ret = -EINVAL;
    goto error;
  }
 
  buf = ioremap(buf_address, buf_size);
 
  if(!buf) {
    printk(KERN_ERR LOG_PREFIX 
      "Failed mapping panic msg buf at address" \
      " 0x%lx of size %ld\n", 
      buf_address, buf_size);
    ret = -EINVAL;
    goto error;
  }
 
  if(atomic_notifier_chain_register(
     &panic_notifier_list, &panic_msg_notifier)) {
    printk(KERN_ERR LOG_PREFIX 
      "Failed registering panic message notifier.\n");
    ret = -EINVAL;
    goto error;
  }
 
  printk(KERN_INFO LOG_PREFIX 
   "Panic message buffer handler initialized.\n");
 
  return 0;
 
error:
  if(res) release_mem_region(buf_address, buf_size);
  if(buf) iounmap(buf);
  return ret;
}
 
static void __exit panic_msg_exit(void) {
 
  if(atomic_notifier_chain_unregister(
     &panic_notifier_list, &panic_msg_notifier)) {
    printk(KERN_ERR LOG_PREFIX 
      "Failed removing panic msg buf mapping.\n");
  }
  iounmap(buf);
  release_mem_region(buf_address, buf_size);
 
  printk(KERN_INFO LOG_PREFIX 
     "Panic message buffer handler unloaded.\n");
 
  return;
}
 
module_init(panic_msg_init);
module_exit(panic_msg_exit);
 
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Gilad Ben-Yossef" \
  " <gilad@codefidence.com>");
MODULE_DESCRIPTION("This module stores kernel  " \
   "panic messages in non volatile memory.");

Panic message example kernel module source code

Dynamic Linker Demystified From WindRiver Conference

The following slides are from a presentation on the Linux dynamic linker and tips and tricks for its use in Linux based embedded system which was given by Gilad Ben-Yossef, Codedfidence CTO, at the Wind River Embedded Linux Conference in Ramat Ilan, Israel at December 4, 2007:

Let’s Play The Weakest Link Slides (PDF)

Setting tasks CPU affinity

Linux kernel 2.6 and latest versions of 2.4 support two system calls that allow one to limit processes to specific CPUs.

The system calls are:

#include <sched.h>
 
int sched_setaffinity(pid_t  pid, unsigned int len, 
   unsigned long *mask);
 
int sched_getaffinity(pid_t pid, unsigned int len, 
   unsigned long *mask);

pid is the process id of the process to assign to certain CPUs. Use ‘0′ here to denote the current process.

len is the length of the CPU bit mask, and mask is a pointer to a bit mask denoting which CPU can the process run on.

For a good discussion and a code example, look here:

http://www-128.ibm.com/developerworks/linux/library/l-affinity.html

Using proccess specific APIs on threads

A lot of POSIX API’s require a PID, or process ID, as a parameter. Sometime it is useful to use such an API on a thread, rather then a process.

Since Linux internally implements all processes and threads as tasks, one can use the Linux specific gettid(2) system call to get the “Thread ID” of a thread, which is really equal to a process PID, at least so much as to be useful as a parameter for a POSIX system call that requires a PID.

The use is Linux specific, non portable and it is not clear if it is a stable API or an undocumented coincidence. Use with care.