检测VMX的简单Linux驱动报returned with preemption imbalance错误
错误根因分析
1. 核心问题:内联汇编缺少寄存器修改声明
x86架构下cpuid指令执行时会无条件覆写rax、rbx、rcx、rdx四个通用寄存器,你写的三段独立__asm__语句没有向GCC声明这些寄存器被修改,编译器会默认这些寄存器的值在汇编执行前后保持不变。
你之前尝试的禁用编译优化、调整inline属性都没有命中核心问题,错误本质是内联汇编的声明不符合GCC规范。Linux内核的模块初始化上下文会用通用寄存器存储包括抢占计数、锁状态等关键上下文信息,汇编代码非法篡改寄存器值后,内核判断抢占状态异常,就抛出preemption imbalance、scheduling while atomic报错。
2. 取消pr_info注释后错误消失的原因
pr_info是内核打印函数,GCC在执行函数调用前后会自动将被调用者需要保存的寄存器入栈、出栈,刚好把cpuid改坏的寄存器值恢复了,属于歪打正着掩盖了问题,并没有真正修复bug。
修复方案
将分散的内联汇编合并,补充完整的输入、输出、寄存器修改声明即可,参考代码如下:
#include <linux/module.h> #include <linux/init.h> #include <linux/types.h> inline bool vmxSupport(void) { u32 ecx; /* 明确指定输入:rax=1,输出:ecx存到变量,声明rbx、rdx会被修改 */ __asm__ volatile ("cpuid" : "=c"(ecx) : "a"(1) : "rbx", "rdx"); return (ecx >> 5) & 1; } static int __init my_init(void) { bool has_vmx = vmxSupport(); pr_info("Hello world from mod1, VMX support: %d\n", has_vmx); return 0; } static void __exit my_exit(void) { pr_info("Goodbye world from mod1\n"); } module_init(my_init); module_exit(my_exit); MODULE_AUTHOR("vpn"); MODULE_DESCRIPTION("module1.c"); MODULE_LICENSE("GPL v2");
代码里补充了返回值的使用逻辑,避免编译器认为这段代码无意义将其裁剪。
内容的提问来源于stack exchange,提问作者Vinod Pn
相关产品推荐
相关产品推荐

