何时以及如何在繁忙的内核线程循环中放弃cpu?

时间:2017-03-29 06:24:18

标签: linux-kernel linux-device-driver kernel-module

我正在编写一个Linux模块,我有一个循环来处理如下工作:

while (1) {
    while (there's work) {
        process_work
    }
    if (should_stop)
        break
    sleep  // wait to be woken up
}

当有大量工作时,它会导致软件锁定。消息是这样的:

[ 1426.067061] BUG: soft lockup - CPU#3 stuck for 23s! [comp_wqa:2969]
[ 1426.067903] Modules linked in: testmodule(OE+) xt_CHECKSUM ipt_MASQUERADE nf_nat_masquerade_ipv4 tun ip6t_rpfilter ip6t_REJECT ipt_REJECT xt_conntrack ebtable_nat ebtable_broute bridge stp llc ebtable_filter ebtables ip6table_nat nf_conntrack_ipv6 nf_defrag_ipv6 nf_nat_ipv6 ip6table_mangle ip6table_security ip6table_raw ip6table_filter ip6_tables iptable_nat nf_conntrack_ipv4 nf_defrag_ipv4 nf_nat_ipv4 nf_nat nf_conntrack iptable_mangle iptable_security iptable_raw iptable_filter hwmon_vid dm_mirror dm_region_hash dm_log dm_mod snd_hda_codec_realtek snd_hda_codec_hdmi snd_hda_codec_generic intel_powerclamp coretemp intel_rapl kvm eeepc_wmi crc32_pclmul asus_wmi ghash_clmulni_intel sparse_keymap rfkill mxm_wmi aesni_intel wmi lrw snd_hda_intel gf128mul glue_helper snd_hda_codec pcspkr ablk_helper sg
[ 1426.067924]  cryptd shpchp snd_hda_core snd_hwdep snd_seq snd_seq_device snd_pcm tpm_infineon acpi_pad snd_timer mei_me mei snd soundcore nfsd auth_rpcgss nfs_acl lockd grace sunrpc ip_tables ext4 mbcache jbd2 sd_mod crc_t10dif crct10dif_generic crct10dif_pclmul crct10dif_common crc32c_intel serio_raw i915 ahci libahci libata i2c_algo_bit drm_kms_helper drm e1000e ptp pps_core i2c_core video
[ 1426.067939] CPU: 3 PID: 2969 Comm: comp_wqa Tainted: G           OE  ------------   3.10.0-327.28.3.el7.x86_64 #1
[ 1426.067940] Hardware name: ASUS All Series/Z97-A, BIOS 2401 04/24/2015
[ 1426.067941] task: ffff88080f212280 ti: ffff880810a68000 task.ti: ffff880810a68000
[ 1426.067942] RIP: 0010:[<ffffffff8107e11f>]  [<ffffffff8107e11f>] vprintk_emit+0x1bf/0x530
[ 1426.067946] RSP: 0018:ffff880810a6bbc0  EFLAGS: 00000246
[ 1426.067947] RAX: 0000000000000001 RBX: 0000000000000003 RCX: 0000000000000000
[ 1426.067948] RDX: 0000000000000001 RSI: ffff88083fb8f6c8 RDI: 0000000000000246
[ 1426.067948] RBP: ffff880810a6bc20 R08: 0000000000000092 R09: 0000000000007d0d
[ 1426.067949] R10: 0000000000008000 R11: ffffc90023effff8 R12: 0000000000000081
[ 1426.067950] R13: ffffffff81a08020 R14: 000000009176cc6c R15: 0000000000000000
[ 1426.067951] FS:  0000000000000000(0000) GS:ffff88083fb80000(0000) knlGS:0000000000000000
[ 1426.067951] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 1426.067952] CR2: 00007f42411ff00e CR3: 000000000194a000 CR4: 00000000001407e0
[ 1426.067953] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
[ 1426.067954] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
[ 1426.067954] Stack:
[ 1426.067955]  ffffffff81cae082 0000000000000071 0000000000000000 ffff880810a6bc40
[ 1426.067956]  ffffffffa07a45a0 000000008116c24e 0000000000000246 ffff8807dfbba800
[ 1426.067958]  ffff880810a70000 ffff8807dfbc5030 ffff8807dfbc4e00 ffff8807e65b3000
[ 1426.067959] Call Trace:

所以经过一些谷歌搜索后,我将代码更改为以下内容:

while (1) {
    while (there's work) {
        process_work
        cond_resched()
    }
    if (should_stop)
        break
    sleep  // wait to be woken up
}

使用此代码,软件锁定发生的可能性较小。但是,它仍然会在负载较重的情况下发生。我想如果这个线程长时间占用cpu,那么cond_resched会放弃cpu。我想我错了。

我想知道如何避免软件锁定,同时不要过多地闲置(我希望模块处理工作时间很长)。

在考虑了这个之后,我意识到我想要的只是让一个cpu核心运行一个专用线程,而不会被打断。看来内核不直接支持这个。有一个名为watchdog_thresh的内核参数,它决定了一个线程可以连续运行多少秒。我已阅读其他帖子,表明这种软件锁定是无害的。我现在更深入地了解我的驱动程序的性能在很大程度上取决于单个cpu核心性能,因为我必须使用单个线程处理工作。

2 个答案:

答案 0 :(得分:1)

虽然内核线程可以保持调度算法相反的CPU,但是线程只有在调度时才能放弃 CPU 算法决定这样做。另请参阅this question,它解释了类似的事情。

您需要调整内核线程,以便其他人可以抢占它。一些可能的解决方案:

  • 线程的优先级较低或更改调度策略。使用sched_setscheduler功能。

  • 经过多次处理(比方说,10)&#34;工作&#34;在一堆中,暂停一小段时间。

答案 1 :(得分:0)

编写用户空间代码和内核代码之间存在根本区别。糟糕的用户代码(关于调度)通常由内核处理(读取纠正),而坏的内核代码是致命的 - 在编写内核代码时有很多方法可以杀死整个机器。 所以你的问题的答案是你必须首先在纸上设计这个。具体来说,与编写用户代码不同,您必须考虑调度和设计适合您的内容。有两个基本问题要问,当然要回答:

  1. 我的任务什么时候开始?
  2. 系统上的所有其他任务何时运行?
  3. 请注意,您没有回答第二个问题。 一旦得到答案(他们将定义CPU如何从一个任务迁移到另一个任务),您就可以开始考虑如何实现它。 确保你了解当前的行为;它将真正帮助您了解相关概念。软锁定意味着你的任务是1)持有CPU和2)不允许内核抢占它(很长一段时间)。找出为什么这个任务不能被抢占(让我们希望它不是一个螺旋锁)。你提到要避免“闲着”;我不确定你的意思是“你的任务没有运行”或“CPU闲置”。这是两个非常不同的情况 - 你的任务必须让系统中的所有其他任务运行(如上所示),所以当其他任务运行时它肯定不会运行,但你不一定要有空闲的CPU ,如果你有“很多工作”。如果你的目标是避免后者,那你就是对的 - 这通常是设计不良的结果(抛入msleep(),而不是花时间计算出适当的调度算法/参数)。

    正如我所说,在编写单行代码之前,第一步是能够描述任务运行的方式和时间。