Intereting Posts
отправка электронной почты с реликвией, даже если Postfix не работает локально Является ли символ «at» @ относительно безопасным в путях файлов? Найти рекурсивные каталоги и получить общее количество Завершение вкладки на псевдониме mosh (ssh)? Очень слабое беспроводное соединение при установке образа Live Debian Удалить столбцы, сумма которых равна нулю Как обновить пакет tbz, который я создаю в Freebsd? Создать переменную на основе порядка, в котором файл находится в алфавитном списке файлов Почему «дата» показывает время UTC, даже с другим часовым поясом? Простой способ просмотра содержимого каталогов в файловых системах Linux / UNIX Подчиненные GIDs / UID с LXC и для непривилегированного пользователя? выходной объем воспроизводимого в данный момент звука Параметр terminfo u6 Как предотвратить использование ярлыка системы для применения к tmux? Несколько записей MODULEPATH в .modulesbeginenv

Как ядро ​​Linux обрабатывает общие IRQ?

Согласно тому, что я читал до сих пор, «когда ядро ​​получает прерывание, вызывается все зарегистрированные обработчики».

Я понимаю, что зарегистрированные обработчики для каждого IRQ могут просматриваться через /proc/interrupts , и я также понимаю, что зарегистрированные обработчики исходят от драйверов, которые вызвали request_irq, проходящих в обратном вызове примерно из формы:

 irqreturn_t (*handler)(int, void *) 

Основываясь на том, что я знаю, каждый из этих обратных вызовов обработчика прерываний, связанных с конкретным IRQ, должен быть вызван, и обработчик должен определить, должно ли это прерывание обрабатываться им. Если обработчик не должен обрабатывать конкретное прерывание, он должен вернуть макрос ядра IRQ_NONE .

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

Причина, по которой я пытаюсь понять эти детали, состоит в том, что я возился с механизмом kexec для повторного запуска ядра в середине работы системы во время игры с выводами сброса и различными регистрами на мосте PCIe, а также в нисходящем потоке PCI. И при этом после перезагрузки я либо получаю панику ядра, либо другие драйверы, жалующиеся на то, что они получают прерывания, даже если операция не выполняется.

Как обработчик решил, что прерывание должно быть обработано им, это тайна.

Изменить: в случае, если это имеет значение, архитектура процессора, о которой идет речь, составляет x86.

Об этом говорится в главе 10 драйверов устройств Linux , 3-е издание, от Corbet et al. Он доступен бесплатно в Интернете , или вы можете бросить несколько шекелей O'Reilly для мертвых деревьев или электронных книг. Часть, относящаяся к вашему вопросу, начинается со страницы 278 в первой ссылке.

Для чего это стоит, вот моя попытка перефразировать эти три страницы, а также другие бит, которые я искал в Google:

  • Когда вы регистрируете общий обработчик IRQ, ядро ​​проверяет это:

    а. для этого прерывания не существует другого обработчика или

    б. все ранее зарегистрированные пользователи также запросили обмен прерываниями

    Если применяется любой из этих случаев, он проверяет, что ваш параметр dev_id уникален, поэтому ядро ​​может отличать несколько обработчиков, например, во время удаления обработчика.

  • Когда аппаратное устройство PCI¹ поднимает строку IRQ, вызывается низкоуровневый обработчик прерываний ядра, и он, в свою очередь, вызывает всех зарегистрированных обработчиков прерываний, передавая каждый назад dev_id вы использовали для регистрации обработчика через request_irq() .

    Значение dev_id должно быть уникальным для машины. Общий способ сделать это – передать указатель на структуру каждого устройства, используемую вашим драйвером для управления этим устройством. Поскольку этот указатель должен находиться в памяти вашего драйвера, чтобы он был полезен для драйвера, он ipso facto уникален для этого драйвера.

    Если для данного прерывания записано несколько драйверов, все они будут вызываться, когда какое-либо из устройств поднимает эту общую линию прерываний. Если бы это не было вашим драйвером, обработчик прерываний вашего драйвера будет передан значение dev_id которое не принадлежит ему. Обработчик прерываний вашего драйвера должен немедленно вернуться, когда это произойдет.

    Другим случаем является то, что ваш драйвер управляет несколькими устройствами. Обработчик прерываний драйвера получит одно из значений dev_id известных водителю. Ваш код должен опросить каждое устройство, чтобы узнать, какой из них вызвал прерывание.

    Пример Corbet et al. это параллельный порт ПК. Когда он утверждает строку прерывания, он также устанавливает верхний бит в свой первый регистр устройства. (То есть, inb(0x378) & 0x80 == true , при условии, что стандартная нумерация портов ввода / вывода.) Когда ваш обработчик обнаруживает это, он должен выполнять свою работу, а затем очистить IRQ, записав значение, считанное с ввода / Вывода обратно в порт с очищенным верхним битом.

    Я не вижу причин, чтобы какой-то конкретный механизм был особенным. Другое аппаратное устройство могло выбрать другой механизм. Важно только то, что для устройства, которое разрешает общие прерывания, оно должно иметь некоторый путь для драйвера, чтобы прочитать состояние прерывания устройства и как-то очистить прерывание. Вам нужно будет прочитать техническое описание вашего устройства или руководство по программированию, чтобы узнать, какой механизм использует ваше конкретное устройство.

  • Когда ваш обработчик прерываний сообщает ядру, что он обрабатывает прерывание, это не останавливает ядро ​​от продолжения вызова других обработчиков, зарегистрированных для этого же прерывания. Это неизбежно, если вы должны делиться линией прерывания при использовании прерываний, вызванных уровнем.

    Представьте, что два устройства одновременно утверждают одну и ту же линию прерывания. (Или, по крайней мере, так близко ко времени, когда ядро ​​не успевает вызывать обработчик прерываний, чтобы очистить строку и тем самым видеть второе утверждение как отдельное.) Ядро должно вызывать все обработчики для этой строки прерывания, чтобы дать каждому возможность запросить связанное с ним оборудование, чтобы узнать, нуждается ли он в этом. Вполне возможно, что два разных драйвера успешно справятся с прерыванием в пределах одного и того же прохода через список обработчиков для данного прерывания.

    Из-за этого крайне важно, чтобы ваш драйвер сообщил устройству, которому он удается очистить свое утверждение прерывания, до того, как обработчик прерывания вернется. Мне непонятно, что происходит иначе. Строка прерывания с непрерывным подтверждением приведет либо к тому, что ядро ​​непрерывно вызовет обработчики общих прерываний, либо замаскирует способность ядра видеть новые прерывания, чтобы обработчики никогда не вызывались. В любом случае, катастрофа.


