本特性可以支撑使用者按照需求分配在对应可靠性的内存上,并对部分可能的UCE或CE故障影响进行一定程度的缓解,达到部分MR内存(address range mirror)的情况下,支撑业务整体可靠性不下降。
本章节介绍该特性的通用约束,每个子特性会有具体的约束,在对应的小节中详细说明。
兼容性限制
设计规格限制
内核态开发时,内存申请操作需要注意:
若内存申请接口支持指定gfp_flag,只有gfp_flag包含__GFP_HIGHMEM且__GFP_MOVABLE的内存申请会强制普通内存区域分配或者将这次内存分配重定向可靠内存区域,其他gfp_flag都不会进行干预。
从slab/slub/slob申请获取的都是高可靠内存(一次性申请内存大于KMALLOC_MAX_CACHE_SIZE时且gfp_flag指定为普通内存区域时可能申请到低可靠内存)。
用户态开发时,内存申请操作需要注意:
当上层业务申请内存的时发现高可靠内存不足(触发zone原生min水线)或者触发对应limit限制,会优先释放pagecache以尝试回收高可靠内存。如果仍然申请不到,内核会根据fallback的开关选择oom或fallback到低可靠内存区域完成内存申请。(fallback指某个内存管理区/节点内存不足时,到其他内存管理区/节点申请内存的情况。)
类似于NUMA_BALANCING的内存动态迁移机制,可能导致已经分配的高/低可靠内存被迁移到别的节点,由于该迁移操作丢失内存申请的上下文,且目标node可能没有对应可靠性的内存,因此可能导致迁移后的内存可靠性与预期不符。
按照用户态高可靠内存用途引入如下三个配置文件:
/proc/sys/vm/task_reliable_limit: 关键进程(包含systemd)使用的高可靠内存上限。包含匿名页和文件页。进程使用的shmem也会被统计到其中(包含在匿名页中)。
/proc/sys/vm/reliable_pagecache_max_bytes:全局pagecache使用的高可靠内存软上限。约束普通进程使用的高可靠pagecache的数量,系统默认不限制pagecache使用的高可靠内存的量。高可靠进程和文件系统元数据等场景不受此约束。无论fallback开关是否开启,普通进程触发该上限时,会默认申请低可靠内存,若低可靠内存申请不到,则遵循原生流程处理。
/proc/sys/vm/shmem_reliable_bytes_limit:全局shmem使用的高可靠内存软上限。约束普通进程shmem使用高可靠内存的数量,系统默认不限制shmem使用的高可靠内存的量。高可靠进程不受此约束。关闭fallback时,普通进程触发该上限会导致内存申请失败,但不会OOM(与原生流程一致)。
触及这些值可能会导致内存申请fallback或者OOM。
关键进程在tmpfs或pagecache流程产生缺页引发的内存申请,有可能触发多个limit,多个limit之间交互关系情况详见表格。
是否触及task_reliable_limit | 是否触及reliable_pagecache_max_bytes或者shmem_reliable_bytes_limit | 内存申请处理策略 |
---|---|---|
是 | 是 | 优先回收pagecache以满足申请,否则Fallback或者OOM |
是 | 否 | 优先回收pagecache以满足申请,否则Fallback或者OOM |
否 | 否 | 先高可靠内存,失败Fallback或者OOM |
否 | 是 | 先高可靠内存,失败Fallback或者OOM |
关键进程会遵循task_reliable_limit的限制,如果task_reliable_limit高于tmpfs或pagecachelimit时,由关键进程产生的pagecache、tmpfs依旧会使用高可靠内存,由此会产生pagecache、tmpfs使用高可靠内存数量高于对应Limit的情况。
当触发task_reliable_limit,如果高可靠filecache低于4M,不会进行同步回收。如果pagecache产生时,高可靠filecache低于4M,那么会fallback到低可靠内存完成申请,如果高于4M那么会优先回收pagecache满足此次申请。但接近4M时,会触发更频繁的cache直接回收,由于cache直接回收锁开销大,会导致高cpu占用率,此时文件读写性能接近裸盘性能。
即使系统存在足够申请的高可靠内存,在如下场景下也存在fallback到低可靠内存区域内存申请的场景。
内存分级fallback关闭时,高可靠内存将不能向低可靠内存扩展,有可能导致用户态应用对内存用量的判断与本特性不兼容,比如通过MemFree判断可用内存量。
内存分级fallback开启时,对原生fallback有影响,主要区别在于内存管理区zone与NUMA节点的选择上,列举如下:
场景限制
性能影响
概述
由于内存按照高低可靠性分为两段,内存的申请释放也需要按照高低可靠来进行分开管理。OS需要能够控制内存申请路径,用户态进程使用低可靠内存,内核态使用高可靠内存。高可靠内存不足时需要能够fallback到低可靠区申请或者直接申请失败。
同时对于进程部分内存段的可靠性需求与进程本身的性质,也需要能够支持按需指定申请高低可靠内存。如指定关键进程使用高可靠内存,减少关键进程遇到内存错误的概率。目前内核使用的都是高可靠内存,用户态进程使用的都是低可靠内存。如此会造成一些关键或者核心服务的不稳定,如业务转发进程,如果发生故障,会造成IO中断,影响业务的稳定性。因此需要对这些关键服务特殊处理,使其使用高可靠内存,提高关键进程运行的稳定性。
在系统遇到内存错误,OS应对未分配的低可靠内存进行覆盖写,以清除未发现的内存错误。
约束限制
关键进程使用高可靠内存
未分配内存覆盖写特性
未分配内存覆盖写特性只能执行一次,不支持并发操作,如果执行会有如下影响:
如果机器性能不佳,将有可能触发内核RCU stall或soft lockup警告,以及进程内存申请操作被阻塞。因此请限制该特性在必要时只在物理机环境下使用,虚拟机等场景大概率出现如上现象。
物理机参考数据可见下表(实际耗时与硬件性能、当前系统负载有关系)。
表:基于物理机TaiShan 2280 V2空载状态下测试数据
测试项 | Node 0 | Node 1 | Node 2 | Node 3 |
---|---|---|---|---|
Free Mem (MB) | 109290 | 81218 | 107365 | 112053 |
总耗时 3.2s
使用方法
本子特性提供较多接口,使能特性并校验只需要步骤1-6即可。
配置启动参数“kernelcore=reliable”,代表打开内存分级管理开关,CONFIG_MEMORY_RELIABLE是必要的配置,否则整个系统的内存可靠性分级管理不使能。
根据需要,可以通过启动参数reliable_debug=[F][,S][,P]来选择性关闭fallback功能(F)、关闭tmpfs使用高可靠内存(S)以及关闭读写缓存使用高可靠内存(P),默认以上功能都使能。
根据BIOS上报的地址段,查找高可靠内存,并进行标记,对于NUMA系统,不一定每个node上都要预留可靠内存,但是node 0上低4G物理空间必须为高可靠的内存,系统启动过程中会申请内存,如果无法分到高可靠内存,则会 fallback 到低可靠内存进行分配(mirror功能自带的fallback逻辑)或导致系统无法启动。如果使用低可靠内存,整个系统都不稳定,所以要保留node0上的高可靠内存且低4G物理空间必须为高可靠的内存。
启动后,用户可以通过启动日志判断内存分级是否使能,应出现如下打印:
mem reliable: init succeed, mirrored memory
高可靠内存对应的物理地址段可以通过启动日志来查询,观察efi上报memory map里的属性,带有“MR”的即为高可靠内存段,如下为启动日志节选,其中内存段mem06为高可靠内存,mem07为低可靠内存,其物理地址范围也列举在后(其他方式无法直接查询高低可靠内存地址范围)。
[ 0.000000] efi: mem06: [Conventional Memory| |MR| | | | | | |WB| | | ] range=[0x0000000100000000-0x000000013fffffff] (1024MB)
[ 0.000000] efi: mem07: [Conventional Memory| | | | | | | | |WB| | | ] range=[0x0000000140000000-0x000000083eb6cfff] (28651MB)
内核态开发时,对于一个页面struct page,可以通过其所处的 zone来判断,ZONE_MOVABLE为低可靠内存区,zone编号小于ZONE_MOVABLE的均为高可靠内存区,判断方式举例如下。
bool page_reliable(struct page *page)
{
if (!mem_reliable_status() || !page)
return false;
return page_zonenum(page) < ZONE_MOVABLE;
}
此外提供的若干接口按照功能点分类列举如下:
**代码层面判断可靠性是否使能:**在内核模块中通过如下接口来判断,返回 true 表示内存分级功能真正使能,返回false则未使能。
#include <linux/mem_reliable.h>
bool mem_reliable_status(void);
**内存热插拔:**如果内核本身使能内存热插拔操作(Logical Memory hot-add),高低可靠内存也支持该操作,操作单位为memory block,与原生流程一致。
# 上线内存到高可靠区
echo online_kernel > /sys/devices/system/memory/auto_online_blocks
# 上线内存到低可靠区
echo online_movable > /sys/devices/system/memory/auto_online_blocks
**动态关闭某项分级管理功能:**使用long类型控制根据每个bit判断内存分级功能开关与关闭某项功能:
其他bit预留,用于扩展。如需更改,可通过如下proc接口(权限为600),取值范围0-15,(只有当总功能bit0为1时才会处理后续功能,否则直接关闭所有功能)。
echo 15 > /proc/sys/vm/reliable_debug
# 关闭所有功能,因为bit0为0
echo 14 > /proc/sys/vm/reliable_debug
此命令只能用于关闭功能,不能打开。对于已经关闭的功能或者运行时关闭的功能,这个命令不能将其打开。
注:此功能用于逃生使用,仅异常场景或者调测阶段需要关闭内存可靠性特性时配置,禁止作为常规功能直接使用。
**查看高可靠内存部分统计信息:**可以通过原生/proc/meminfo来查看,其中:
**未分配内存覆盖写:**该功能需要打开配置项。
CONFIG_CLEAR_FREELIST_PAGE,并且添加启动参数clear_freelist,两者具备才会使能。通过proc接口触发,取值范围只能为1(权限为0200)。
echo 1 > /proc/sys/vm/clear_freelist_pages
注:该特性依赖启动参数clear_freelist,内核对启动参数只匹配前缀,故诸如“clear_freelisttt”也会生效该特性。
为防止误操作,加入内核模块参数cfp_timeout_ms,代表调用覆盖写功能的最长执行时长(超时则没写完也退出),通过sys接口触发,默认取值为2000ms(权限为0644):
echo 500 > /sys/module/clear_freelist_page/parameters/cfp_timeout_ms # 设置超时为500ms
**查看更改当前进程高低可靠属性:**可以通过/proc//reliable来查看该进程是否是高可靠进程;运行写入,该标识会继承,如果子进程不需要,则手动修改子进程属性;systemd和内核线程不支持该属性的读写;可以写0或 者1,默认为0,代表低可靠进程(权限为0644)。
# 更改pid=1024的进程为高可靠进程,从更改之后开始进程缺页申请的内存是从高可靠内存区域申请,申请不到有可能fallback到低可靠
echo 1 > /proc/1024/reliable
**设置用户态高可靠进程申请上限:**通过/proc/sys/vm/task_reliable_limit来修改用户态进程申请高可靠内存的上限,对应取值范围为[ReliableTaskUsed, ReliableTotal],单位为Byte(权限为0644)。需注意:
概述
Page cache 也叫页缓冲或文件缓冲,在linux读写文件时,它用于缓存文件的逻辑内容,从而加快对磁盘上映像和数据的访问。Page cache申请如果使用低可靠内存,当访问时可能触发UCE导致系统异常。因此,需要将读写缓存(page cache)放到高可靠内存区域,同时为了防止Page cache申请过多(默认无限制)将高可靠内存耗尽,需要对page cache的总量及使用可靠内存总量进行限制。
约束限制
使用方法
读写缓存使用高可靠内存默认使能,如需关闭,可通过启动项参数设置reliable_debug=P。且page cache不能无限使用,需要限制page cache的使用量。限制page cache量的功能依赖的config开关为CONFIG_SHRINK_PAGECACHE。
/proc/meminfo中的FileCache可以用来查询page cache的使用量,ReliableFileCache可以用来查询page cache中可靠内存的使用量。
限制page cache量的功能依赖若干proc接口,接口定义在/proc/sys/vm/下,用来控制page cache的使用量,具体如下表:
接口名称(原生/新增) | 权限 | 说明 | 默认值 |
---|---|---|---|
cache_reclaim_enable(原生) | 644 | 表示page cache限制的功能的使能开关。 **取值范围:**0 或者 1,输入非法值,返回错误。 **示例:**echo 1 > cache_reclaim_enable | 1 |
cache_limit_mbytes(新增) | 644 | **含义:**表示cache的上限,以M为单位,最小值0(表示关闭限制功能),最大值为meminfo中的MemTotal值(以M为单位换算后)。 **取值范围:**最小值0(表示关闭限制功能),最大值为内存大小(以M为单位,如free –m看到的值)。 示例: echo 1024 > cache_limit_mbytes **其他:**建议cache上限不要低于总内存的一半,否则cache过小可能影响IO性能。 | 0 |
cache_reclaim_s(原生) | 644 | **含义:**表示定期触发cache回收的时间,以秒为单位系统会根据当前online的cpu个数来创建工作队列,如果有n个cpu则创建n个工作队列,每个工作队列每隔cache_reclaim_s秒进行一次回收。该参数与cpu上下线功能兼容,如果cpu offline,则会减少工作队列个数,cpu online,则会增加。 **取值范围:**最小值0(表示关闭定期回收功能),最大43200,输入非法值,返回错误。 **示例:**echo 120 > cache_reclaim_s **其他:**建议定期回收时间设成几分钟的级别(如2分钟),否则频繁回收可能导致系统卡顿。 | 0 |
cache_reclaim_weight(原生) | 644 | **含义:**表示每次回收的权值,内核每个CPU每次期望回收32 * cache_reclaim_weight个page。该权值同时作用于page上限触发的回收和定期page cache回收。 **取值范围:**最小值1,最大值100,输入非法值,返回错误。 **示例:**echo 10 > cache_reclaim_weight **其他:**建议设为10或以下,否则每次回收过多内存时,系统可能卡顿。 | 1 |
reliable_pagecache_max_bytes(新增) | 644 | **含义:**该接口用于控制page cache中高可靠内存的总量。 取值范围:0 ~ 高可靠内存最大值,单位为Bytes,高可靠内存的的最大值可以通过/proc/meminfo查询,输入非法值返回错误。 **示例:**echo 4096000 > reliable_pagecache_max_bytes | unsigned long 类型的最大值,代表不限制用量 |
概述
对于使用tmpfs做rootfs,rootfs中存放的操作系统使用的核心文件和数据。但是tmpfs默认使用的是低可靠内存,这样会造成核心文件和数据的不可靠。因此需要tmpfs整体使用高可靠内存。
使用方法
tmpfs使用高可靠内存默认使能,如需关闭,可通过启动项参数设置reliable_debug=S,可以通过/proc/sys/vm/reliable_debug动态关闭,但不能动态打开。
在使能tmpfs使用高可靠内存时,可通过/proc/meminfo中ReliableShmem查看当前tmpfs已经使用的高可靠内存。
默认tmpfs使用上限是物理内存的一半(rootfs使用tmpfs时除外)。基于传统的SYS V的共享内存,它的使用同时受/proc/sys/kernel/shmmax以及/proc/sys/kernel/shmall的限制,可以动态配置。同时他们也受tmpfs使用高可靠内存的限制。详见下表.
参数 | 说明 |
---|---|
/proc/sys/kernel/shmmax(原生) | SysV共享内存单个段可使用的大小 |
/proc/sys/kernel/shmall(原生) | SysV共享内存总的可使用的大小 |
新增接口 /proc/sys/vm/shmem_reliable_bytes_limit 用户设置系统级别 tmpfs 可用的高可靠大小(单位为Byte),默认值为LONG_MAX,代表用量不受限。可设置的范围为[0, 系统可靠内存总大小],权限为644。fallback关闭时,在达到该使用上限时,返回没有内存的错误,fallback开启时会尝试从低可靠区域申请。使用示例:
echo 10000000 > /proc/sys/vm/shmem_reliable_bytes_limit
概述
按照内存可靠性分级的方案,内核以及关键进程使用高可靠内存。大部分用户态进程使用低可靠内存。系统运行时,涉及大量的用户态与内核态数据交换,数据传入内核态时,即发生低可靠内存上数据拷贝到高可靠内存区域。拷贝动作发生在内核态,如果这时在读取用户态数据时发生UCE错误,即发生内核态消费内存UCE,系统会触发panic。本子特性对部分用户态穿越内核UCE场景提供解决方案,避免系统复位,包括COW场景、copy_to_user场景、copy_from_user场景、get_user场景、put_user场景、coredump场景,其余场景均不支持。
约束限制
使用方法
确保内核开启config开关CONFIG_ARCH_HAS_COPY_MC,/proc/sys/kernel/machine_check_safe值为1时代表全场景使能,改为0代表不使能,其他值均为非法。
当前各场景容错处理机制如下:
序号 | 场景 | 现象 | 应对措施 |
---|---|---|---|
1 | copy_from/to_user:最基本的用户态穿越,主要涉及syscall,sysctl,procfs的操作 | 如果在拷贝时出现UCE异常,会导致内核复位 | 出现 UCE 异常时,kill 当前进程,内核不主动复位 |
2 | get/put_user:用于简单的变量拷贝,主要是netlink的场景用的比较多 | 如果在拷贝时出现UCE异常,会导致内核复位 | 出现 UCE 异常时,kill 当前进程,内核不主动复位 |
3 | COW:fork子进程,触发写时拷贝 | 触发写时拷贝,如果出现UCE会导致内核复位 | 出现 UCE 异常时,kill 相关进程,内核不主动复位 已实现,见36节 |
4 | 读缓存:用户态使用了低可靠内存,用户态程序读写文件时,操作系统会使用空闲内存缓存硬盘文件,提升性能。但用户态程序对文件的读取会先经过内核访问缓存 | 出现UCE异常,会导致内核复位 | 出现 UCE 错误时,kill 当前进程,内核不主动复位 已实现,见36节 |
5 | coredump时内存访问触发UCE | 出现UCE异常,会导致内核复位 | 出现 UCE 错误时,kill 当前进程,内核不主动复位 |
6 | 写缓存:写缓存回刷到磁盘时,触发UCE | 回刷缓存其实就是磁盘DMA数据的搬移,如果在此过程中触发UCE,超时结束后,页面写失败,这样会造成数据不一致,进而会导致文件系统不可用,如果是关键的数据,会出现内核复位 | 没有解决方案,不支持,内核会发生复位 |
7 | 内核启动参数、模块参数使用的都是高可靠内存 | / | 不支持,且本身风险降低 |
8 | relayfs:是一个快速的转发数据的文件系统,用于从内核态转发数据到用户态, | / | 不支持,且本身风险降低 |
9 | seq_file:将内核数据通过文件的形式传输到用户态 | / | 不支持,且本身风险降低 |
由于用户态数据大多使用低可靠内存,本项目只涉及内核态读取用户态数据的场景。Linux系统下用户空间与内核空间数据交换有九种方式, 包括内核启动参数、模块参数与 sysfs、sysctl、syscall(系统调用)、netlink、procfs、seq_file、debugfs和relayfs。另有两种情况,进程创建时,COW(copy on write,写时复制)场景,和读写文件缓存(pagecache)场景。
其中sysfs,syscall, netlink, procfs 等方式从用户态传输数据到内核都是通过copy_from_user或者get_user的方式。
因此用户态穿越到内核有如下几种场景:
copy_from_user、get_user、COW、读缓存、写缓存回刷。
内核态传到用户态有如下几种场景:
relayfs、seq_file、copy_to_user、put_user。
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。
登录 后才可以发表评论
FileDragTip