Linux阅码场 - Linux内核月报(2020年06月)

关于Linux内核月报

Linux阅码场

Linux阅码场内核月报栏目,是汇总当月Linux内核社区最重要的一线开发动态,方便读者们更容易跟踪Linux内核的最前沿发展动向。

限于篇幅,只会对最新技术做些粗略概括,技术细节敬请期待后续文章,也欢迎广大读者踊跃投稿为阅码场社区添砖加瓦。

本期月报(总第1期)主要贡献人员:

张健、宋老师、廖威雄、理查德、Dog250、Bob

(月报的完善和专业,离不开大牛们的持续贡献,欢迎更多大牛加入月报贡献团队)

1. 体系结构

1.1 MTE(Memory Tagging Extension)

近期Linux社区在大量讨论ARM v8.4和v8.5的扩展特性,例如v8.4的嵌套虚拟化和v8.5的MTE(Memory Tagging Extension) 。MTE是硬件支持的memory tag,目的是解决spatial safety和temporal safety两类安全性问题。

所谓的memory tag是指利用地址中固定的无用的bit传递更多信息(即tag)的方法。例如,在64位系统下,实际只有最高位63bit用于表示内核和用户空间,假设系统是48位VA,最多[62:49]可以用于做tag。ARM的MTE特性使用[59:56]传递tag。

用户可以通过为mmap,mprotect添加的新的flag PROTMTE指定使用tag的range。补丁同时还需要考虑所有与用户空间相关的page操作,例如fork进程导致的

copy on write;page migration;page比较(memcmppages)等方面。这个系列的补丁是由ARM64 maintainer catalin主导的,如果没有什么意外,计划在5.9合入.

参考资料:MTE社区支持情况,MTE白皮书等等:

https://developer.arm.com/tools-and-software/open-source-software/linux-kernel/memory-tagging

1.2 Support new pmem flush and sync instructions for POWER

Persistent memory (pmem)有存储器件的容量和与接近内存的访问速度。它与内存相同的寻址方式,但由于数据可能缓存在cache中,为了保证数据一致性,需要额外的flush,sync等操作。

本补丁支持了POWER10新增的dcbf系列指令(dcbstps和dcbfps),用于把修改的信息写回pmem。

1.3 arm64: add the time namespace support

去年年底,社区增加了time namespace,这允许在容器内部修改日期和时间,并保证容器迁移后时间不变。当时的工作仅仅支持了x86。Andrei Vagin这组补丁旨在为ARM64架构支持time namespace。

为了支持这个能力,需要在vdso传递数据区域(vvar)建立对应root namespace和non-root的namespace的页。当容器内部namespace变化时会释放当前页的映射,并在下次需要时映射新页。

2. 驱动和工具

2.1 Data Access MONitor (DAMON)

DAMON这个工具主要提供跟踪一个特定用户进程的内存访问pattern的功能,这个功能对于性能优化非常有意义。

一个最直接的用户case就是对程序的行为进行分析,根据DAMON的输出,我们能确定一个程序是否按照预期在运行。

对于系统管理员而言,你可以利用监控的结果数据,确定你的workload的动态working set size

对于程序员而言,你可以根据监控结果反映出来的实际data访问pattern,来对你的程序的内存进行管理。比如,你可以用DAMON获知热点数据,从而用mlock()把热点锁入内存,或者用MADV_PAGEOUT参数的madvise()来激进地回收冷数据。

DAMON向用户汇报哪些pages被频繁地访问,它采用了一个“Region Based Sampling”的技术,把相邻的pages绑定在一起成为一个group,假定整个region有相同的访问频率,从而避免了一页一页扫描accessed bit的开销。

2.2  Netgpu: networking between NIC and GPU/CPU

这个patchset提供了一个功能:在网卡和GPU之间实现数据的DMA 零拷贝,但是协议的处理仍然保留在主机的CPU上面进行。

2.3 iommu: Shared Virtual Addressing for SMMUv3(PT sharing part)

SVA的主要目的是让设备和CPU里面运行的进程共享页表,从而让进程的地址空间直接对设备的DMA可见。

Linux阅码场 - Linux内核月报(2020年06月)_第1张图片

自从V7版本开始,这个patchset被分为3个部分:

1.page table共享的功能到SMMUv3

2.增加基于PRIstallIO Page fault支持;

3.附加的以及可选的特性,如DVM,VHEHTTU

3. 内存管理

