Search code examples
linux-kernelstructurepanic

Using struct causes kernel panic?


I'm taking my first crack at writing some linux kernel code, and I'm hitting a weird kernel panic.

I have a linked list I am maintaining with the kernel's built-in macros (include/linux/list.h). If the list is empty, I allocate an instance of the following structure:

struct time_span
{
   struct timeval start;
   struct timeval end;
};

and point to it with a pointer called "tmp". I add tmp to the list I'm maintaining with list_add_tail().

Later, if the list is not empty (I'm trying to test with one list item to simplify debugging), I point to the first item in the list with tmp and try to print out the contents of tmp->end.tv_sec. Unfortunately, this causes a kernel panic.

tmp is not NULL (I check at run-time) and neither is "tmp->end" (I am able to print both). It's only when I try to access one of the fields in "end" that I get a kernel panic. I've never seen something like this before -- does anyone have any ideas?

Thanks for any assistance!

-------EDIT------

Code example (this lives in a function that will be called repeatedly):

// .........
struct timeval now_tv;
do_gettimeofday(&now_tv);
if(!list_empty(&(my_list.time_list)))
    {
        tmp = list_first_entry(&(my_list.time_list), struct time_span, time_list);
        if(tmp != NULL)
        {
                    tmp->end.tv_sec = now_tv.tv_sec; // THIS BREAKS
                                                     // Attempting to print "tmp->end.tv_sec" also breaks.
            tmp->end.tv_usec = now_tv.tv_usec;
        }
    }

        // .........

    if(list_empty(&(my_list.time_list)))
        {
        new_time_span = (struct time_span *) kmalloc(sizeof(struct time_span), GFP_KERNEL);
        INIT_LIST_HEAD(&(new_time_span->time_list));
        list_add_tail(&(new_time_span->time_list), &(my_list.time_list));
            do_gettimeofday(&(new_time_span->start));
    }

    // ........

Solution

  • You're missing some fundamentals about Linux linked lists. The following should change:

    struct time_span
    {
       struct timeval start;
       struct timeval end;
    };
    

    To:

    struct time_span
    {
       struct timeval start;
       struct timeval end;
       struct list_head time_list;
    }
    

    When using Linux linked lists you should put the struct list_head inside your struct that you want a list of.
    In the code below, you're allocating a type struct time_span and referencing a variable named time_list inside the allocated variable new_time_span... but you haven't added that to your struct above.

    // .........
    struct timeval now_tv;
    do_gettimeofday(&now_tv);
    if(!list_empty(&(my_list.time_list)))
        {
            tmp = list_first_entry(&(my_list.time_list), struct time_span, time_list);
            if(tmp != NULL)
            {
                        tmp->end.tv_sec = now_tv.tv_sec; // THIS BREAKS
                                                         // Attempting to print "tmp->end.tv_sec" also breaks.
                    tmp->end.tv_usec = now_tv.tv_usec;
            }
        }
    

    Based on the information you've provided, I don't know why the above breaks. Maybe it's just that tmp is a pointer pointing to garbage and that's why it crashes? If you have a kernel debugger setup it's easy to verify.

            // .........
    
        if(list_empty(&(my_list.time_list)))
            {
            new_time_span = (struct time_span *) kmalloc(sizeof(struct time_span), GFP_KERNEL);
            INIT_LIST_HEAD(&(new_time_span->time_list));
            list_add_tail(&(new_time_span->time_list), &(my_list.time_list));
                do_gettimeofday(&(new_time_span->start));
        }
    
        // ........
    

    Here are some good articles that should help:
    http://kernelnewbies.org/FAQ/LinkedLists
    http://sumanadak.blogspot.com/2006/09/linux-kernel-linked-list.html