Linux 割り込み仮想化 2

Linux 割り込み仮想化 2

[[437706]]

PIC 仮想化

コンピュータ システムには、サービスを必要とする周辺機器が多数あります。明らかに、CPU はポーリングを使用して周辺機器に 1 つずつサービスが必要かどうかを問い合わせますが、これは、特に頻繁にサービスを必要としないデバイスの場合、CPU コンピューティングの無駄遣いになります。そのため、コンピューター科学者は、周辺機器が CPU へのサービス要求、つまり割り込みを積極的に開始する方法を設計しました。割り込みモードを採用した後は、周辺機器からの要求がない場合、CPUは不要なポーリングを大量に実行するのではなく、他のコンピューティングタスクを続行できるため、システムのスループットが大幅に向上します[1]。各命令サイクルの後に、CPU 割り込みフラグ (IF) が設定されていない場合は、割り込み要求があるかどうかがチェックされます。割り込み要求があった場合、対応する割り込みサービス プログラムを実行し、その後、中断されたコンピューティング タスクに戻って実行を続行します。

CPU がハードウェアごとに割り込みを受信するための専用ピンを設計することは不可能です。ピンの数、回路の複雑さ、柔軟性に関する制限は現実的ではありません。そのため、割り込みを管理するための専用のユニットを設計する必要があります。割り込み管理ユニットは、周辺機器からの要求を受け付け、要求の優先順位を決定し、CPU に割り込みを発行します。 1981 年に IBM が発売した第 1 世代のパーソナル コンピュータ PC/XT では、割り込みコントローラとして独立した 8259A が使用されていました。それ以来、8259A はシングルコア時代の割り込みチップの事実上の標準となりました。ソフトウェアプログラミングによって制御できるため、たとえば、ピンがデバイス信号を受信したときに送信する割り込みベクター番号をプログラムして制御できます。そのため、割り込みコントローラは、プログラマブル割り込みコントローラ (PIC) とも呼ばれます。 1 つの 8259A チップは 8 つの周辺機器の割り込み信号ラインに接続でき、複数のチップをカスケード接続してさらに多くの周辺機器をサポートできます。

8259A と CPU 間の接続を図 5 に示します。

図5 8259AとCPUの接続

チップセレクトはアドレスデコーダに接続されています。 CPU が 8259A にアクセスする準備ができたら、8259A に対応するアドレスをアドレス バスに送信する必要があります。デコーダーを通過すると、デコーダーはそれが 8259A に対応するアドレスであることを検出するので、8259A の CS に接続されたピンのレベルをプルダウンし、それによって 8259A を選択し、CPU が 8259A とデータを交換する準備ができていることを 8259A に通知します。

8259A の D0 ~ 7 ピンは CPU のデータ バスに接続されています。 CPU から 8259A への ICW と OCW の送信、および 8259A から CPU への 8259A ステータスと割り込みベクター番号の送信は、すべてデータ バスを介して送信されます。

CPU が ICW と OCW を 8259A に送信する場合、データをデータ バスに送信した後、8259A にデータを読み取るように通知する必要があります。 CPU は WR ピンのレベルをプルダウンして 8259A に通知します。 8259A の WR ピンがローレベルを受信すると、データ バス上のデータが読み取られます。同様に、CPU が 8259A のステータスを読み取る準備ができると、RD ピンをローに引き下げて 8259A に通知します。

8259A と CPU 間の割り込み信号の通知には専用回線を使用します。 8259A の INTR (割り込み要求) ピンと INTA (割り込み確認) ピンは、それぞれプロセッサの INTR ピンと INTA ピンに接続されます。 8259A はピン INTR を介して CPU に割り込み要求を送信し、CPU はピン INTA を介して PIC に割り込み確認を送信し、PIC が割り込みを受信して​​処理を開始したことを通知します。 8259A と CPU 間の具体的な割り込みプロセスは次のとおりです。

1) 8259A の IR0 ~ 7 ピンはハイレベルで有効であるため、割り込みソースがサービスを要求すると、IR0 ~ 7 に接続されたピンがハイにプルアップされ、割り込み要求が生成されます。

2) 8259A はこれらの信号を記録する必要があるため、割り込み要求を記録する内部レジスタ IRR (割り込み要求レジスタ) を備えています。この例では、IRR のビット 0 が 1 に設定されます。

3) 場合によっては、特定のデバイスの割り込みをブロックすることがあります。つまり、割り込みソースが 8259A に信号を送信しても、8259A は CPU に割り込み信号を送信しません。読者は、シールドと、cli コマンドによる割り込みの無効化を混同しないでください。 CPU が割り込みを無効にした場合、割り込みは引き続き CPU に送信されますが、割り込みが無効になっている期間中は CPU は割り込みを処理しません。 8259A のレジスタ IMR (割り込みマスク レジスタ) は、特定の割り込みソースがマスクされているかどうかを記録する役割を担います。たとえば、割り込みソース 0 がデバイスによってマスクされている場合、IMR のビット 0 が設定されます。では、この IMR を設定するのは誰でしょうか?もちろん、CPU 内のオペレーティング システムです。したがって、このステップでは、8259A は受信した割り込み要求がマスクされているかどうかを確認します。

