接上文:浅谈下Load_balance函数情景分析~(上文)
Sched domain的负载统计更新主要在update_sd_lb_stats函数中,其逻辑大致如下:
这一段主要是遍历该sched domain的所有group,对其负载统计进行更新。更新完负载之后,我们选定两个sched group:其一是local group,另外一个是最繁忙的non local group。具体逻辑过程解释如下:A、更新sched group的算力。在basedomain(在手机平台上就是MC domain)上,我们会更新发起均衡所在CPU的算力。注意:这里说的CPU算力指的是该CPU可以用于cfs任务的算力,即需要去掉由于thermal pressure而损失的算力,去掉RT/DL/IRQ消耗的算力。具体请参考update_cpu_capacity函数。在其他non-base domain(在手机平台上就是DIE domain)上,我们需要对本地sched group(包括发起均衡的CPU所在的group)进行算力更新。这个比较简单,就是把child domain(即MC domain)的所有sched group的算力加起来就OK了。更新后的算力保存在sched group中的sgc成员中。另外,更新算力没有必要更新的太频繁,这里做了两个限制:其一是只有local group才进行算力更新,其二是通过时间间隔来减少new idle频繁的更新算力。
B、更新该sched group的负载统计,下面的章节会详细描述。
C、在sched domain的各个group遍历中,我们需要两个group信息,一个是localgroup,另外一个就是non local group中的最忙的那个group。显然,如果是local group,不需要下面的比拼最忙的过程。
D、找到non local group中的最忙的那个group。由于涉及各种grouptype,我们在下一章详述如何判断一个group更忙。E、更新sched domain上各个schedgroup总的负载和算力F、更新root domain的overload和overutil状态。对于顶层的scheddomain,我们需要把各个sched group的overload和overutil状态体现到root domain中。
更多linux内核视频教程文档资料免费领取后台私信【内核】自行获取.
Linux内核源码/内存调优/文件系统/进程管理/设备驱动/网络协议栈-学习视频教程-腾讯课堂
更新sched group负载是在update_sg_lb_stats函数中完成的,我们分段来描述该函数的逻辑,我们先看第一段代码:
A、sched group负载有三种,load、runnableload和util,把所有cpu上load、runnable load和util累计起来就是sched group的负载。除了PELT跟踪的load avg信息,我们还统计了sched group中的cfs任务和总任务数量。
B、只要该sched group上有一个CPU上有1个以上的任务,那么就标记该schedgroup为overload状态。C、只要该sched group上有一个CPU处于overutilized(该cpu利用率已经达到cpu算力的80%),那么就标记该schedgroup为overutilized状态。D、统计该sched group中的idle cpu的个数E、当sched domain包括了算力不同的CPU(例如DIE domain),那么即便cpu上只有一个任务,但是如果该任务是misfit task那么也标记sched group为overload状态,并记录sched group中最大的misfit task load。需要注意的是:idle cpu不需要检测misfit task,此外,对于local group,也没有必要检测misfit task,毕竟同一个group,算力相同,不可能拉取misfit task到本cpu上。
第二段代码如下:
A、更新sched group的总算力和cpu个数。再次强调一下,这里的capacity是指cpu可以用于cfs任务的算力B、判定sched group当前的负载状态C、计算sched group平均负载(仅在groupoverloaded状态才计算)。在overload的情况下,通过sched group平均负载可以识别更繁忙的group。sched group负载状态如下(按照负载从重到轻排列),括号中的数字是该group type的值,数值越大,载荷越重:
判断sched group繁忙程度的函数是group_classify,可以对照代码理解各schedgroup繁忙状态的含义。
在对比sched group繁忙程度的时候,我们主要是对比group_type的值,大的值更忙,小的值比较闲,在相等的时候的判断规则如下:
一旦通过local group和busiestgroup的信息确定sched domain处于不均衡状态,我们就可以调用calculate_imbalance函数来计算通过什么方式(migrate task还是migrate load/util)来恢复sched domain的负载均衡状态,也就是设定均衡上下文的migration_type和imbalance成员,下面我们分段来描述该函数的逻辑,我们先看第一段代码:
A、如果busiest group上有misfit task,那么优先对其进行misfit任务迁移,并且一次迁移一个misfittask。B、如果busiest group是因为cpuaffinity而导致的不均衡,那么通过通过迁移任务来达到平衡,并且一次迁移一个任务。上面的代码主要处理busiest group中的一些特殊情况,后面的代码主要分两段段来根据local group的状态来进行不均衡的计算。我们首先看local group有空闲算力的情况,我们分成两段分析,第一段代码如下:
A、如果local group有一些空闲算力,那么我们还是争取把它利用起来,只要迁移的负载量既不overload local group,也不会让busiest group变得无事可做。B、如果sched domain标记了SD_SHARE_PKG_RESOURCES(MC domain),那么其在task placement的时候会尽量选择idlecpu。这里load balance路径需要和placement对齐:不使用空闲capacity而是使用nr_running来进行均衡。如果没有设置SD_SHARE_PKG_RESOURCES那么考虑使用migrate_util方式来达到均衡。C、如果local group有一些空闲算力,busiestgroup又处于繁忙状态(大于full busy),同时满足未设定SD_SHARE_PKG_RESOURCES(对于手机场景就是DIE domain,MC domain需要使用nr_running而不是util来进行均衡)。这种状态下,我们采用util来指导均衡,具体迁的utility设定为local group当前空闲的算力。D、有些场景下,local group的util大于其groupcapacity,根据步骤C计算的imbalance等于0(意味着不需要均衡)。然而,在这种场景下,如果local cpu处于idle状态,那么需要从busiest group迁移过来一个runnable task,从而确保了性能。Local gorup有空闲算力的第二段代码如下:
代码逻辑走到这里,说明busiest group也有空闲算力(local group也一样),这时候主要考虑的是任务的迁移,让sched domain中的idle cpu尽量的均衡。还有一种可能就是busiest group的状态是繁忙(大于fully busy),但是是在MC domain中进行均衡,这时候均衡的逻辑也是一样的看idle cpu。
A、对于base domain(group只有一个CPU),我们还是希望任务散布在各个sched group(cpu)上。因此,这时候需要从busiest group中迁移任务,保证迁移之后,local group和busiest group中的任务数量相等。
B、如果group中有多个CPU,那么我们的目标就是让localgroup和busiest group中的idle cpu的数量相等
上面处理了local group有空闲算力的情况,下面的代码处理local group处于非group_has_spare状态的情况,代码如下:
如果local group没有空闲算力,但是也没有overloaded,可以从busiest group迁移一些负载过来,但是这也许会导致local group进入overloaded状态。因此这里使用了avg_load来进一步确认是否进行负载迁移。具体的判断方法是local group的平均负载是否大于sched domain的平均负载。如果local group和busiest group都overloaded并且走入calculate imbalance,那么早就确认了busiest group的平均负载大于local group的平均负载。当local group或者busiest group都进入(或者即将进入)overloaded状态,这时候采用迁移负载的方式进行均衡,具体代码如下:
具体迁移的负载量是综合考虑localgroup、busiest group和sched domain的平均负载情况,确保迁移负载之和,local group、busiest group向sched domain的平均负载靠拢。
find_busiest_queue函数用来寻找busiestgroup中最繁忙的cpu。代码逻辑比较简单,和buiest group在上面判断的migrate type相关,不同的type使用不同的方法来寻找busiest cpu:
一旦找到最忙的CPU,那么任务迁移的目标和源头都确定了,后续就可以通过detach tasks和attach tasks进行任务迁移了。
至此,我们已经确定了从src cpu runqueue(即最繁忙的group中最繁忙的cpu)搬移若干load/util/task到dest cpu runqueue。不过无论是load还是util,最后还是要转成任务。detach_tasks就是确定具体从src rq迁移哪些任务,并把这些任务挂入lb_env->tasks链表中。detach_tasks函数第一段的代码逻辑如下:
A、src rq的cfs_tasks链表就是该队列上的全部cfs任务,detach_tasks函数的主要逻辑就是遍历这个cfs_tasks链表,找到最适合迁移到目标cpurq的任务,并挂入lb_env->tasks链表B、在idle balance的时候,没有必要把src上的唯一的task拉取到本cpu上,否则的话任务可能会在两个CPU上来回拉扯。C、从cfs_tasks链表队尾摘下一个任务。这个链表的头部是最近访问的任务。从尾部摘任务可以保证任务是cache cold的。D、当把dest rq上的任务都遍历过之后,或者当达到循环上限(sysctl_sched_nr_migrate)的时候退出循环。E、当dest rq上的任务数比较多的时候,并且需要迁移大量的任务才能完成均衡,为了减少关中断的区间,迁移需要分段进行(每sched_nr_migrate_break暂停一下),把大的临界区分成几个小的临界区,确保系统的延迟性能。F、如果该任务不适合迁移,那么将其移到cfs_tasks链表头部。上面对从cfs_tasks链表摘下的任务进行基本的判断,具体迁移该任务是否能达到均衡是由detach_tasks函数第二段代码逻辑完成的,具体如下:
A、计算该任务的负载。这里设定任务的最小负载是1。
B、LB_MIN特性限制迁移小任务,如果LB_MIN等于true,那么task load小于16的任务将不参与负载均衡。目前LB_MIN系统缺省设置为false。
C、不要迁移过多的load,确保迁移的load不大于env->imbalance。随着迁移错误次增加,这个限制可以适当放宽一些。
D、对于migrate_util类型的迁移,我们通过任务的util和env->imbalance来判断是否迁移了足够的utility。需要注意的是这里使用了任务的estimation utilization。
E、migrate_task类型的迁移不关注load或者utility,只关心迁移的任务数
F、找到misfit task即完成迁移
detach_tasks函数最后一段的代码逻辑如下:
A、程序执行至此,说明任务P需要被迁移(不能迁移的都跳转到next符号了),因此需要从src rq上摘下,挂入env->tasks链表B、New idlebalance是调度延迟的主要来源,所有对于这种balance,我们一次只迁移一个任务C、如果完成迁移,那么就退出遍历src rq的cfs task链表。attach_tasks主要的逻辑就是遍历均衡上下文的tasks链表,摘下一个个的任务,挂入目标cpu的队列。
can_migrate_task函数用来判断一个任务是否可以迁移至目标CPU,具体代码逻辑如下:
A、如果任务p所在的task group在src或者dest cpu上被限流了,那么不能迁移该任务,否者限流的逻辑会有问题B、Percpu的内核线程不能迁移C、任务由于affinity的原因不能在destcpu上运行,因此这里设置上LBF_SOME_PINNED标志,表示至少有一个任务由于affinity无法迁移D、下面的逻辑(E段)会设备备选目标CPU,如果是已经设定好了备选CPU那么直接返回,如果是newidle balance那么也不需要备选CPU,它的主要目标就是迁移一个任务到本idle的cpu。E、设定备选CPU,以便后续第二轮的均衡可以把任务迁移到备选CPU上can_migrate_task函数第二段代码逻辑如下
A、至少有一个任务是可以运行在dest cpu上(从affinity角度),因此清除allpinned标记B、正处于运行状态的任务不参与迁移,迁移runningtask是后续active migration的逻辑。C、判断该任务是否是cache-hot的,这主要从近期在srccpu上的执行时间点来判断,如果上次任务在src cpu上开始执行的时间比较久远(sysctl_sched_migration_cost是门限,目前设定0.5ms),那么其在cache中的内容大概率是被刷掉了,可以认为是cache-cold的。此外如果任务p是src cpu上的next buddy或者last buddy,那么任务是cache hot的。D、一般而言,我们只迁移cache cold的任务。但是如果进行了太多轮的尝试仍然未能让负载达到均衡,那么cache hot的任务也一样迁移。
| 留言与评论(共有 0 条评论) “” |