/* * Higher prio mvp can preempt lower prio mvp. * * However, the lower prio MVP slice will be more since we expect them to * be the work horses. For example, binders will have higher prio MVP and * they can preempt long running rtg prio tasks but binders loose their * powers with in 3 msec where as rtg prio tasks can run more than that. */ intwalt_get_mvp_task_prio(struct task_struct *p) { if (walt_procfs_low_latency_task(p) || walt_pipeline_low_latency_task(p)) return WALT_LL_PIPE_MVP if (per_task_boost(p) == TASK_BOOST_STRICT_MAX) return WALT_TASK_BOOST_MVP;
if (walt_binder_low_latency_task(p)) return WALT_BINDER_MVP;
if (unlikely(walt_disabled)) return; if (task && ((task_in_related_thread_group(current) && task->group_leader->prio < MAX_RT_PRIO) || (current->group_leader->prio < MAX_RT_PRIO && task_in_related_thread_group(task)))) wts->low_latency |= WALT_LOW_LATENCY_BINDER; else /* * Clear low_latency flag if criterion above is not met, this * will handle usecase where for a binder thread WALT_LOW_LATENCY_BINDER * is set by one task and before WALT clears this flag after timer expiry * some other task tries to use same binder thread. * * The only gets cleared when binder transaction is initiated * and the above condition to set flasg is nto satisfied. */ wts->low_latency &= ~WALT_LOW_LATENCY_BINDER;
}
该 task 需要满足如下条件:
当前 waker 在 related thread group 组中,目前可以直接理解为 top app 线程 && 该 waker 为 rt 线程或者该 waker 的主进程为 rt 并且 wakee 的线程也需要在 rtg 组里面。
/* * This can happen during migration or enq/deq for prio/class change. * it was once MVP but got demoted, it will not be MVP until * it goes to sleep again. */ if (wts->total_exec > walt_cfs_mvp_task_limit(p)) return;
/* * We inserted the task at the appropriate position. Take the * task runtime snapshot. From now onwards we use this point as a * baseline to enforce the slice and demotion. */ if (!wts->total_exec) /* queue after sleep */ { wts->sum_exec_snapshot_for_total = p->se.sum_exec_runtime; wts->sum_exec_snapshot_for_slice = p->se.sum_exec_runtime; } }
total_exec 记录了 task 运行的总时间,如果超过了该 mvp task time limit 就不会加入到 mvp task list 中。
if (list_empty(&wts->mvp_list) || (wts->mvp_list.next == NULL)) goto out;
walt_cfs_account_mvp_runtime(rq, rq->curr); /* * If the current is not MVP means, we have to re-schedule to * see if we can run any other task including MVP tasks. */ if ((wrq->mvp_tasks.next != &wts->mvp_list) && rq->cfs.h_nr_running > 1) resched_curr(rq);
/* * current is not MVP, so preemption decision * is simple. */ if (!curr_is_mvp) { if (p_is_mvp) goto preempt; return; /* CFS decides preemption */ }
/* * current is MVP. update its runtime before deciding the * preemption. */ walt_cfs_account_mvp_runtime(rq, c); resched = (wrq->mvp_tasks.next != &wts_c->mvp_list);
/* * current is no longer eligible to run. It must have been * picked (because of MVP) ahead of other tasks in the CFS * tree, so drive preemption to pick up the next task from * the tree, which also includes picking up the first in * the MVP queue. */ if (resched) goto preempt;
/* current is the first in the queue, so no preemption */ *nopreempt = true; trace_walt_cfs_mvp_wakeup_nopreempt(c, wts_c, walt_cfs_mvp_task_limit(c)); return; preempt: *preempt = true; trace_walt_cfs_mvp_wakeup_preempt(p, wts_p, walt_cfs_mvp_task_limit(p)); }
/* * MVP task runtime update happens here. Three possibilities: * * de-activated: The MVP consumed its runtime. Non MVP can preempt. * slice expired: MVP slice is expired and other MVP can preempt. * slice not expired: This MVP task can continue to run. */ staticvoidwalt_cfs_account_mvp_runtime(struct rq *rq, struct task_struct *curr) { structwalt_rq *wrq = (structwalt_rq *) rq->android_vendor_data1; structwalt_task_struct *wts = (structwalt_task_struct *) curr->android_vendor_data1; u64 slice; unsignedint limit;
lockdep_assert_held(&rq->__lock);
/* * RQ clock update happens in tick path in the scheduler. * Since we drop the lock in the scheduler before calling * into vendor hook, it is possible that update flags are * reset by another rq lock and unlock. Do the update here * if required. */ if (!(rq->clock_update_flags & RQCF_UPDATED)) update_rq_clock(rq);
/* slice is not expired */ if (slice < WALT_MVP_SLICE) return;
wts->sum_exec_snapshot_for_slice = curr->se.sum_exec_runtime; /* * slice is expired, check if we have to deactivate the * MVP task, otherwise requeue the task in the list so * that other MVP tasks gets a chance. */
/* We don't have MVP tasks queued */ if (list_empty(&wrq->mvp_tasks)) return;
/* Return the first task from MVP queue */ wts = list_first_entry(&wrq->mvp_tasks, struct walt_task_struct, mvp_list); mvp = wts_to_ts(wts);
*p = mvp; *se = &mvp->se; *repick = true;
if (simple) { for_each_sched_entity((*se)) { /* * TODO If CFS_BANDWIDTH is enabled, we might pick * from a throttled cfs_rq */ cfs_rq = cfs_rq_of(*se); set_next_entity(cfs_rq, *se); } }
if (!list_empty(&wts->mvp_list) && wts->mvp_list.next) walt_cfs_deactivate_mvp_task(rq, p);
/* * Reset the exec time during sleep so that it starts * from scratch upon next wakeup. total_exec should * be preserved when task is enq/deq while it is on * runqueue. */ if (READ_ONCE(p->__state) != TASK_RUNNING) wts->total_exec = 0; }
六、总结
以上为高通原生的 mvp 调度,我们可以看到如下特点:
覆盖面广,加入 mvp 的粒度大,根据进程的 prio 和 related thread group,而不明确是否和绘制/动画/显示/交互相关的进程,需要被优先调度的进程数量可能会过多
This is copyright.