1

您可以在页面错误处理程序中启用中断吗?是否存在抢占式调度的 ARM 内核争用?

我在 UDP 中使用 CONFIG_PREEMPT 接收代码,或者在故障处理程序中启用了中断时,得到了一个 ARM 内核 oops。

该问题与其他用户在此处报告的问题相似。但在我的情况下,当我向系统发送 110% 负载 UDP 数据包(系统丢弃大约 10% 数据包)时,内核会在几分钟内发生错误。仅当有一些busybox shell 脚本正在运行时才会发生这种情况,而不是仅当UDP 接收程序正在运行时才会发生。我跟踪了看起来总是不错的数据地址,缓冲区在释放之前已分配和使用。

有两种方法可以避免:

[1] 将调度从抢占 (CONFIG_PREEMPT) 更改为抢占自愿时,问题就消失了。这是内核 2.6.39 上 ARM 的一个已知问题吗?通过抢占调度,我在很长一段时间后也看到了 jffs2 的问题,但 preempt_volunt 没有。

有一瞬间我怀疑是以太网 DMA 充分利用了总线,从而阻止 CPU 加载其 TLB 条目,从而导致页面错误。我推断是因为busybox脚本需要在图片中,当一个脚本被生成时,它会创建地址空间并加载许多TLB条目,从而使总线超载。如果 preempt_volunte 是一个解决方案,是否可以排除 DMA 阻塞总线?

我正在运行的测试是基于 phy3250 的系统上的 LTIB 内核 2.6.39.4 lpclinux。

[2] 更多测试表明页面错误处理程序嵌套在以太网中断中。当在内核页面错误处理程序 __dabt_svc 中禁用中断,但在用户页面错误处理程序 __dabt_user 中保持启用时,问题就消失了。如果不是,则嵌套级别上升到 4 并且它 oops'ed。所以问题是:在页面错误处理程序中启用中断是否正确?

[2] 的测试代码如下。添加或修改带有@@@@ 的行。然后在 do_DataAbort() 中捕获嵌套级别。

file arch/arm/kernel/entry-armv.S:
__dabt_svc:
    svc_entry