4) ある瞬間に、複数の割り込み要求が発生したり、IRR に以前保存された割り込みが処理されずに、8259A にいくつかの割り込みが蓄積されたりすることがあります。ある瞬間、8259A は CPU に 1 つの割り込み要求しか送信できません。したがって、複数の割り込み要求がある場合、8259A は割り込みの優先順位を決定する必要があります。このユニットは優先度リゾルバと呼ばれます。優先度リゾルバは、IRR 内で優先度が最も高い割り込みを選択します。

5) 最も優先度の高い割り込みを選択した後、8259A はピン INTR のレベルをプルアップして CPU に信号を送信します。

6) CPU は現在の命令サイクルを完了した後、レジスタ FLAGS 内の割り込み有効フラグ IF をチェックします。割り込みが許可されている場合は、INTR で割り込みがあるかどうかを確認します。割り込みが発生した場合、ピン INTR を介して 8259A プロセッサに割り込み処理を開始するように通知します。

7) CPU から INTA 信号を受信した後、8259A は ISR (インサービス レジスタ) 内の最高優先度の割り込みの対応するビットを設定し、IRR 内の対応するビットをクリアします。

8) 通常、x86 CPU は 2 番目の INTA を送信します。 2 番目の INTA を受信した後、8259A は割り込みベクトル番号 (ベクトル) をデータ バス D0 ~ D7 に送信します。

9) 8259A が AEOI (自動割り込み終了) モードに設定されている場合、8259A は ISR 内の対応するビットをリセットします。それ以外の場合、ISR 内の対応するビットは、システムの割り込みサービス ルーチンから EOI コマンドが受信されるまでそのまま残ります。

割り込みサービス ルーチンは配列に格納されており、配列内の各項目は割り込みサービス ルーチンに対応していることがわかっています。リアルモードでは、この配列は IVT (割り込みベクター テーブル) と呼ばれます。保護モードでは、この配列は IDT (割り込み記述子テーブル) と呼ばれます。

この配列に格納されるサービス プログラムはすべて外部割り込みではなく、CPU 内部例外を処理するプログラムやソフト割り込みサービス プログラムもあります。 x86 CPU の最初の 32 個の割り込み番号 (0 ~ 31) は、プロセッサ例外用に予約されています。たとえば、割り込み番号 0 は、プロセッサにゼロ除算例外があり、占有できない場合です。したがって、ピン IR0 に対応する ISR を IVT 配列の 32 番目の要素に格納することを想定して、8259A を初期化するときに、ICW を介して開始 irq ベースを 32 に設定します。次に、8259A がピン IR0 に対して割り込み要求を発行すると、発行される値は 32 になり、ピン IR1 の対応する値は 33 になります。 32 と 33 は、いわゆる割り込みベクターです。つまり、割り込みベクターは、IVT/IDT 内の割り込みサービス ルーチンのインデックスです。以下は、初期化中に 2 番目の初期化コマンド ワード (ICW2) を通じて設定される irq_base を設定するコードです。

  1. コミット85f455f7ddbed403b34b4d54b1eaf0e14126a126
  2. KVM:サポート追加 カーネルPICエミュレーション
  3. linux.git/drivers/kvm/i8259.c
  4. 静的void pic_ioport_write(void *不透明、u32addr、u32 val)
  5. {
  6.  スイッチ(s->init_state) {
  7. ケース1:
  8. s->irq_base = val & 0xf8;
  9. }
  10. }

その後、APIC と MSI の登場により、割り込みベクターの設定はより柔軟になりました。割り込みベクターは PCI ごとに設定でき、この割り込みベクターは PCI デバイスの構成空間に格納されます。

カーネルは、各 8259A のステータスを記録するために構造体 kvm_kpic_state を抽象化します。

  1. コミット85f455f7ddbed403b34b4d54b1eaf0e14126a126
  2. KVM:サポート追加 カーネルPICエミュレーション
  3. 構造体kvm_kpic_state{
  4. u8last_irr; /* エッジ検出 */
  5. u8 irr; /* 割り込み要求レジスタ */
  6. u8imr; /* 割り込みマスクレジスタ */
  7. u8isr; /* 割り込みサービスレジスタ */
  8. };
  9.   
  10. 構造体kvm_pic{
  11. structkvm_kpic_state pics[2]; /* 0マスター ピクチャ、1 はスレーブ ピクチャです*/
  12. irq_request_func *irq_request;
  13. void*irq_request_opaque;
  14. 入力; /* マスター PICからのintr */
  15. structkvm_io_device デバイス;
  16. };

1 台の 8259A は最大 8 台の周辺機器にしか接続できません。さらに多くの周辺機器をサポートする必要がある場合は、複数の 8259A チップをカスケード接続する必要があります。構造kvm_picには、pic[0]とpic[1]の2つの8259Aピースがあることがわかります。 KVM は、前述の IRR、IMR、ISR などを含む 8259A のステータスを記録するために、構造体 kvm_kpic_state を定義します。

1 仮想デバイスはPICに割り込み要求を送信します

