ARM平台3.10内核GPIO中断驱动首次加载告警求助
GPIO中断驱动首次加载触发
kernel/irq/handle.c告警,重新加载正常的排查方案 问题背景
在ARM架构、Linux 3.10.65内核环境下实现GPIO中断驱动spiio.c,核心功能为注册字符设备与GPIO中断。遇到以下异常:
- 首次加载模块时,触发
kernel/irq/handle.c相关告警 - 卸载模块后重新加载,驱动运行正常
- 已按建议将互斥锁替换为自旋锁,告警仍未消除
相关日志与代码示例
首次加载的dmesg告警片段
[xxx.xxxxxx] irq xxx: nobody cared (try booting with the "irqpoll" option) [xxx.xxxxxx] CPU: 0 PID: xxx Comm: insmod Not tainted 3.10.65 #1 [xxx.xxxxxx] [<c0014a48>] (unwind_backtrace+0x0/0x11c) from [<c0011740>] (show_stack+0x10/0x14) [xxx.xxxxxx] [<c0011740>] (show_stack+0x10/0x14) from [<c0227b10>] (__report_bad_irq+0x38/0xc4) [xxx.xxxxxx] [<c0227b10>] (__report_bad_irq+0x38/0xc4) from [<c0227e34>] (note_interrupt+0x194/0x1e0) [xxx.xxxxxx] [<c0227e34>] (note_interrupt+0x194/0x1e0) from [<c0226968>] (handle_irq_event_percpu+0x98/0x160)
驱动核心代码片段
static irqreturn_t spiio_irq_handler(int irq, void *dev_id) { struct spiio_dev *dev = dev_id; unsigned long flags; spin_lock_irqsave(&dev->lock, flags); // 中断处理逻辑 spin_unlock_irqrestore(&dev->lock, flags); return IRQ_HANDLED; } static int __init spiio_init(void) { int ret; struct spiio_dev *dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) return -ENOMEM; spin_lock_init(&dev->lock); // 注册字符设备 ret = register_chrdev(SPIIO_MAJOR, "spiio", &spiio_fops); if (ret < 0) { kfree(dev); return ret; } // 申请GPIO并注册中断 dev->irq = gpio_to_irq(SPIIO_GPIO); ret = request_irq(dev->irq, spiio_irq_handler, IRQF_TRIGGER_FALLING | IRQF_SHARED, "spiio", dev); if (ret) { unregister_chrdev(SPIIO_MAJOR, "spiio"); kfree(dev); return ret; } return 0; } static void __exit spiio_exit(void) { struct spiio_dev *dev = spiio_dev; free_irq(dev->irq, dev); unregister_chrdev(SPIIO_MAJOR, "spiio"); kfree(dev); } module_init(spiio_init); module_exit(spiio_exit);
具体排查方向
中断触发时机与初始化顺序问题
- 首次加载时GPIO引脚可能处于不稳定状态,导致
request_irq刚完成就触发中断,此时驱动内部资源未完全就绪。在申请中断前先固定GPIO状态:gpio_direction_input(SPIIO_GPIO); // 根据触发条件设置初始电平,避免误触发 gpio_set_value(SPIIO_GPIO, 1); - 注册中断前先屏蔽该中断,完成初始化后再解除屏蔽:
unsigned int irq = gpio_to_irq(SPIIO_GPIO); disable_irq(irq); ret = request_irq(irq, spiio_irq_handler, IRQF_TRIGGER_FALLING | IRQF_SHARED, "spiio", dev); if (!ret) enable_irq(irq);
- 首次加载时GPIO引脚可能处于不稳定状态,导致
共享中断的有效性校验
- 若使用
IRQF_SHARED,中断处理函数必须先确认是否为本设备触发的中断,避免误处理其他设备的中断:irqreturn_t spiio_irq_handler(int irq, void *dev_id) { struct spiio_dev *dev = dev_id; unsigned long flags; // 先校验中断是否由本设备触发 if (gpio_get_value(dev->gpio) != 0) { return IRQ_NONE; } spin_lock_irqsave(&dev->lock, flags); // 执行实际处理逻辑 spin_unlock_irqrestore(&dev->lock, flags); return IRQ_HANDLED; }
- 若使用
3.10内核中断子系统的特性适配
- Linux 3.10版本的
request_irq存在提前触发中断的场景,可尝试将中断注册逻辑延迟到字符设备初始化完成后,或使用request_threaded_irq替代,将中断处理放到线程上下文执行,避免初始化阶段的竞态。
- Linux 3.10版本的
卸载时的资源完整性检查
- 首次卸载模块时,确保GPIO状态、中断标记等资源完全恢复,避免第二次加载时环境异常:
static void __exit spiio_exit(void) { struct spiio_dev *dev = spiio_dev; free_irq(dev->irq, dev); // 恢复GPIO初始状态 gpio_direction_output(SPIIO_GPIO, 0); unregister_chrdev(SPIIO_MAJOR, "spiio"); kfree(dev); }
- 首次卸载模块时,确保GPIO状态、中断标记等资源完全恢复,避免第二次加载时环境异常:
告警根源定位
handle.c的nobody cared告警,本质是中断触发后无处理函数返回IRQ_HANDLED。首次加载时需检查:dev_id是否在中断触发前已正确关联到中断处理函数- 中断处理函数是否因未初始化的内存访问导致异常退出
内容的提问来源于stack exchange,提问作者WangYangMing
相关产品推荐
相关产品推荐

