最近在一次稳定性测试中,发现Kernel Log中出现了如下的Warring,如下:
代码语言:javascript复制WARNING: CPU: 4 PID: 2956 at /space/builder/repo/sprdroid6.0_trunk_k318_dev/kernel/kernel/irq/manage.c:444 __enable_irq 0x50/0x94()
Unbalanced enable for IRQ 227
Call trace:
c4 [<ffffffc00008b480>] dump_backtrace 0x0/0x164
c4 [<ffffffc00008b600>] show_stack 0x1c/0x28
c4 [<ffffffc0009ceefc>] dump_stack 0x74/0xb8
c4 [<ffffffc0000bb340>] warn_slowpath_common 0x98/0xc0
c4 [<ffffffc0000bb3dc>] warn_slowpath_fmt 0x74/0x88
c4 [<ffffffc00010eb50>] __enable_irq 0x4c/0x94
c4 [<ffffffc00010ebd8>] enable_irq 0x40/0x80
c4 [<ffffffc000630a38>] spidev_ioctl 0x6e4/0xbc0
c4 [<ffffffc0002016f0>] do_vfs_ioctl 0x374/0x5b8
c4 [<ffffffc0002019c0>] SyS_ioctl 0x8c/0xa4
c4 ---[ end trace 3e8cbdaab32a5c30 ]---
根据Kernel log的提示,在内核源码中找到出错的地方:
代码语言:javascript复制<kernel/irq/manage.c>
--------------------------------------------------------
void __enable_irq(struct irq_desc *desc, unsigned int irq)
{
switch (desc->depth) {
case 0:
err_out:
WARN(1, KERN_WARNING "Unbalanced enable for IRQ %dn", irq);
break;
case 1: {
if (desc->istate & IRQS_SUSPENDED)
goto err_out;
/* Prevent probing on this irq: */
irq_settings_set_noprobe(desc);
irq_enable(desc);
check_irq_resend(desc, irq);
/* fall-through */
}
default:
desc->depth--;
}
}
可以看到当desc->depth等于0的时候,就会出现上述的log。
而出现此问题的原因是什么? 或者是谁导致出现了这种情况?
通过栈调用关系可以看出,应用程序通过ioctl命令去操作底层的指纹识别的过程出现的。 SyS_ioct->do_vfs_ioctl->spidev_ioctl->enable_irq->__enable_irq
从调用关系看,最后调用__enable_irq的时候desc->depth=0,出现了“Unbalanced enable for IRQ”, 而根据字面意思是“使能不匹配的IRQ”。
根据提示猜想,是不是enable_irq需要与disable_irq成对的匹配调用。
基于此猜想,可以通过disable_irq与enable_irq的源代码验证猜想。
先看disable_irq的代码:
代码语言:javascript复制<kernel/irq/manage.c>
---------------------------------------------------------------------
void __disable_irq(struct irq_desc *desc, unsigned int irq)
{
if (!desc->depth )
irq_disable(desc);
}
从代码可以看出disable_irq的时候,对desc->depth做 操作。
代码语言:javascript复制<kernel/irq/manage.c>
---------------------------------------------------------------------
void __enable_irq(struct irq_desc *desc, unsigned int irq)
{
switch (desc->depth) {
case 0:
err_out:
WARN(1, KERN_WARNING "Unbalanced enable for IRQ %dn", irq);
break;
case 1: {
if (desc->istate & IRQS_SUSPENDED)
goto err_out;
/* Prevent probing on this irq: */
irq_settings_set_noprobe(desc);
irq_enable(desc);
check_irq_resend(desc, irq);
/* fall-through */
}
default:
desc->depth--;
}
}
而__enable_irq代码是对desc->depth做–操作了。
根据一个做 操作,一个做–操作。可以得出disable_irq与enable_irq需要成对的使用。
而出现上述的原因就是在没有调用disable_irq的前提下,直接调用enable_irq,那时候desc->depth等于0,这时候enable_irq就会出现Warring, 也就是说这次的enable_irq没有起作用的。
假设驱动中先disable_irq,这时候desc->depth 了, desc->depth就等于1, 而这时候再次enable_irq就会走进case 1的分支中去。 然后desc->depth–了,最后desc->depth等于0
所以根据分析,出现上述的原因就是enable_irq与disable_irq在驱动中没有匹配使用导致的。