1、 内存热插拔
— linux/Documentation/memeory-hotplug.txt
1.1、 内存热插拔的目的
内存热插拔允许使用者去增加或减少内存的大小。这样做通常出于两种目的:
(A) For changing the amount of memory. This is to allow a feature like capacity on demand.
调整内存容量,这是为了允许按照需求容量进行划分;
(B) For installing/removing DIMMs or NUMA-nodes physically. This is to exchange DIMMs/NUMA-nodes, reduce power consumption, etc.
物理上安装或卸载 DIMMs 或NUMA-nodes ,交换DIMMs/NUMA-nodes,减少功耗;
(A) is required by highly virtualized environments and (B) is required by hardware which supports memory power management.
(A) 是高度虚拟化环境所必需的,(B) 是支持内存电源管理的硬件所必需的。
1.2、 内存热插拔的阶段
内存热插拔分为两个阶段
1) 物理内存热插拔阶段
第一阶段是为热插拔内存通信硬件/固件和创建/擦除环境。基本上,对目的(B)来说这个阶段时必须的;当内存被热插入时,内核会识别新内存,创建新的内存管理表,并为新内存的操作创建 sysfs 文件。如果固件支持新内存连接到操作系统的通知,则此阶段会自动触发。ACPI可以通知系统中有新的内存加入事件。如果不支持,系统管理员可以使用“probe”操作。
- 逻辑内存热插拔阶段
逻辑内存热插拔阶段是将内存状态更改为对用户可用/不可用。
此阶段更改了用户视角中的内存量。当内存范围可用时,内核会将其中的所有内存作为空闲页面。
在这个文档中,逻辑内存热插拔阶段被描述为上线、下线。
通过系统管理员对sysfs文件进行写操作,可以触发逻辑内存热插拔阶段。对这种热添加情况,在物理热插拔完成以后,逻辑热插拔阶段必须被执行通过手动操作。但是,如果你写了一个为内存热插拔的udev’s hotplug scripts脚本的话,这些阶段可以被透明的执行。
1.3、 内核配置支持该功能
内存模型
Memory Management options —>
Memory model (Sparse Memory)
允许内存热添加
Memory Management options —>
[] Allow for memory hot-add
若需要在插入新内存,系统自动将其上线则配置:
[] Online the newly added memory blocks by default
为了能移除内存,下面的选项是必须的
Memory Management options —>
[] Allow for memory hot remove
[] Allow for memory compaction
2、 内存热插拔怎样通过/proc简单操作配置
All memory blocks have their device information in sysfs. Each memory block
is described under /sys/devices/system/memory as:
/sys/devices/system/memory/memoryXXX
(XXX is the memory block id.)
以某aarch64平台为例,内核配置为:
整体内存大小为8GB,OS划分为2GB,地址从0x0~0x80000000;
系统内存:
【添加】
将2GB为起始的一个section添加到OS中;
[debug@dvrdvs memory] # echo 0x80000000 > /sys/devices/system/memory/probe
[debug @dvrdvs memory32] # ls
online phys_index state uevent
phys_device removable subsystem valid_zones
[debug @dvrdvs memory32] # cat online
0
[debug @dvrdvs memory32] # cat …/memory31/online
1
[debug @dvrdvs memory32] # cat state
offline
[debug @dvrdvs memory32] # echo 1 > online
[debug @dvrdvs memory32] # cat valid_zones
Normal
2106636 – 2041100 KB = 65536 KB = 64MB
在/proc/iomem中可以看到刚刚添加的内存信息:
80000000-83ffffff : System RAM
【移除】
逻辑内存移除
内存下线比内存上线更加的复杂。因为内存下线必须使整个内存段都未使用,如果内存段中包含有仍未释放的内存段,内存下线操作就可能失败。
[debug @dvrdvs] # echo offline > /sys/devices/system/memory/memory32/state
顺利时系统会将该部分内存下线:[ 507.843281] Offlined Pages 16384
如果下线操作失败,内核将会返回一些错误值(像:-EBUSY)。即使一个内存段不属于ZONE_MOVABLE,你也可以尝试去下线它。处于ZONE_MOVABLE的内存段被认为是能很容易下线的。但是如果这样的内存段处于繁忙的状态下,对它们进行下线,系统会返回-EBUSY。即使一个内存段由于-EBUSY而不能下线,你可以重试,并且有可能会成功与有可能失败。
物理内存的移除
Need more implementation yet…
- Notification completion of remove works by OS to firmware.
- Guard from remove if not yet.
但系统中仍然可以看到memory32节点以及上面添加到iomem中的内存段信息。
一个memoryXXX节点有哪些属性呢?
经测试,对state的设置online和online_kernel属性相同,都是将页内存添加到了ZONE_Normal区。在配置为online_movable时页内存添加到了ZONE_Movable区,这一点可以从/proc/zoneinfo中看到。
ZONE_MOVABLE是无法被slab、get_free_pages、vmalloc分配,只能被alloc_pages接口并且带有GFP_MOVABLE参数属性时优先从该区域分配,理论上应用的堆栈是可以分配到该ZONE内存的。如此可能会出现剩余内存的ZONE_MOVABLE内存较大,而调用的slab、get_free_pages、vmalloc分配内存失败的情况。
3、 内存热插拔的实现基础
内存热插拔特性是基于稀疏内存模型(Sparse Memory Model)实现;Linux内核支持3种内存模型,分别是flat memory model、Discontiguous memory model和sparse memory model。memory model是从cpu的角度看,其物理内存的分布状况。三种内存模型对应的特性如下:
1、 平坦内存(Flat Memory):内存的物理地址空间是连续的,没有空洞;
2、 不连续内存(Discontiguous Memory):内存的物理地址空间存在空洞;
3、 稀疏内存(Sparse Memory):存在空洞且支持内存的热插拔;
从图中可见整个物理内存空间被分成了n个section。在稀疏内存模型中物理内存以section为单位进行了划分。
3.1、section
在SPARSEMEM中,被管理的物理内存由一个个任意大小的section(struct mem_section表示)构成,所以整个物理内存可被视为一个mem_section数组。每一个mem_section包含了一个间接指向struct page数组的指针。
ARM64平台默认都配置为稀疏内存模型,在不同平台中section的大小各不相同。
下面为内存probe上线以及添加到伙伴系统的大致流程图,