Linux kernel 当mov指令在x86上导致页面错误并禁用中断时会发生什么情况?

Linux kernel 当mov指令在x86上导致页面错误并禁用中断时会发生什么情况?,linux-kernel,x86,linux-device-driver,interrupt,page-fault,Linux Kernel,X86,Linux Device Driver,Interrupt,Page Fault,我最近在一个定制Linux内核(2.6.31.5,x86)驱动程序中遇到了一个问题,在这个驱动程序中,copy_to_user会定期地将任何字节复制到用户空间。它将返回传递给它的字节数,表明它没有复制任何内容。在代码检查之后,我们发现代码在调用copy_to_user时禁用了中断,这违反了它的约定。更正此问题后,问题停止发生。因为这个问题很少发生,我需要证明禁用中断导致了这个问题 如果您查看下面来自arch/x86/lib/usercopy_32.c rep的代码片段;movsl按CX中的计数将

我最近在一个定制Linux内核(2.6.31.5,x86)驱动程序中遇到了一个问题,在这个驱动程序中,copy_to_user会定期地将任何字节复制到用户空间。它将返回传递给它的字节数,表明它没有复制任何内容。在代码检查之后,我们发现代码在调用copy_to_user时禁用了中断,这违反了它的约定。更正此问题后,问题停止发生。因为这个问题很少发生,我需要证明禁用中断导致了这个问题

如果您查看下面来自arch/x86/lib/usercopy_32.c rep的代码片段;movsl按CX中的计数将单词复制到用户空间。退出时使用CX更新大小。如果movsl正确执行,则CX将为0。因为CX不是零,所以MOV?为了符合“复制到用户”的定义和观察到的行为,不得执行指令

/* Generic arbitrary sized copy.  */
#define __copy_user(to, from, size)                 \
do {                                    \
    int __d0, __d1, __d2;                       \
    __asm__ __volatile__(                       \
        "   cmp  $7,%0\n"                   \
        "   jbe  1f\n"                  \
        "   movl %1,%0\n"                   \
        "   negl %0\n"                  \
        "   andl $7,%0\n"                   \
        "   subl %0,%3\n"                   \
        "4: rep; movsb\n"                   \
        "   movl %3,%0\n"                   \
        "   shrl $2,%0\n"                   \
        "   andl $3,%3\n"                   \
        "   .align 2,0x90\n"                \
        "0: rep; movsl\n"                   \
        "   movl %3,%0\n"                   \
        "1: rep; movsb\n"                   \
        "2:\n"                          \
        ".section .fixup,\"ax\"\n"              \
        "5: addl %3,%0\n"                   \
        "   jmp 2b\n"                   \
        "3: lea 0(%3,%0,4),%0\n"                \
        "   jmp 2b\n"                   \
        ".previous\n"                       \
        ".section __ex_table,\"a\"\n"               \
        "   .align 4\n"                 \
        "   .long 4b,5b\n"                  \
        "   .long 0b,3b\n"                  \
        "   .long 1b,2b\n"                  \
        ".previous"                     \
        : "=&c"(size), "=&D" (__d0), "=&S" (__d1), "=r"(__d2)   \
        : "3"(size), "0"(size), "1"(to), "2"(from)      \
        : "memory");                        \
} while (0)
我有两个想法:

  • 当中断被禁用时,页面错误不会发生,并且 然后代表;莫夫?什么都不做就跳过了。返回值 然后将是CX,或未复制到用户空间的量,作为 定义指定观察到的行为
  • 页面错误确实发生了,但是linux无法处理它,因为中断被禁用,所以页面错误处理程序跳过了该指令,尽管我不知道页面错误处理程序是如何做到这一点的。同样,在这种情况下,CX将保持不变,并且返回值是正确的
    有人能给我指出英特尔手册中指定此行为的章节吗,或者给我指出任何可能有用的其他Linux源代码吗?

    页面错误是不可屏蔽的中断。事实上,它们在技术上根本不是中断——而是例外,尽管我同意这种区别在语义上更为明显

    在禁用中断的原子上下文中调用复制到用户失败的原因是代码对此进行了显式检查

    我找到了答案。我的建议是正确的,机械装置就在我面前。页面错误确实会发生,但fixup_异常机制用于提供异常/继续机制。本节向异常处理程序表中添加条目:

        ".section __ex_table,\"a\"\n"               \
        "   .align 4\n"                 \
        "   .long 4b,5b\n"                  \
        "   .long 0b,3b\n"                  \
        "   .long 1b,6b\n"                  \
        ".previous"                     \
    
    这表示:如果IP地址是第一个条目,并且在错误处理程序中遇到异常,则将IP地址设置为第二个地址并继续

    因此,如果异常发生在“4:”处,则跳到“5:”。如果异常发生在“0:”处,则跳到“3:”,如果异常发生在“1:”处,则跳到“6:”

    缺少的部分位于arch/x86/mm/fault.c中的do_page_fault()中:

    /*
     * If we're in an interrupt, have no user context or are running
     * in an atomic region then we must not take the fault:
     */
    if (unlikely(in_atomic() || !mm)) {
        bad_area_nosemaphore(regs, error_code, address);
        return;
    }
    
    in_atomic返回true,因为我们处于写锁\u bh()锁中!bad_area_nosemaphore最终完成了修复


    如果发生页面错误(由于工作空间的概念,这不太可能),则函数调用将失败并跳出uuu copy_user宏,未复制字节设置为size,因为禁用了抢占

    您提到“代码是禁用中断的”。你能详细说明哪些中断以及如何中断吗?…@TheCodeArtist:write_lock_bh();被扣留,据我所知,这将禁用软件中断。@codeartist:谢谢!你的评论让我更仔细地研究了write_lock_bh(),为我指明了方向!谢谢你的回答。这通电话大部分时间都有效。它很少失败。如果这是因为原子环境,我希望它总是失败。这种情况无论如何都不应该在奔腾上执行,boot_cpu_data.wp_工作_ok应该==0(对于大于386的所有对象)。