You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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);

具体排查方向

  1. 中断触发时机与初始化顺序问题

    • 首次加载时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);
      
  2. 共享中断的有效性校验

    • 若使用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. 3.10内核中断子系统的特性适配

    • Linux 3.10版本的request_irq存在提前触发中断的场景,可尝试将中断注册逻辑延迟到字符设备初始化完成后,或使用request_threaded_irq替代,将中断处理放到线程上下文执行,避免初始化阶段的竞态。
  4. 卸载时的资源完整性检查

    • 首次卸载模块时,确保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);
      }
      
  5. 告警根源定位

    • handle.c的nobody cared告警,本质是中断触发后无处理函数返回IRQ_HANDLED。首次加载时需检查:
      • dev_id是否在中断触发前已正确关联到中断处理函数
      • 中断处理函数是否因未初始化的内存访问导致异常退出

内容的提问来源于stack exchange,提问作者WangYangMing

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 15:45:05