Linux内核驱动程序:中断触发或超时

3
在Linux内核驱动程序中,我想无限循环执行以下序列:
  • 在时间T启用硬件IRQ
  • 在时间T到T + “大约” 15ms之间,如果触发了IRQ,则可以到达IRQ回调函数。 这里的“大约”是因为我没有使用RT内核,如果是14或16ms,那也可以。 在IRQ回调函数中,我需要写下get cpu_clock(0)并调用wake_up_interruptible。 超时需要被取消。 整个过程需要在5ms内重新开始。
  • 如果在T +“大约” 15ms时,未触发IRQ,则需要执行一些其他代码。然后应禁用IRQ。 整个过程需要在5ms内重新开始。

因此,在最坏情况下,在T +“大约”20ms时,整个过程需要重新开始。

请注意,如果在物理上在18ms触发IRQ,则太糟了,“我错过了火车”。 我将在下一个序列中捕获另一个硬件触发器。

在测试时,我使用了以下伪代码:

INIT_DELAYED_WORK(&priv->work, driver_work);
INIT_DELAYED_WORK(&priv->timeout, driver_timeout);
request_irq(priv->irq, driver_interrupt, IRQF_TRIGGER_RISING, "my_irq", priv);

那么:

queue_delayed_work(priv->workq, &priv->work, 0ms);

static void driver_work(struct work_struct *work) {
    queue_delayed_work(priv->workq, &priv->timeout, 15ms);
    priv->interruptCalled = 0;
    enable_irq(priv->irq);
}

然后:

static irqreturn_t driver_interrupt(int irq, void *_priv) {
    disable_irq_nosync(priv->irq);
    priv->interruptCalled = 1;
    cancel_delayed_work(&priv->timeout);
    priv->stamp = cpu_clock(0);
    wake_up_interruptible(&driver_wait);
    queue_delayed_work(priv->workq, &priv->work, 5ms);
    return IRQ_HANDLED;

}

And:

static void driver_timeout(struct work_struct *work) {
    if (priv->interruptCalled == 0) {
        disable_irq_nosync(priv->irq);
        //Do other small cleanup
        queue_delayed_work(priv->workq, &priv->work, 5ms);
    }
}

我正在尝试编写一个既健壮又简单的驱动程序。这个实现是否合适?怎么样可以改进这个实现呢?


cancel_delayed_work有时会生成“WARNING: at kernel/timer.c:1061 del_timer_sync+0x44/0x64()”警告。我该如何避免这种情况发生? - gregoiregentil
这个警告对应于WARN_ON(in_irq()); 从irq上下文中删除定时器似乎不是一个好主意。 - gregoiregentil
我已将“cancel_delayed_work(&priv->timeout);”放在“driver_work”的第一行。没有更多的警告了,而且我认为逻辑没有受损,因为priv->interruptCalled可以保护超时被触发的情况,即使IRQ已经被触发。 - gregoiregentil
1个回答

1
回答自己的问题:问题在于queue_delayed_work基于jiffies。或者说,当HZ=100(1 jiffy = 10ms)时,5ms是不可能的。高分辨率计时器提供了一个很好的解决方案。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接