4

Intel Atomプロセッサ ( 2 コアのx86_64 ) でLinux v3.2カーネル モジュールを作成しています。特定の IRQ 番号を無効にしたいのですが、Linux ではうまくいきません。

8259 PICチップと直接通信することで、Intel シンタックス x86 アセンブリの割り込みを簡単に無効にできるMS-DOSをデュアル ブートしています。

CLI                ; disable all interrupts
MOV    DX, 0x21    ; set 8259 ioport address
IN     AL, DX      ; store current interrupt mask in AL
AND    AL, 0xDF    ; modify mask to disable IRQ 5
OUT    DX, AL      ; send new mask to 8259
STI                ; reenable interrupts

これは非常にうまく機能し、特定の IRQ 番号を無効にすることに成功しました。

Linux では、マクロを使用して割り込みを無効にする必要があることは認識してdisable_irqいますが、効果がないようです。

#include <linux/interrupt.h>
...
disable_irq(5);    // disable IRQ 5

このdisable_irq行は、キャラクター ドライバーのopen関数の先頭にあります。ただし、デバイス ノードを開くと、関数の残りのコードはopen通常どおり実行されますが、IRQ 5 は有効のままですdisable_irq。まったく効果がないように見えます。

マクロを正しく使用しているかどうか確信が持てなかったdisable_irqので、ロジックが正しいことを確認するために、ストレート インライン アセンブリを試すことにしました。シンプルに始めて、最初にすべての割り込みを無効にすることにしました。

__asm__("cli");

ただし、すべての割り込みが有効のままであるため、この単一の命令でさえ実行されないようです。

私は今完全に混乱しています.Linuxでストレートアセンブリが割り込みを無効にしないのはなぜですか? Linuxで割り込みを無効にする正しい方法は何ですか?


更新disable_irq:の後に実行された場合にのみ機能することを発見しましたrequest_irq。これはバグですか、それとも予想される動作ですか?

私が見ている動作を漠然と説明しているように見えるスレッドを見つけましたが、それは時代遅れであり、私のバージョンの Linux にまだ関連があるかどうかはわかりません。


更新 2:

Linux v3.2.0-4を実行しているDebianで試したカーネル モジュールは次のとおりです。

#include <linux/module.h>
#include <linux/kernel.h> /* Needed for KERN_INFO */
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irqflags.h> /* Needed for local_irq_disable et al. */

MODULE_LICENSE("GPL");

static unsigned long flags = 0;

static int __init initialization_routine(void)
{
        local_irq_save(flags);
        local_irq_disable();

        /* __asm__("cli"); */
        /* disable_irq(15); */
        return 0;
}

static void __exit cleanup_routine(void) {
        local_irq_restore(flags);

        /* __asm__("sti"); */
        /* enable_irq(15); */
        return;
}   

module_init(initialization_routine);
module_exit(cleanup_routine);

disable_irq/enable_irq正常に動作します。私は単純な組み立て説明書にはあまり興味がありません (それらが機能しないのは奇妙なことです)。さらに、どのコアにも観察可能な影響がないのはなぜでしょうかlocal_irq_disable。つまり、IRQ はすべてのコアに表示されます。

割り込みを確認するには、ターミナルで次のコマンドを実行します。

$ watch -d -n 0.5 cat /proc/interrupt

disable_irq現在は完全に動作しているため、enable_irqある種の初期化コードを忘れているだけなのかlocal_irq_disable、関連する機能が非推奨になっているのか、x86 プロセッサには適用されないのでしょうか?

4

2 に答える 2

5

私はこの質問に出くわしました。1年経ちましたが、まだ良い答えではありません。Intel のアーキテクチャーと Linux のアーキテクチャーに欠けていると思われる高レベルの視点を提供したいと思います。優れた Intel® 64 and IA-32 Architectures Software Developer's Manual を詳しく調べることができます。これは、Intel が Web サイトで PDF 形式で共有しています。基本的に、すべての CPU に対して特定のベクトルをブロックしようとしないでください。spin_locks と local_irq_disable() を慎重に使用してください。

