Подтвердить что ты не робот

Защита от перехвата прерываний ядра Linux?

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

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

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(...);
}
  • Должно interrupt_handler: down_interruptible быть spin_lock_irq/spin_lock_irqsave/local_irq_disable?
  • Должно set/clear_intr: spin_lock_irq быть spin_lock_irqsave/local_irq_disable?
  • Может ли он (H/W → kernel → обработчик драйверов) продолжать генерировать/получать прерывания до его очистки? Может ли interrupt_handler продолжать получать вызов внутри него?
  • Если в настоящее время реализован обработчик прерываний, он будет заблокирован на down_interruptible?

От LDD3: -

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


Изменить 1) после некоторой приятной помощи, предложения: -

  • удалите down_interruptible из interrupt_handler
  • Переместить 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: чтение Почему код ядра/поток, выполняемый в контексте прерывания, не может спать?, который ссылается на Robert Loves article, я прочел следующее:

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

И у меня есть быстрые прерывания (SA_INTERRUPT)! Поэтому нет необходимости в mutex/locks/semafhores/spins/waits/sleeps/etc/etc!

4b9b3361

Ответ 1

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

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

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

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

Ответ 2

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

Ответ 3

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

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

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