前言
項目中需要通過驅動與fpga通訊,獲取fpga往內存里寫的數據。因為數據量比較大,需要驅動分配600多M的內存給fpga來寫數據,且因為是與fpga通訊,需要連續的內存,還得是uncached的,因此打算采用dma接口dma_alloc_coherent
來分配如此大的內存。然而,在分配的過程中遇到了一些問題,下面對這次的調試進行總結。
環境說明
- soc: zc702(32bit arm + fpga)
- ddr: 1g
- linux kernel: 3.15.0
分配200M DMA空間失敗
原因分析:
系統默認的DMA最大允許空間為128M,其他模塊,如網卡驅動用去了一些,導致可使用的DMA空間低於128M了。要去分配200M,當然不夠!
解決方法:
修改默認配置選項為CONFIG_CMA_SIZE_MBYTES=384
,重新編譯內核即可解決。當然,也可以通過給內核傳參cma=384M,這樣就不用重新編譯內核了。
分配600M DMA空間失敗
當期望的dma分配為600M時,通過修改CMA_SIZE_MBYTES=700
已經沒用了,這會導致系統啟動時報cma失敗,進而導致系統其他模塊dma申請都失敗。CMA_SIZE_MBYTES=512
(500是可以的)時報錯信息如下:
cma: CMA: failed to reserve 512 MiB
原因分析:
沒有這么大的連續空間
解決方法1:
找到沒有這么大連續空間原因,畢竟CMA_SIZE_MBYTES
已經為512M了
解決方法2:
如果方法1不能解決,那么只有通過另一種方式了,采用保留內存,然后再通過ioremap_nocache
的方式申請這么大片保留的連續區域
在嘗試方法1和方法2的過程中,又遇到了一些其他問題。
先說說方法2吧!保留內存很容易,在給內核傳參時加上mem=256,那么就意味着我保留了768M內存,然而在ioremap時失敗了,地址不夠!!!這也很正常,畢竟內核的ioremap區域的地址才幾十M,而我期望申請的是幾百M!!!解決方法有,修改user/kernel 3G/1G的分配為1G/3G,這樣內核的地址空間就夠多了吧!經過驗證,確實可以了。
再說說方法1,通過查看啟動信息,發現dtb加載在0x20000000處,剛好是512M那里,這也就是導致了不連續的原因!通過修改uboot環境變量fdt_high
(同理,如果用了ramdisk,那么可能也需要修改initrd_high
的地址,后面就不再重復了)來修改dtb加載地址為0x28000000,成功啟動。看來導致不連續的問題確實就是dtb導致的!那么將CMA_SIZE_MBYTES=512
改為我們的目標CMA_SIZE_MBYTES=700 fdt_high=0x2c000000
,是否可以呢?答案是no。錯誤信息如下:
cma: CMA: failed to reserve 700 MiB
這次我認為不是dtb的問題了,應該是linux kernel內存布局的限制!由於當前采用的user/kernel 3G/1G的分割,內核總共才1G的地址空間,且開啟了CONFIG_HIGHMEM
,而內核默認會保留240M(之前是128M,該問題后面細說)地址空間,低端內存總共才760M,而由於某些我暫時不知道的原因,限制了dma分配700M空間(可能是按某種比例划分導致的)!怎么解決呢???我的做法,第一步(當然,該方法不是解決該問題的關鍵),修改內核編譯選項CONFIG_HIGHMEM
,去掉highmem該功能,畢竟我們才1G內存,用那個選項會顯得有點多余,還影響了性能!然而,新問題又出現了,這次是系統起來后,內存不是1G了,變成760M,再次分析得出,即使沒有開啟CONFIG_HIGHMEM
,內核仍然會有240M地址空間作為保留,用於io remap等;第二步,采用方法2中的方法,修改user/kernel 3G/1G的分配為1G/3G。通過啟動信息:
Memory: 416196K/1048576K available (5240K kernel code, 260K rwdata, 1616K rodata, 200K init, 301K bss, 632380K reserved)
Virtual kernel memory layout:
vector : 0xffff0000 - 0xffff1000 ( 4 kB)
fixmap : 0xfff00000 - 0xfffe0000 ( 896 kB)
vmalloc : 0x80800000 - 0xff000000 (2024 MB)
lowmem : 0x40000000 - 0x80000000 (1024 MB)
modules : 0x3f000000 - 0x40000000 ( 16 MB)
.text : 0x40008000 - 0x406ba454 (6858 kB)
.init : 0x406bb000 - 0x406ed380 ( 201 kB)
.data : 0x406ee000 - 0x4072f320 ( 261 kB)
.bss : 0x4072f32c - 0x4077a7a4 ( 302 kB)
可以看出,低端內存1G正常了,內存又回來了_,現在我將CMA_SIZE_MBYTES=700 fdt_high=0x30000000
dma分配也正常了
240M的原因
Linux內核版本從3.2到3.3 默認的vmalloc size由128M 增大到了240M,3.4.0上的
修改Commit信息如下:
To accommodate all static mappings on machines withpossible highmem usage,
the default vmalloc area size is changed to 240 MB sothat VMALLOC_START
is no higher than 0xf0000000 by default.
總結
本文提供了兩種方式來解決期望DMA區域特別大的情況(幾百M的情況),通過這次的bug調試,更加深入的理解了linux內存管理相關知識。突然想感嘆下,好久沒碰內核了,看來以后有時間就的補補!!!
關於cma擴展閱讀
完!
2016年12月