ドライバ割り込み処理に関する考察
ドライバ割り込み処理について迷いがあるのでまとめてみよう。
Mona 0.3.0系の割り込み処理 - 現状
割り込みを受け付けたいドライバのやるべきこと
システムコール syscall_set_irq_receiver を呼び出す。引数は割り込みを受け付ける IRQ番号である。
この関数を呼び出した「スレッド」は割り込みがあったことをメッセージ MSG_INTERRUPTED で知ることができるようになる。
メッセージ受信スレッドを用意してメッセージループでこのメッセージを受信し、メッセージをきっかけに割り込み処理をするという流れで多くのドライバが実装されている。
カーネル側の処理
カーネルは自身が用意した割り込みハンドラで割り込みを受け付ける。
割り込みが発生すると EOI を発行し、その IRQ番号で割り込みを待つスレッドがいれば MSG_INTERRUPTED をそのスレッドに送信する。
メッセージ送信対象スレッドが存在する場合はメッセージを受信するスレッドの優先度を上げて再スケジュールする。(すぐにメッセージが受信されることを期待している)
Monaのメッセージ機構に関する補足
Monaのメッセージ送信・受信は必ずコンテキストスイッチを伴います。
現象の原因
PCIがレベルトリガの割り込みなので、ひとつの割り込み要因に対して割り込みハンドラがたくさん呼ばれてしまう。
1.割り込み要因発生(NIC)
2.割り込み通知(PIC)
3.割り込みハンドラ
1.EOI
2.メッセージ送信
4.再スケジュール(高確率で受信スレッドにスイッチ)
5.受信スレッドが割り込み処理中・・・
6.5.の処理中に2.に戻る
解決案1 - export & call
問題点の1つとして現状のMonaではカーネルの割り込みハンドラでドライバ割り込み処理が一切行われない(メッセージ通知だけ)ことが挙げられる。
これはカーネルとドライバの独立性を高めるために行われているが、この仕組みのため実際のドライバの割り込み処理が行われるのがだいぶ後になったり、EOIを発行後にドライバ処理が行われるなどの弊害がある。
そこで大きく方針を変えてみる。
カーネルの割り込みハンドラ内からドライバの割り込み処理関数を call できるようにするというのはどうだろうか?
これをやれば
- 割り込み処理後にEOIを発行可能
- 割り込み処理を即座に行える
という良いことがあります。
ただしドライバが割り込み処理を export し、それをカーネルが call するので今までよりはドライバ・カーネルの結合が密になるというデメリットがあります。
解決案2 - ドライバでEOI
割り込み処理が多数発生する原因は、割り込み要因が解決されていない(=ドライバの割り込み処理が完結していない)のにも関わらず、EOIを発行しているからである。
なのでドライバにEOIを発行させれば解決しそうだというのがこの案。
Gakuさんが教えてくれた指定 EOI を使えば IRQ を指定してのEOIが出来そう。
メリットは
- 実装が簡単なこと
- 今の仕組みをほぼ維持できること
デメリットは
- 異常割り込みに対しての EOI が発行されない危険性があること(Sato氏による指摘)
ではどうするか?
僕としては
- 解決案 2 - ドライバで EOI が簡単に試すことが出来るのでやってみる
- だめなら解決案 1を実装
と思っているが、他に方法もあるかもしれないのでご意見がある人方はぜひお寄せください。
追記
osdev-jでいろいろな人と話した。
- EOI遅延は他の割り込みをストップしてしまうのがよろしくない
- ドライバにEOIを任せるのは危険
要請としては
- 他の割り込みを邪魔しない
- 割り込み不可能状態は出来るだけ短く
- EOIは速めにやるべき
- 1つの割り込み要因からは複数回連続で割り込みが来ないようにする
となり
案3が浮上(正確には以前からEDS1275さんが暫定処置で行った方法)
1.割り込み発生
2.カーネル割り込みハンドラに突入
1.EOI発行
2.カーネルの割り込みハンドラ内で該当IRQの割り込みをマスクする
3.割り込みをメッセージで知らせる
3.ドライバはメッセージを受信
4.ドライバは割り込み要因を解除
5.マスク解除
となる。