物理周辺機器が割り込み要求時に 8259A に接続されたピンの電圧を上げるのと同様に、仮想デバイスは、API を通じて仮想 8259A チップに要求を中断するように指示することで割り込みを要求します。 kvmtool の virtio blk 仮想デバイスを例に挙げます。

  1. コミット4155ba8cda055b7831489e4c4a412b073493115b
  2. kvm: virtio ブロックデバイスのサポートをさらに修正
  3. kvmtool.git/blk-virtio.c
  4. 静的ブール blk_virtio_out(…)
  5. {
  6.  caseVIRTIO_PCI_QUEUE_NOTIFY: {
  7. while(queue->vring.avail->idx != queue->last_avail_idx) {
  8. if(!blk_virtio_read(自分、キュー))
  9. 戻る 間違い;
  10. }
  11. kvm__irq_line(自己、VIRTIO_BLK_IRQ、1);
  12.   
  13. 壊す;
  14. }
  15. }

ゲストカーネルのブロックデバイスドライバがI/O通知VIRTIO_PCI_QUEUE_NOTIFYを発行すると、CPUがゲストモードからホストモードに切り替わり、KVM内のブロックシミュレーションデバイスがゲストファイルシステムを保存するイメージファイルへのアクセスなどのI/O操作を開始します。 virtio blk 送信の場合、ブロック デバイスの I/O 処理は同期されます。つまり、ファイル操作が完了するまで、ゲストに割り込みが送信されず、ゲストに戻ります。もちろん、ここで同期ブロッキングは合理的ではありません。代わりに、ゲストが他のタスクを実行できるように、ゲストをすぐに戻す必要があります。仮想デバイスが I/O 操作を完了すると、ゲストに通知されます。これは kvmtool の初期の実装であり、後に非同期メソッドに改良されました。コード内の while ループがデバイス ドライバーの I/O 要求を処理した後、関数 kvm__irq_line が呼び出されます。 irq_line は 8259A のピン IR0 ~ 7 に対応します。コードは次のとおりです。

  1. コミット4155ba8cda055b7831489e4c4a412b073493115b
  2. kvm: virtio ブロックデバイスのサポートをさらに修正
  3. kvmtool.git/kvm.c
  4. void kvm__irq_line(構造体 kvm *​​self, int irq, intlevel)
  5. {
  6. structkvm_irq_level irq_level;
  7.   
  8. irq_level= (構造体 kvm_irq_level) {
  9. {
  10. .irq = irq、
  11. },
  12. レベル=レベル
  13. };
  14.   
  15. if(ioctl(self->vm_fd, KVM_IRQ_LINE, &irq_level) < 0)
  16. die_perror( "KVM_IRQ_LINE が失敗しました" );
  17. }
  18. 関数 kvm__irq_line は、irq 番号とピン レベル情報 (ここではレベルが高くなっていることを示す 1) を構造体 kvm_irq_level にカプセル化し、カーネル内の KVM モジュールに渡します。
  19. コミット85f455f7ddbed403b34b4d54b1eaf0e14126a126
  20. KVM:サポート追加 カーネルPICエミュレーション
  21. linux.git/drivers/kvm/kvm_main.c
  22. 静的ロング kvm_vm_ioctl(…)
  23. {
  24. ケースKVM_IRQ_LINE: {
  25. kvm_pic_set_irq(pic_irqchip(kvm),
  26. irq_event.irq、
  27. irq_event.レベル);
  28. 壊す;
  29. }
  30. }

KVM モジュールは、kvmtool で整理された割り込み情報をユーザー空間からカーネル空間にコピーし、仮想 8259A モジュールで提供される API kvm_pic_set_irq を呼び出して、8259A に割り込み要求を送信します。

2 IRRへの記録中断

割り込み処理には、周辺機器が要求を送信してから ISR 処理が完了して EOI が発行されるまでのプロセスが必要です。さらに、割り込みがすぐに処理されない場合や、以前にいくつかの割り込みが蓄積されている場合があり、すべての割り込みがキューに登録され、CPU に順番に処理するように要求する必要がある場合などがあります。したがって、これらの状態を記録するにはいくつかのレジスタが必要です。周辺機器の割り込み要求が来ると、8259A はまずそれを記録する必要があります。このレジスタはIRR(割り込み要求レジスタ)です。 8259A はこれを使用して、処理する必要がある保留中の割り込みを記録します。

