Есть драйвер для некоего устройства, через ioctl запрос процессу нужно ждать внешнее событие, которое генерирует аппаратное прерывание на устройстве.
В коде запроса я использую wait_event_interruptible_timeout(), передавая туда объект очереди ожидания и условие, в тасклете вызываю wake_up_interruptible() с тем же объектом очереди ожидания. В обработчике прерывания условие становится истинным, и вызывается тасклет. Используя printk, в системном логе я вижу, что выполняется обработчик прерывания и тасклет, но процесс просыпается только по истечении таймаута, независимо от того было прерывание или нет!
В чём может быть проблема?
Приведу код запроса, обработчика и тасклета.
Ioctl запрос:
int wait_event(struct *dev, ...)
{
struct dev_priv *dp = dev->private;
...
spin_lock_irqsave(dp->lock, flags);
dp->flag = 1;
spin_unlock_irqrestore(dp->lock, flags);
wait_event_interruptible_timeout(dp->queue, (flag == 0), timeout);
spin_lock_irqsave(dp->lock, flags);
dp->flag = 0; // явно очищаю флаг, если таймаут истёк
spin_unlock_irqrestore(dp->lock, flags);
...
}
Обработчик прерывания:
irq_return_t dev_isr(void *dev_id, ...)
{
struct device *dev = (struct device*)dev_id;
struct dev_priv *dp = dev->private;
int sched_bh = 0;
...
spin_lock_irqsave(dp->lock, flags);
if(dp->flag != 0) // процесс ждёт событие
{
dp->flag = 0;
sched_bh = 1;
printk(...); // сообщение появляется в логе при прерывании
}
spin_unlock_irqrestore(dp->lock, flags);
...
if(sched_bh == 1)
tasklet_schedule(&dev_tasklet);
return IRQ_HANDLED;
}
Тасклет:
void do_tasklet(ulong param)
{
struct device* dev = (struct device*)param;
struct dev_priv *dp = dev->private;
wake_up_interruptible(&dp->queue);
}
Да, очередь ожидания инициализируется при старте драйвера для каждого устройства.