コードをきれいにしましょう:devres APIはいつ有害ですか?

Linuxカーネルのマネージリソース(デバイスリソース管理またはdevres APIとも呼ばれます)は、以前に短いメモを書きましたが、非常に便利ですが、ドライバーを作成したり、既存のものを変更したりするときに、この補助的な機能セットを特効薬として使用しないでください。 これらの方法を慎重に適用する必要がある場合を考慮してください。



タスクレットがある場合の割り込みハンドラーの登録



割り込みとタスクレットについては、EmBox社のブログの記事で明快に説明されているため、読者はすでにこの資料または類似の資料に精通していることが理解されます。



例として次の擬似コードを取り上げます。

struct my_struct {struct tasklet_struct *tasklet; int irq; }; void tasklet_handler(…) { do_the_things_right(…); } irqreturn_t irq_handler(void *param) { struct my_struct *ms = param; … tasklet_schedule(&ms->tasklet); return IRQ_HANDLED; } int probe(…) { struct my_struct *ms; int err; ms = devm_kzalloc(…); … tasklet_init(&ms->tasklet, tasklet_handler, (unsigned long)ms); … err = devm_request_irq(ms->irq, irq_handler, …, ms); if (err) return err; return 0; } int remove(…) { struct my_struct *ms = …; … tasklet_kill(&ms->tasklet); }
      
      





気配りのある読者はすぐに次のように叫ぶだろう:「これが無限のサイクルにつながる競争条件だ!」そして彼は正しいだろう。



理由を見てみましょう。 タスクレットはソフト割り込み(softirq)のコンテキストで実行されるため、スケジューリング( tasklet_schedule()



)とタスクの間に遅延が発生する可能性があります。 このとき、ユーザーがrmmod my_module



、ドライバーをメモリーから削除するだけでよい場合があります。 もちろん、明示的にtasklet_kill()



削除を呼び出しますtasklet_kill()



参照してください。ただし、割り込みハンドラはまだアクティブです。 devres APIを使用し、実行にキューの順に削除することを計画しました->remove()







治す方法は? はい、非常に簡単です、手を見てください:

 int remove(…) { struct my_struct *ms = …; … devm_free_irq(ms->irq, ms); tasklet_kill(&ms->tasklet); }
      
      





オブジェクトの削除時にdevres APIを使用する場合、まさにそのまれなケースです。



キャラクターデバイスなどのドライバーを隠すものは何ですか?



ここで、次の擬似コードを検討してください。

 int closecb(…) { struct my_struct *ms = …; do_something_on_close(ms, …); } struct file_ops fops = { .close = closecb, … }; int probe(…) { struct my_struct *ms; int err; ms = devm_kzalloc(…); … err = register_char_device(ms, "node_served_by_driver", &fops, …); if (err) return err; return 0; } int remove(…) { struct my_struct *ms = …; … }
      
      





次のシナリオを想像してください。

  1. ドライバがロードされ、デバイスに接続されていることを確認してください。
  2. /dev/node_served_by_driver



    を開き、デバイスが開いたままになるようにします。
  3. たとえば、次のコマンドを実行して、デバイスからドライバーを解放します。

     echo our_device_name > /sys/bus/platform/drivers/our_driver_name/unbind
          
          



    または、可能であれば、デバイスをバスから単に切断します。たとえば、USBドライブを切断します。
  4. 次に、デバイスを閉じます。
  5. カーネルの崩壊をお楽しみください。


なぜこれが起こっているのですか? はい。ステージ->probe()



割り当てられたメモリ->probe()



、デバイスが解放される->probe()



解放されるためです。 そして、私たちはまだこのメモリを使用しています! この場合、デバイスドライバーは削除されず、削除できません。 デバイスを開いたプログラムによって保持され、明示的に閉じて削除されるまでメモリに残ります。



治療方法は? シンプル。 ドライバのファイル操作でdevm_kzalloc()



を使用して割り当てられたメモリを使用しないでください。オブジェクトの寿命を注意深く監視してください。 devres APIの作成者によると、 devプレフィックスはただそこにあるのではなく、リソースがユーザーからのイベントの処理ではなくハードウェアに直接関係していることを示すためです。



PS実際には、問題はより広く、将来のカーネルサミット2015での議論のために提起されています。



頑張ってデバッグしてください!



All Articles