1

Kernel module code:

#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/workqueue.h>

MODULE_LICENSE("GPL");

static struct workqueue_struct *queue;

static void work_func(struct work_struct *work)
{
    int i = 0;
    while (i < 5) {
        printk(KERN_INFO "%d\n", i);
        usleep_range(1000000, 1000001);
        i++;
    }
}

DECLARE_WORK(work, work_func);

int init_module(void)
{
    queue = create_workqueue("myworkqueue");
    queue_work(queue, &work);
    return 0;
}

void cleanup_module(void)
{
    cancel_work_sync(&work);    
    destroy_workqueue(queue);
}

If I do:

insmod mymod.ko
rmmod mymod

rmmod hangs on cancel_work_sync, which first waits for the work to finish, until the counting is over.

Is it possible to immediately cancel that work item?

Minimal runnable example here.

Tested in Linux kernel 4.9.

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
  • I think you should use cancel_work instead of cancel_work_sync for this. – rk1825 May 15 '17 at 07:32
  • @rk1825 thanks! For some reason, I'm getting "Unknown symbol cancel_work" at insert time after recompile, but it compiled fine! I'll look into related threads. – Ciro Santilli OurBigBook.com May 15 '17 at 07:51
  • @rk1825 that symbol is not exported apparently: http://elixir.free-electrons.com/linux/v4.9/source/kernel/workqueue.c#L2997 – Ciro Santilli OurBigBook.com May 15 '17 at 07:58
  • 2
    Even if you will find *non-waiting* replacement for `cancel_work_sync`, `destroy_workqueue()` call will wait the work to finish. So nothing will change for your module. (Actually, you may omit `cancel_work_sync()` call in your module). – Tsyvarev May 15 '17 at 08:15
  • @Tsyvarev true. Is there any way around this? – Ciro Santilli OurBigBook.com May 15 '17 at 08:33
  • 1
    Way around **what**? Do you want to unload module while one of its functions is executed? This will crash. Or do you want to **interrupt** execution of your function when module is unloaded, so it wouldn't continue after that? If so, this is completely different story. – Tsyvarev May 15 '17 at 19:48

2 Answers2

1

There is another way to stop kthread with signals. This approach is better than yours because it doesn't require your thread to wake up regularly and poll the stop variable with kthread_should_stop(). No wasting CPU time, it allows your thread to sleep as long as it neeeded.

static int kthread_handle(void *param)
{
    allow_signal(SIGINT);
    allow_signal(SIGKILL);

    for (;;)
    {
        // ...
        // Some blocking functions such as kernel_read()/kernel_write()
        // ...
        if (signal_pending(current))
        {
            goto end;
        }

        // ...
        // Some interruptible functions
        // ...
        if (mutex_lock_interruptible(...) == -EINTR)
        {
            goto end;
        }
    }

end:
    while (!kthread_should_stop())
    {
        schedule();
    }

    return 0;
}

static int __init drv_init(void)
{
    // Create and start kernel thread
    kthread = kthread_run(kthread_handle, NULL, "kthread");

    return 0;
}

static void __exit drv_exit(void)
{
    send_sig(SIGKILL, kthread, 1);
    kthread_stop(kthread);
}

module_init(drv_init);
module_exit(drv_exit);

I don't know how to send signals to work queues, so the solution is only for kthreads by now.

0

Atomic control variable

I could not find a way to stop work in a workqueue, but using a simple control variable is a possible solution.

#include <linux/delay.h> /* usleep_range */
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h> /* atomic_t */
#include <linux/workqueue.h>

MODULE_LICENSE("GPL");

static struct workqueue_struct *queue;
static atomic_t run = ATOMIC_INIT(1);

static void work_func(struct work_struct *work)
{
    int i = 0;
    while (atomic_read(&run)) {
        printk(KERN_INFO "%d\n", i);
        usleep_range(1000000, 1000001);
        i++;
        if (i == 10)
            i = 0;
    }
}

DECLARE_WORK(work, work_func);

int init_module(void)
{
    queue = create_workqueue("myworkqueue");
    queue_work(queue, &work);
    return 0;
}

void cleanup_module(void)
{
    atomic_set(&run, 0);
    destroy_workqueue(queue);
}

kthread kthread_stop

Work queues are based on kthreads, and a work queue is basically useless in that example, so we could use the kthreads directly.

kthread_stop waits for the thread to return.

See also:

Signal handling in kthreads seems to have been a polemic subject, and is now not possible: https://unix.stackexchange.com/questions/355280/how-signals-are-handled-in-kernel

#include <linux/delay.h> /* usleep_range */
#include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/module.h>

MODULE_LICENSE("GPL");

static struct task_struct *kthread;

static int work_func(void *data)
{
    int i = 0;
    while (!kthread_should_stop()) {
        printk(KERN_INFO "%d\n", i);
        usleep_range(1000000, 1000001);
        i++;
        if (i == 10)
            i = 0;
    }
    return 0;
}

int init_module(void)
{
    kthread = kthread_create(work_func, NULL, "mykthread");
    wake_up_process(kthread);
    return 0;
}

void cleanup_module(void)
{
    kthread_stop(kthread);
}

Timer

Run in interrupt context directly, so more accurate, but more restricted.

See also: How to use timers in Linux kernel device drivers?

#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/timer.h>

MODULE_LICENSE("GPL");

static void callback(unsigned long data);
static unsigned long onesec;

DEFINE_TIMER(mytimer, callback, 0, 0);

static void callback(unsigned long data)
{
    pr_info("%u\n", (unsigned)jiffies);
    mod_timer(&mytimer, jiffies + onesec);
}

int init_module(void)
{
    onesec = msecs_to_jiffies(1000);
    mod_timer(&mytimer, jiffies + onesec);
    return 0;
}

void cleanup_module(void)
{
    del_timer(&mytimer);
}
Community
  • 1
  • 1
Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985