KVM モジュールは周辺機器から要求を受信すると、仮想 8259A API kvm_pic_set_irq を呼び出します。最初に行うことは、IRR レジスタに割り込みを記録することです。

  1. コミット85f455f7ddbed403b34b4d54b1eaf0e14126a126
  2. KVM:サポート追加 カーネルPICエミュレーション
  3. linux.git/drivers/kvm/i8259.c
  4. void kvm_pic_set_irq(void *不透明、 int irq、intlevel)
  5. {
  6. structkvm_pic *s = 不透明;
  7.   
  8. pic_set_irq1(&s->pics[irq >> 3], irq & 7,レベル);
  9. }
  10.   
  11. 静的インライン void pic_set_irq1(structkvm_kpic_state *s,
  12. 整数irq、整数 レベル
  13. {
  14. 整数マスク;
  15. マスク = 1<< irq;
  16. if(s->elcr & mask) /*レベルがトリガーされました */
  17. else /* エッジがトリガーされました */
  18. if(レベル) {
  19. if((s->last_irr & マスク) == 0)
  20. s->irr |= マスク;
  21. s->last_irr |= マスク;
  22. }それ以外 
  23. s->last_irr &= ~マスク;
  24. }

信号はエッジトリガーまたはレベルトリガーにすることができます。物理的には、これは 8329A がピン信号が前のサイクルでは 0 であり、現在のサイクルでは 1 であることを検出することを意味します。立ち上がりエッジ トリガー モードの場合、8259A は周辺機器に要求があると判断します。このトリガー モードはエッジ トリガーです。水平トリガーの場合、ハイレベルトリガーを例にとると、8259A はピンがハイレベルであることを検出すると、周辺機器から要求が行われたものとみなします。

仮想 8259A の構造体 kvm_kpic_state では、レジスタ elcr を使用して 8259A に設定されたトリガー モードを記録します。パラメータ レベルはハードウェア レベルでの電気信号に相当し、0 は低レベル、1 は高レベルを表します。エッジ トリガーを例にとると、ピンが低レベルを受信すると、つまりレベルの値が 0 になると、コードは else 分岐に入り、構造体 kvm_kpic_state のフィールド last_irr は IRQ に対応する IRR のビットをクリアします。これは、割り込みピンを低レベル状態に設定することと同じです。ピンがハイレベルを受信すると、つまりレベルの値が 1 になると、コードは if ブランチに入ります。このとき、8259A はピンの前の状態を判断します。つまり、構造体 kvm_kpic_state のフィールド last_irr 内の IRR に対応する IRQ のビットを判断します。低レベルの場合、割り込みソースに割り込み要求があると見なされ、IRR に記録されます。もちろん、ピンの現在のステータスは last_irr フィールドに記録する必要があります。

3 割り込みフラグを設定する

8259A が割り込み要求を IRR に記録した後、次のステップは、割り込みがマスクされているかどうか、複数の割り込み要求の優先順位などを含む割り込み評価プロセスを開始することです。最後に、外部割り込みを処理するために、INTA ピンを通じて CPU に通知されます。ここでは、8259A が割り込みプロセスをアクティブに開始していることがわかりますが、仮想割り込みは多少異なります。割り込みは仮想割り込みチップによって開始されなくなりました。代わりに、ゲストがカットインされるたびに、KVM は割り込みチップを照会します。保留中の割り込みがある場合は、割り込み挿入が実行されます。シミュレートされた 8259A は割り込み要求を受信し、IRR を記録した後、変数を設定します。 KVM はゲストに切り替える前にこの変数を照会します。

  1. コミット85f455f7ddbed403b34b4d54b1eaf0e14126a126
  2. KVM:サポート追加 カーネルPICエミュレーション
  3. linux.git/drivers/kvm/i8259.c
  4. void kvm_pic_set_irq(void *不透明、 int irq、intlevel)
  5. {
  6. structkvm_pic *s = 不透明;
  7.   
  8. pic_set_irq1(&s->pics[irq >> 3], irq & 7,レベル);
  9. pic_update_irq(s);
  10. }
  11.   
  12. 静的void pic_update_irq(構造体 kvm_pic *s)
  13. {
  14. irq =pic_get_irq(&s->pics[0]);
  15. (irq>= 0)の場合
  16. s->irq_request(s->irq_request_opaque、1);
  17. それ以外 
  18. s->irq_request(s->irq_request_opaque、0);
  19. }
  20.   
  21. 静的void pic_irq_request(void *不透明、intlevel)
  22. {
  23. 構造体 kvm*kvm = 不透明;
  24.   
  25. pic_irqchip(kvm)->出力=レベル;
  26. }

vmx_vcpu_run 関数では、ゲストに入る前に、関数 vmx_intr_assist が呼び出され、仮想割り込みチップに処理待ちの割り込みがあるかどうかが確認されます。関連するコードは次のとおりです。

  1. コミット85f455f7ddbed403b34b4d54b1eaf0e14126a126
  2. KVM:サポート追加 カーネルPICエミュレーション
  3. linux.git/drivers/kvm/vmx.c
  4. 静的  int vmx_vcpu_run(…)
  5. {
  6. vmx_intr_assist(vcpu);
  7. }
  8.   
  9. 静的void vmx_intr_assist(構造体 kvm_vcpu*vcpu)
  10. {
  11. has_ext_irq = kvm_cpu_has_interrupt(vcpu);
  12. if(!has_ext_irq)
  13. 戻る;
  14. 割り込みウィンドウを開く =
  15. ((vmcs_readl(GUEST_RFLAGS) & X86_EFLAGS_IF) &&
  16. (vmcs_read32(GUEST_INTERRUPTIBILITY_INFO) & 3) == 0);
  17.  if(interrupt_window_open)
  18. vmx_inject_irq(vcpu、kvm_cpu_get_interrupt(vcpu));
  19. }

関数 kvm_cpu_has_interrupt は、8259A によって設定された変数出力を照会します。

  1. コミット85f455f7ddbed403b34b4d54b1eaf0e14126a126
  2. KVM:サポート追加 カーネルPICエミュレーション
  3. linux.git/drivers/kvm/irq.c
  4. int kvm_cpu_has_interrupt(構造体 kvm_vcpu *v)
  5. {
  6. structkvm_pic *s = pic_irqchip(v->kvm);
  7.   
  8. if(s->出力) /* PIC */
  9. 戻り値1;
  10. 0を返します
  11. }

出力が設定されている場合は、処理を待機している外部割り込みがあることを意味します。次に、ゲストが中断されるかどうか、ゲストが中断できない命令を実行しているかどうかなど、ゲストが中断される可能性があるかどうかが判断されます。挿入できる場合は、vmx_inject_irq が呼び出され、割り込み挿入が完了します。このうち、関数 vmx_inject_irq に渡される 2 番目のパラメータは、挿入する割り込みを取得する関数 kvm_cpu_get_interrupt によって返される結果です。このプロセスは割り込み評価プロセスであり、次のセクションで説明します。

4 割り込み評価

前のセクションでは、注入を実行する前に、vmx_inject_irq が関数 kvm_cpu_get_interrupt を呼び出して、注入する必要がある割り込みを取得することを説明しました。関数 kvm_cpu_get_interrupt のコア ロジックは、保留中の割り込みがブロックされているかどうかなど、割り込み評価です。保留中の割り込みの優先度は、CPU によって処理されている割り込みの優先度よりも高いですか?コードは次のとおりです。

  1. コミット85f455f7ddbed403b34b4d54b1eaf0e14126a126
  2. KVM:サポート追加 カーネルPICエミュレーション
  3. linux.git/drivers/kvm/irq.c
  4. int kvm_cpu_get_interrupt(構造体 kvm_vcpu *v)
  5. {
  6. ベクトル = kvm_pic_read_irq(s);
  7. (ベクトル != -1) の場合
  8. ベクトルを返します。
  9. }
  10.   
  11. linux.git/drivers/kvm/i8259.c
  12. 整数kvm_pic_read_irq(構造体 kvm_pic *s)
  13. {
  14. 整数irq、irq2、intno;
  15.   
  16. irq =pic_get_irq(&s->pics[0]);
  17. (irq> = 0)の場合{
  18. intno = s->pics[0].irq_base + irq;
  19. }それ以外{
  20. int no を返します。
  21. }

kvm_pic_read_irq は関数 pic_get_irq を呼び出して、評価された割り込みを取得します。上記の太字でマークされているように、割り込みベクターと割り込みピンの関係が明確にわかります。 irq_base が重ね合わされます。この irq_base は、8259A を初期化して IRn から割り込みベクターへの変換を完了するときに ICW によって設定されます。

割り込みチップは通常、複数の周辺機器に接続されているため、ある瞬間に複数のデバイス要求が到着する可能性があります。このとき、どのリクエストを最初に処理するかという問題があります。したがって、割り込みには優先度の概念があります。 8259A を例にとると、通常、次の 2 つの割り込み優先モードがあります。

1) 固定優先度。つまり、優先度は固定されており、IR0 から IR7 まで減少し、IR0 は常に最高の優先度を持ち、IR7 は常に最低の優先度を持ちます。

2) 優先順位のローテーション、つまり、現在処理中の IRn の優先順位が最低に調整され、次に処理される IRn+1 の優先順位が最高に調整されます。たとえば、現在処理中の割り込みが irq 2 の場合、irq3 の優先度が最も高く設定され、次に irq4、irq5、irq6、irq7、irq1、irq2、irq3 の優先度が設定されます。 irq5 と irq2 が同時に割り込まれたと仮定すると、明らかに irq5 が最初に処理されます。次に、irq6 が最高の優先度に設定され、その後に irq7、irq1、irq2、irq3、irq4、irq5 が続きます。

