前置条件

  1. 本特性目前仅适用于ARM64。
  2. 硬件需支持部分MR内存(address range mirror),即通过UEFI标准接口上报属性为EFI_MEMORY_MORE_RELIABLE的内存,普通内存不用额外置位。镜像内存(MR)对应为高可靠内存,普通内存对应为低可靠内存。在暂时没有MR内存的机器上可以通过 efi_fake_mem 启动参数进行模拟验证。

使用方法

OS支持内存分级管理

概述

由于内存按照高低可靠性分为两段,内存的申请释放也需要按照高低可靠来进行分开管理。OS需要能够控制内存申请路径,用户态进程使用低可靠内存,内核态使用高可靠内存。高可靠内存不足时需要能够fallback到低可靠区申请或者直接申请失败。

同时对于进程部分内存段的可靠性需求与进程本身的性质,也需要能够支持按需指定申请高低可靠内存。如指定关键进程使用高可靠内存,减少关键进程遇到内存错误的概率。目前内核使用的都是高可靠内存,用户态进程使用的都是低可靠内存。如此会造成一些关键或者核心服务的不稳定,如业务转发进程,如果发生故障,会造成IO中断,影响业务的稳定性。因此需要对这些关键服务特殊处理,使其使用高可靠内存,提高关键进程运行的稳定性。

在系统遇到内存错误,OS应对未分配的低可靠内存进行覆盖写,以清除未发现的内存错误。

约束限制

  • 关键进程使用高可靠内存

    1. /proc/ <pid>/reliable 接口的滥用可能存在高可靠内存被过多使用的风险。
    2. 用户态进程 reliable 属性只能在进程被拉起后,通过 proc 接口修改或者直接继承父进程该属性。systemd(pid=1)使用高可靠内存,其 reliable 属性无作用,也不继承,内核态线程reliable属性无效。
    3. 进程的程序段和数据使用高可靠内存,高可靠不足,使用低可靠启动。
    4. 普通进程在某些场景也会使用到高可靠内存,如hugetlb、pagecache、vdso、tmpfs等。
  • 未分配内存覆盖写特性

    未分配内存覆盖写特性只能执行一次,不支持并发操作,如果执行会有如下影响:

    1. 该特性耗时较大,每个 Node 有一个 CPU 被覆盖写线程所占用,其他任务在该 CPU 上无法调度。
    2. 覆盖写过程需获取 Zone lock,其他业务进程内存申请要等待覆盖写完成,可能导致内存分配不及时。
    3. 并发执行情况下会排队阻塞,产生更大的延时。

    如果机器性能不佳,将有可能触发内核RCU stall或soft lockup警告,以及进程内存申请操作被阻塞。因此请限制该特性在必要时只在物理机环境下使用,虚拟机等场景大概率出现如上现象。

    物理机参考数据可见下表(实际耗时与硬件性能、当前系统负载有关系)。

表:基于物理机TaiShan 2280 V2空载状态下测试数据

测试项Node 0Node 1Node 2Node 3
Free Mem (MB)10929081218107365112053

总耗时 3.2s

使用方法

