Як ядро ​​Linux обробляє спільні IRQ?


14

Згідно з тим, що я читав до цього часу, "коли ядро ​​отримує переривання, викликаються всі зареєстровані обробники".

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

irqreturn_t (*handler)(int, void *)

Виходячи з того, що мені відомо, кожен із цих зворотних викликів обробника переривань, пов'язаних із конкретним IRQ, повинен викликатись, і саме обробник повинен визначити, чи дійсно він повинен обробляти переривання. Якщо обробник не повинен працювати з певним перериванням, він повинен повернути макрос ядра IRQ_NONE.

Що у мене виникають проблеми з розумінням, це те, як очікується, що кожен водій повинен визначити, чи повинен він працювати з перериванням чи ні. Я гадаю, що вони можуть постійно відслідковувати, якщо їх очікує перерва. Якщо так, я не знаю, як вони зможуть впоратися з ситуацією, коли кілька водіїв, що стоять за одним IRQ, очікують перерви.

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

Як обробник вирішив, що переривання має вирішуватись, це таємниця.

Редагувати: Якщо це доречно, йдеться про архітектуру процесора x86.


1
stackoverflow.com/questions/14371513/for-a-shared-interrupt-line-how-do-i-find-which-interrupt-handler-to-usec
Чіро Сантіллі新疆改造中心法轮功六四事件

Відповіді:


14

Про це розповідається в главі 10 з драйверів пристроїв Linux 3 - е видання, по Корбет і ін. Він доступний безкоштовно в Інтернеті , або ви можете підкинути кілька сиклів О'Рейлі за форми мертвого дерева або книги. Частина, що стосується вашого питання, починається на сторінці 278 у першому посиланні.

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

  • Коли ви реєструєте спільний обробник 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, записавши значення, прочитане з порту вводу / виводу, назад до порту вгорі трохи очищено.

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

  • Коли ваш обробник переривання повідомляє ядро, що воно обробляло переривання, це не зупиняє ядро ​​продовжувати виклик будь-яких інших обробників, зареєстрованих для цього самого переривання. Це неминуче, якщо вам потрібно поділити лінію переривання при використанні переривань, викликаних рівнем.

    Уявіть, що два пристрої одночасно стверджують ту саму лінію переривання. (Або, принаймні, настільки близький за часом, що ядро ​​не встигає викликати обробник переривання, щоб очистити лінію і тим самим побачити друге твердження як окреме.) Ядро повинне викликати всі обробники для цієї лінії переривання, щоб дати кожному можливість запитувати відповідне обладнання, щоб побачити, чи потребує його уваги. Цілком можливо для двох різних драйверів успішно вирішити перерву в межах одного проходу через список обробників для даного переривання.

    Через це, обов'язково, щоб ваш драйвер повідомив пристрою, яким він керує, очистити своє твердження про переривання десь до того, як обробник переривання повернеться. Мені не зрозуміло, що відбувається інакше. Постійно підтверджувана лінія переривання або призводить до того, що ядро ​​постійно викликає спільні обробники переривань, або замаскує здатність ядра бачити нові переривання, щоб обробники ніколи не викликалися. У будь-якому випадку, катастрофа.


Виноски:

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

  2. Усі драйвери ядра Linux ділять один і той же простір пам'яті, але не пов'язаний драйвер не повинен перебиратись в чужій пам'яті. Якщо ви не передасте цей вказівник навколо, ви можете бути впевнені, що інший драйвер не збирається самостійно придумати таке саме значення.


1
Як ви згадали, обробник переривання може бути переданий тим, dev_idщо йому не належить . Мені здається, існує ненульовий шанс, що драйвер, який не володіє dev_idструктурою, може все-таки помилитися як власний на основі того, як він інтерпретує вміст. Якщо це не так, то який механізм перешкоджав би цьому?
bsirang

Ви запобігаєте цьому, зробивши dev_idвказівник на щось, що знаходиться в пам’яті вашого драйвера. Інший драйвер міг скласти dev_idзначення, яке трапилось змішувати з вказівником на пам'ять, яким володіє ваш драйвер, але це не відбудеться, тому що всі грають за правилами. Це ядро-простір, пам’ятайте: самодисципліна вважається само собою зрозумілою, на відміну від коду простору користувача, який, можливо, припускає, що все, що не заборонено, дозволено.
Воррен Янг

Відповідно до десятої глави LDD3: "Кожного разу, коли два або більше драйверів ділять лінію переривання і апаратне забезпечення перебиває процесор у цій лінії, ядро ​​викликає кожного обробника, зареєстрованого для цього переривання, передаючи кожен власний dev_id" Схоже, попереднє розуміння був невірним щодо того, чи може бути переданий обробник переривання у тому, dev_idщо він не має.
bsirang

Це було неправильно прочитане з мого боку. Коли я це писав, я плутав два поняття. Я відредагував свою відповідь. Умова, яка вимагає, щоб ваш обробник перерви швидко повернувся, - це те, що він викликається через твердження переривання пристроєм, яким він не керує. Значення dev_idне допомагає визначити, чи це сталося. Ви повинні запитати обладнання: "Ви зателефонували?"
Воррен Янг

Так, тепер мені потрібно розібратися, як насправді те, що я замислююся, змушує інших драйверів вважати, що їхні пристрої "задзвонили" після перезавантаження ядра через kexec.
bsirang

4

Коли драйвер вимагає спільного 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реєстр, щоб визначити, чи є перерва у обслуговуванні.

Драйвер 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;

Ще один приклад.


0

Перейдіть за посиланням :

Це звичайна практика запускати нижню половину або будь-яку іншу логіку в обробці IRQ лише після перевірки стану IRQ з реєстру, відображеного в пам'яті. Отже, проблему за замовчуванням вирішує хороший програміст.


Вміст вашого посилання недоступний
user3405291
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.