はじめに
Linux では DMA Bufferを mmap した時に、ある条件が揃うと CPU Cache が無効になり、パフォーマンスが極端に落ちる場合があります。そこで、何故そのようなことが起こるのか説明します。少し長くなるので、次のように記事を幾つかに分けて投稿します。
- はじめに(この記事)
- Cache Coherence 問題
- Cache Aliasing 問題
- Linux Kernel の Cache 問題の扱い
- Linux では Cache Coherence Hardware を持っていないとDMA Buffer をmmap する際に CPU Cache が無効になる
- Raspberry Pi の例
- RISC-V CPU の注意点
- 所感
1〜3は、コンピューターアーキテクチャの基本的な事項を、簡単に説明したものです。すでにご存じの方は読み飛ばしてください。
4 はこれらの問題を Linux Kernel 内でどのように扱っているかを説明します。
5 がこれらの記事群の結論です。結論だけ知りたい方はここだけ読んでください。
この記事では、まずタイトルの「Linux で DMA Bufferを mmap した時に CPU Cacheが無効になる場合がある」を説明する際に登場する主要なコンポーネントの紹介と、DMA Buffer と mmap などの基本的な知識について説明します。すでにご存じの方は読み飛ばしてください。
基礎知識
主要コンポーネントの紹介
ここでは、「Linux で DMA Bufferを mmap した時に CPU Cacheが無効になる場合がある」 のを説明する際に登場するコンポーネントを紹介します。なお、実際のコンピューターシステムはもっと複雑ですが、この記事では説明に必要な分だけを簡潔にモデル化しています。
この簡潔版コンピューターモデルでは、次の図で示すように、各コンポーネントを4つに分類しています。
Fig.1 Introduction of main components
- CPU: Central Processing Unit
ここでは Linux が動く CPU を指します。
CPU には仮想アドレスを物理アドレスに変換する MMU(Memory Management Unit)とメモリアクセスを高速に見せるための CACHE が含まれます。特に CACHE はこの話題の主役になります。 - MEMORY:
ここでは Linux が管理するメモリを指します。 - DEVICE:
ここでは、CPU 以外の機能ユニットを十把一絡げに DEVICE と称します。
USB I/F や Net I/F 等の I/O ユニット、GPU や NPU などのアクセラレータ等も、ここでは DEVICE に分類します。
さらに CPU は積んでいるけど Linux が動作していないようなサブシステムもここでは DEVICE に分類しています。 - InterConnect:
CPU、MEMORY、DEVICE を相互接続するユニットです。
ここでは説明を単純にするために、一つの箱で表現していますが、実際のコンピューターシステムはもっと複雑です。
一般的には相互接続する際には AXI、TileLink、PCI-Express、CLX などのプロトコルが使われます。InterConnect はこれらのプロトコルで CPU、MEMORY、DEVICE を相互接続します。
DMA バッファとは
CPU が DEVICE とデータのやり取りする方法は大きく二つあります。
一つ目は DEVICE の持つレジスタやメモリに CPU が直接アクセスする方法です。通常、DEVICE は CPU からアクセスできるレジスタを持っており、CPU がこれらのレジスタにアクセスすることによって DEVICE の制御を行います。このレジスタを経由してデータを転送することが可能です。
しかし、このレジスタをアクセスしてデータを転送する方法には問題があります。それは、CPU にとってレジスタアクセスが非常に遅いことです。例えば、CPUがレジスタにアクセスする際に数マイクロ秒かかることはざらにありますが、これは 1GHz で動作する CPU にとっては数千命令を実行する時間に相当します。こんな速度で大量のデータを転送していては、CPU の無駄遣いと言わざるを得ません。
そこで MEMORY に DEVICE がアクセスできるバッファを用意しておいて、DEVICE が直接 MEMORY にアクセスすることで CPU によるデータ転送を行わないようにする方法が考えられます。これをDMA (Direct Memory Access) 方式と言い、MEMORY上にある DEVICE がアクセスできるバッファを DMA バッファと呼びます。
Fig.2 DMA(Direct Memory Access) and DMA Buffer
Linux では DMA バッファは Linux Kernel の管理下におかれます。これは仮想記憶と紐づけるためです。通常 DEVICE が MEMORY にアクセスする際は物理アドレスが用いられます。一方、仮想記憶方式では CPU が MEMORY にアクセスする際は仮想アドレスが用いられます。これらの異なるアドレス空間を取り持っているハードウェアが MMU であり、それを管理しているのが Linux Kernel だからです。
また、Linux では DMA バッファは Kernel 空間に配置されます。ユーザー空間上のバッファだと、場合によってはメモリの再配置などによって物理アドレスが替わることがありますが、これは DEVICE が DMA する際に問題になります。そこで Linux では DMA バッファは Kernel 空間に配置してしまって動かないようにしています。
Linux では DMA バッファの管理は DMA Mapping API で行われます。
mmap とは
前節で Linux では DMA バッファは Linux Kernel の管理下におかれ Kernel 空間に配置されると説明しました。では、ユーザープログラムが DEVICE とデータを転送する際にはどうするのでしょうか。
まず最も簡単なのが、ユーザープログラム上にあるデータを Linux Kernel が DMA バッファにコピーする方式です。
コピー方式ではデータのコピーが発生しますが、このデータのコピーは CPU がメモリにアクセスすることで行われます。したがって CPU に負荷がかかるため、性能上問題があります。
Fig.3 Copy data between DMA Buffer and User Data Area
そこでもう一つの方法として mmap 方式が考えられました。これは Kernel 空間に割り当てられた DMA バッファをユーザープログラムからアクセスできるように User Space に仮想アドレスを割り当てる方法です。このようにすれば、ユーザープログラムが直接 DMA バッファにアクセスすることができるようになり、余計なコピーが発生しません。
Fig.4 Mmap DMA Buffer to User Space
まとめ
この記事では、「Linux で DMA Bufferを mmap した時に CPU Cacheが無効になる場合がある」のを説明する際に登場するコンポーネントと DMA Buffer と mmap について説明しました。
本来、DEVICE にデータを効率的に転送する方法である、DMA Buffer と mmap ですが、何故かCPU Cache が無効になり却って性能が落ちる場合があるのです。
次の記事 『Linux で DMA Bufferを mmap した時に CPU Cacheが無効になる場合がある (Cache Coherence 問題とは何か)』 では、その原因の一つである Cache Coherence 問題 について説明します。
補足
モチベーション
これらの記事は、筆者が 『Kernel/VM探検隊@北陸 Part 6』 で説明した内容を記事にしたものです。一応、動画としても残っているのですが、筆者のプレゼンテーションがあまりにも下手くそで何を言ってるのかさっぱり判らないので、少なくとも資料として残しておこうと思った次第です。
参考
- 『Kernel/VM探検隊@北陸 Part 6』@connpass
https://kernelvm.connpass.com/event/297033 - 『Kernel/VM探検隊@北陸 Part 6』@YouTube
https://www.youtube.com/watch?v=fp-LZl0GIFs