... ...
    @
    @ set desired IRQ state, then call main handler
    @
    debug_entry r1
    @@@@Not_Enable_Irq_In_Dabtsvc
    ldr r2, =armv_dabtsvc_count @@@@
    ldr r3, [r2]    @@@@
    add r3, r3, #1  @@@@
    str r3, [r2]    @@@@
    msr cpsr_c, r9 @@@@disable thisk
    mov r2, r2 @@@@add this extra inst
    mov r2, sp
    bl  do_DataAbort

    @
    @ IRQs off again before pulling preserved data off the stack
    @
    disable_irq_notrace

    ldr r2, =armv_dabtsvc_count @@@@
    ldr r3, [r2]    @@@@
    sub r3, r3, #1  @@@@
    str r3, [r2]    @@@@
    @
    @ restore SPSR and restart the instruction
    @
    ldr r2, [sp, #S_PSR]
    svc_exit r2             @ return from exception
 UNWIND(.fnend      )
ENDPROC(__dabt_svc)

并将变量也添加到文件中:

file arch/arm/kernel/entry-armv.S:
@@@@save nesting level:
    .data            @@@@
    .align           @@@@
armv_dabtsvc_count:  @@@@
    .long   0   @ count svc entry    @@@@

我正在尝试将所有这些联系起来。内核专家能否看到所有测试是否有意义?在页面错误处理程序中禁用中断是一个有效的解决方案吗?

编辑:页面错误处理程序中的 oops 不是第一次失败。正在进行的对齐处理程序中有一个“do_bad_area”。随后,对未对齐访问的修复失败导致页面错误。是的,正如下面有人评论的那样,修复未对齐的访问非常麻烦。那些未对齐的访问来自 ip_input、ip_fragment 和 udp 堆栈。一旦我修复了堆栈中的所有内容,问题就消失了。

再次编辑:问题在于对齐处理程序中的两个操作:它获取指令,并获取指令引用的数据。oops 由数据访问报告,但原因是获取指令失败,第一个页面错误失败。由于 fetch 指令在内核空间中,因此页面始终有效,这表明存在硅错误。如果更改代码以再次获取它会成功,这证实它更有可能是一个硅错误。中断进入画面是因为它带来了过多的 TLB 刷新。简而言之,TLB 加载是自动的,因此在内核空间中获取指令不会失败。但它仍然失败了。

4

1 回答 1

1

我想这就是答案(不完整,有待测试):

过早启用中断会出现问题。当在 do_alignment() 中启用中断时,假设 __get_user() 用于原子上下文。如果中断启用被推迟到该点之后,一切都应该没问题。

请查看两个内核提交。2011 年 6 月 25 日的第一个,延迟启用中断。第二个是 2013 年 2 月 25 日,它将 __get_user() 的使用更改为 probling_kernel_address()。

第一次提交:

3.x 内核删除了低级处理程序 __dabt_svc 和 __dabt_user 等中的中断启用。提交消息:

git diff 8b418616..02fe2845 entry-armv.S
commit 02fe2845d6a837ab02f0738f6cf4591a02cc88d4
Author: Russell King <rmk+kernel@rm.linux.org.uk>
Date:   Sat Jun 25 11:44:06 2011 +0100

    ARM: entry: avoid enabling interrupts in prefetch/data abort handlers

    Avoid enabling interrupts if the parent context had interrupts enabled
    in the abort handler assembly code, and move this into the breakpoint/
    page/alignment fault handlers instead.

    This gets rid of some special-casing for the breakpoint fault handlers
    from the low level abort handler path.

    Acked-by: Will Deacon <will.deacon@arm.com>
    Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

commit 8b4186160b7894ca4583f702a562856d5d9e9118
Author: Russell King <rmk+kernel@rm.linux.org.uk>
Date:   Sat Jun 25 19:25:02 2011 +0100

和代码差异片段:

diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S
index d644d02..c46bafa 100644
--- a/arch/arm/kernel/entry-armv.S
+++ b/arch/arm/kernel/entry-armv.S
@@ -185,20 +185,15 @@ ENDPROC(__und_invalid)
 __dabt_svc:
        svc_entry
... ...
        dabt_helper

        @
-       @ set desired IRQ state, then call main handler
+       @ call main handler
        @
-       debug_entry r1
-       msr     cpsr_c, r9
        mov     r2, sp
        bl      do_DataAbort
......

这证实了不需要在故障处理程序中过早启用中断。

第二次提交:

commit b255188f90e2bade1bd11a986dd1ca4861869f4d
Author: Russell King <rmk+kernel@arm.linux.org.uk>
Date:   Mon Feb 25 16:10:42 2013 +0000

    ARM: fix scheduling while atomic warning in alignment handling code

    Paolo Pisati reports that IPv6 triggers this warning:

    BUG: scheduling while atomic: swapper/0/0/0x40000100
    [<c001b1c4>] (unwind_backtrace+0x0/0xf0) from [<c0503c5c>] (__schedule_bug+0x48/0x5c)
    [<c0503c5c>] (__schedule_bug+0x48/0x5c) from [<c0508608>] (__schedule+0x700/0x740)
    [<c0508608>] (__schedule+0x700/0x740) from [<c007007c>] (__cond_resched+0x24/0x34)
    [<c007007c>] (__cond_resched+0x24/0x34) from [<c05086dc>] (_cond_resched+0x3c/0x44)
    [<c05086dc>] (_cond_resched+0x3c/0x44) from [<c0021f6c>] (do_alignment+0x178/0x78c)
    [<c0021f6c>] (do_alignment+0x178/0x78c) from [<c00083e0>] (do_DataAbort+0x34/0x98)
    [<c00083e0>] (do_DataAbort+0x34/0x98) from [<c0509a60>] (__dabt_svc+0x40/0x60)
    Exception stack(0xc0763d70 to 0xc0763db8)
    [<c0509a60>] (__dabt_svc+0x40/0x60) from [<c02a8490>] (__csum_ipv6_magic+0x8/0xc8)

Fix this by using probe_kernel_address() stead of __get_user().
 arch/arm/mm/alignment.c |   11 ++++-------
于 2013-05-23T21:18:49.163 回答