Linux で DMA Bufferを mmap した時に CPU Cacheが無効になる場合がある (RISC-V CPU の注意点)







# はじめに


Linux では DMA Bufferを mmap した時に、ある条件が揃うと CPU Cache が無効になり、パフォーマンスが極端に落ちる場合があります。そこで、何故そのようなことが起こるのか説明します。少し長くなるので、次のように記事を幾つかに分けて投稿します。



1. [はじめに] 
2. [Cache Coherence 問題]
3. [Cache Aliasing 問題]
4. [Linux Kernel の Cache 問題の扱い]
5. [Linux では Cache Coherence Hardware を持っていないとDMA Buffer をmmap する際に CPU Cache が無効になる]
6. [Raspberry Pi の例]
7.  __RISC-V CPU の注意点 （この記事)__  
8. [所感]



1〜3は、コンピューターアーキテクチャの基本的な事項を、簡単に説明したものです。すでにご存じの方は読み飛ばしてください。

4 はこれらの問題を Linux Kernel 内でどのように扱っているかを説明します。

5 がこれらの記事群の結論です。結論だけ知りたい方はここだけ読んでください。



前々回の記事 [『Linux では Cache Cohrence Hardware を持っていないとDMA Buffer をmmap する際に CPU Cache が無効になる』] では、Cache Coherence Hardware を持っていないコンピューターシステムの場合は、例え Cache Aliasing 問題が起きない場合でも、 DMA Buffer を mmap する際にキャッシュが無効になることを説明しました。この記事では、RISC-V CPU を採用しているコンピューターシステムでも、場合によっては注意が必要なことを説明します。


# RISC-V CPU の場合



## dev_is_dma_coherent の紹介(再掲)


[『Linux で DMA Bufferを mmap した時に CPU Cacheが無効になる場合がある (Linux Kernel の Cache 問題の扱い)』]という記事で、dev_is_dma_coherent という Linux Kernel で Cache Coherence 問題と Cache Aliasing 問題を扱う際にとても重要な役目を持つインライン関数を次のように紹介しました。



dev_is_dma_coherent() は次のように定義されています。


```C:https://elixir.bootlin.com/linux/v6.1.97/source/include/linux/dma-map-ops.h#L267
#if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE) || \
        defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU) || \
        defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU_ALL)
extern bool dma_default_coherent;
static inline bool dev_is_dma_coherent(struct device *dev)
{
        return dev->dma_coherent;
}
#else
static inline bool dev_is_dma_coherent(struct device *dev)
{
        return true;
}
#endif /* CONFIG_ARCH_HAS_DMA_COHERENCE_H */
```




この定義を見てもわかる通り、dev_is_dma_coherent() は２種類に実装方法があります。この実装方法の違いはアーキテクチャによってだいたい次のように分かれています。



* struct device の dma_cohrent フィールドの値を返すように定義
  * arc, arm, arm64,m68k, sh 等の主に組み込み系のプロセッサ
  *  __riscv (linux 6.0 以降)__  
* 常に true を返すように定義
  * x86, ia64 等 PC 用のプロセッサ
  *  __riscv (linux 5.19 以前)__  



上記のように Linux Kernel 5.19 以前と 6.0 以降で、RISC-V CPU の場合の定義方法が異なっています。5.19 以前は何も指定していなくても dev_is_dma_coherent() は true を返していたのですが、6.0 以降は struct device の dma_cohrent フィールドの値を返すように変更されています。


## Linux 6.0 以降の arch/riscv/Kconfig



### CONFIG_RISCV_DMA_NONCOHERENT


Linux Kernel 6.0 以降、arch/riscv/Kconfig に  CONFIG_RISCV_DMA_NONCOHERENT が追加されました。 Kconfig の内容をみてわかるように、これが指定されている場合は dev_is_dma_coherent() は struct device の dma_cohrent フィールドの値を返すようになりました。

なお、CONFIG_RISCV_DMA_NONCOHERENT は menuconfig 等でユーザーが明示的に指定するものではなく、他の CONFIG によって間接的に指定されるようです。


