LWN:继续完善core scheduling!

关注了就能看到更多这么棒的文章哦~

Completing and merging core scheduling

By  Jonathan Corbet

OSPM

原文来自:https://lwn.net/Articles/820321/

主译:DeepL

Core scheduling是对内核的CPU调度器的一种改进方案,用来供系统管理员控制哪些进程可以在同一个处理器上同时运行。它最初是作为一种安全机制提出的,但随着时间的推移,也出现了其他的使用场景。在2020 Power Management and Scheduling in the Linux Kernel summit(OSPM)上,约50名开发者在线聚集在一起,讨论了core scheduling patch的现状,以及还需要作什么才能将其纳入mainline kernel。

Status update

Vineeth Pillai首先指出,虽然已经针对已知的Spectre漏洞开发出了防护措施,但这些措施并不能为simultaneous multi-threading (并行多线程SMT或 大家熟知的"hyperthreading")的系统提供完整保护。SMT会造成多个CPU在共享硬件上运行的假象,从而提高性能。然而,共享使用底层处理器的话,为speculative-execution漏洞和covert channel攻击提供了机会。真正保护系统不受这些漏洞影响的唯一方法是禁用SMT,这对某些工作场景来说是导致的性能下降是无法接受的。

Pillai表示,Spectre漏洞还是有办法缓解的,也就是通过选择合适的cache来flush出去,但在使用SMT时,这种方法无法使用了。cache flush操作将丢弃在flush之前存放在cache中的数据,使其在flush后无法被访问。不过,这个机制要求,相互不信任的task必须确保在多次flush之间不共享使用CPU core。在没有SMT的情况下,内核可以在切换任务时执行cache flush,就能确保这种情况永远不会发生。但在SMT core上,每个CPU都是独立调度的,所以这种cache flush规则是无法确保的。

要避免这个问题,有一个方法是让彼此不信任的任务不在同一个core上运行,这意味着在当前CPU core上禁用SMT。另一种方法是core scheduling,即对task按互相信任与否进行分组。scheduler调度器了解每个CPU core的可信task,确保只有互相信任的task才能 同时在一个CPU core上同时运行。在进行调度决策的时候,如果最高优先级的task没有可信的sibling(兄弟task),那么在该任务运行时,当前CPU可以被强制空闲,而不是同时运行一个不受信任的task。

[ Vineeth Pillai, Dario Faggioli, and Tim Chen at OSPM 2020]

Pillai说,还有一点尚未决定,就是关于负载均衡方面。目前core scheduling跟load balance配合得不太好。可能的改善方案就是选择一个单一run queue来存放core scheduling决策所以来的那些共享信息。当调度事件发生时,会像往常一样选择优先级最高的任务。然后,只要它有匹配的兄弟任务存在,就可以在相应的core上来填充这些兄弟任务。

Core scheduling目前使用CPU control groups来控制task分入哪个cgroup。cpu.tag字段里可以填一个 "cookie "来标记此task属于哪个调度组。这样做的目的是为了能先快速、简单的实现出来,这不一定是最终的正式用法。在每个run queue中都有一个红黑树,按cookie值排序,用于给兄弟处理器选择合适的task。

这一组patch set已经到了第5版,其中增加了一些load balancing的改进。早期的版本根本不考虑负载均衡,所以如果一个任务被迁移到到目标CPU上正在运行一个包含了不相容的tag的task,最终可能会导致它自己的时间片用光都无法执行。并且后面很快就会提出第六版。

有一个很大的挑战是,需要比较不同sibling之间的任务的优先级。在一个run queue中,一个任务的vruntime值用来决定它是否应该下一个运行。这个值是一种算出来的 虚拟 运行时间,表示此task相对于其他task所获得的CPU时间(尽管它会根据进程优先级进行一些放大缩小,还有其他一些方式进行调整),但这个值是每个run queue所特有的。一个run queue中的vruntime不能与另一个run queue中的vruntime直接比较。

一个可能的解决方案是将这些值归一化。每个队列都维护一个min_vruntime值,这是该队列中任何任务的最低vruntime。如果一个特定任务的vruntime通过减去本地的min_vruntime来归一化,那么在跟其他run queue对比的时候只要再加上该队列中的min_vruntime即可。但是,使用这个方案的测试发现有进程饿死问题。于是,后面人们开始考虑创建一个core特有的vruntime值了。不过这种方案仍然有进程饿死问题,所以这方面的探索还在进行中。