3.1 workingset protection on anonymous LRU list

为了更好的利用内存,内核会将“冷”页释放挪作他用。而这个“冷热”的区分是用过不同的LRU链表实现的。

最近内核开发者发现匿名页的冷热判断在某些情况下会把“热”页挤到”冷”页的队列里,这样就会导致“热”页的再次需要通过缺页中断回到系统中。

下面我们将用一个简单的图展示这种特殊情况下LRU链表的变化:

   开始有50个热页

50(h) | 0

然后来了50个新的页,但是只需要用一次

50(uo) | 50(h)

接着又来了新的一批50个新的,但是只用一次的页

50(uo) | 50(uo), swap-out 50(h)

上面的示例使用的标记方式是:

  • LRU链表状态:active | inactive

  • (h) 热页

  • (uo) 只用一次的页

可以看到,在这个情况下真正的热页将被挤出LRU链表,下次需要使用时则需要再次通过缺页中断。

那内核开发者是如何避免这样的情况发生的呢?

新添加的页不是放到active链表上,而是先放到inactive链表,等访问次数达标后再放到active链表上。

调整后整个过程就变成了:

50(h) | 0

50(h) | 50(uo)

50(h) | 50(uo), swap-out 50(uo)


3.2 per memcg lru lock

这是一个历经了13版,还在review的patch set。从整个patch set来说,最核心的目标是这个patch中想要做的。

mm/lru: replace pgdat lru_lock with lruvec lock

就像这个patch中描述的一样,这个patch的目的是

This patch moves per node lrulock into lruvec, thus bring a lrulock for each of memcg per node. So on a large machine, each of memcg don't have to suffer from per node pgdat->lrulock competition. They could go fast with their self lrulock.

用一个简图解释一下这个变化。

原来操作lruvec的锁每个node上只有一把,在pg_data_t结构体中。这样明显竞争会比较大。

Linux阅码场 - Linux内核月报(2020年06月)_第2张图片

作者则把这个锁放到了lruvec结构体中,这样每次去抢的锁就是在自己的lruvec上了。以此来减少锁的竞争。

Linux阅码场 - Linux内核月报(2020年06月)_第3张图片

而且作者在使用了这组patch后,测试的结果非常惊人。

With this and later patches, the readtwice performance increases about 80% within concurrent containers

3.3 memcg: Slow down swap allocation as the available space gets depleted

当系统的swap空间耗尽时,系统的状态会有一个非常突然的变化。整个系统的匿名页变得不可管理,甚至会crash整个系统。


当前业界采用了一个叫 oomd的用户态程序来监控swap空间,当发现swap空间变小时就选择一些低优先级的进程杀死。这个oomd的地址是

https://github.com/facebookincubator/oomd

内核开发者提出了优化,可以不借助oomd来应对swap空间耗尽。内核开发者提出的方式是借用了memcg的接口,除了检测内存分配的警戒线之外,还检测swap使用的情况。只要其中之一处于高位,那么就开启回收进程来避免swap耗尽。

