LWN:让BPF program可以睡眠!

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

Sleepable BPF programs

By Jonathan Corbet
July 7, 2020
原文来自:https://lwn.net/Articles/825415/
主译:DeepL

多年前,当classic BPF功能被添加到内核中时,并不存在BPF program在执行过程中是否会导致阻塞的问题,因为当时它们的功能只限于检查数据包的内容,并决定数据包是否应该被转发,这样的 program是无法阻塞的。它们的功能仅限于检查一个数据包的内容,并决定该数据包是否应该被转发。这样的 program没有什么需要阻塞的地方。这么长时间过去了,BPF已经发生了很多变化,但BPF program不能sleep的假设已经成为了BPF机制中的一个公理条件了。最近,classic BPF被extended BPF这种变种挤压的很厉害,而extended BPF具有更广泛的适用性,现在使得人们重新思考一些基本假设。

BPF program现在可以做许多classic BPF program不可能做到的事情,包括调用内核中的helper函数,访问与内核或用户空间共享的数据结构(maps),以及利用spinlock进行同步。不过,BPF program是个原子操作,这一重大假设并没有改变过。一旦内核跳入一个BPF program,该 program必须在完成前不可以做任何会导致当前线程进入sleep状态的动作。BPF program本身没有办法调用任何形式的阻塞动作,而内核提供给BPF program的帮助函数也必须是原子的。

不过随着BPF功能的不断增加,并且越来越成为一个不可替代的功能,这时,无法进行阻塞操作的这个限制越来越碍事。因此,一段时间以来,人们一直在调查如何对使BPF program可以sleep。这也是Alexei Starovoitov的这组最新patch的由来。

这个补丁增加了一个新的flag,即BPF_F_SLEEPABLE,可以在将BPF program加载到内核中时使用。它标志着 program在执行过程中可能会sleep。这反过来又会告知 BPF verifier这里需要注意了,从而可以让它进行更多的限制条件检查。这些限制条件大多是由于BPF子系统在设计时没有考虑到proram可能会sleep。BPF子系统的某些部分已经更新过,可以正确处理sleep的program了,但还有许多代码没有改好。当然后面会都慢慢改好,但在此之前,BPF子系统中要求原子性的部分,仍然期望不可以运行sleep program。

例如,在内核支持的诸多种类的BPF program中,只有两种program可以允许阻塞:那些从Linux security module运行的program和tracing program(BPF_PROG_TYPE_LSM和BPF_PROG_TYPE_TRACING)。并且,tracing program也只有在挂载到security hook或那些已经支持error injection的函数时,才能睡眠。其他类型的program很可能未来可以支持sleep,但估计永远不会支持所有的类型。一些类型的BPF program本身就是在不允许睡眠的上下文中调用的,比如在网络包处理代码的底层位置、或挂载在原子操作函数上,所以今后是不可能允许这些program支持sleep的。

可以sleep的那些BPF program也仅限可以访问少数几种类型的map。除了hash或array maps之外,其他map都是不允许使用的。hash map还有一个限制:它们必须是预先分配好的,因此在可以sleep的program运行时不需要allocate新的element。目前,map内部的大部分内务管理都是使用read-copy-update(RCU)来完成的,这是一种保护机制,如果BPF program在持有一个map entry的时候被阻塞住的话,那么这种保护机制就会失效。hash map和array map已经增加了使用RCU tasks的能力,因此可以在某些操作中支持sleep代码了。如前面所说,对BPF maps和sleepable program的组合来说,今后可能可以慢慢支持更多种的组合。

因此,sleepable BPF program,在运行时会受到一些限制,而这些限制并不适用于原子操作。通过使用Starovoitov的patch,正好可以做一件atomic BPF program所不能做的事情,这是利用了一个新的helper函数:

long bpf_copy_from_user(void *dest, u32 size, const void *user_ptr);

这个helper函数是对内核的copy_from_user()函数的一个封装,它将数据从用户空间复制到内核中。当进行这样的调用时,用户空间的数据可能不会驻留在内存中,调用者很有可能会面对要从磁盘存储设备中复制多个page进来的情况。这导致BPF program不敢直接读取用户空间数据。现在,sleepable BPF program就可以这样做了。这个功能的一个潜在用途就是允许与安全相关的BPF program跟踪用户空间指针,更好地了解user space在做什么。

在写这篇文章的时候,这个补丁集已经是第五版了,很可能在 5.9 合并窗口期间进入mainline。在那之后,对sleepable BPF program的限制很可能会变成用户抱怨最多的问题,所以看到这些解除限制的patch会很快出现。至少对于某些用例来说,BPF program无法睡眠的问题应该很快就会成为过去了。

全文完

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

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值