Error identification in the Linux kernel module

I mark Michael as he was the first. Thanks to osgx and the employee of the month for additional information and help.

I am trying to identify an error in a user / production kernel module. This is a problem that I am given on a course at the university. My teacher was not able to understand this, and my professor said that everything is in order if I upload online ( he does not think that Stack can understand this !).

  • I included the module, make file and Kbuild.
  • Running the program does not guarantee that the error will be displayed by itself.
  • I thought the problem was on line 30, as the thread might rush to line 36 and starve with other threads. My professor said that this is not what he is looking for.
  • Unrelated question: what is the purpose of line 40? It seems inappropriate to me, but my professor said that he serves purple.
  • My professor said the mistake is very subtle. A mistake is not a dead end.
  • My approach was to define critical sections and common variables, but I'm at a dead end. I am not familiar with tracing (as a debugging method), and I was told that although this may help, there is no need to identify the problem.

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 KBuild to build an out-of-tree
# kernel module

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

all: modules

clean modules:
+4
source share
2 answers

When cleaning into a tgs_exit()function, it does the following without holding a spin lock:

    if (task_info[i].task)
        kthread_stop(task_info[i].task);

It is possible for a thread that finishes setting task_info[i].taskto NULL between validation and invocation kthread_stop().

+2

.

, , , . ? , TA . , , ?

() , Stack

, - , . , , , , . , , . , ?

imho , .

. , , tgs_exit , , a > → task, -NULL, NULL. , kthread_stop (NULL), .

, , OR kthread_stop (, , - ), .

, . , tgs_exit , , . , kthread_stop , , , . , , , , .

, - - kthread_stop, tgs_exit .

Kthread_ * api , , .

, ​​( ). , exit .

, , , . , 0.

, , : kthread_stop, , . , "":

actor_info->task = NULL;

, , ...

spin_unlock(&lock);
return 0;

... ( !), , .

, , kthread_stop.

, ( " " ), .

, , , , ? , , , , ?

, - , , , , . , , ?

, show_over .

, , , . , , . .

+2

Source: https://habr.com/ru/post/1677312/


All Articles