ラウンドロビン優先順位アルゴリズムを理解すれば、8259A から最高優先順位の要求を取得するコードは簡単に理解できます。

  1. commit85f455f7ddbed403b34b4d54b1eaf0e14126a126
  2. KVM:サポート追加 カーネルPICエミュレーション
  3. linux.git/drivers/kvm/i8259.c
  4. 静的  int pic_get_irq(構造体 kvm_kpic_state *s)
  5. {
  6. intマスク、cur_priority、優先度;
  7.   
  8. マスク =s->irr & ~s->imr;
  9. 優先度 =get_priority(s, マスク);
  10. 優先度 == 8 の場合
  11. -1 を返します
  12. マスク =s->isr;
  13. cur_priority = get_priority(s, マスク);
  14. if(優先度 < 現在の優先度)
  15. /*
  16. *より高い優先度が見つかりました: IRQを生成する必要があります
  17. */
  18. (優先度 + s->priority_add) & 7 を返します
  19. それ以外 
  20. -1 を返します
  21. }
  22.   
  23. 静的インラインint get_priority(structkvm_kpic_state *s, intマスク)
  24. {
  25. 優先度;
  26. (マスク== 0)の場合
  27. 戻り値8;
  28. 優先度 =0;
  29. while((mask & (1 << ((priority + s->priority_add) & 7))) == 0)
  30. 優先度++;
  31. 優先順位を返します。
  32. }

