一、技术背景
内核模块,比如RDMA驱动或者GPU驱动,需要分配大容量物理连续的内存,以提高硬件的访问性能。当系统刚启动时,尽管系统中往往有大块的物理连续内存,但是内核对于一次分配内存的最大容量有限制(一般是4MB)。目前分配大内存的方式有以下几种,都有其限制:
- 大页机制。但是大页机制只有2MB和1GB两种尺寸,2MB不够,1GB又浪费,并且其主要面向用户态,内核态不能直接使用。
- 调大MAX_ORDER。这种方式需要修改内核配置重新编译,并且调大MAX_ORDER对buddy system的影响较大,这也是内核默认一次分配最大4MB内存块的原因。
- CMA。这种方式需要内核开启CMA重新编译,并且默认CMA是共享内存,会被所有的设备驱动共享。
- 启动参数mem预留大块内存。需要更改系统配置重启,并且需要依赖BIOS的memmap信息来配置。
本文描述的方法在不重编内核,不修改系统,不影响其他模块的情况下,采用多次分配,拼接内存的方式,满足内核模块大内存的需求,对于开机自起的内核模块或者驱动,成功率极高。
二、技术概述
当系统刚启动时,系统中往往有大块的物理连续内存,但是内核对于一次分配内存的最大容量有限制(一般是4MB)。如果此时加载的内核模块需要较大(比如大于4MB)的物理连续内存,可以通过多次分配内存拼接的方式满足需求而不用重编内核,修改系统或对已有模块产生影响:
- 在系统启动后加载内核模块时,内核模块按照内核允许的一次能分配物理连续内存的最大容量(比如4M)多次分配内存,以拼接成所需大小的物理连续内存。
- 内核模块每次分配的内存,如果和上次分配的内存或者累计合并的内存是物理相邻的,则可以合并成一块内存,否则上次分配的内存或者合并的内存作废,失败一次,本次分配的内存当作是第一块内存,继续分配内存。
- 内核模块继续分配内存,直到合并内存满足所需大小或者失败次数达到最大。
- 内核模块释放所有作废的内存块。
三、技术细节
一种内核大内存的分配方法,详细流程如下:
- 按照内核允许的一次分配内存的最大容量(比如4MB)分配内存,
- 如果分配成功,该内存块作为目标内存,继续2,
- 如果分配失败,流程失败,结束。
- 按照内核允许的一次分配内存的最大容量(比如4MB)分配内存,
- 如果分配成功,
- 如果分配的内存块和目标内存物理相邻,即分配内存的起始地址=目标内存的起始地址+目标内存的长度或者目标内存的起始地址=分配内存的起始地址+分配内存的长度,则两者合并后作为新的目标内存,
- 如果目标内存已满足所需大小,流程成功,继续3。
- 如果目标内存小于所需大小,继续2。
- 如果分配的内存块和目标内存物理不相邻,目标内存标记作废,失败次数加1,
- 失败次数达到最大,本次分配内存标记作废,流程失败,继续3。
- 失败次数未达到最大,本次分配内存作为目标内存,继续2。
- 如果分配失败,目标内存标记作废,流程失败,继续3。
- 如果分配的内存块和目标内存物理相邻,即分配内存的起始地址=目标内存的起始地址+目标内存的长度或者目标内存的起始地址=分配内存的起始地址+分配内存的长度,则两者合并后作为新的目标内存,
- 释放作废内存,结束。
- 如果分配成功,