```Kconfig:https://elixir.bootlin.com/linux/v6.0/source/arch/riscv/Kconfig#L22242
config RISCV_DMA_NONCOHERENT
        bool
        select ARCH_HAS_DMA_PREP_COHERENT
        select ARCH_HAS_SYNC_DMA_FOR_DEVICE
        select ARCH_HAS_SYNC_DMA_FOR_CPU
        select ARCH_HAS_SETUP_DMA_OPS
        select DMA_DIRECT_REMAP

```



### CONFIG_RISCV_ISA_ZICBOM


CONFIG_RISCV_DMA_NONCOHERENT を間接的に指定しているのが、同じく arch/riscv/Kconfig にある CONFIG_RISCV_ISA_ZICBOM です。


```Kconfig:https://elixir.bootlin.com/linux/v6.0/source/arch/riscv/Kconfig#L410
config RISCV_ISA_ZICBOM
        bool "Zicbom extension support for non-coherent DMA operation"
        depends on CC_HAS_ZICBOM
        depends on !XIP_KERNEL && MMU
        select RISCV_DMA_NONCOHERENT
        select RISCV_ALTERNATIVE
        default y
        help
           Adds support to dynamically detect the presence of the ZICBOM
           extension (Cache Block Management Operations) and enable its
           usage.
           The Zicbom extension can be used to handle for example
           non-coherent DMA support on devices that need it.
           If you don't know what to do here, say Y.

```


RISC-V CPU は、当初、Cache の Coherence 問題はハードウェアで解決するポリシーだったようです(原典不詳、間違ってたらゴメンナサイ。たまたま RISC-V CPU の最初の実装がそうだっただけかもしれません。)

しかし、その後、キャッシュをソフトウェアで制御するための命令が RISC-V に追加されました。それが ZICBOM extension (Cache Block Management Operations) です。詳細は次の資料を参照してください。



* 「RISC-V Base Cache Management Operation ISA Extensions」   
https://raw.githubusercontent.com/riscv/riscv-CMOs/master/specifications/cmobase-v1.0.1.pdf
* 「Cache Management Operations for RISC-V」   
https://github.com/riscv/riscv-CMOs



そして、次のように、コンパイル時に RISC-V CPU が ZICBOM をサポートしている場合は、自動的に CONFIG_CC_HAS_ZICBCM が true になり、CONFIG_RISCV_ISA_ZICBOM も true になります。


```Kconfig:https://elixir.bootlin.com/linux/v6.0/source/arch/riscv/Kconfig#L405
config CC_HAS_ZICBOM
        bool
        default y if 64BIT && $(cc-option,-mabi=lp64 -march=rv64ima_zicbom)
        default y if 32BIT && $(cc-option,-mabi=ilp32 -march=rv32ima_zicbom)

```




つまり、Linux Kernel 6.0 以降は、RISC-V が ZICBOM をサポートしている場合は自動的に dev_is_dma_coherent() は struct device の dma_cohrent フィールドの値を返すようになります。

ここで struct device の dma_cohrent フィールドを適切に指定していなければ、場合によっては DMA Buffer を mmap した際にキャッシュが無効になってしまいます。

Linux Kernel 5.19 以前は特に問題なかったのに、Linux Kerne 6.0 にしたら何故かカメラ入力が遅くなったとか、DEVICEとのデータのやり取りが遅くなったとか発生するかもしれません。その場合は、struct device の dma_cohrent フィールドがちゃんと設定されていないのが原因かもしれません。




### CONFIG_ERRATA_THEAD


もうひとつ、CONFIG_RISCV_DMA_NONCOHERENT を間接的に指定しているのが、arch/riscv/Kconfig.erratas にある CONFIG_ERRATA_THEAD です。

Errata とは、もともと誤字、誤植、誤謬などを意味する英語ですが、ここでは CPU などの半導体製品に回路設計の段階のミスがあって出荷時(または後)に公表されるものを指します。そして、Linux Kernel の Kconfig.erratas とかには、その Errata(ハードウェアのミス)をソフトウェア側で対処するための回避策に関わる内容が定義されます。

どうやら Alibaba 社の設計した T-HEAD という RISC-V CPU にキャッシュ上の設計ミスが見つかって、その対処が必要な場合に CONFIG_ERRATA_THEAD を定義しているようです。そして、その対処方法の一つが、CONFIG_RISCV_DMA_NONCOHERENT を設定することのようです。もしかしたら、Linux Kernel 6.0 以降、T-HEAD を採用しているコンピューターシステムは、DMA Buffer を mmap した時に CPU Cache が無効になるかもしれません。


