> For the complete documentation index, see [llms.txt](https://jizhuoran.gitbook.io/mali-gpu/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://jizhuoran.gitbook.io/mali-gpu/mali-gpu-driver/cong-gpu-de-jiao-du-kan-job-wan-cheng.md).

# 从GPU的角度看job的完成

今天已经是腊月二十八了，开车走在路上就像十年前一样畅通无阻，想必是要过年了。今天在朋友圈看到很多山东人回家指南，里面有不少对山东酒文化的讨论。作为一个非典型山东城市的人，其实我对于这些山东酒文化是没有调查就没有多少发言权的。权且把这些朋友圈的言论当作事实，来比较一下山东其他的确和青岛过年喝酒的特点。

首先最大的区别就是，在青岛是允许喝除了白酒之外的酒的。作为啤酒之都（这个各有看法吧，你当然不能拿工业化生产的拉格和费尽心思生产的艾尔比。个人感觉无论是三四块钱的尿啤还是十几块钱的原浆，青岛啤酒的水平都是说的过去的），即使是在过年，青岛也会允许你喝啤酒，只要你按照一定的比例。但是如果别人直接跟你说啤酒和白酒应该1:4或者1:6，并为此大为辩论的，那么他一般不是一个青岛人。因为在青岛，这个比例是有所变化的，其实这个是非常讲理的，在夏天和在冬天喝啤酒的感觉是极为不一样的。在夏天，太阳落山之后，即使是在室外也不会感觉到炎热，这时吃着烧烤，吹着牛B，人家喝一瓶低度白酒你就要喝六瓶啤酒。而在过年期间，即使是在温暖的室内，啤酒的威力也会在人们的心中被放大，这个时候我们只需要拿五瓶啤酒就能顶得上一斤低度白酒了。这里我们特地强调了低度白酒，因为如果有人喝高度白酒，就说明这真是个喝主儿，一般也就不会计较这些比例了。

其次呢，劝酒在青岛基本是不怎么存在的。喝酒无外乎就两种情况，第一是和亲朋好友们一起喝酒，大家都知道你的酒量，这时候如果你还要别人劝，那就有点不够意思了。第二种情况就是和不太熟的人一起喝酒，这个时候其实劝酒是有极大风险的，因为可能确实对方只有一杯白酒的酒量，如果真的碍于面子被强劝几杯，那么很有可能要出问题。本来是一件开心的事儿，如果一不小心变成了悲剧，那么年也不怎么好过了。

最后呢，就是讲一点全国，乃至全世界都相同的道理吧。以酒会友交天下朋友，用心处事结四海豪杰。

其实，刚才不能是最后，还是得书归正传，讲点干货。

在**kbase\_gpu\_complete\_hw**这个函数里呢，首先会处理和ISSUE有关的问题，然后再真正的处理完成的jobs。

在有TMIX\_8438这个ISSUE的时候，如果一个hard stop紧接着一个soft stop产生，那么这个hard stop的停止代码可能被设置成了STOP，虽然他真是应该是TERMINATED。

如果当前GPU有ISSUE 6787，并且BASE\_JD\_REQ\_SKIP\_CACHE\_END被设置了，那么对于失败的job chain，GPU的cache不会被刷出来。为了防止之后eviction造成存储的破坏，我们需要把缓存里的内容刷出来。如果GPU有ISSUE 10676，也需要进行如上操作，只不过判断条件稍有不同。

在处理完和ISSUE相关的事宜后，我们就把这个atom从JS中dequeue出来，说白了就是把他的状态设置成NOT\_IN\_SLOT。

如果当前的complete\_code是STOPPED，那么我们就需要按需来把ringbuffer中的下一个atom从中dequeue掉。这个atom应该已经被从GPU中的NEXT寄存器中remove掉了，这个过程是在kbase\_gpu\_soft\_hard\_stop\_slot完成的。我们检测是否有next\_atom，这个atom是否跟当前的atom属于同一个kctx并且priority相等。如果上述条件都满足，那么我们就会dequeue掉这个next atom并且把他送还到JS里。

对于剩下的complete\_code，如果不是DONE，那么就执行相同的操作。我们首先先禁止当前atom所对应的context再向device递交任何job。然后我们会把这个context的所有有dependency的job，不论是在哪个job slots里的，都从JM里dequeue出来。当然，已经被submit到GPU的不会被移除，因为但凡他们被提交到GPU上，就证明他们没有dependency的问题。

在处理完以上的情况之后，所有的分支都会被归并到一段代码，就是说之后的代码是所有的进入这个函数的完成了的atom都会执行的。如果这个atom的job\_tail不是0或者katom->jc，那么就说明他没有执行完，我们就需要把katom->jc指向现在的tail，这样之后resume的时候，就能从正确的地方开始。随后，我们把complete\_code储存到katom的结构体里，除非是CANCELLED。

如果complete\_code是STOPPED，那么就要把这个katom还给JS，否则都视为完成并调用kbase\_jm\_complete。而且因为当前的katom完成了，那么有一些cross-slot的dependency可能被解决了，我们就可以试着去提交当前katom的slot上job。

最后，这个job的完成可能会unblock其他atom，我们调用kbase\_backend\_slot\_update去更新slot上job的的状态（之前介绍过这个函数）。