関数 pic_get_irq は 2 つの部分に分かれています。最初の部分は、現在保留中の割り込みから最も優先度の高い割り込みを取得することです。その前に、ブロックされた割り込みをフィルタリングする必要があります。 2 番目の部分は、CPU によって処理されている割り込みの優先度を取得することです。これにより、読者は、割り込みのステータスを記録するために 8259A がこれらのレジスタを必要とする理由をより具体的に理解できます。次に、2 つの割り込みの優先度を比較します。保留中の優先度が高い場合は、INTR ピンの電圧を下げて CPU に割り込み要求が送信されます。

優先度を計算する関数 get_priority を見てみましょう。変数 priority_add は現在の最高優先度のピンを記録するため、論理的には現在の最高優先度のピンから開始し、高から低の順にチェックして、保留中の割り込みがあるかどうかを確認します。たとえば、IR4 が現在最高である場合、priority_add の値は 4 になります。while ループはピン IR(4+0) から開始し、ピン IR(4+1)、IR(4+2) などをチェックします。

5 割り込みACK

物理的には、CPU が割り込み要求を処理する準備ができたら、ピン INTA を介して 8259A に確認パルスを送信します。同様に、ソフトウェアシミュレーションでも同様の処理が必要です。割り込み評価を完了し、ゲストに注入する前に、仮想 8259A で確認操作を実行する必要があります。コードは次のとおりです。

  1. コミット85f455f7ddbed403b34b4d54b1eaf0e14126a126
  2. KVM:サポート追加 カーネルPICエミュレーション
  3. linux.git/drivers/kvm/i8259.c
  4. 整数kvm_pic_read_irq(構造体 kvm_pic *s)
  5. {
  6. 整数irq、irq2、intno;
  7.   
  8. irq =pic_get_irq(&s->pics[0]);
  9. (irq> = 0)の場合{
  10. pic_intack(&s->pics[0], irq);
  11. }
  12.   
  13. 静的インライン void pic_intack(structkvm_kpic_state *s, int irq)
  14. {
  15. s->auto_eoi の場合 {
  16. }それ以外 
  17. s->isr |= (1 << irq);
  18. /*
  19. * ここではレベルセンシティブな割り込みをクリアしません
  20. */
  21. if(!(s->elcr & (1 << irq)))
  22. s->irr &= ~(1 << irq);
  23. }

割り込み評価では、関数 kvm_pic_read_irq を呼び出して評価された割り込み結果を取得した後、関数 pic_intack がすぐに呼び出され、割り込み確認アクションが完了します。 CPU から割り込み確認を受信した後、割り込みが処理され始めたため、8259A は IRR からの待機サービス要求をクリアするなど、自身のステータスを更新する必要があります。さらに、処理中の割り込みを記録するために ISR ビットを設定する必要がありますが、これは少し複雑です。

ISR サービス ビットを設定すると、プロセッサが割り込みを処理していることを示します。 ISR を設定する一般的な役割は、ISR が割り込みを処理して EOI を 8259A に送信すると、8259A が処理中の IRn を認識することです。たとえば、8259A が循環優先順位を使用する場合、最も高い優先順位は現在処理中の IRn の次の優先順位である必要があります。

8259A が AEOI モードの場合、割り込みサービス ルーチンの実行後に EOI コマンドは送信されないため、ISR を設定する必要はありません。したがって、AEOI モード (上記コードの if 分岐) では、8259A が EOI を受信したときに処理する必要があるロジックを完了する必要があります。この部分については次のセクションで説明します。

エッジ トリガーの場合、ISR ステージに入った後、IRR をリセットする必要があります。レベル トリガーの場合、8259A は割り込み要求を受信して​​から処理します。詳しくは議論しません。興味がある読者は、関数 pic_set_irq1 のレベル トリガー部分のロジックを読むことができます。

6. EOI処理について

割り込みサービス ルーチンが実行されると、割り込み処理が完了したことを 8259A に通知する EOI が 8259A に送信されます。 8259A はこの EOI を受信すると、ISR をリセットします。循環優先順位を使用する場合は、現在 IRn を処理している次の IRn を指すように変数 priority_add を設定することも必要です。

  1. commit85f455f7ddbed403b34b4d54b1eaf0e14126a126
  2. KVM:サポート追加 カーネルPICエミュレーション
  3. linux.git/drivers/kvm/i8259.c
  4. 静的void pic_ioport_write(void *不透明、u32addr、u32 val)
  5. {
  6. ケース1: /*終了 割り込み*/
  7. ケース5:
  8. 優先度 = get_priority(s, s->isr);
  9. 優先度が8の場合
  10. irq = (優先度 + s->priority_add) & 7;
  11. s->isr &= ~(1 << irq);
  12. if(コマンド == 5)
  13. s->priority_add = (irq + 1) & 7;
  14. pic_update_irq(s->pics_state);
  15. }
  16. 壊す;
  17. }