各プロセッサ コアには、そのコアでのみ割り込みメカニズムを有効または無効にする "割り込みフラグ" があります。それが、Linux の local_irq_* ルーチンが変更するものです (それらは CLI/STI 命令を使用するか、フラグ レジスタを保存、変更、およびリロードして、IF フラグを変更します)。

デバイスの割り込みは、特定のプロセッサ コア、そのローカル APIC (高度なプログラマブル割り込みコントローラー) にルーティングされ、特定の割り込みベクトルに対して IRQ ビットが設定されます。特定の割り込みベクトルが挿入されないようにするいくつかの方法の 1 つは、そのコアのローカル APIC (LAPIC) でマスク レジスタを設定することです。その特定のコアで実際に実行しているときにのみ実行できるため、コードがそのコアで実行されていることがわかっていない限り、実行するのは困難です。一般に、このメカニズムは、ネストされた割り込みが発生しないように注意するために使用すると便利です。ただし、LAPIC は入れ子になった割り込みをより簡単に処理できます。LAPIC で提供される優先度マスキングを使用します。これは、通常、ハンドラーでの実行中に現在の割り込みとすべての優先度の低い割り込みをブロックする必要があるためです。これについては、LAPIC PPR レジスタ機能を見てください。

一般に、割り込みをグローバルに「無効化」しようとする考えを完全に回避する方法でデバイス ドライバー コードを設計することをお勧めします。「飛行中」の割り込みの損失やその他の災害を防ぐ簡単な方法はありません。代わりに、優先メカニズムを使用してデバイス割り込みハンドラーで割り込みを処理し、spin_lock または spin_lock_irqsave を慎重に使用して他のコードを構築します。これは、副作用ブロックとして、それを実行している CPU に割り込みます。

インターロック コードが短い場合は、デバイスの割り込みハンドラー自体で spin_lock_irqsave を使用するようにデバイスを設計できるはずです。これにより、割り込み中に他のコアがデバイスに触れるのを安全に防ぐことができます。逆の場合も同様です。

ランダムに選択されたコアが他のコアの割り込みをブロックする必要がある場合、それが割り込みハンドラーの外部で動作している場合、割り込みが実際にコアの LAPIC にどのように配信されるかを確認する必要があります。なぜこれを行う必要があるのか​​ わかりません.ほとんどのデバイスドライバーはこれを実行しようとはしませんが、デバイス固有の理由で関連している可能性があります.

割り込みは、共有割り込みバスを介して LAPIC に配信されます。これにより、割り込みを 1 回のトランザクションで 1 つ以上の LAPIC に送信できます。(マルチキャスト モードがデバイスに使用されることはめったにないため、ここでは説明しません。Intel のマニュアルを参照してください)。共有割り込みバスは、割り込みを IOAPIC デバイスに送信するか、フロントサイド バスを介して LAPIC に直接送信し、APIC ID を介してアドレス指定し、使用するベクター番号を指定します。(FSB メカニズムは、より新しいデバイス (PCI Express および HPET) で直接使用できますが、ほとんどのレガシー デバイスは IOAPIC を使用します。)

すべてのプロセッサで特定のベクトルをグローバルにブロックするには、概念的に次の 3 つの方法のいずれかを実行できます。a) デバイスが「マスク」ビットを設定するために使用する IOAPIC または PCI Express レジスタを再プログラムします。ただし、それは多くのことであり、割り込みまたはその他の魔法を取得している可能性のある他のすべての CPU コアを停止する必要がある場合があります。これは、デバイスを 1 回だけ開始または停止するときに使用できます。b) 共有 IDT を使用して一時的に割り込みをキャプチャし、後で適切なハンドラに転送します。これも複雑で危険です。c) デバイスの状態を変更して、割り込みをまったく生成しないようにします。ほとんどのデバイスには、割り込みを生成しない機能があります。

ただし、このグローバルな抑制を行おうとしているときに、1 つ以上の割り込みが「実行中」になる可能性があることを覚えておく必要があります。

于 2015-06-05T21:59:40.497 に答える