前几天在一台测试机器上遇到了rmmod失败的现象,通过lsmod可以看到它的引用计数为1。但是我可以确定已经没有被使用了,所以这应该是一个代码中的bug。
从网上可以找到一篇写得非常好的rmmod失败的分析文章,里面还提供了一段代码,可以编译出一个ko文件,通过加载这个module并且传入有问题的module名字,可以达到强行问题module引用计数的效果,然后就可以使用rmmod命令来删除它了。
这篇文章的地址是 https://blog.csdn.net/gatieme...
作者也给出了代码的github地址https://github.com/gatieme/LD...
可是里面提供的源代码对应的内核版本是4以上的,我的机器是3.10,所以在编译的时候会遇到语法问题
[root@controller22860 force_rmmod]# make
echo /root/force_rmmod
/root/force_rmmod
echo 3.10.0-957.10.2.el7.x86_64
3.10.0-957.10.2.el7.x86_64
echo /lib/modules/3.10.0-957.10.2.el7.x86_64/build
/lib/modules/3.10.0-957.10.2.el7.x86_64/build
make -C /lib/modules/3.10.0-957.10.2.el7.x86_64/build M=/root/force_rmmod modules
make[1]: Entering directory `/usr/src/kernels/3.10.0-957.10.2.el7.x86_64'
CC [M] /root/force_rmmod/force_rmmod.o
/root/force_rmmod/force_rmmod.c: In function ‘force_cleanup_module’:
/root/force_rmmod/force_rmmod.c:85:17: warning: format ‘%u’ expects argument of type ‘unsigned int’, but argument 4 has type ‘long unsigned int’ [-Wformat=]
mod->name ,mod->state, module_refcount(mod));
^
In file included from /root/force_rmmod/force_rmmod.c:6:0:
/root/force_rmmod/force_rmmod.c:110:46: error: ‘struct module’ has no member named ‘refcnt’
local_set((local_t*)per_cpu_ptr(&(mod->refcnt), cpu), 0);
^
include/asm-generic/local.h:29:43: note: in definition of macro ‘local_set’
#define local_set(l,i) atomic_long_set((&(l)->a),(i))
^
include/asm-generic/percpu.h:46:2: note: in expansion of macro ‘__verify_pcpu_ptr’
__verify_pcpu_ptr((__p)); \
^
include/linux/percpu.h:149:31: note: in expansion of macro ‘SHIFT_PERCPU_PTR’
#define per_cpu_ptr(ptr, cpu) SHIFT_PERCPU_PTR((ptr), per_cpu_offset((cpu)))
^
/root/force_rmmod/force_rmmod.c:110:29: note: in expansion of macro ‘per_cpu_ptr’
local_set((local_t*)per_cpu_ptr(&(mod->refcnt), cpu), 0);
^
compilation terminated due to -Wfatal-errors.
于是我简单分析了一下3.10的代码,发现它的 struct module 里面确实是没有定义 refcnt 这个成员的。所以需要修改一下 force_rmmod.c 的代码,我把报错那段改成了下面这样:
// 清除驱动的引用计数
int ref_cnt = 0;
for_each_possible_cpu(cpu)
{
//local_set((local_t*)per_cpu_ptr(&(mod->refcnt), cpu), 0);
//local_set(__module_ref_addr(mod, cpu), 0);
if (per_cpu_ptr(mod->refptr, cpu)->decs) {
printk("module has dec %d on cpu %d\n", per_cpu_ptr(mod->refptr, cpu)->decs, cpu);
ref_cnt -= per_cpu_ptr(mod->refptr, cpu)->decs;
}
if (per_cpu_ptr(mod->refptr, cpu)->incs) {
//module_put(mod);
printk("module has inc %d on cpu %d\n", per_cpu_ptr(mod->refptr, cpu)->incs, cpu);
ref_cnt += per_cpu_ptr(mod->refptr, cpu)->incs;
}
}
for(int i = 0; i < ref_cnt; i++) {
module_put(mod);
}
主要的原理就是通过计算各个cpu上对该模块的引用计数累计得到当前的计数值,然后按照计数值做module_put动作,就可以把引用计数值降到0了,随后就可以正常rmmod了。
我把作者的repo fork之后修改了一下,新的文件在 https://github.com/yzx1983/LD...
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。