web-dev-qa-db-ja.com

Linuxカーネルは共有IRQをどのように処理しますか?

これまで読んだことによると、「カーネルが割り込みを受け取ると、登録されているすべてのハンドラーが呼び出されます」。

各IRQの登録済みハンドラーは/proc/interruptsを介して表示できることを理解しています。また、登録済みハンドラーはrequest_irqを呼び出したドライバーからのものであり、おおよその形式のコールバックで渡されることも理解しています。

irqreturn_t (*handler)(int, void *)

私が知っていることに基づいて、特定のIRQに関連付けられたこれらの割り込みハンドラーコールバックのそれぞれを呼び出す必要があり、割り込みが実際に処理されるかどうかを決定するのはハンドラー次第です。ハンドラが特定の割り込みを処理できない場合は、カーネルマクロIRQ_NONEを返す必要があります。

私が理解できないのは、各ドライバが割り込みを処理する必要があるかどうかをどのように判断するかです。割り込みを想定している場合は、内部で追跡できると思います。その場合、同じIRQの背後にある複数のドライバーが割り込みを予期している状況にどのように対処できるかわかりません。

これらの詳細を理解しようとしているのは、kexecメカニズムをいじって、PCIeブリッ​​ジのリセットピンとさまざまなレジスタを操作しながら、システム操作の途中でカーネルを再実行しているためです。ダウンストリームPCIデバイスも同様です。その際、再起動後、カーネルパニックが発生するか、他のドライバーが、操作が行われていなくても割り込みを受けていると不平を言っています。

ハンドラーが割り込みをハンドラーで処理する必要があると決定した方法は謎です。

編集:関連がある場合、問題のCPUアーキテクチャはx86です。

14
bsirang

これは、Corbet et al。による 第10章Linuxデバイスドライバーの第3版で説明されています。それは無料で利用できます online 、またはあなたは 死んだ木または電子ブックフォームにいくつかのシェケルを投げるO'Reillyの方法 を利用できます。質問に関連する部分は、最初のリンクの278ページから始まります。

