コードをよりきれいにしましょう:デバイスドライバー用のLinuxカーネルの管理対象リソースについて一言

Linuxカーネルに表示されるドライバーを見ると、開発者はカーネルインフラストラクチャ、またはデバイスドライバーを記述する際の生活を大幅に簡素化する内部APIについて十分に知らないことに注意するしかありません。 今日は、管理対象リソースのトピックに触れます。 特に、それらがどのように機能し、どのようにドライバー開発を簡素化するかを説明します。

提供されたAPIを条件付きで以下に分割します。


各カテゴリで、利用可能な機能のリストと、必要に応じて注意する価値のある追加情報を提供します。 しかし、最初に、これらのリソースがどのように管理されるかの原理について簡単に説明します。

デバイスのリソース管理


2007年、Tejun Heoは、いわゆるdevres APIを作成することにより、管理対象デバイスリソースを使用してデバイスドライバーを簡素化することを提案しました。

2007年の事例
コミット9ac7849e35f705830f7b016ff272b0ff1f7ff759
著者:Tejun Heo <htejun@gmail.com>

devres:デバイスのリソース管理

その考え方は次のとおりです。 Linuxカーネルのデバイスツリーには、各デバイスにstruct device表現struct device deviceがあります。 devres_headフィールドがdevres_head (実際にはアクセス同期用の別のフィールド)に追加されました。これは、ステージ->probe()使用された場合、管理対象リソースのリストを示します。 管理リソースには、何らかのデータ構造に関連付けられたヒープ上のメモリが割り当てられます。たとえば、 kmalloc()場合、それは単なるvoid *ポインターです。

ステージ->probe() 、既に述べたように、管理されたリソースのリストが補充されます。ステージ->remove()で、ドライバーサービスの主要部分自体が、占有されているすべてのリソースを追加とまったく逆の順序で解放します。

例外的なケースの場合、対応するリリースをより早く明示的に引き起こすことができます。 ここに問題があります。次のパートの例を使用して分析します。

DMAおよびI / Oに割り当てられたものを含むメモリ


次の基本関数を使用して、メモリを割り当てることができます。
devm_kasprintf()
devm_kcalloc()
devm_kmalloc()
devm_kmemdup()
devm_kstrdup()
devm_kzalloc()

私の意見ではそれぞれの目的は明らかであり、これについては詳しく語りません。 使用例に移りましょう。

構造にメモリを割り当て、ドライバーが終了したときに自動的に解放されるようにするとします。
 struct my_cool_device { struct device *dev; void *memregion; }; int mydev_probe(...) { struct device *dev = ...; struct my_cool_device *cool; cool = devm_kzalloc(dev, ...); if (!cool) return -ENOMEM; cool->memregion = devm_kmalloc(dev, ...); if (!cool->memregion) return -ENOMEM; cool->dev = dev; dev_info(dev, "Found my cool device\n"); return 0; } void mydev_remove(...) { /* Nothing! */ } 


関数の途中でエラーが発生したときに、出口パスがどれほど美しいかに気づきましたか? そして、それはどのように素晴らしい->remove()

何らかの理由でこれを明示的に行う必要がある場合、リソースを解放する方法の例を次に示します。
正しく:
 void mydev_remove(...) { struct device *dev = ...; struct my_cool_device *cool = ...; devm_kfree(dev, cool->memregion); } 

間違った:
 void mydev_remove(...) { struct my_cool_device *cool = ...; kfree(cool->memregion); } 


DMAの使用に適したメモリを割り当てるには
dmam_alloc_coherent()
dmam_alloc_noncoherent()
dmam_pool_create()

I / Oリソースは、以下の機能を使用して割り当てることができます。
devm_ioport_map()
devm_ioremap()
devm_ioremap_resource():リソースをチェックし、メモリ領域を要求し、デバイスの物理アドレスに投影します
pcim_iomap()
pcim_iomap_regions():領域を照会し、必要なBARで指定された物理アドレスをその領域に投影します
pcim_iomap_table():BAR番号でインデックス付けされた予測アドレスの配列

下部はPCIバス上のデバイスを指しますが、同時にI / Oカテゴリに配置されることに注意してください。

中断


devm_request_any_context_irq()
devm_request_irq()
devm_request_threaded_irq()


電圧調整器


devm_regulator_bulk_get()
devm_regulator_get()
devm_regulator_register()


バスドライバー(PCI、SPI、IIO)の場合


バスIIO:
devm_iio_device_alloc()
devm_iio_device_register()
devm_iio_kfifo_allocate()
devm_iio_trigger_alloc()

MDIOバス:
devm_mdiobus_alloc()
devm_mdiobus_alloc_size()

PCIバス:
pcim_enable_device():成功した場合、すべての操作が管理されていると見なされます
pcim_pin_device():リリース後にデバイスを許可したままにする

pcim_enable_device()特に注意してpcim_enable_device() 。 適用したら、選択と投影が古い方法で実行されている場合でも、領域のリリースをクリーンアップする必要があります。 たとえば、 コミット5618955c4269で詳細を確認できます。

SPIバス:
devm_spi_register_master()


GPIO、ピン制御


GPIO:
devm_gpiod_get()
devm_gpiod_get_index()
devm_gpiod_get_index_optional()
devm_gpiod_get_optional()

ピン制御:
devm_pinctrl_get()

残り


リソース管理で最も一般的で使用されているヘルパー関数のみに触れました。 残りは、カーネルAPIの対応するドキュメントであるdevres.txtにあります。

Source: https://habr.com/ru/post/J255459/


All Articles