有没有好的方法来保持内核模块加载直到关联的定时器回调 returns

Is there good way to keep kernel module loaded until an associated timer callback returns

我正在编写一个内核模块,它设置一个定时器,在一段时间后调用模块中的回调函数。 代码如下:

static struct timer_list test_timer;
static void timeout_cb(unsigned long data)
{
    printk("cb called\n");
}    
static int __init timer_module_init( void )
{
    init_timer(&test_timer);
    test_timer.expires = jiffies + HZ*5;
    test_timer.data = 0;
    test_timer.function = timeout_cb;
    add_timer(&test_timer);
    return 0;
}

我以为在调用回调函数之前卸载模块,系统会挂掉。而且,这确实发生了。

# insmod timer_test.ko && lsmod | grep timer_test && rmmod timer_test.ko
timer_test              1034  0 ### No ref count to the module
### After some seconds, the system hung up 

我认为这个问题的一个简单解决方案是在 add_timer() 之前递增模块的引用计数并在 timeout_cb() 结束时递减它,这样可以保持模块加载直到 timeout_cb() 结束.

static void timeout_cb(unsigned long data)
{
    printk("cb called\n");
    module_put(THIS_MODULE); // decrementing ref count
}    
static int __init timer_module_init( void )
{
...
    try_module_get(THIS_MODULE); // incrementing ref count
    add_timer(&test_timer);
    return 0;
}

这似乎工作正常,但严格来说,如果模块在 module_put() returns timeout_cb() returns 之后 timeout_cb() returns 之前卸载,系统将挂起。 =21=]

//objdump 
static void timeout_cb(unsigned long data)
{
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
   4:   e8 00 00 00 00          callq  9 <timeout_cb+0x9>
   9:   48 c7 c7 00 00 00 00    mov    [=14=]x0,%rdi
  10:   31 c0                   xor    %eax,%eax
  12:   e8 00 00 00 00          callq  17 <timeout_cb+0x17>
        printk("cb called\n");
        module_put(THIS_MODULE);
  17:   48 c7 c7 00 00 00 00    mov    [=14=]x0,%rdi
  1e:   e8 00 00 00 00          callq  23 <timeout_cb+0x23>
}
  // I think the system would hang up if the module is unloaded here.
  23:   c9                      leaveq
  24:   c3                      retq
  25:   90                      nop

有什么好的方法可以让模块加载到 timeout_cb() returns 完全?

简单的解决方案可以正常工作,因为 timeout_cb() 是在中断上下文中调用的,尽管我不确定这是常规方法。

参考:http://www.makelinux.net/ldd3/chp-7-sect-4

module_exit函数中您需要停用定时器。您可以使用

以同步方式执行此操作
timer_del_sync(&test_timer);

这将保证计时器的回调不会在返回时执行。因此,只有两种可能的变体:

  1. 计时器在执行回调之前已停用。由于停用,回调执行将永远不会发生。

  2. 计时器已停用,回调已完成。

您可以将此方法与 module_put/try_module_get 方法结合使用,这样您的回调将在任何情况下执行。