第6版patch预计也会 包括对选择 进程这部分的 代码的一些更改,这部分代码目前只选择最高优先级的任务来运行。这也会导致进程饥饿问题。还有一些已经进入kernel的task的特有问题(例如在处理中断),最终可能会被与一个不信任的任务调度到同一个CPU上执行。这部分的问题需要大改才能解决。一些想法慢慢形成API,比如可以利用prctl()来设置task的分组。

Pillai最后问道,这项工作是否应该合并到主线内核中。支持者提供了不少证据。从一些生产环境测试来看,core scheduling比禁用SMT测出了更好的性能表现。它是由一个内核配置选项控制的,并且就算打开配置了,它的功能也是默认关闭的。当禁用core scheduling时,这些代码对性能没有任何影响。可是从反对者来看,它仍然只是对问题的一个部分缓解方案,并且有一些公平性问题,还需要进行不少代码清理,而且它仍然缺乏一个被广泛接受的API。

IRQ leak mitigation and accounting

随后,Joel Fernandes接过来谈了剩下的Spectre缓解问题之一:interrupt leak。谷歌(他所服务的公司)希望在Chrome OS上使用core scheduling,因为有一些实实在在的好处。在Chromebook上进行的摄像头测试中,core scheduling大大降低了按键延迟,同时摄像头的帧率提高了3%。但Google的开发人员担心,中断(包括硬中断和软中断)会导致不信任的任务与内核的中断处理程序同时运行,使内核面临数据泄露的问题。内核调度目前只在task level有效,对中断处理程序没有控制,因此无法解决这个问题。

提出的解决方案就是刚刚发布的IRQ leak mitigation patch。它会跟踪某个core中的一个CPU何时调用irq_enter()——也就是说,跟踪此CPU开始处理一个irq的时间点。一个处理器core内的第一个irq_enter()调用时,如果发现另一个CPU正在运行一个不受信任的任务,则会触发一个处理器间中断(inter-processor interrupt),命令另一个CPU进入idle状态。调度器本身也必须进行修改,这样,当它从一个任务切换到另一个任务时,它就会检查自己的兄弟CPU此时是否在处理中断。如果是这样,它就会等到兄弟CPU的中断处理完并且和cache flush掉之后再进行调度执行。

关于Chrome OS是具体如何使用core scheduling的,人们提了一些问题。看起来似乎所有的 "system task "都可以一起运行,不在某个具体的core-scheduling group之中。而基于浏览器的任务(Chrome OS中所有的用户任务 几乎都是这一类 )都会被各自放到自己的组中,从而隔离运行。换句话说,系统会对那些互相不信任的task打好标记。Peter Zijlstra表示,这意味着任务会默认为受信任状态,这似乎不够安全,他建议将默认状态改为不信任。

Juri Lelli问到其他scheduling class。具体问题是如果系统中存在realtime FIFO task,会发生什么情况?Zijlstra回答说,将遵循通常的顺序规则。FIFO任务,因为它的优先级最高,所以会被优先选择。同一组中的非实时任务可以在兄弟cpu上运行,尽管这有点不正常,因为这样的任务可能会干扰实时任务的执行。

Dario Faggioli谈了一下SUSE对core scheduling的使用方法:确保对虚拟化的guest os的统计是准确的。一个典型的主机系统中正在运行很多task,其中许多其实都是运行在不同虚拟机中的虚拟CPU(vCPU)上。调度器可以以任何方式将这些vCPU混合在一起,不用管它们对应的是哪个虚拟机。

由于在兄弟CPU上运行的任务都在跟本CPU上的任务争夺底层硬件,因此它们会影响到对方的性能。两台vCPU可能看起来运行时间是相同的,但也许实际上其中的一台可能实际消耗的物理CPU运行时间比另一台多得多。这样就导致了对CPU时间计算可能是不公平的。core scheduling可以通过确保同一台虚拟机的vCPU都在同一一个cpu core上运行,从而确保它们只会进行内部竞争,这样一来的计算能力的统计和计费就更加公平。

如果定义好虚拟机,使其vCPU中的一些vCPU被设置为SMT的兄弟task,这样一来guest os就能够相应地优化其调度策略。不过,只有当虚拟机的定义信息与物理CPU存在对应关系时,这才能真正有用。而core scheduling就可以帮助实现这一点。

Faggioli说,安全用例也适用于虚拟化场景。core scheduling对此有帮助,但还不是一个完整的解决方案。Fernandes讨论的interrupt的情况就是一个仍需要改进的地方。一个完整的解决方案可能还需要用上地址空间隔离(address-space isolation)这样的技术。

Performance