本子特性提供较多接口,使能特性并校验只需要步骤1-6即可。

  1. 配置启动参数“kernelcore=reliable”,代表打开内存分级管理开关,CONFIG_MEMORY_RELIABLE是必要的配置,否则整个系统的内存可靠性分级管理不使能。

  2. 根据需要,可以通过启动参数reliable_debug=[F][,S][,P]来选择性关闭fallback功能(F)、关闭tmpfs使用高可靠内存(S)以及关闭读写缓存使用高可靠内存(P),默认以上功能都使能。

  3. 根据BIOS上报的地址段,查找高可靠内存,并进行标记,对于NUMA系统,不一定每个node上都要预留可靠内存,但是node 0上低4G物理空间必须为高可靠的内存,系统启动过程中会申请内存,如果无法分到高可靠内存,则会 fallback 到低可靠内存进行分配(mirror功能自带的fallback逻辑)或导致系统无法启动。如果使用低可靠内存,整个系统都不稳定,所以要保留node0上的高可靠内存且低4G物理空间必须为高可靠的内存。

  4. 启动后,用户可以通过启动日志判断内存分级是否使能,应出现如下打印:

    mem reliable: init succeed, mirrored memory
    
  5. 高可靠内存对应的物理地址段可以通过启动日志来查询,观察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)     
    
  6. 内核态开发时,对于一个页面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; 
     }
    

    此外提供的若干接口按照功能点分类列举如下:

    1. 代码层面判断可靠性是否使能: 在内核模块中通过如下接口来判断,返回 true 表示内存分级功能真正使能,返回false则未使能。
    #include <linux/mem_reliable.h>   
    bool mem_reliable_status(void);
    
    1. 内存热插拔: 如果内核本身使能内存热插拔操作(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
    
    1. 动态关闭某项分级管理功能: 使用long类型控制根据每个bit判断内存分级功能开关与关闭某项功能:
    • bit0:内存可靠性分级管理功能。

    • bit1:禁止fallback到低可靠区域。

    • bit2:关闭tmpfs使用高可靠内存。

    • bit3:关闭pagecache使用高可靠内存。

      其他bit预留,用于扩展。如需更改,可通过如下proc接口(权限为600),取值范围0-15,(只有当总功能bit0为1时才会处理后续功能,否则直接关闭所有功能)。

      echo 15 > /proc/sys/vm/reliable_debug 
      # 关闭所有功能,因为bit0为0 
      echo 14 > /proc/sys/vm/reliable_debug
      

      此命令只能用于关闭功能,不能打开。对于已经关闭的功能或者运行时关闭的功能,这个命令不能将其打开。

      注:此功能用于逃生使用,仅异常场景或者调测阶段需要关闭内存可靠性特性时配置,禁止作为常规功能直接使用。

    1. 查看高可靠内存部分统计信息: 可以通过原生/proc/meminfo来查看,其中:
    • ReliableTotal:内核管理可靠内存总大小。
    • ReliableUsed:系统使用可靠内存总大小,包含系统阶段reserved使用。
    • ReliableBuddyMem:伙伴系统剩余可靠总内存大小。
    • ReliableTaskUsed:表示当前关键用户进程,systemd使用的高可靠内存大小,包括匿名页与文件页。
    • ReliableShmem:表示共享内存高可靠用量,包括共享内存、tmpfs、rootfs使用高可靠内存总大小。
    • ReliableFileCache:表示读写缓存高可靠内存用量。
    1. 未分配内存覆盖写: 该功能需要打开配置项。

    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
    
    1. 查看更改当前进程高低可靠属性: 可以通过/proc//reliable来查看该进程是否是高可靠进程;运行写入,该标识会继承,如果子进程不需要,则手动修改子进程属性;systemd和内核线程不支持该属性的读写;可以写0或 者1,默认为0,代表低可靠进程(权限为0644)。
    # 更改pid=1024的进程为高可靠进程,从更改之后开始进程缺页申请的内存是从高可靠内存区域申请,申请不到有可能fallback到低可靠 
     echo 1 > /proc/1024/reliable
    
    1. 设置用户态高可靠进程申请上限: 通过/proc/sys/vm/task_reliable_limit来修改用户态进程申请高可靠内存的上限,对应取值范围为[ReliableTaskUsed, ReliableTotal],单位为Byte(权限为0644)。需注意:
    • 默认值为ulong_max,代表不受限制。
    • 当该值为0,可靠进程不可使用高可靠内存,fallback模式下,fallback到低可靠内存区域申请,否则直接OOM。
    • 当该值不为0并且触发该limit, 使能fallback功能,fallback到低可靠内存区域申请内存,不使能fallback功能,则返回OOM。

读写缓存使用高可靠内存

概述

Page cache 也叫页缓冲或文件缓冲,在linux读写文件时,它用于缓存文件的逻辑内容,从而加快对磁盘上映像和数据的访问。Page cache申请如果使用低可靠内存,当访问时可能触发UCE导致系统异常。因此,需要将读写缓存(page cache)放到高可靠内存区域,同时为了防止Page cache申请过多(默认无限制)将高可靠内存耗尽,需要对page cache的总量及使用可靠内存总量进行限制。

约束限制

  1. page cache超过限制后,page cache会进行定期回收,如果page cache的产生的速度大于page cache回收的速度则无法保证page cache的数量在指定的限制之下。
  2. /proc/sys/vm/reliable_pagecache_max_bytes的使用有一定限制,有部分场景的page cache会强制使用可靠内存,如读文件系统的元数据(inode, dentry等),会导致page cache使用的可靠内存超过接口的限制,可以通过 echo 2 > /proc/sys/vm/drop_caches 来释放inode和dentry。
  3. page cache使用的高可靠内存超过reliable_pagecache_max_bytes限制时,会默认申请低可靠内存,若低可靠内存申请不到,则遵循原生流程处理。
  4. FileCache的统计会先统计到per cpu的缓存中,当缓存中的值超过阈值时才会加到整个系统中,之后才能在/proc/meminfo中体现,ReliableFileCache在/proc/meminfo中没有上述的阈值,会导致有时ReliableFileCache比FileCache的统计值稍大。
  5. 写缓存场景会被dirty_limit限制(由/proc/sys/vm/dirty_ratio限制,代表单个内存节点脏页百分比),超过阈值会跳过当前zone。对于内存分级而言,由于高低可靠内存处于不同的zone,写缓存有可能触发本节点的fallback,使用本节点的低可靠内存。可以通过echo 100 > /proc/sys/vm/dirty_ratio来取消限制。
  6. 读写缓存使用高可靠内存的特性中会限制page cache的使用量,如下几种情况会导致系统性能受影响:
    • 如果page cache的上限值限制的过小,会导致IO增加,影响系统性能。
    • 如果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_enable1
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_bytesunsigned long 类型的最大值,代表不限制用量。

tmpfs使用高可靠内存

概述

对于使用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错误,即发生内核态消费内存UCE,系统会触发panic。本子特性对部分用户态穿越内核UCE场景提供解决方案,避免系统复位,包括COW场景、copy_to_user场景、copy_from_user场景、get_user场景、put_user场景、coredump场景,其余场景均不支持。

约束限制

  1. ARMv8.2及以上版本支持的RAS特性。
  2. 本特性更改的是同步异常处理策略,因此本特性的生效依赖于内核收到Firmware上报的同步异常。
  3. 内核处理依赖BIOS上报的错误类型,不能处理fatal级别硬件错误,可以处理recoverable级别的硬件错误。
  4. 仅支持COW场景、copy_to_user场景(包含读缓存pagecache)、copy_from_user场景、get_user场景、put_user场景、coredump场景六个用户态穿越内核态场景,其余场景不支持。
  5. 在coredump场景中,因为需要在文件系统的write接口上做UCE容错,本特性只支持常用的三个文件系统:ext4、tmpfs、pipefs,对应的容错接口如下:
    • pipefs:copy_page_from_iter
    • ext4/tmpfs:iov_iter_copy_from_user_atomic

使用方法

确保内核开启config开关CONFIG_ARCH_HAS_COPY_MC,/proc/sys/kernel/machine_check_safe值为1时代表全场景使能,改为0代表不使能,其他值均为非法。

当前各场景容错处理机制如下:

序号场景现象应对措施
1copy_from/to_user:最基本的用户态穿越,主要涉及syscall,sysctl,procfs的操作如果在拷贝时出现UCE异常,会导致内核复位出现 UCE 异常时,kill 当前进程,内核不主动复位
2get/put_user:用于简单的变量拷贝,主要是netlink的场景用的比较多如果在拷贝时出现UCE异常,会导致内核复位出现 UCE 异常时,kill 当前进程,内核不主动复位
3COW:fork子进程,触发写时拷贝触发写时拷贝,如果出现UCE会导致内核复位出现 UCE 异常时,kill 相关进程,内核不主动复位
4读缓存:用户态使用了低可靠内存,用户态程序读写文件时,操作系统会使用空闲内存缓存硬盘文件,提升性能。但用户态程序对文件的读取会先经过内核访问缓存出现UCE异常,会导致内核复位出现 UCE 错误时,kill 当前进程,内核不主动复位
5coredump时内存访问触发UCE出现UCE异常,会导致内核复位出现 UCE 错误时,kill 当前进程,内核不主动复位
6写缓存:写缓存回刷到磁盘时,触发UCE回刷缓存其实就是磁盘DMA数据的搬移,如果在此过程中触发UCE,超时结束后,页面写失败,这样会造成数据不一致,进而会导致文件系统不可用,如果是关键的数据,会出现内核复位没有解决方案,不支持,内核会发生复位
7内核启动参数、模块参数使用的都是高可靠内存/不支持,且本身风险降低
8relayfs:是一个快速的转发数据的文件系统,用于从内核态转发数据到用户态,/不支持,且本身风险降低
9seq_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。

文档捉虫

“有虫”文档片段

问题描述

提交类型 issue
有点复杂...
找人问问吧。
PR
小问题,全程线上修改...
一键搞定!
问题类型
规范和低错类

● 错别字或拼写错误;标点符号使用错误;

● 链接错误、空单元格、格式错误;

● 英文中包含中文字符;

● 界面和描述不一致,但不影响操作;

● 表述不通顺,但不影响理解;

● 版本号不匹配:如软件包名称、界面版本号;

易用性

● 关键步骤错误或缺失,无法指导用户完成任务;

● 缺少必要的前提条件、注意事项等;

● 图形、表格、文字等晦涩难懂;

● 逻辑不清晰,该分类、分项、分步骤的没有给出;

正确性

● 技术原理、功能、规格等描述和软件不一致,存在错误;

● 原理图、架构图等存在错误;

● 命令、命令参数等错误;

● 代码片段错误;

● 命令无法完成对应功能;

● 界面错误,无法指导操作;

风险提示

● 对重要数据或系统存在风险的操作,缺少安全提示;

内容合规

● 违反法律法规,涉及政治、领土主权等敏感词;

● 内容侵权;

您对文档的总体满意度

非常不满意
非常满意
提交
根据您的反馈,会自动生成issue模板。您只需点击按钮,创建issue即可。