Identifying bug in linux kernel module

Viewed 362

I am marking Michael's as he was the first. Thank you to osgx and employee of the month for additional information and assistance.

I am attempting to identify a bug in a consumer/produce kernel module. This is a problem being given to me for a course in university. My teaching assistant was not able to figure it out, and my professor said it was okay if I uploaded online (he doesn't think Stack can figure it out!).

  • I have included the module, the makefile, and the Kbuild.
  • Running the program does not guarantee the bug will present itself.
  • I thought the issue was on line 30 since it is possible for a thread to rush to line 36, and starve the other threads. My professor said that is not what he is looking for.
  • Unrelated question: What is the purpose of line 40? It seems out of place to me, but my professor said it serves a purporse.
  • My professor said the bug is very subtle. The bug is not deadlock.
  • My approach was to identify critical sections and shared variables, but I'm stumped. I am not familiar with tracing (as a method of debugging), and was told that while it may help it is not necessary to identify the issue.

File: final.c

#include <linux/completion.h>
#include <linux/init.h>
#include <linux/kthread.h>
#include <linux/module.h>

static int actor_kthread(void *);
static int writer_kthread(void *);

static DECLARE_COMPLETION(episode_cv);
static DEFINE_SPINLOCK(lock);
static int episodes_written;
static const int MAX_EPISODES = 21;
static bool show_over;
static struct task_info {
    struct task_struct *task;
    const char *name;
    int (*threadfn) (void *);
} task_info[] = {
    {.name = "Liz", .threadfn = writer_kthread},
    {.name = "Tracy", .threadfn = actor_kthread},
    {.name = "Jenna", .threadfn = actor_kthread},
    {.name = "Josh", .threadfn = actor_kthread},
};

static int actor_kthread(void *data) {
    struct task_info *actor_info = (struct task_info *)data;
    spin_lock(&lock);
    while (!show_over) {
        spin_unlock(&lock);
        wait_for_completion_interruptible(&episode_cv); //Line 30
        spin_lock(&lock);
        while (episodes_written) {
            pr_info("%s is in a skit\n", actor_info->name);
            episodes_written--;
        }
        reinit_completion(&episode_cv); // Line 36
    }

    pr_info("%s is done for the season\n", actor_info->name);
    complete(&episode_cv); //Why do we need this line?
    actor_info->task = NULL;
    spin_unlock(&lock);
    return 0;
}

static int writer_kthread(void *data) {
    struct task_info *writer_info = (struct task_info *)data;
    size_t ep_num;

    spin_lock(&lock);
    for (ep_num = 0; ep_num < MAX_EPISODES && !show_over; ep_num++) {
        spin_unlock(&lock);

        /* spend some time writing the next episode */
        schedule_timeout_interruptible(2 * HZ);

        spin_lock(&lock);
        episodes_written++;
        complete_all(&episode_cv);
    }

    pr_info("%s wrote the last episode for the season\n", writer_info->name);
    show_over = true;
    complete_all(&episode_cv);
    writer_info->task = NULL;
    spin_unlock(&lock);
    return 0;
}

static int __init tgs_init(void) {
    size_t i;
    for (i = 0; i < ARRAY_SIZE(task_info); i++) {
        struct task_info *info = &task_info[i];
        info->task = kthread_run(info->threadfn, info, info->name);
    }
    return 0;
}

static void __exit tgs_exit(void) {
    size_t i;
    spin_lock(&lock);
    show_over = true;
    spin_unlock(&lock);
    for (i = 0; i < ARRAY_SIZE(task_info); i++)
        if (task_info[i].task)
            kthread_stop(task_info[i].task);
}

module_init(tgs_init);
module_exit(tgs_exit);
MODULE_DESCRIPTION("CS421 Final");
MODULE_LICENSE("GPL");

File: kbuild

Kobj-m := final.o

File: Makefile

# Basic Makefile to pull in kernel's KBuild to build an out-of-tree
# kernel module

KDIR ?= /lib/modules/$(shell uname -r)/build

all: modules

clean modules:
2 Answers
Related