Linux 内核中关于 cpuset cgroup 继承语义的“破坏”是什么?

Wil*_*ard 5 systemd cgroups cpu-usage

引用2013 年 systemd 发布的新控制组界面的公告(加了重点):

请注意,当前作为单元属性公开的 cgroup 属性的数量是有限的。这将在稍后扩展,因为它们的内核接口被清理。例如,由于内核逻辑的继承语义被破坏,cpuset 或 freezer 目前根本没有公开。此外,不支持在运行时将单元迁移到不同的切片(即更改运行单元的 Slice= 属性),因为内核当前缺乏原子 cgroup 子树移动。

那么,内核逻辑的继承语义有什么问题cpuset(以及这种问题如何不适用于其他 cgroup 控制器,例如cpu)?

RedHat 网站上一篇文章给出了如何在 RHEL 7 中使用 cgroup cpuset 的未经验证的解决方案,尽管它们缺乏易于管理的 systemd 单元属性的支持......但这甚至是一个好主意吗?上面的粗体引文是有关的。

换句话说,这里引用的 cgroup v1 cpuset 有哪些“陷阱”(陷阱)?


我正在为此悬赏。

回答这个问题的可能信息来源(排名不分先后)包括:

  1. cgroup v1 文档;
  2. 内核源代码;
  3. 检测结果;
  4. 真实世界的体验。

上面引用中粗线的一个可能含义是,当一个新进程被分叉时,它不会与其父进程留在同一个 cpuset cgroup 中,或者它在同一个 cgroup 中但处于某种“非强制”状态因此它实际上可能运行在与 cgroup 允许的 CPU 不同的 CPU 上。然而,这纯粹是我的猜测,我需要一个明确的答案。

Wil*_*ard 4

此处的内核错误跟踪器记录了至少一个明确且未解决的 cpuset 问题:

Bug 42789 - cpuset cgroup:当一个CPU离线时,它会从所有cgroup的cpuset.cpus中删除,但当它上线时,它只会恢复到根cpuset.cpus

引用票证中的一条评论(我将超链接添加到实际提交,并删除 IBM 电子邮件地址,以防出现垃圾邮件机器人):

这是由 Prashanth Nageshappa 独立报告的...并在提交8f2f748b0656257153bcf0941df8d6060acc5ca6中修复,但随后由 Linus 恢复为提交4293f20c19f44ca66e5ac836b411d25e14b9f185。根据他的承诺,该修复导致其他地方出现回归。

修复提交(后来被恢复)很好地描述了这个问题:

目前,在 CPU 热插拔期间,cpuset 回调会修改 cpuset 以反映系统状态,并且此处理是不对称的。也就是说,当 CPU 离线时,该 CPU 将从所有 cpuset 中删除。然而,当它重新上线时,它仅被放回根 cpuset。

这在挂起/恢复期间引起了严重的问题。在挂起期间,我们将所有非启动 cpu 脱机,并在恢复期间将它们重新联机。这意味着,恢复后,所有 cpuset(根 cpuset 除外)将仅限于一个 CPU(启动 cpu)。但挂起/恢复的全部目的是将系统恢复到尽可能接近挂起之前的状态。


描述了相同的非对称热插拔问题,并进一步深入了解了它与继承的关系,如下:

Bug 188101 - cgroup 的 cpuset 中的进程调度无法正常工作。

引用那张票:

当容器的 cpuset(docker/lxc 都使用底层 cgroup)变空(由于 hotplug/hotunplug)时,在该容器中运行的进程可以调度到其最近的非空祖先的 cpuset 中的任何 cpu 上。

但是,当正在运行的容器(docker / lxc)的cpuset通过更新正在运行的容器的cpuset(通过使用echo方法)从空状态变为非空(将cpu添加到空cpuset)时,在该容器中运行的进程仍然使用与其最近的非空祖先相同的 cpuset。


虽然 cpuset 可能存在其他问题,但以上内容足以理解和理解 systemd 不会公开或利用 cpuset“由于内核逻辑的继承语义被破坏”的说法。

从这两个错误报告来看,CPU 不仅在恢复后不会添加回 cpuset,而且即使(手动)添加它们该 cgroup 中的进程仍将在 cpuset 可能不允许的 CPU 上运行。


我发现Lennart Poettering 发来的一条消息直接证实了这一点(加粗):

Lennart Poettering 在 2016 年 8 月 3 日星期三 16:56 +0200 写道:

周三,2016 年 8 月 3 日 14:46,Werner Fink 博士(werner at suse.de)写道:

v228 的问题(我猜这也是后来来自当前 git 日志的 AFAICS)重复 CPU 热插拔事件(离线/在线)。根本原因是 cpuset.cpus 无法通过机械加工恢复。请注意,libvirt 无法执行此操作,因为不允许这样做。

这是内核 cpuset 接口的限制,也是我们现在不在 systemd 中公开 cpuset 的原因之一。值得庆幸的是,还有一个 cpuset 的替代方案,即通过 systemd 中的 CPUAffinity= 公开的 CPU 亲和力控制,它的作用与此基本相同,但语义较少。

我们希望直接在 systemd 中支持 cpuset,但只要内核接口像它们一样无聊,我们就不会这样做。例如,当前,当系统经历挂起/恢复周期时,CPU 集会被完全刷新。