8259A が AEOI モードに設定されている場合、後続の割り込みサービス ルーチンの EOI コマンドを受信しません。次に、CPU から ACK を受信した後、8259A は EOI コマンドの受信時に実行されるロジックを処理する必要があります。前に説明したように、AEOI モードでは ISR を設定する必要がないため、ここで ISR をリセットする必要はありません。最も高い優先度の位置を記録するには、変数 priority_add を調整するだけです。

  1. コミット85f455f7ddbed403b34b4d54b1eaf0e14126a126
  2. KVM:サポート追加 カーネルPICエミュレーション
  3. 静的インライン void pic_intack(structkvm_kpic_state *s, int irq)
  4. {
  5. s->auto_eoi の場合 {
  6. s->rotate_on_auto_eoi の場合
  7. s->priority_add = (irq + 1) & 7;
  8. }それ以外 
  9. }

7 割り込みインジェクション

外部割り込みの場合、各 CPU は命令サイクルの終了後に INTR をチェックして割り込み要求があるかどうかを確認します。では、ゲスト モードの CPU は割り込み要求があることをどのようにして知るのでしょうか? Intel は VMCS に VM エントリ中断情報フィールドを設定します。 CPU は VM エントリ時にこのフィールドをチェックします。このフィールドの形式を表 3-1 に示します。

表3-1 VMエントリ中断情報フォーマット(一部)

少し

コンテンツ

7:0

割り込みまたは例外ベクトル

10:8

割り込みタイプ:

0: 外部割り込み

1: 予約済み

2: マスク不可能割り込み (NMI)

3: ハードウェア例外

4: ソフトウェア割り込み

5: 特権ソフトウェア例外

6: ソフトウェア例外

7: その他のイベント

31

効果はありますか? [2]  

VMエントリの前に、KVMモジュールは仮想8259Aをチェックして、処理する必要がある保留中の割り込みがあるかどうかを確認し、処理する割り込み情報をVMCSフィールドVMエントリに書き込みます。

  1. 中断情報:
  2. コミット85f455f7ddbed403b34b4d54b1eaf0e14126a126
  3. KVM:サポート追加 カーネルPICエミュレーション
  4. linux.git/drivers/kvm/vmx.c
  5. 静的void vmx_inject_irq(構造体 kvm_vcpu *vcpu、 int irq)
  6. {
  7. vmcs_write32(VM_ENTRY_INTR_INFO_FIELD、
  8. IRQ |INTR_TYPE_EXT_INTR | INTR_INFO_VALID_MASK は、有効なマスクです。
  9. }

上で説明したように、割り込み挿入は、KVM モジュールが各 VM エントリで 8259A で処理を待機している保留中の割り込みがあるかどうかを確認するときに発生します。これにより、通常、次の 2 つの状況で割り込みに一定の遅延が発生する可能性があります。

(1)CPUはゲストモードになっている可能性があるため、次のVM終了とVM開始を待つ必要があります。

(2)VCPUスレッドがスリープ状態になっている可能性があります。たとえば、ゲスト VCPU が hlt 命令を実行すると、ホスト モードに戻り、スレッドが中断されます。

最初のケースは、ターゲット CPU がゲストを実行しているマルチプロセッサ システムの典型的なケースです。 KVM は、ゲストで VM を終了し、ホストに切り替える方法をトリガーする必要があります。ゲスト モードの CPU が外部割り込みを受信すると、VM 終了がトリガーされ、ホストが割り込みを処理することがわかっています。したがって、KVM はターゲット CPU に IPI 割り込みを送信し、ターゲット CPU で VM 終了をトリガーできます。

2 番目のケースでは、まずスリープ状態の VCPU スレッドを起動し、CPU 準備完了キューに入れてスケジュールする必要があります。マルチプロセッサ システムの場合、「再スケジュールされた」 IPI 割り込みがターゲット CPU に送信され、起動された VCPU スレッドはゲストへの切り替えプロセスを実行するようにすぐにスケジュールされ、割り込み挿入が完了します。

したがって、割り込み要求があると、仮想割り込みチップはターゲット CPU をアクティブに「キック」します。この「キック」の機能は kvm_vcpu_kick です。

  1. コミットb6958ce44a11a9e9425d2b67a653b1ca2a27796f
  2. KVM:カーネル内でhltをエミュレートする
  3. linux.git/drivers/kvm/i8259.c
  4. 静的void pic_irq_request(void *不透明、intlevel)
  5. {
  6. pic_irqchip(kvm)->出力=レベル;
  7. (vCPUの場合)
  8. kvm_vcpu_kick(vcpu);
  9. }

仮想 CPU スレッドがスリープ状態の場合は、「ウェイクアップ」します。ターゲット CPU がゲスト モードで実行されている場合は、ゲスト モードからホスト モードに「キック」され、VM エントリ時に割り込み挿入が完了します。キック方式とは、先ほど説明した IPI です。コードは次のとおりです。

  1. コミットb6958ce44a11a9e9425d2b67a653b1ca2a27796f
  2. KVM:カーネル内でhltをエミュレートする
  3. linux.git/drivers/kvm/irq.c
  4. void kvm_vcpu_kick(構造体 kvm_vcpu *vcpu)
  5. {
  6. intipi_pcpu = vcpu->cpu;
  7.   
  8. waitqueue_active(&vcpu->wq) の場合
  9. 割り込み可能なウェイクアップ(&vcpu->wq);
  10. ++vcpu->stat.halt_wakeup;
  11. }
  12. if(vcpu->ゲストモード)
  13. smp_call_function_single(ipi_pcpu、vcpu_kick_intr、
  14. vCPU, 0, 0);
  15. }