Tim Chen介绍了几种性能测试,分别模拟了几种不同使用场景。一组virtualization test显示,在启用core scheduling的情况下,系统运行的性能为未修改内核的96%。这里有4%的性能损失,但比完全关闭SMT后的87%的性能的结果要好得多。使用sysbench benchmark的一些测试给出了类似的结果。如果关闭SMT,性能几乎降低了一半。最重要的kernel-build benchmark可以看出,core scheduling几乎没有带来任何性能损失,而关闭SMT的话性能降低了8%。

Julien Desfossez介绍了一个MySQL benchmark的结果,证明使用core scheduling的话性能下降了60%。很痛苦,但关闭SMT则要更加糟糕。还有一个基于Linpack的CPU密集型的测试,结果显示,core scheduling的性能略好于mainline kernel,而关闭SMT则会带来90%的性能打击。

Faggioli在一个256CPU的AMD系统上运行了一组测试,这个系统完全不需要使用core scheduling来解决Spectre问题。他主要关注如果在内核配置中enable core scheduling但并不真正打开它的情况下有多少性能损失。他使用Kernbench运行了128个job,此时性能达到mainline kernel的98.6%。在256个job的情况下,能达到99.9%。其他各种测试也得到了类似的数据。

有一些关于公平性测试的讨论——也就是确保所有任务都能获得同等的CPU时间。这个测试结果看其实非常"messy(混乱)",难以解释,但总体印象是,core scheduling产生的公平性表现较差。

Discussion

这个三小时的会议的最后部分是随意讨论。最终决定core scheduling是否能合入mainline的很可能是Zijlstra。他首先发言说,他希望看到一些更好的文档,尤其是能有一些明确的信息说明core scheduling在哪些情况有好处,哪些情况下没有。他认为还有一些事情需要解决,包括公平性以及CPU hotplug的一些问题。这些都是可以解决的,但要想推进这项工作,文档是必须具备的。

Dhaval Giani说,不是所有的问题都可以在调度器中解决。为了对(已知的)Spectre问题有一个完整的解决方案,可能还需要address-space isolation(地址空间隔离)。Zijlstra重复说,需要有文档来说明哪些情况是可以用core scheduling来解决的。这样用户才能决定他们是否需要在自己的应用场景中打开core scheduling。

Aaron Lu说,vruntime的问题仍然存在。如果两个任务有相同的tag,但权重(优先级)不同,那么此core的vruntime就会变成权重较低的那个任务的vruntime,因为这个任务的时间很少。两个进程之间的差异可能会变得很大。Zijlstra回答说,任务之间的vruntime如果差异过大肯定不是一件好事,但进行归一化处理的话则会耗费不少资源。这通常也是不必要的。一旦一个兄弟CPU进入了idle状态,那么就只有一个运行队列才对它有影响,这就是一个同步vruntime值的好时机。Lu表示希望看到一个实现这一点的patch,Zijlstra不耐烦地表示愿意尝试找时间来做个样子出来。

Vincent Guittot提出了负载平衡问题。他说,调度选择结果可能是不公平的。如果一个4个CPU系统上有5个任务,其中一个任务最终会比其他任务运行得更慢。他将在会议的晚些时候再谈这个问题。Zijlstra说,无论如何,在解决负载均衡之前需要先解决vruntime的问题。

会议即将结束的时候,Giani尝试着把要做的事情列出来,包括vruntime、CPU热插拔和公平性问题。Pillai加了一项,需要处理那些untagged task被饿死的问题。Zijlstra问,这个问题是否是针对cpu.tag值为0的任务,也就是完全没有tag的任务。Pillai回答说是的,但是tag为0这种特殊情况意味着这个task不会进入core-scheduling的红黑树。Zijlstra建议也将这些任务添加到红黑树树中管理起来,这样就可以去掉特殊情况,再次正常运转起来。

Fernandes提出了一个相关的问题:那个红黑树包含任务vruntime值,用来参考选择兼容的task(也就是互相信任的task),但这些值不会随着任务的运行而及时更新。Pillai说,这是一个问题,旧的vruntime值会导致调度器选择了错误的任务运行。Zijlstra说,选择一个要执行的任务应该把它从这个树上删除,就像run-queue的红黑树一样。这样做会多耗费一些资源,但这可能是值得的。对于virtualization workload来说,这里的代码应该是相对较小的,因为vCPU并不会那么频繁地reschedule。

会议结束时,Zijlstra说,这项工作看起来已经准备好了继续推进了,剩下的问题都可以在邮件列表中解决。

全文完

LWN文章遵循CC BY-SA 4.0许可协议。

欢迎分享、转载及基于现有协议再创作~

长按下面二维码关注,关注LWN深度文章以及开源社区的各种新近言论~

我来评几句
登录后评论

已发表评论数()

相关站点

热门文章