Согласно тому, что я прочитал до сих пор, «когда ядро получает прерывание, все зарегистрированные обработчики вызываются».
Я понимаю, что зарегистрированные обработчики для каждого IRQ могут просматриваться через /proc/interrupts
, и я также понимаю, что зарегистрированные обработчики происходят от драйверов, которые вызвали request_irq
передачу обратного вызова примерно в форме:
irqreturn_t (*handler)(int, void *)
Исходя из того, что я знаю, должен быть вызван каждый из этих обратных вызовов обработчика прерываний, связанных с конкретным IRQ, и обработчик должен определить, действительно ли оно должно обрабатывать прерывание. Если обработчик не должен обрабатывать конкретное прерывание, он должен вернуть макрос ядра IRQ_NONE
.
Что мне трудно понять, так это то, как каждый драйвер должен определять, должен ли он обрабатывать прерывание или нет. Я предполагаю, что они могут отслеживать внутренне, если они ожидают прерывания. Если это так, я не знаю, как они могли бы справиться с ситуацией, в которой несколько водителей за одним IRQ ожидают прерывания.
Причина, по которой я пытаюсь понять эти детали, заключается в том, что я перебираю kexec
механизм повторного запуска ядра в середине работы системы, играя с выводами сброса и различными регистрами на мосту PCIe, а также в нисходящем PCI устройство. И при этом после перезагрузки у меня либо паника ядра, либо другие драйверы, жалующиеся на то, что они получают прерывания, даже если никакие операции не выполнялись.
Как обработчик решил, что прерывание должно обрабатываться им, остается загадкой.
Редактировать: В случае, если это актуально, рассматриваемая архитектура процессора x86
.
Ответы:
Это описано в главе 10 « Драйверы устройств Linux» , 3-е издание, Corbet et al. Он доступен бесплатно онлайн , или вы можете отказаться от шекелей О'Рейли за формы мертвых деревьев или электронных книг. Часть, относящаяся к вашему вопросу, начинается на странице 278 в первой ссылке.
Для чего это стоит, вот моя попытка перефразировать эти три страницы, а также другие биты, которые я гуглил:
Когда вы регистрируете общий обработчик IRQ, ядро проверяет, что либо:
а. другого обработчика для этого прерывания не существует, или
б. все ранее зарегистрированные также запрашивали обмен прерываниями
Если применяется любой из этих случаев, он проверяет
dev_id
уникальность вашего параметра, чтобы ядро могло различать несколько обработчиков, например, при удалении обработчика.Когда аппаратное устройство PCI¹ поднимает линию IRQ, вызывается низкоуровневый обработчик прерываний ядра, который, в свою очередь, вызывает все зарегистрированные обработчики прерываний, передавая каждый назад тот, который
dev_id
вы использовали для регистрации обработчикаrequest_irq()
.dev_id
Значение должно быть машинно-уникальным. Обычный способ сделать это - передать указатель на устройство,struct
используемое вашим драйвером для управления этим устройством. Поскольку этот указатель должен находиться в области памяти вашего драйвера, чтобы он был полезным для драйвера, он ipso facto уникален для этого драйвера.Если для данного прерывания зарегистрировано несколько драйверов, все они будут вызваны, когда какое-либо из устройств вызовет эту общую линию прерывания. Если это было не устройство вашего драйвера, которое сделало это, обработчику прерывания вашего драйвера будет передано
dev_id
значение, которое ему не принадлежит. Обработчик прерываний вашего водителя должен немедленно вернуться, когда это произойдет.Другое дело, что ваш драйвер управляет несколькими устройствами. Обработчик прерываний драйвера получит одно из
dev_id
значений, известных драйверу. Ваш код должен опрашивать каждое устройство, чтобы выяснить, какое из них вызвало прерывание.Пример Corbet et al. дать это параллельный порт ПК. Когда он утверждает строку прерывания, он также устанавливает старший бит в своем первом регистре устройства. (То есть
inb(0x378) & 0x80 == true
при условии стандартной нумерации портов ввода / вывода.) Когда ваш обработчик обнаружит это, он должен выполнить свою работу, а затем очистить IRQ, записав значение, считанное из порта ввода / вывода, обратно в порт с верхним немного очищен.Я не вижу никакой причины, по которой конкретный механизм является особенным. Другое аппаратное устройство может выбрать другой механизм. Единственная важная вещь заключается в том, что для того, чтобы устройство разрешало общие прерывания, у него должен был быть какой-то способ, чтобы драйвер мог прочитать состояние прерывания устройства, и какой-то способ, чтобы очистить прерывание. Вы должны прочитать таблицу данных вашего устройства или руководство по программированию, чтобы узнать, какой механизм использует ваше устройство.
Когда ваш обработчик прерываний сообщает ядру, что обработал прерывание, это не мешает ядру продолжать вызывать любые другие обработчики, зарегистрированные для того же самого прерывания. Это неизбежно, если вам нужно совместно использовать линию прерываний при использовании прерываний, инициируемых уровнем.
Представьте, что два устройства одновременно устанавливают одну линию прерывания. (Или, по крайней мере, настолько близко по времени, что ядро не успевает вызвать обработчик прерывания, чтобы очистить строку и, таким образом, увидеть второе утверждение как отдельное.) Ядро должно вызвать все обработчики для этой строки прерывания, чтобы дать каждому возможность запросить связанное с ним оборудование, чтобы узнать, нужно ли ему уделить внимание. Для двух разных драйверов вполне возможно успешно обработать прерывание в пределах одного прохода через список обработчиков для данного прерывания.
Из-за этого обязательно, чтобы ваш драйвер сообщил устройству, что ему удается очистить свое утверждение прерывания за некоторое время до возвращения обработчика прерывания. Мне не ясно, что происходит иначе. Непрерывно установленная строка прерывания либо приведет к тому, что ядро будет непрерывно вызывать общие обработчики прерываний, либо замаскирует способность ядра видеть новые прерывания, поэтому обработчики никогда не будут вызываться. В любом случае, катастрофа.
Примечания:
Я указывал PCI выше, потому что все вышеперечисленное предполагает прерывания, инициируемые уровнем , как использовалось в оригинальной спецификации PCI. В ISA использовались прерывания, инициируемые фронтом , что делало совместное использование в лучшем случае хитрым, и возможно даже тогда, только когда поддерживается аппаратным обеспечением. PCIe использует сообщения с прерываниями; сообщение о прерывании содержит уникальное значение, которое ядро может использовать, чтобы избежать круговой игры, предполагающей совместное использование прерываний PCI. PCIe может устранить саму необходимость разделения прерываний. (Я не знаю, так ли это на самом деле, просто у этого есть потенциал.)
Все драйверы ядра Linux находятся в одном и том же пространстве памяти, но несвязанный драйвер не должен возиться в чужой области памяти. Если вы не передадите этот указатель, вы можете быть уверены, что другой драйвер сам по себе не получит такое же значение случайно.
источник
dev_id
который ему не принадлежит. Мне кажется, что существует ненулевая вероятность того, что драйвер, который не владеетdev_id
структурой, все равно может принять ее за свою собственную, основываясь на том, как он интерпретирует содержимое. Если это не так, то какой механизм предотвратит это?dev_id
указатель на что-то в области памяти вашего драйвера. Другой водитель может составитьdev_id
значение , которое случилось быть начертанием с указателем на память драйвер владеет, но это не будет происходить , потому что все играют по правилам. Помните, это пространство ядра: самодисциплина подразумевается как нечто само собой разумеющееся, в отличие от кода пользовательского пространства, который может безболезненно предполагать, что все, что не запрещено, разрешено.dev_id
что ему не принадлежит.dev_id
не помогает вам определить, произошло ли это. Вы должны спросить аппаратное обеспечение, "Вы звонили?"kexec
.Когда драйвер запрашивает общий IRQ, он передает указатель ядру на ссылку на специфическую для устройства структуру в области памяти драйвера.
Согласно LDD3:
После проверки обработчиков IRQ нескольких драйверов выясняется, что они проверяют само оборудование, чтобы определить, должно ли оно обрабатывать прерывание или возврат
IRQ_NONE
.Примеры
UHCI-HCD ДрайверВ приведенном выше коде драйвер читает
SDHCI ДрайверUSBSTS
регистр, чтобы определить, имеется ли прерывание для обслуживания.Как и в предыдущем примере, драйвер проверяет регистр состояния,
Ath5k DriverSDHCI_INT_STATUS
чтобы определить, нужно ли ему обслуживать прерывание.Еще один пример.
источник
Пожалуйста, посетите проверить эту ссылку :
источник