3

Detecting and handling split locks

 3 years ago
source link: https://kernel.taobao.org/2019/07/Detecting-and-handling-split-locks/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Jul 17, 2019

Detecting and handling split locks

本文在解读 https://lwn.net/Articles/790464/ 的基础上,加入自己理解,与原文存在差异。

从地址不对齐访问到split lock

Intel CPU微架构允许不对齐的内存访问,但ARM、RISC-V等架构却不允许。在众多的不对齐中,一个特殊的场景是:原子操作的操作数(由于地址不对齐)跨越两个cache lines,Intel将之叫做split lock。它有两个特征:

  1. 原子操作,即汇编指令包含Lock前缀;
  2. 操作数地址不对齐,还跨越两个cache lines;

其实大部分吃瓜群众都不知道这个特性,但是它却对应用性能影响极大。最近,Intel工程师Fenghua Yu同学正在开发一组内核补丁,用于检测和处理split lock,现在已经发出了第8版code review。阿里巴巴在多年前就意识到split lock的危害,在线上实施了大规模监控,并采取必要隔离措施。

下面通过一小段代码给吃瓜群众一个感性的认识:

  1 #include<stdio.h>
  2 #include <sys/mman.h>
  3 
  4 #pragma pack(push,2)
  5 struct counter
  6 {
  7     char buf[62];
  8     long long c;
  9 };
 10 #pragma pack(pop)
 11 
 12 int main () {
 13     struct counter *p;
 14     int size = sizeof(struct counter);
 15     int prot = PROT_READ | PROT_WRITE;
 16     int flags = MAP_PRIVATE | MAP_ANONYMOUS;
 17 
 18     p = (struct counter *) mmap(0, size, prot, flags, -1, 0);
 19 
 20     while(1) {
 21         __sync_fetch_and_add(&p->c, 1);
 22     }
 23 
 24     return 0;
 25 }

Intel cpu中,一个cache line 只有64个字节,struct counter中的成员 c 占8个字节,buf填充了62个字节。因此,一旦访问成员c,就涉及两个cache lines的内容的拼接;代码21行中,执行原子操作 __sync_fetch_and_add()会触发split lock。

split lock 的危害

Intel处理器很贴心,自动处理了不对齐的内存访问,甚至能保证split lock操作的正确性;但是也给程序的性能埋下一颗很难被发现的雷。

学过体系结构的同学都应该知道,缓存一制性协议MESI只能保证cache line粒度的一致性。同时访问两个cache lines不是常见操作,为保证split lock的原子性,设计硬件时使用特殊逻辑(冷路径)来处理:锁住整个访存总线,阻止其它逻辑cpu访存。

从原理出发,我们很容易想到,锁住总线将导致其它core上访存操作受阻,宏观表现为平均访存延时显著上升。为不让各位看官白走一趟,小编在自己的skylake机器上测了一组数据,随着split lock速率的增加,访存延迟呈指数恶化。

1.png

云场景下,在Guest中执行一个用户态进程,就可以阻塞物理机上其它核的访存操作,导致整机性能下降;极端情况下,甚导致整机的拒绝服务(DoS)攻击。这是一个严重的故障隔离问题。

Intel 即将发布的下一代处理器 (Tremont和Ice Lake) 新增了Alignment Check (#AC)异常,并提供一个硬件开关。当开关使能的情况下,cpu产生split lock时,可以触发异常。至于采取什么动作就取决于注册的异常处理程序了,这正是Fenghua同学要做的事。而在早期的cpu上,只能通过perf监控sq_misc.split_lock 事件的数量,用于调试目的。

接下来,我们来看看这组内核补丁是如何处理#AC异常的。从整体看,可以将一个机器上运行的代码分为三类:固件、内核、用户态软件。

如果split lock出现在内核中,只打印warning,然后关闭当前cpu的检测功能。报出warning之后,重新执行被打断的指令,系统继续运行。是打印warning还是直接panic,是社区当前讨论的一个焦点。比较合理的解释是:内核中产生split lock是bug,但是该bug没有严重到需要直接panic。

如果split lock出现在用户态程序中,默认会发送一个SIGBUS信号,导致进程异常退出。只有当开发人员修复后才能正常运行程序。

当split lock出现在系统固件代码中,系统将会夯(hang)住。之所以这么设计,是内核开发人员担心,如果不暴露问题,固件厂商永远不会关注修复这类问题。

处理split lock的方式,有warning,sigbus, hang,都不是省油的灯。我们可能会担心,代码我改不了,一旦出现了split lock怎么办?好在设计者提供了关闭接口:

  1. 在内核启动参数中加入nosplit_lock_detect
  2. echo 0 > /sys/devices/system/cpu/split_lock_detect

这组内核补丁经过多轮review,社区的反馈都比较积极,包括Thomas Gleixner和Ingo Molnar等大佬。虽然还剩下一些小问题,预计不久将被合入内核主线。

如何在现有的cpu上检测split lock呢?

perf stat -e cpu/event=0xf4,umask=0x10/ -a -I 1000  

其中 event=0xf4, umask=0x10 对应PMU事件sq_misc.split_lock。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK