我需要一个内核线程能够长时间工作而不会屈服,基本上可以根据需要完全专用CPU内核:
int my_kthread(void *arg)
{
while(!kthread_should_stop()) {
do_some_work();
if(sleeping_enabled) msleep(1000);
else {
// What to do here to avoid lockup warnings
// and ensure system stability?
}
}
return 0;
}
当我正在处理的模块被加载时,线程就像这样创建:
my_task = kthread_run(&my_kthread, (void *)some_data, "My KThread")
set_cpus_allowed(my_task, *cpumask_of(10)); // Pin thread to core #10
并在卸载模块时停止:
kthread_stop(my_task);
当sleeping_enabled
为true
时,一切正常。
否则,在线程启动后不久,内核就会抱怨明显的锁定。 起初,我只是想避免各种警告,如
BUG: soft lockup - CPU#10 stuck for 22s!
和
INFO: rcu_sched detected stalls on CPUs/tasks: { 10} (detected by 15, t=30567 jiffies)
因为他们倾向于使用所有> 20核心的转储以及"锁定"是理想的行为。
我试着这样看门狗:
if(sleeping_enabled) msleep(1000);
else touch_softlockup_watchdog();
与(echo 1 > /sys/module/rcupdate/parameters/rcu_cpu_stall_suppress)
结合使用
并且几乎得到了我想要的东西(一个永不休眠的线程,成功地做了我想要的,在控制台中没有垃圾邮件)。
然而,这不仅仅是解决方案"感觉像是作弊,似乎我通过占用那个核心完全打破了一些东西:当通过rmmod
卸载模块时,整个系统冻结了。控制台开始定期在所有核心上转储软锁定,使用此调用跟踪:
[<ffffffff810c96b0>] ? queue_stop_cpus_work+0xd0/0xd0
[<ffffffff810c9903>] cpu_stopper_thread+0xe3/0x1b0
[<ffffffff8108639a>] ? finish_task_switch+0x4a/0xf0
[<ffffffff8169e654>] ? __schedule+0x3c4/0x700
[<ffffffff81080e98>] ? __wake_up_common+0x58/0x90
[<ffffffff810c9820>] ? __stop_cpus+0x80/0x80
[<ffffffff81077e93>] kthread+0x93/0xa0
[<ffffffff816a9724>] kernel_thread_helper+0x4/0x10
[<ffffffff81077e00>] ? flush_kthread_worker+0xb0/0xb0
[<ffffffff816a9720>] ? gs_change+0x13/0x13
与此同时,我的内核线程继续运行(正如一些控制台消息所证明的那样,它偶尔打印出来),所以它从未看到kthread_should_stop()
返回true
。
卸载确实正常工作并在我切换到完全没有睡觉之前停止了线程。现在,我无法在不重新启动的情况下进行迭代修改。
请注意,我在这里简化了很多描述。我试图添加这样一个线程(以轮询一些硬件寄存器并记录他们的更改)到GPU驱动程序,因此可能存在依赖于模块的原因导致卸载冻结。但是,这并没有改变我关于如何最好地实现永不休眠的线程的一般性问题。