VCPU スレッドが待機キューでスリープ状態の場合は、起動されて CPU の準備完了タスク キューに入ります。複数の CPU があり、ターゲット CPU がゲスト モードの場合、コア間割り込みを送信する必要があります。ターゲット CPU がゲストを実行している場合、この IPI 割り込みによって VM が終了し、次回ゲストに入るときに割り込みが挿入されるようになります。

実際、ターゲット CPU はコールバックを実行する必要はなく、IPI が戻るのを待つ必要もないため、smp_call_function_single を使用する必要はありません。代わりに、ターゲット CPU に再スケジュールを要求する IPI を直接送信できます。したがって、関数 smp_send_reschedule は後で直接呼び出されます。関数 smp_send_reschedule はシンプルで直接的であり、RESCHEDULE の IPI を直接送信します。

  1. コミット32f8840064d88cc3f6e85203aec7b6b57bebcb97
  2. KVM: kvm_vcpu_kicksmp_send_reschedule を使用する
  3. linux.git/arch/x86/kvm/x86.c
  4. void kvm_vcpu_kick(構造体 kvm_vcpu *vcpu)
  5. {
  6. smp_send_reschedule(CPU);
  7. }
  8.   
  9. linux.git/arch/x86/カーネル/smp.c
  10. 静的void native_smp_send_reschedule( int cpu)
  11. {
  12. apic->send_IPI_mask(cpumask_of(cpu), RESCHEDULE_VECTOR);
  13. }

この記事はWeChatの公開アカウント「Linux Reading Field」から転載したものです。以下のQRコードからフォローできます。この記事を転載する場合は、Linux Reading Field 公式アカウントまでご連絡ください。

<<:  5分でDockerの基本原理を学ぶ

>>:  マルチテナントクラウドプラットフォームの導入方法を学ぶ

推薦する

Heyteaの価値が600億ドルなのはなぜですか?

那雪茶(02150.HK)は上場時にIPO価格を下回り、現在の時価総額は300億香港ドル未満となって...

justhost シンガポール VPS はいかがでしょうか?月額36元で200Mの帯域幅で無制限のトラフィックを提供するシンガポールVPSの簡単なレビュー

Justhost は今週、シンガポール VPS 事業を開始しました。ご存知のとおり、シンガポールは地...

Baidu ウェブマスター プラットフォーム: Baidu スナップショットの問題に関する説明

長い間、一部のウェブマスターは、Baidu スナップショットの更新時間について誤解しており、ウェブサ...

K8s の交換が必要です!

著者 |趙雲現在、Kubernetes はマイクロサービスのデプロイメント問題を解決し、すでにコンテ...

企業はどのようにしてブランドの魅力を際立たせる独立した電子商取引ウェブサイトシステムを構築できるのでしょうか?

2018年最もホットなプロジェクト:テレマーケティングロボットがあなたの参加を待っています電子商取引...

ニュース: Linode が PayPal と Google Pay を定期支払い方法として正式に追加

Linode は、PayPal とGoogle Pay が支払い方法に追加されることを公式に発表しま...

小さくて美しいマーケティングアプローチは、独自の特徴を出すことです

最近、「小さくて美しい」という言葉をよく耳にします。小さくて美しいとは、いったいどういう意味でしょう...

SEOは常に検索エンジンのルールと一致する必要があります

最近、ウェブマスターフォーラムでは「検索エンジンのルール」や「検索エンジンのアルゴリズム」という言葉...

今年のダブル11マーケティングに関する30の真実

興奮した心と震える手で、親切なリマインダーです。最終支払いをされるすべての方に、2日以内に最終支払い...

個人的な経験からSEO最適化計画の7つの重要なポイントをまとめました

1. ウェブサイトの位置づけウェブサイトのポジショニングとは、主に、私たちが属する業界に応じて行うべ...

Baidu Green Radishの外部リンクは依然として機能しており、コンテンツは小さくて美しい傾向がある

Baidu Green Radish Algorithm がリリースされてからしばらく経ちました。こ...

偽装外皮を脱ぎ、本来の皮を着ける

おそらく、この記事のタイトルを読んだ読者は、私が「独創性が必須」と主張していると思うに違いありません...

prometeus-4.5 USD/256 MB RAM/5 GB SSD/XEN/インド

Prometeus はインドのデータセンターで VPS を開始しました (XEN PV ベース、SS...

スマートな名刺ブランドとは?インターネット起業とフランチャイズの第一選択肢!

2018年最もホットなプロジェクト:テレマーケティングロボットがあなたの参加を待っています人工知能の...