if (mem_high || swap_high) {      /*       * The allocating tasks in this cgroup will need to do       * reclaim or be throttled to prevent further growth       * of the memory or swap footprints.       *       * Target some best-effort fairness between the tasks,       * and distribute reclaim work and delay penalties       * based on how much each task is actually allocating.       */

3.4 make dma_alloc_coherent NUMA-aware by per-NUMA CMA

这个patchset的作用,是给每个NUMA节点分配一个默认的CMA区域,这样保证设备dma_alloc_coherent()的时候,可以申请到本地NUMA节点的dma buffer。使得ARM SMMU的command queue等可存放于本地内存,避免跨NUMA NODE进行远程访问。对IOMMU passthrough的场景,也会有性能提升。

4. 文件系统和Block Layer

4.1 Enable ext4 support for per-file/directory DAX operations

跟 xfs 一样,最新代码也已经在ext4上支持单个文件、文件夹设置 DAX 特性了。DAX 指 Direct Access for file,即不需要缓存,直接访问存储设备上的文件。

我们知道,目前常见的 内存(dram)的性能远大于 存储(HDD\SSD\Nand\Emmc),所以一般会使用内存作为缓存(PageCache),把磁盘的数据缓存起来,这样的设计的确极大提高了系统的IO性能,但对新出的 NVDIMM 设备而言,缓存却显得多余了。

NVDIMM 是 non-volatile dual in-line memory module 的缩写,中文翻译为非易失性双列直插式内存模块。从技术上来说,这实际是 Dram + Nand + 超级电容 实现 内存的高速,存储的掉电非易失性。原理可以简述为,正常工作时,外部供电,数据存储在 NVDIMM 的 Dram 部分;在掉电时,超级电容供电,把 NVDIMM 的 Dram 数据 写入 NVDIMM 的 Nand ,在下次启动时恢复到 NVDIMM 的 Dram。

NVDIMM 设备具有内存的高速,此时再使用 PageCache 缓存数据就很累赘了,因此有了 DAX 技术,不使用缓存,直接访问设备。除了 NVDIMM 这样的设备之外,对虚拟机也有很大作用,可以避免虚拟机内部和主机上产生两份缓存。

xfs 上很早就支持了单个文件、文件夹设置 DAX 属性,而ext2/4还只是全局的 DAX 挂载属性,现在只是在 ext4 上也支持了单个文件、文件夹操作。

4.2 support batching dispatch from scheduler

在之前的实现中,为了尽可能在IO调度中合并请求,调度器每次只从队列中获取和分发一个请求,但实际上越来越多的设备支持批处理多个请求,每次只处理一个请求会造成性能浪费。这个 patchset 就实现了在IO电梯调度器中支持批量分发IO请求,只要设备还是空闲,就可以把多个IO请求分发下去,充分利用设备的批处理资源。

除了 mmc 驱动、一些基于tcp的存储驱动之外,virtio-scsi、virtio-blk等虚拟设备批处理能力更强,这个 patchset 能发挥非常大的作用。在virtio-scsi 上测试IO性能提高 5%~10%。

4.3 Inline Encryption support

内联加密硬件变得越来越普遍,这是一个硬件加持的加密方案。从概念上来讲,内联加密硬件在逻辑上作用于内存与磁盘之间,可以在数据进出磁盘时对其加密和解密。与全盘加密的方案不同,内联加密支持磁盘上任意一个块都可以单独使用提供的任意一个加密上下文进行加密。每个块可独立的加密上下文,这无疑给加密提供了更灵活的方式。

为了支持内联加密,patchset 修改了 fscrypt, f2fs, ext4以及块层和驱动的代码。为了让驱动也知道加密上下文,patchset 在 structbio 中添加了 strcut bio_crypt_ctx,以及提供了一系列的操作接口。

对用户来说,可以使用 fscrypt 的 Linux 文件系统加密 工具来设置加密的属性。目前来看,patchset 使得内联加密支持 f2fs 和 ext4 文件系统以及 UFS 设备。

4.4 kernfs: proposed locking and concurrency improvement

在一些超大型的设备上,例如 patchset 提到的 数百个cpu 和 64TB 内存 的超大型设备,发现启动用时超过30分钟。经过分析,大部分启动时间都是花在了频繁路径遍历和遍历时申请锁。这是在启动时,udev 产生了大量通知,systemd 做出响应时访问 sysfs 产生的路径遍历。问题不是出在于 systemd,而是路径遍历时的互斥和大量对不存在文件的遍历。这毫无疑问是一个高并发和锁的问题。

kernfs 是 sysfs 的核心代码,patchset 作者通过在 kernfs 中添加了对不存在路径的 dentry 缓存来避免连续分配和释放 dentry,同时添加了读写信号量,以此提高 kernfs 在路径遍历时的高并发性。加上一些 udev 和 systemd 的配置,最快的启动时间做到了小于5分钟。

5. 网络

5.1 bpf:Add support for XDP programs in DEVMAP entries

增加了一个新的eBPF程序的挂载点,使XDP在基于DEVMAP的REDIREC决策被作出之后运行一个eBPF程序成为可能。该特性允许新挂载点上的eBPF程序可以同时看到Rx和Tx设备端口。

bpf: introduce support for XDP programs in CPUMAP 与 

bpf: Add support for XDP programs in DEVMAP entries 类似,在基于CPUMAP的REDIREC决策被作出之后,在目标CPU上允许运行一个eBPF程序。

5.2 Strict mode for VRF

该新特性提供了一种在VRF和其使用的路由表之间创建严格一对一映射关系的方法。strict mode增强了VRF路由表的隔离性,在多个VRF隔离的进程操作相同路由表时,消除了误操作,有效降低了造成冲突的机会。同时,SRv6也将从该特性中获得收益,详情参见:

https://lkml.org/lkml/2020/6/12/566

5.3 implement bpf iterator for tcp and udp sockets

使用eBPF来遍历系统中的TCP/UDP socket,增强了灵活性。该特性旨在替换传统的基于硬编码的TCP/UDP socket遍历方式,比如当前/proc/net/tcp的实现。

5.4 net: tso: expand to UDP support

该patch为UDP提供了和TCP一样的卸载设施。patch本身是简单的,但它的背景很有意思。随着Quic越来越被各类云厂商关注和部署,有越来越多的针对它的传输协议UDP的优化手段被提了出来。关于UDP的优化,目前仅仅是个开始,我们会持续关注。


5.5 multi release pacing for UDP GSO

这是针对UDP的一个具体优化patch。简单来讲,该patch使UDP在GSO开启的情况下增强了pacing发送的效果,相当于为一个UDP GSO段配置了多个发送时间,有效避免了属于同一个GSO段的所有数据包同时以burst方式发送。无论是pacing还是GSO,均可以有效提高包括Quic在内的协议处理性能以及端到端性能,在以往,这两个特性是无法共存的,甚至是矛盾的,该patch的意义在于将两者结合了起来。


5.6 ptq: Per Thread Queues

该patch涉及Linux内核网卡多队列技术的增强。此前的网卡队列只能和CPU进行对应,该patch允许具体的Thread直接和网卡队列进行绑定。

6. 虚拟化和容器

6.1 KVM: Dirty ring interface

当前KVM使用bitmaps来跟踪脏内存,当用户态查询kvm的脏页信息时,这些bitmaps需要从内核拷贝到用户态。在一些场景下(比如内存很大但是脏页很少),频繁的拷贝和查询bitmap可能效率会不高。

 

这个系列patch实现一种新的收集虚拟机脏页的接口,旨在想解决当前dirty logging的缺点。

新的接口主要有三方面差异:

 - 数据格式: 脏数据通过per-vcpuring格式管理,而不再是per-vmbitmap格式

 - 数据拷贝: 脏页sync时不再需要通过数据拷贝的方式,而是让ring在用户空间和内核态直接共享

 - 接口:     引入新的KVM_RESET_DIRTY_RINGSioctl接口

 

不过作者在24G guest上的测试结果还没有发现很有说服力的测试数据,原因可能是因为dirty bitmap1M本身就不大,还需要更多有价值的测试才行

 

2. vfio: expose virtual Shared VirtualAddressing to VMs

 

虚拟地址共享Shared Virtual Addressing (SVA)是指设备dma能够直接使用应用程序的虚拟地址,这样能够增强安全性和降低编程的复杂性。

这个系列patch是要让guest里面的应用程序也能和透传的设备直接共享虚拟地址(vSVA),涉及到QEMU/VFIO/IOMMU等模块的改动

 

vSVA的大概架构如下,主要是要使能IOMMU的二级地址转换能力:

Linux阅码场 - Linux内核月报(2020年06月)_第4张图片

 

6.3 vhost: ring format independence

这个系列patch比较直观,主要是为了让vhost能够支持更多的ring格式。所以抽象出来一层通用结构,给virtqueue引入一个通用的struct vhost_desc数组,通过这个数组再去找到真正的描述符,虽然这样增加了额外的消耗,但是能够批量的拿到描述符能够提高cache的命中率,在实际测试中只有很小的性能影响

 

6.4 Add support for Nitro Enclaves

 

挺有意思的一个patch,给linux内核增加Nitro Enclaves的支持,Nitro Enclaves (NE) Amazon EC2实例的一个新能力,允许客户在Amazon EC2实例内再分割出来一个独立隔离的运行环境。

例如Amazon EC2 实例中的某个应用程序要处理些敏感数据时,就可以让这个应用程序在一个独立的新隔离环境(Nitro Encalve)中运行。

Nitro Enclaves 提供了隔离的、增强的且约束性很高的环境,可帮助客户减少他们用于处理最敏感数据的应用程序的攻击面。

Enclaves运行时所要的cpu/memory等资源都来自主EC2实例。

感兴趣的读者可以猜猜看是如何实现的,再去看看具体的Patch。

Linux阅码场 - Linux内核月报(2020年06月)_第5张图片

(END)

Linux阅码场原创精华文章汇总

更多精彩,尽在"Linux阅码场",扫描下方二维码关注

球分享

球点赞

球在看

你可能感兴趣的:(Linux阅码场 - Linux内核月报(2020年06月))