```Kconfig:https://elixir.bootlin.com/linux/v6.0/source/arch/riscv/Kconfig.erratas#L36
config ERRATA_THEAD
        bool "T-HEAD errata"
        depends on !XIP_KERNEL
        select RISCV_ALTERNATIVE
        help
          All T-HEAD errata Kconfig depend on this Kconfig. Disabling
          this Kconfig will disable all T-HEAD errata. Please say "Y"
          here if your platform uses T-HEAD CPU cores.
          Otherwise, please say "N" here to avoid unnecessary overhead.
config ERRATA_THEAD_PBMT
        bool "Apply T-Head memory type errata"
        depends on ERRATA_THEAD && 64BIT && MMU
        select RISCV_ALTERNATIVE_EARLY
        default y
        help
           This will apply the memory type errata to handle the non-standard
           memory type bits in page-table-entries on T-Head SoCs.
          If you don't know what to do here, say "Y".
config ERRATA_THEAD_CMO
        bool "Apply T-Head cache management errata"
        depends on ERRATA_THEAD && MMU
        select RISCV_DMA_NONCOHERENT
        default y
        help
          This will apply the cache management errata to handle the
          non-standard handling on non-coherent operations on T-Head SoCs.
          If you don't know what to do here, say "Y".

```



# まとめ


この記事では[『Linux では Cache Cohrence Hardware を持っていないとDMA Buffer をmmap する際に CPU Cache が無効になる』] コンピューターシステムの例として RISC-V CPU を紹介しました。ただ、必ずそうなるわけではなく、Linux Kernel 6.0 以降ではビルド時の設定や device tree などの設定に注意してくださいという注意喚起です。



