如何调试ARM Linux内核(msleep())死锁?

3
我首先寻求调试提示。如果有人能指出一个要更改的代码行或一个外设配置位来解决问题,那就太好了。但这不是我所希望的;我更想知道如何进行调试。
在Google上搜索“msleep hang linux kernel site:stackoverflow.com”可以得到13个答案,但没有一个是关键点,所以我认为我可以放心地提问。
我重新构建了一个ARM Linux内核,用于嵌入式TI AM1808 ARM处理器(Sitara / DaVinci?)。我看到所有启动日志都从串口输出,但尝试登录没有响应,甚至没有回显我输入的内容。
经过大量调试,我到达了内核,并在第828行和第830行之间添加了调试代码(是的,内核版本是2.6.37)。这是在'sbin/init'被调用之前的内核模式中的一个点:

http://lxr.linux.no/linux+v2.6.37/init/main.c#L815

在第830行之前,我添加了一个无限循环的printk,并且我看到了结果。我让它运行了几个小时,它计数到了大约200万。示例行:

dbg:init/main.c:1202: 2088430

因此,它已经成功地输出了 6000 万个字节。

然而,如果在循环中添加 msleep(1000),它只会打印一次,即 msleep() 不返回。

详细信息: 在调度程序的第 4073 行添加一个有条件的 printk,该条件取决于上述永久测试循环开始时设置的标志,显示当其挂起时不再调用 schedule():

http://lxr.linux.no/linux+v2.6.37/kernel/sched.c#L4064

.config/'Device Drivers'下唯一可选项为: 块设备 I2C支持 SPI支持
内核及其ramdisk使用uboot/TFTP加载。 我不认为它会尝试使用以太网。 由于所有这些都发生在'/ sbin / init'之前,因此应该很少发生。
更多细节: 我有一个非常相似的板子,使用相同的CPU。 我可以运行相同的uImage和相同的ramdisk,在那里它工作正常。 我可以登录并做通常的事情。
我运行了内存测试(总共64 MB,将内核限制为32M并测试另外32M;它是单芯片DDR2),并没有发现问题。 一个板子使用UART0,另一个使用UART2,但启动日志从两个板子上都输出,所以这不应该是问题。
非常感谢任何调试提示。 我没有适当的JTAG,所以无法使用它。

调度程序是否依赖于某些硬件定时器?这个定时器可能已经损坏了?或者使用了不同的IO地址? - Willem Hengeveld
据我所知,所有东西都应该在芯片上(我想这值得再次确认),因此除了串行端口(所有3个应该都是活动的,只需选择哪一个是活动的)外,他们应该看到相同的环境。我想我会寻找时间间隔IRQ并在那里添加一个printk(如果我能找到的话:) - user1261470
2个回答

0
如果msleep 没有返回或者没有到达schedule,那么为了调试我们可以跟随调用栈。 msleep调用schedule_timeout_uninterruptible(timeout),然后调用schedule_timeout(timeout),在默认情况下,如果传递给它的jiffies超时小于0,则退出而不调用schedule,因此这是需要检查的一件事情。
如果timeout是正数,则会调用setup_timer_on_stack(&timer, process_timeout, (unsigned long)current);,然后调用__mod_timer(&timer, expire, false, TIMER_NOT_PINNED);,再调用schedule
如果我们没有到达schedule,那么必须有一些东西发生在setup_timer_on_stack__mod_timer中。

setup_timer_on_stack 的调用跟踪是 setup_timer_on_stack 调用 setup_timer_on_stack_key,后者调用 init_timer_on_stack_key。如果启用了 CONFIG_DEBUG_OBJECTS_TIMERS,则该函数是外部的;否则,它会调用 init_timer_key(timer, name, key);,然后调用 debug_init,最后调用 __init_timer(timer, name, key)

__mod_timer 首先调用 timer_stats_timer_set_start_info(timer);,然后调用许多其他函数。

我建议从在 schedule_timeout 中放置一个或两个 printk 开始,可能是在 setup_timer_on_stack 调用的任一侧或在 __mod_timer 调用的任一侧。


这对我来说是个巨大的挑战。谢谢,我会回报的。 - user1261470
在 'schedule();' 前后添加 "before" 和 "after":http://lxr.linux.no/linux+v2.6.37/kernel/timer.c#L1477同时在 'schedule();' 进入和退出处添加 printk():http://lxr.linux.no/linux+v2.6.37/kernel/sched.c#L4073 http://lxr.linux.no/linux+v2.6.37/kernel/sched.c#L4152 - user1261470
在两个板子上都看到了“before”。在坏板子上看到了两次schedule()的进入/退出对,而在好板子上看到了三次。还可以看到: http://lxr.linux.no/linux+v2.6.37/kernel/sched.c#L4133上下文切换已经在我们下面翻转了堆栈所以我想schedule()返回到其他地方,只有第三次它才返回到我的msleep调用。所以我想问题是为什么调用msleep()的任务还没有准备好再次进行调度,或者CPU已经崩溃了(我认为至少会有一个恐慌消息)。 - user1261470
你看到了多少个“before”?2个是针对坏板,3个是针对好板?你可能需要更新你的问题并提供更多信息,而不是将其放在评论中。 - Appleman1234

0

这个问题已经解决了。

通过大量使用prink,确定schedule()确实会切换到另一个任务,即空闲任务。在这种情况下,作为嵌入式Linux,我复制的原始代码库安装了一个空闲任务。那个空闲任务似乎不适合我的板子,并且锁定了CPU,从而导致了崩溃。注释掉对空闲任务的调用即可。

http://lxr.linux.no/linux+v2.6.37/arch/arm/mach-davinci/cpuidle.c#L93

解决了这个问题。


我也遇到了和你一样的问题,我的板子在msleep调用时挂起,但我没有得到你的解决方案....你注释掉了"schedule_timeout"下面的schedule()吗? - Nishith Goswami
正如我所说,我注释掉了对空闲任务的调用。Linux浏览器有一个新的URL:http://lxr.free-electrons.com/source/arch/arm/mach-davinci/cpuidle.c?v=2.6.37#L93,而我注释掉的行是:`cpu_do_idle();`。 - user6162589

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接