如何实现平台驱动模块卸载控制?现有方案失效原因排查
问题背景
我开发了一个支持多线程访问的platform driver,通过IOCTL命令设置atomic_t标志,期望实现:
- 当标志为0时,允许卸载模块;
- 当标志为1时,调用
rmmod my_module无法卸载模块。
尝试了两种方案都失败:
方案1:返回-EBUSY被系统忽略
代码实现:
int driver_remove(struct platform_device *_pdev) { int reg = 0; reg = atomic_read(&_pdev->my_atomic_lock); if (reg) { pr_error("Cannot remove the module as it is still being used somewhere."); return -EBUSY; } // REST OF THE REMOVAL .... return 0; } static struct platform_driver my_driver = { .probe = driver_probe, .remove = driver_remove, .driver = { .name = MODULE_NAME, .owner = THIS_MODULE, .of_match_table = match_types, }, }; module_platform_driver(my_driver);
系统提示返回非零值会被忽略。
方案2:自旋锁阻止卸载未生效
代码实现:
int driver_remove(struct platform_device *_pdev) { // Be certain that no one has blocked the device (to prevent killing a running function) spin_lock(&_dev->_my_lock); // REST OF THE REMOVAL .... return 0; } static struct platform_driver my_driver = { .probe = driver_probe, .remove = driver_remove, .driver = { .name = MODULE_NAME, .owner = THIS_MODULE, .of_match_table = match_types, }, }; module_platform_driver(my_driver);
已确认自旋锁处于锁定状态,但无法阻止卸载。
错误原因分析
方案1错误:platform_driver的remove返回值不影响模块卸载流程
Linux内核中,platform_driver的remove函数返回值仅用于设备与驱动解绑的内部逻辑,完全不作用于模块卸载判断。执行rmmod时,内核只检查模块的引用计数,只要计数为0,不管remove返回什么,模块都会被强制卸载,你看到的“返回非零值被忽略”就是这个原因。
方案2错误:自旋锁逻辑完全倒置
你在remove函数里加锁的行为,实际效果是让卸载流程等待锁被释放后继续执行,而不是阻止卸载。正确逻辑应该是:当设备被使用时(其他线程持有锁),模块引用计数保持大于0,让rmmod直接失败,而不是在卸载时去抢锁等待。
正确实现方案
要实现“设备忙时阻止模块卸载”,核心是利用内核的模块引用计数机制,结合atomic变量标记设备状态:
示例代码
// 标记设备忙状态的原子变量 static atomic_t device_busy = ATOMIC_INIT(0); // IOCTL处理函数 long my_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { switch(cmd) { case IOCTL_SET_BUSY: // 设备转为忙状态,首次设置时增加模块引用计数 if (atomic_inc_return(&device_busy) == 1) { if (!try_module_get(THIS_MODULE)) { atomic_dec(&device_busy); return -EINVAL; } } return 0; case IOCTL_SET_IDLE: // 设备转为空闲状态,最后一次释放时减少模块引用计数 if (atomic_dec_return(&device_busy) == 0) { module_put(THIS_MODULE); } return 0; default: return -ENOTTY; } } // remove函数仅负责清理设备资源 int driver_remove(struct platform_device *_pdev) { // 可选:最后检查设备状态,记录错误日志 if (atomic_read(&device_busy) != 0) { pr_error("Device is still busy during removal!\n"); return -EBUSY; } // 清理设备硬件资源、内存等... return 0; } static struct platform_driver my_driver = { .probe = driver_probe, .remove = driver_remove, .driver = { .name = MODULE_NAME, .owner = THIS_MODULE, .of_match_table = match_types, }, }; module_platform_driver(my_driver);
关键说明
- 模块引用计数是核心:
rmmod会直接检查模块的refcount,只要计数>0,就会返回Resource temporarily unavailable,阻止卸载。 - 严格匹配引用操作:
try_module_get和module_put的调用次数必须完全对应,避免引用计数泄露导致模块无法卸载。 - 字符设备场景优化:如果你的驱动是字符设备,也可以在
open函数调用try_module_get,release函数调用module_put,这样只要设备文件被打开,模块就无法卸载,这是更通用的做法。
内容的提问来源于stack exchange,提问作者PMDP3
相关产品推荐
相关产品推荐