それが価値があるもののために、これらの3つのページを言い換えようとする私の試みと、私がグーグルアップした他のビットです:

  • 共有IRQハンドラーを登録すると、カーネルは次のいずれかを確認します。

    a。その割り込みに対して他のハンドラーが存在しない、または

    b。以前に登録されたものすべて割り込み共有を要求

    どちらの場合も当てはまる場合、次に_dev_id_パラメータが一意であることを確認して、カーネルが複数のハンドラを区別できるようにします。ハンドラーの取り外し中。

  • PCI¹ハードウェアデバイスがIRQラインを上げると、カーネルの低レベルの割り込みハンドラーが呼び出され、次に、登録されている割り込みハンドラーのallを呼び出します。 request_irq()を介してハンドラーを登録するために使用した_dev_id_をそれぞれに戻します。

    _dev_id_値はマシン固有である必要があります。これを行う一般的な方法は、ドライバーがそのデバイスを管理するために使用するデバイスごとのstructにポインターを渡すことです。このポインタは、ドライバにとって有用であるためには、ドライバのメモリ領域内にある必要があるため、そのドライバに固有のipso factoです。²

    特定の割り込みに登録されている複数のドライバーがある場合、allが呼び出されますanyデバイスの共有割り込みラインを発生させます。これを行ったのがドライバのデバイスではなかった場合、ドライバの割り込みハンドラには、それに属していない_dev_id_値が渡されます。これが発生すると、ドライバーの割り込みハンドラーはすぐに戻る必要があります。

    別のケースは、ドライバーが複数のデバイスを管理している場合です。ドライバの割り込みハンドラは、ドライバが認識している_dev_id_値の1つを取得します。コードは各デバイスをポーリングして、割り込みを発生させたデバイスを見つけることになっています。

    Corbetet al。の例は、PCパラレルポートの例です。割り込みラインをアサートするとき、最初のデバイスレジスタのトップビットも設定します。 (つまり、inb(0x378) & 0x80 == true、標準のI/Oポート番号を想定しています。)ハンドラーがこれを検出すると、ハンドラーが処理を実行し、I/Oポートから読み取った値を書き込んでIRQをクリアします。トップビットがクリアされた状態でポートに戻ります。

    特定のメカニズムが特別であるという理由はわかりません。別のハードウェアデバイスが別のメカニズムを選択する可能性があります。唯一の重要なことは、デバイスが共有割り込みを許可するためには、ドライバーがデバイスの割り込みステータスをreadするための何らかの方法を持っている必要があることです。割り込みをクリアする方法。特定のデバイスが使用するメカニズムを見つけるには、デバイスのデータシートまたはプログラミングマニュアルを読む必要があります。

  • 割り込みハンドラーがカーネルに割り込みを処理したことを通知しても、カーネルは同じ割り込みに登録されている他のハンドラーの呼び出しを続行します。レベルトリガー割り込みを使用するときに割り込みラインを共有する場合、これは避けられません。

    2つのデバイスが同じ割り込みラインを同時にアサートするとします。 (または、少なくとも、カーネルが割り込みハンドラを呼び出してラインをクリアし、それによって2番目のアサーションを個別に表示する時間がないため、カーネルがその割り込みラインのすべてのハンドラを呼び出して、それぞれを与える必要があります。関連するハードウェアに問い合わせて、注意が必要かどうかを確認する機会。 2つの異なるドライバーが、特定の割り込みのハンドラーリストを介して同じパス内で割り込みを正常に処理することは十分に可能です。

    このため、割り込みハンドラーが戻る前に、ドライバーが割り込みアサーションをクリアするように管理しているデバイスにドライバーに通知することが不可欠です。他に何が起こるか私にははっきりしていません。継続的にアサートされた割り込みラインにより、カーネルは継続的に共有割り込みハンドラーを呼び出すか、またはカーネルが新しい割り込みを確認してハンドラーが呼び出されないようにする機能をマスクします。いずれにせよ、災害。


脚注:

  1. 上記のすべてのPCIは、元のPCI仕様で使用されている level-triggered 割り込みを想定しているため、上記のPCIを指定しました。 ISA使用 エッジトリガー 割り込みにより、ハードウェアでサポートされている場合にのみ、共有は困難であり、それでも可能でした。PCIeは message-signalled interrupts;割り込みメッセージには、PCI割り込み共有で必要なラウンドロビン推測ゲームを回避するためにカーネルが使用できる一意の値が含まれています。PCIeは割り込み共有の必要性を非常に排除する可能性があります(実際にそうであるかどうかはわかりません) 、それが可能性を秘めているというだけです。)

  2. Linuxカーネルドライバーはすべて同じメモリ空間を共有しますが、無関係なドライバーが他のドライバーのメモリ空間をいじくり回すことはありません。そのポインターを渡さない限り、別のドライバーが誤って同じ値を独自に考え出すことはないでしょう。

14
Warren Young

ドライバーが共有IRQを要求すると、ドライバーへのポインターをドライバーのメモリ空間内のデバイス固有の構造への参照に渡します。

LDD3によると:

2つ以上のドライバーが割り込みラインを共有していて、ハードウェアがそのラインのプロセッサに割り込みをかけると、カーネルはその割り込みに登録されているすべてのハンドラーを呼び出し、それぞれに独自のdev_idを渡します。

いくつかのドライバーのIRQハンドラーをチェックすると、割り込みを処理するか、またはIRQ_NONEを返すかどうかを判断するために、ハードウェア自体をプローブするようです。

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

上記のコードでは、ドライバーはUSBSTSレジスターを読み取って、サービスへの割り込みがあるかどうかを判別しています。

  intmask = sdhci_readl(Host, SDHCI_INT_STATUS);

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

前の例と同様に、ドライバーはステータスレジスタSDHCI_INT_STATUSをチェックして、割り込みを処理する必要があるかどうかを判断します。

  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;

もう1つの例です。

4
bsirang

これをチェックしてください link

メモリマップレジスタからIRQステータスを確認した後にのみ、IRQハンドラーでボトムハーフまたはその他のロジックをトリガーするという通常の方法です。したがって、問題はデフォルトで優れたプログラマーによって解決されます。

0
Priyaranjan