DMA Contiguous Memory Allocator の x86_64 アーキテクチャサポート

私の関わっている業務では Linux kernel をいじることがあるのですが、その中で Linux kernel の変更を提案してマージしてもらった経験があったので、その内容や経緯を紹介したいと思います。

DMA Contiguous Memory Allocator とはどのようなものか

DMA Contiguous Memory Allocator は、デバイスドライバがホストとデバイス間の DMA 転送時に使用するメモリとして、かなり大きな連続領域を割り当てることを可能にするためのオプショナルな機能です。 この機能を理解するのに知っておく必要がある Linux kernel でのシステムのメモリ割り当てについて簡単に説明します。

まずページアロケータというシステムのメモリ管理の基盤となる仕組みがあり、システムのメモリをページという単位 (CPU のメモリアクセス機構によって定まり、大抵は 4KiB ですが、それ以外のサイズで設定できるプラットフォームもある) で管理して、連続した 2 のべき乗個のページの割り当て要求に応えることができるインターフェースを提供します。ただし、ページアロケータによる一回のメモリ割り当てによって得られる連続メモリ領域の上限は 2 の (MAX_ORDER – 1) 乗個までに制限されます。 (MAX_ORDER は大抵は 11 なので 1 ページ 4KiB の場合は 4MiB となる)

特殊な用途向けに、ページアロケータの上限サイズを超える連続メモリ領域を割り当てることが可能な CMA (Contiguous memory allocator) というオプショナルなメモリ割り当ての仕組みも存在します。これは、カーネルビルド時に設定したデフォルトサイズか、カーネル起動パラメータの cma オプションで指定したサイズのメモリを、あらかじめ確保しておいて、そこから連続メモリ領域を割り当てます。

デバイスドライバが DMA 転送可能なメモリ領域を必要とする場合 DMA API によって提供されている dma_alloc_coherent 関数を呼び出します。この関数は、対象デバイスのメモリアクセス制約等を考慮した上で適切なメモリ領域を割り当てますが、通常はページアロケータを土台としてメモリを割り当てるため、最大のメモリ領域のサイズは、ページアロケータと同様に制限されます。しかし DMA Contiguous Memory Allocator を有効にした場合は CMA によってメモリ領域を割り当てるため、かなり大きな連続サイズの DMA メモリでも割り当てることができるようになります。

しかし、通常はデバイスが比較的大きい合計サイズの DMA メモリを必要とする場合であっても、デバイスが細切れの複数のメモリ領域へのアクセスをサポートしているので、それほど大きな連続領域を必要とすることはなく DMA Contiguous Memory Allocator の機能も必要ありません。ところが特殊なデバイスが、数十から数百メガバイトの連続した単一のメモリ領域を必要とすることがあり、その場合に必要となる機能です。

x86_64 アーキテクチャサポートを追加した経緯

私が携わっていたデバイスでは、まさに上述のような大きな連続メモリ領域をデバイスが要求していたため DMA Contiguous Memory Allocator の機能を有効にして活用したかったのですが、その当時は ARM アーキテクチャでは動作実績があったものの、私たちが主に使いたかった x86 64-bit アーキテクチャでは完全にサポートされていませんでした。そのため、代わりにカーネル起動パラメータに mem オプションでシステムの使用するメモリを制限させて、システムの管轄外としたメモリ領域をデバイスに使用させたり、別の crashkernel オプションを乱用し、巨大な DMA メモリ領域を捻出していました。

しかし、こうした不完全な代替方法には数えきれないほどの問題点や制限事項があるため DMA Contiguous Memory Allocator の x86_64 アーキテクチャサポートを動作するようにしました。そして、次のような理由からこの変更は Linux kernel そのものにマージするよう提案したほうがよいのではと考えました。

  •  自分のやり方が間違っていないか、この分野に一番詳しい人たちにレビューしてもらいたかった。
  • デバイスドライバのソースコード外の Linux kernel の中核に近い部分の変更なので、マージされなければ将来的に様々な面倒事が待ち受けていそうなため。(他の人に使ってもらう場合、必要なソースコードをどのように提供するかなど)

こうして Linux 開発者のための linux-kernel メーリングリストに提案してマージして貰うことができました。

すんなりとマージに至ったわけではなく、前述の通り特殊なデバイスで実験的な機能を検証するために必要だったのですが x86_64 アーキテクチャでも利用可能で、かつこの機能を必要とするデバイスドライバがメインラインの Linux kernel の中に存在することを示すことはできなかったため x86_64 アーキテクチャには不要な機能ではないか、という反対意見もありました。それでもメーリングリストを介さないプライベートなメールでこの機能についての問い合わせがあったことや、動作確認する環境がないためサポート対象外のままにしていた AMD IOMMU ハードウェアの対応が、のちに別の開発者によって行われたことなどから、私たち以外にも x86_64 アーキテクチャで必要されていた機能なのだと思います。

マージされた後も x86_64 アーキテクチャで DMA Contiguous Memory Allocator を意図せずに有効にして、深刻な性能劣化が生じたという報告がありました。 この問題は CMA のために確保したメモリ領域のサイズが、システムに搭載されているデバイスによって利用される DMA メモリの合計サイズに比べて充分な大きさでない場合に CMA のメモリ割り当てに時間をかかってしまうために起きる現象でした。私たちが関わっていたデバイスでは上述したように、ときには数百メガバイトといった大きいサイズを割り当てて実験することもあったため、かなり余裕をもった大きさのメモリ領域を CMA 用に確保していたので、この報告によって初めて気づくことになりました。

まとめ

Linux のデバイスドライバが巨大な DMA 転送用の連続メモリ領域を割り当てるための機能を紹介しました。しかし、それを自分たちが利用するためには Linux カーネルを変更する必要がありました。その変更を Linux kernel にマージしてもらうまでには労力が必要ですが、それに見合う対価があると判断して開発メーリングリストに提案してマージしてもらいました。マージ後も不具合の報告がありましたが、想定することができなかった現象を見つけることができたと考えると利点の一つであったと思います。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

This site uses Akismet to reduce spam. Learn how your comment data is processed.