[はじめに]: https://qiita.com/ikwzm/items/aa7e2373705872ccf764 "『Linux で DMA Bufferを mmap した時に CPU Cacheが無効になる場合がある (はじめに)』@Qiita"
[『Linux で DMA Bufferを mmap した時に CPU Cacheが無効になる場合がある (はじめに)』]: https://qiita.com/ikwzm/items/aa7e2373705872ccf764 "『Linux で DMA Bufferを mmap した時に CPU Cacheが無効になる場合がある (はじめに)』@Qiita"
[Cache Coherence 問題]: https://qiita.com/ikwzm/items/f60281491cc83f29c019 "『Linux で DMA Bufferを mmap した時に CPU Cacheが無効になる場合がある (Cache Coherence 問題とは何か)』@Qiita"
[Cache Coherence 問題とは何か]: https://qiita.com/ikwzm/items/f60281491cc83f29c019 "『Linux で DMA Bufferを mmap した時に CPU Cacheが無効になる場合がある (Cache Coherence 問題とは何か)』@Qiita"
[『Linux で DMA Bufferを mmap した時に CPU Cacheが無効になる場合がある (Cache Coherence 問題とは何か)』]: https://qiita.com/ikwzm/items/f60281491cc83f29c019 "『Linux で DMA Bufferを mmap した時に CPU Cacheが無効になる場合がある (Cache Coherence 問題とは何か)』@Qiita"
[Cache Coherence 問題の解決方法]: https://qiita.com/ikwzm/items/6d3225e4cbdbbabcabd5 "『Linux で DMA Bufferを mmap した時に CPU Cacheが無効になる場合がある (Cache Coherence 問題の解決方法)』@Qiita"
[『Linux で DMA Bufferを mmap した時に CPU Cacheが無効になる場合がある (Cache Coherence 問題の解決方法)』]: https://qiita.com/ikwzm/items/6d3225e4cbdbbabcabd5 "『Linux で DMA Bufferを mmap した時に CPU Cacheが無効になる場合がある (Cache Coherence 問題の解決方法)』@Qiita"
[Cache Aliasing 問題]: https://qiita.com/ikwzm/items/1b8dc72e04d0fa031a0f "『Linux で DMA Bufferを mmap した時に CPU Cacheが無効になる場合がある (Cache Aliasing 問題とは何か)』@Qiita"
[Cache Aliasing 問題とは何か]: https://qiita.com/ikwzm/items/1b8dc72e04d0fa031a0f "『Linux で DMA Bufferを mmap した時に CPU Cacheが無効になる場合がある (Cache Aliasing 問題とは何か)』@Qiita"
[『Linux で DMA Bufferを mmap した時に CPU Cacheが無効になる場合がある (Cache Aliasing 問題とは何か)』]: https://qiita.com/ikwzm/items/1b8dc72e04d0fa031a0f "『Linux で DMA Bufferを mmap した時に CPU Cacheが無効になる場合がある (Cache Aliasing 問題とは何か)』@Qiita"
[Cache Aliasing 問題の解決方法]: https://qiita.com/ikwzm/items/b3c24f04121a0d2deefd "『Linux で DMA Bufferを mmap した時に CPU Cacheが無効になる場合がある (Cache Aliasing 問題の解決方法)』@Qiita"
[『Linux で DMA Bufferを mmap した時に CPU Cacheが無効になる場合がある (Cache Aliasing 問題の解決方法)』]: https://qiita.com/ikwzm/items/b3c24f04121a0d2deefd "『Linux で DMA Bufferを mmap した時に CPU Cacheが無効になる場合がある (Cache Aliasing 問題の解決方法)』@Qiita"
[Linux Kernel の Cache 問題の扱い]: https://qiita.com/ikwzm/items/f32985890b2013ef5528 "『Linux で DMA Bufferを mmap した時に CPU Cacheが無効になる場合がある (Linux Kernel の Cache 問題の扱い)』@Qiita"
[『Linux で DMA Bufferを mmap した時に CPU Cacheが無効になる場合がある (Linux Kernel の Cache 問題の扱い)』]: https://qiita.com/ikwzm/items/f32985890b2013ef5528 "『Linux で DMA Bufferを mmap した時に CPU Cacheが無効になる場合がある (Linux Kernel の Cache 問題の扱い)』@Qiita"
[Linux では Cache Coherence Hardware を持っていないとDMA Buffer をmmap する際に CPU Cache が無効になる]: https://qiita.com/ikwzm/items/0f77072158ce842018fc "『Linux では Cache Coherence Hardware を持っていないとDMA Buffer をmmap する際に CPU Cache が無効になる』@Qiita"
[『Linux では Cache Coherence Hardware を持っていないとDMA Buffer をmmap する際に CPU Cache が無効になる』]: https://qiita.com/ikwzm/items/0f77072158ce842018fc "『Linux では Cache Coherence Hardware を持っていないとDMA Buffer をmmap する際に CPU Cache が無効になる』@Qiita"
[Raspberry Pi の例]: https://qiita.com/ikwzm/items/f8bcb82cfdb818bdb07d "『Raspberry Pi では DMA Buffer をmmap する際に CPU Cache が無効になる』@Qiita"
[『Raspberry Pi では DMA Buffer をmmap する際に CPU Cache が無効になる』]: https://qiita.com/ikwzm/items/f8bcb82cfdb818bdb07d "『Raspberry Pi では DMA Buffer をmmap する際に CPU Cache が無効になる』@Qiita"
[RISC-V CPU の注意点]: https://qiita.com/ikwzm/items/3c6b15029780be57f3a9 "『Linux で DMA Bufferを mmap した時に CPU Cacheが無効になる場合がある (RISC-V CPU の注意点)』@Qiita"
[『Linux で DMA Bufferを mmap した時に CPU Cacheが無効になる場合がある (RISC-V CPU の注意点)』]: https://qiita.com/ikwzm/items/3c6b15029780be57f3a9 "『Linux で DMA Bufferを mmap した時に CPU Cacheが無効になる場合がある (RISC-V CPU の注意点)』@Qiita"
[所感]: https://qiita.com/ikwzm/items/46f00c39b7d92c06654e "『Linux で DMA Bufferを mmap した時に CPU Cacheが無効になる場合がある (所感)』@Qiita"
[『Linux で DMA Bufferを mmap した時に CPU Cacheが無効になる場合がある (所感)』]: https://qiita.com/ikwzm/items/46f00c39b7d92c06654e "『Linux で DMA Bufferを mmap した時に CPU Cacheが無効になる場合がある (所感)』@Qiita"
[『Kernel/VM探検隊@北陸 Part 6』]: https://kernelvm.connpass.com/event/297033/ "『Kernel/VM探検隊@北陸 Part 6』"