LINUX.ORG.RU

Сообщения linux__user

 

linux driver: wake_up_interruptible from tasklet

Предыдущая тема похоже забыта уже, решил написать ещё раз. Почему не работает wake_up_interruptible() из такслета? (в обработчике прерывания процесс просыпается когда нужно.)
Трассирую запрос, обработчик прерывания и такслет. Единственный процесс делает ioctl запрос с периодом 10 сек. процесс вызывает wait_event_interruptible_timeout(), имитирую прерывание (на входном канале появляется единственный передний фронт сигнала). Вызывается обработчик прерывания, изменяет условие (флаг обнуляется, вижу это через printk()), вызывается такслет (вижу через printk()), такслет вызывает wake_up_interrubtible(), процесс просыпается через 10 секунд (как будто wake_up_... просто игнорируется) значение флага равно 0 !!! (то есть планировщик должен был разбудить поток, но не сделал этого.)
В чём проблема, может чего-то не хватает? (ядро 2.6.35)
Ещё раз код, относящийся к тасклету:

// обработчик для тасклета
static void do_tasklet(unsigned long param)
{
   struct daq_device *dev = (struct daq_device*)param;
   struct dev_priv *dp    = (struct dev_priv*)dev->private;
   printk(KERN_INFO "from tasklet\n");
   wake_up_interruptible(&dp->wait_queue); 

}

// объявление такслета
static DECLARE_TASKLET(dev_tasklet, do_tasklet, 0);

// в коде инициализации устройства
int dev_init(struct daq_device* dev)
{
   ...
   // сохраняю указатель на устройство в структуре тасклета
   dev_tasklet.data = (unsigned long)dev;
   ...
}

linux__user
()

linux driver: wake_up_interruptible

Есть драйвер для некоего устройства, через 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);
}

Да, очередь ожидания инициализируется при старте драйвера для каждого устройства.

linux__user
()

RSS подписка на новые темы