引言

仲裁进程对硬件资源的访问是linux内核的一个重要功能,需要保证每个进程都能获得公平的硬件资源配额(译者注:此处公平性取决于系统管理员设置的策略)。IO资源必须也进行公平性控制,否则一个IO饥饿型作业会轻易耗尽整个系统IO资源,使其他进程难以获得IO资源。近年来陆续有若干IO资源控制器被引进内核,但效果都不完全令人满意。最近TeJun Heo向block社区提出了一种新的内核IO控制器,可能会改善现在的局面。

实现一个IO控制器有非常多的挑战,比如一些进程需要始终能稳定的获得部分磁盘带宽,防止被饿死,然而到了最近,进程对IO资源的诉求开始从带宽变为IO时延:控制器需要确保一个进程的IO请求在指定时间间隔内完成。IO控制器不仅需要满足前述保证,还需要尽可能最大发挥设备能力,不能浪费太多带宽资源,同时各种存储设备不尽相同,IO控制器需要尽可能的适配各式各样的存储设备。

最早出现的IO控制器是基于带宽的,需要系统管理员设置每个cgroup的IO带宽上限,其最大缺点是即使设备空闲,配置上限的cgroup在其已发送IO超过上限时不能继续发送IO,引起存储资源浪费。最近引入内核的基于IO时延的控制器侧重控制每个cgroup的IO完成时延,但TeJun Heo也指出该IO控制器只保证设置了最低时延目标的cgroupIO资源使用。基于上述问题,TeJun Heo提出了一个新的IO控制器,能更好的在多个cgroup之间分配磁盘带宽。

io.weight

新IO控制器给每个cgroup配置一个权重weight,以下图所示的cgroup层次结构为例: 1.png cgroupA权重为100,cgroupB权重为300,则cgroupB及其子节点允许使用整个设备可用带宽的75%。权重数值本身没有特殊含义,每个cgroup所占设备磁盘带宽的份额取决于其权重占同层次所有cgroup权重之和的占比。

IO控制器如何根据权重分配每个cgroup所能使用的磁盘能力不太容易,这是由于不同类型IO请求实际所占用磁盘处理资源是不一样的,简单统计每cgroup的iops或者带宽是不充分的,因此io.weight控制器提出一个IO请求成本计算模型(cost model),该模型计算每个IO请求预计会占用的设备时间(device time),大体计算逻辑为:首先基于该IO请求是顺序还是随机,会赋值一个初始预计消耗设备时间,然后根据该请求的大小,以page为单位,计算出一个设备时间(IO请求越大,则相应的设备时间越大),然后将初始设备时间和根据请求大小算出的设备时间相加,就得到一个IO请求预计将消耗的总设备时间。控制器已经内置若干默认IO成本计算参数,但系统管理员也可以通过配置root cgroup中的io.weight.cost_model来修改特定设备对应的IO成本计算参数,需要配置的参数有设备最大吞吐量,rand iops, sequential iops。

从实践来看,上述默认的IO请求成本计算模型已经可以工作的很好,但也可能不适用特定的设备,此时系统管理员可以基于BPF去添加新的IO请求成本计算模型。

vtime

控制器为每个设备建立一个虚拟时钟(vtime),且通常该虚拟时钟的步进速率与时间中的秒一致。每个cgroup也拥有自己的vtime时钟, 用于判断该cgroup能否继续发送IO请求。工作机制为:对于每个到来的IO请求,首先基于上文中的IO请求成本计算模型计算其所预计消耗的设备时间,然后将计算出的设备时间转换为基于虚拟时钟vtime维度下的时间片,只有该时间片和该cgroup当前vtime之和超过设备的vtime,才能继续下发请求,同时将计算出的时间片加入cgroup的vtime。如果没有超过设备的vtime,则当前不能继续发送该IO请求,需要等待设备的vtime随着时间的演进变大后,再来继续判断能否下发该cgroup的IO请求。

IO请求成本模型计算出的设备时间需要转为vtime维度下的时间片,计算逻辑为:假设一个cgroup配置成占有a%的磁盘能力,则将其计算出的设备时间乘以 1 / a%,就得到vtime虚拟时钟维度下的时间片

为了避免设备带宽资源浪费,若某个cgroup没有用满其对应的磁盘配额,控制器会临时降低该cgroup的权重,允许其他cgroup用满磁盘带宽,但一旦降低权重的cgroup需要重新发送更多IO,则可以立刻回收其释放的权重,从而允许该cgroup重新发送更多IO。

vtime机制用来确保IO请求的发送速率和设备的实际处理能力相匹配,但是IO请求成本计算模型不是完美的,设备的性能也可能随着时间而变化。如果IO成本模型失效,控制器可能发送过多的IO请求,导致IO请求延时增大,控制器也可能发送过少的IO请求,导致设备带宽资源浪费,两种情况都是需要避免的。

控制器本身会记录IO请求的完成时延,如果完成时延过大,则意味着当前下发了过多IO请求,此时可以通过降低设备vtime的步进速度来降低IO请求的发送速率,如果设备不繁忙,增大设备vtime步进速度,则可以增大设备的IO请求发送速率。控制器尝试自动调整设备vtime的步进速度,但对于某些场景来说不太容易,比如设备有较深的内部队列,多个写操作可能在设备中排队,稍后异步的被设备执行完成,会导致IO延时比较高。如果用户比较关注IO请求时延,可能需要更加激进的降低IO请求发送速率,以降低IO请求时延,但会浪费磁盘带宽。控制器在root cgroup中提供一个控制文件io.weight.qos,设置用户自定义的IO延时目标及设备vtime的步进速度。

TeJun Heo反馈在读请求较多场景下,该控制器工作的很好,但一旦出现读写混合场景,可能需要调节控制器参数来达到最佳性能。Heo同时承诺会提供很多的文档和工具让系统管理员更好的调节该控制器。

Reference

The io.weight I/O-bandwidth controller