Примечания:

  1. Я указал PCI выше, потому что все вышеперечисленное предполагает прерывания, вызванные уровнем , которые используются в исходной спецификации PCI. В ISA использовались прерывания с прерыванием по фронту , что в лучшем случае позволяло делиться друг с другом, и возможно даже тогда, когда оно поддерживается аппаратным обеспечением. PCIe использует прерывания с сообщениями; сообщение прерывания содержит уникальное значение, которое ядро ​​может использовать, чтобы избежать игры с галочкой раунда, требуемой при совместном использовании прерываний PCI. PCIe может исключить необходимость обмена прерываниями. (Я не знаю, на самом деле ли это, просто у него есть потенциал.)

  2. Все драйверы ядра Linux используют одно и то же пространство памяти, но не связанный с ним драйвер не должен находиться в другом пространстве памяти. Если вы не передадите этот указатель, вы можете быть уверены, что другой драйвер не будет придумывать это же значение случайно сам по себе.

Когда драйвер запрашивает общий IRQ, он передает указатель на ядро ​​на ссылку на структуру, специфичную для устройства, в пространстве памяти драйвера.

Согласно LDD3:

Всякий раз, когда два или более драйверов используют линию прерывания, а аппаратное прерывание процессора на этой линии, ядро ​​вызывает каждый обработчик, зарегистрированный для этого прерывания, передавая каждый свой собственный dev_id.

Проверяя обработчики IRQ нескольких драйверов, выясняется, что они сами исследуют аппаратное обеспечение, чтобы определить, должно ли оно обрабатывать прерывание или возвращать IRQ_NONE .

Примеры

Драйвер UHCI-HCD

  status = inw(uhci->io_addr + USBSTS); if (!(status & ~USBSTS_HCH)) /* shared interrupt, not mine */ return IRQ_NONE; 

В приведенном выше коде драйвер USBSTS регистр USBSTS чтобы определить, есть ли прерывание для обслуживания.

Драйвер SDHCI

  intmask = sdhci_readl(host, SDHCI_INT_STATUS); if (!intmask || intmask == 0xffffffff) { result = IRQ_NONE; goto out; } 

Как и в предыдущем примере, драйвер проверяет регистр состояния SDHCI_INT_STATUS чтобы определить, нужно ли ему обслуживать прерывание.

Драйвер Ath5k

  struct ath5k_softc *sc = dev_id; struct ath5k_hw *ah = sc->ah; enum ath5k_int status; unsigned int counter = 1000; if (unlikely(test_bit(ATH_STAT_INVALID, sc->status) || !ath5k_hw_is_intr_pending(ah))) return IRQ_NONE; 

Еще один пример.

Пожалуйста, посетите эту ссылку :

Его обычная практика запуска нижних половин или любой другой логики в обработчике IRQ только после проверки статуса IRQ из зарегистрированного в памяти регистра. Следовательно, проблема по умолчанию решена хорошим программистом.