Защита мьютекса обработчика прерываний ядра Linux? - PullRequest
13 голосов
/ 04 июля 2011

Нужно ли защищать мой обработчик прерываний, вызываемый много раз для одного и того же прерывания?

Учитывая следующий код, я не уверен в системных вызовах, которые я должен сделать. Я получаю редкие, случайные тупики с этой текущей реализацией: -

void interrupt_handler(void)
{
    down_interruptible(&sem);  // or use a lock here ?

    clear_intr(); // clear interrupt source on H/W

    wake_up_interruptible(...);

    up(&sem); // unlock?

    return IRQ_HANDLED;
}

void set/clear_intr()
{
    spin_lock_irq(&lock);
    RMW(x); // set/clear a bit by read/modify/write the H/W interrupt routing register
    spin_unlock_irq(&lock);
}

void read()
{
    set_intr();  // same as clear_intr, but sets a bit
    wait_event_interruptible(...);
}
  1. Должно ли interrupt_handler: down_interruptible быть spin_lock_irq / spin_lock_irqsave / local_irq_disable?
  2. Должно ли set/clear_intr: spin_lock_irq быть spin_lock_irqsave / local_irq_disable?
  3. Может ли он (H / W -> kernel -> обработчик драйвера) продолжать генерировать / получать прерывания, пока он не будет очищен? Может ли interrupt_handler продолжать звонить, находясь внутри него?
  4. Если, как в настоящее время реализовано, обработчик прерываний является реентерабельным, то он будет блокироваться на down_interruptible?

От LDD3: -

должен быть reentrant - он должен быть способен работать в более чем одном контексте одновременно.


Редактировать 1) после небольшой помощи, предложения: -

  1. удалить down_interruptible изнутри interrupt_handler
  2. Переместить spin_lock_irq за пределы методов set / clear (не нужно spin_lock_irqsave говорите?) Я действительно не вижу в этом пользы?!

Код: -

void interrupt_handler(void)
{
    read_reg(y); // eg of other stuff in the handler

    spin_lock_irq(&lock);

    clear_intr(); // clear interrupt source on H/W

    spin_unlock_irq(&lock);

    wake_up_interruptible(...);

    return IRQ_HANDLED;
}

void set/clear_intr()
{
    RMW(x);
}

void read()
{
    error_checks(); // eg of some other stuff in the read method

    spin_lock_irq(&lock);

    set_intr();  // same as clear_intr, but sets a bit

    spin_unlock_irq(&lock);

    wait_event_interruptible(...);

    // more code here...
}

Edit2) После прочтения еще нескольких SO-сообщений: чтение Почему код / ​​поток ядра, выполняющийся в контексте прерываний, не может спать? , который ссылается на Роберта Лавса статья , я прочитал это:

некоторые обработчики прерываний (известные в Linux как быстрые обработчики прерываний) со всеми прерываниями на местном процессор отключен. Это сделано для убедитесь, что обработчик прерываний работает без перерыва, так быстро, как возможный. Более того, все прерывают обработчики бегут со своим текущим линия прерывания отключена на всех процессоры. Это гарантирует, что два обработчики прерываний для того же линия прерывания не запускается одновременно. Это также предотвращает устройство писатели драйвера от необходимости справляться рекурсивные прерывания, которые усложняют программирования.

И у меня включены быстрые прерывания (SA_INTERRUPT)! Так что нет необходимости в мьютексе / замках / семафорах / вращениях / ожиданиях / снах и т. Д. / И т. Д.

Ответы [ 3 ]

12 голосов
/ 04 июля 2011

Не используйте семафоры в контексте прерывания, используйте spin_lock_irqsave. цитируя LDD3:

Если у вас есть спин-блокировка, которая может быть взят код, который работает в (оборудование или программное обеспечение) контекст прерывания, вы должен использовать одну из форм spin_lock это отключает прерывания. дела в противном случае может заблокировать систему, рано или поздно. Если у вас нет доступа Ваша блокировка в аппаратном прерывании обработчик, но вы делаете через программное обеспечение прерывания (в коде, который заканчивается Тасклет, например, тема покрыта в главе 7), вы можете использовать spin_lock_bh, чтобы безопасно избежать тупиков все еще позволяя аппаратное обеспечение прерывания для обслуживания.

Что касается пункта 2, заставьте свои set_intr и clear_intr требовать, чтобы вызывающий абонент заблокировал спин-блокировку, в противном случае вы обнаружите, что ваш код заблокирован. Снова из LDD3:

Для правильной работы блокировки Вы должны написать некоторые функции с предположение, что их вызывающий уже приобрел соответствующий замок (ы). Обычно только ваши внутренние, статичные функции могут быть написаны таким образом; функции, вызываемые извне, должны обрабатывать блокировку явно. Когда ты написать внутренние функции, которые делают предположения о блокировке, сделай сам (и любой, кто работает с вашим код) одолжение и документировать те предположения явно. Это может быть очень трудно вернуться через несколько месяцев и выяснить, нужно ли вам держать блокировка для вызова определенной функции или нет.

2 голосов
/ 20 марта 2013

Используйте спин-блокировку в контексте прерывания, потому что вы не хотите спать в контексте прерывания, если не получили блокировку.

0 голосов
/ 11 сентября 2013

Код, который вы разместили, не похож на обработчик irq драйвера устройства. Обработчики irq в драйверах ядра возвращают irqreturn_t и принимают в качестве аргумента int irq_no, void * data.

Вы также не указали, регистрируете ли вы многопоточный или непоточный обработчик. Непоточный обработчик irq не может иметь никаких спящих вызовов независимо от того, удерживаете ли вы какие-либо спин-блокировки wait_event, mutex, семафор и т. д., все являются спящими вызовами и не должны использоваться в непотоковом обработчике irq. Однако вы можете удерживать спин-блокировку, чтобы предотвратить прерывание обработчика прерываний. Это будет гарантировать, что маскируемые irq и планировщик не прервут ваш обработчик irq в середине.

В потоковом обработчике irq можно использовать такие вещи, как спящие вызовы (очереди ожидания, мьютекс и т. Д.), Но они по-прежнему не рекомендуются.

...