| // SPDX-License-Identifier: GPL-2.0-only | |
| /* fair.c | |
| * | |
| * Android Vendor Hook Support | |
| * | |
| * Copyright 2020 Google LLC | |
| */ | |
| #include <linux/cpuidle.h> | |
| #include <linux/sched/clock.h> | |
| #include <linux/sched/cputime.h> | |
| #include <kernel/sched/autogroup.h> | |
| #include <kernel/sched/sched.h> | |
| #include <kernel/sched/pelt.h> | |
| #include <linux/moduleparam.h> | |
| #include <trace/events/power.h> | |
| #include <trace/hooks/systrace.h> | |
| #include <uapi/linux/sched/types.h> | |
| #include "sched_priv.h" | |
| #include "sched_events.h" | |
| #if IS_ENABLED(CONFIG_PIXEL_EM) | |
| #include "../../include/pixel_em.h" | |
| struct pixel_em_profile **vendor_sched_pixel_em_profile; | |
| struct pixel_idle_em *vendor_sched_pixel_idle_em; | |
| EXPORT_SYMBOL_GPL(vendor_sched_pixel_em_profile); | |
| EXPORT_SYMBOL_GPL(vendor_sched_pixel_idle_em); | |
| raw_spinlock_t vendor_sched_pixel_em_lock; | |
| EXPORT_SYMBOL_GPL(vendor_sched_pixel_em_lock); | |
| #endif | |
| extern inline void update_misfit_status(struct task_struct *p, struct rq *rq); | |
| #if IS_ENABLED(CONFIG_USE_VENDOR_GROUP_UTIL) | |
| extern int ___update_load_sum(u64 now, struct sched_avg *sa, | |
| unsigned long load, unsigned long runnable, int running); | |
| extern void ___update_load_avg(struct sched_avg *sa, unsigned long load); | |
| static struct vendor_util_group_property ug[UG_MAX]; | |
| struct vendor_cfs_util vendor_cfs_util[UG_MAX][CONFIG_VH_SCHED_MAX_CPU_NR]; | |
| #endif | |
| extern unsigned int vendor_sched_util_post_init_scale; | |
| extern bool vendor_sched_npi_packing; | |
| extern bool vendor_sched_boost_adpf_prio; | |
| extern unsigned int sysctl_sched_min_granularity; | |
| extern unsigned int sysctl_sched_wakeup_granularity; | |
| extern unsigned int sysctl_sched_idle_min_granularity; | |
| extern struct cpumask skip_prefer_prev_mask; | |
| static unsigned int early_boot_boost_uclamp_min = 563; | |
| module_param(early_boot_boost_uclamp_min, uint, 0644); | |
| static struct mutex thermal_cap_mutex; | |
| unsigned int sched_auto_fits_capacity[CONFIG_VH_SCHED_MAX_CPU_NR]; | |
| unsigned int sched_capacity_margin[CONFIG_VH_SCHED_MAX_CPU_NR] = | |
| { [0 ... CONFIG_VH_SCHED_MAX_CPU_NR - 1] = DEF_UTIL_THRESHOLD }; | |
| unsigned int sched_dvfs_headroom[CONFIG_VH_SCHED_MAX_CPU_NR] = | |
| { [0 ... CONFIG_VH_SCHED_MAX_CPU_NR - 1] = DEF_UTIL_THRESHOLD }; | |
| unsigned int sched_auto_uclamp_max[CONFIG_VH_SCHED_MAX_CPU_NR] = | |
| { [0 ... CONFIG_VH_SCHED_MAX_CPU_NR - 1] = SCHED_CAPACITY_SCALE }; | |
| unsigned int thermal_cap_margin[CONFIG_VH_SCHED_MAX_CPU_NR] = | |
| { [0 ... CONFIG_VH_SCHED_MAX_CPU_NR - 1] = DEF_THERMAL_CAP_MARGIN }; | |
| struct thermal_cap thermal_cap[CONFIG_VH_SCHED_MAX_CPU_NR] = { | |
| [0 ... CONFIG_VH_SCHED_MAX_CPU_NR - 1].uclamp_max = SCHED_CAPACITY_SCALE, | |
| [0 ... CONFIG_VH_SCHED_MAX_CPU_NR - 1].freq = UINT_MAX}; | |
| unsigned int __read_mostly sched_per_task_iowait_boost_max_value = 0; | |
| struct vendor_group_property vg[VG_MAX]; | |
| extern struct vendor_group_list vendor_group_list[VG_MAX]; | |
| bool wait_for_init = true; | |
| bool in_suspend_resume; | |
| unsigned int vh_sched_max_load_balance_interval; | |
| unsigned int vh_sched_min_granularity_ns; | |
| unsigned int vh_sched_wakeup_granularity_ns; | |
| unsigned int vh_sched_latency_ns; | |
| unsigned long schedutil_cpu_util_pixel_mod(int cpu, unsigned long util_cfs, | |
| unsigned long max, enum cpu_util_type type, | |
| struct task_struct *p); | |
| unsigned int map_scaling_freq(int cpu, unsigned int freq); | |
| extern void rvh_uclamp_eff_get_pixel_mod(void *data, struct task_struct *p, enum uclamp_id clamp_id, | |
| struct uclamp_se *uclamp_max, struct uclamp_se *uclamp_eff, | |
| int *ret); | |
| /*****************************************************************************/ | |
| /* Upstream Code Section */ | |
| /*****************************************************************************/ | |
| /* | |
| * This part of code is copied from Android common GKI kernel and unmodified. | |
| * Any change for these functions in upstream GKI would require extensive review | |
| * to make proper adjustment in vendor hook. | |
| */ | |
| #define for_each_clamp_id(clamp_id) \ | |
| for ((clamp_id) = 0; (clamp_id) < UCLAMP_CNT; (clamp_id)++) | |
| static void attach_task(struct rq *rq, struct task_struct *p) | |
| { | |
| lockdep_assert_held(&rq->__lock); | |
| BUG_ON(task_rq(p) != rq); | |
| activate_task(rq, p, ENQUEUE_NOCLOCK); | |
| check_preempt_curr(rq, p, 0); | |
| } | |
| /* | |
| * attach_one_task() -- attaches the task returned from detach_one_task() to | |
| * its new rq. | |
| */ | |
| static void attach_one_task(struct rq *rq, struct task_struct *p) | |
| { | |
| struct rq_flags rf; | |
| rq_lock(rq, &rf); | |
| update_rq_clock(rq); | |
| attach_task(rq, p); | |
| rq_unlock(rq, &rf); | |
| } | |
| static inline u64 cfs_rq_last_update_time(struct cfs_rq *cfs_rq) | |
| { | |
| return u64_u32_load_copy(cfs_rq->avg.last_update_time, | |
| cfs_rq->last_update_time_copy); | |
| } | |
| #if IS_ENABLED(CONFIG_UCLAMP_TASK) | |
| static inline unsigned long uclamp_task_util(struct task_struct *p, | |
| unsigned long uclamp_min, | |
| unsigned long uclamp_max) | |
| { | |
| return clamp(task_util_est(p), uclamp_min, uclamp_max); | |
| } | |
| #else | |
| static inline unsigned long uclamp_task_util(struct task_struct *p, | |
| unsigned long uclamp_min, | |
| unsigned long uclamp_max) | |
| { | |
| return task_util_est(p); | |
| } | |
| #endif | |
| static unsigned long capacity_curr_of(int cpu) | |
| { | |
| unsigned long max_cap = cpu_rq(cpu)->cpu_capacity_orig; | |
| return cap_scale(max_cap, per_cpu(arch_freq_scale, cpu)); | |
| } | |
| /* Runqueue only has SCHED_IDLE tasks enqueued */ | |
| static int sched_idle_rq(struct rq *rq) | |
| { | |
| return unlikely(rq->nr_running == rq->cfs.idle_h_nr_running && | |
| rq->nr_running); | |
| } | |
| #if IS_ENABLED(CONFIG_SMP) | |
| int sched_cpu_idle(int cpu) | |
| { | |
| return sched_idle_rq(cpu_rq(cpu)); | |
| } | |
| #endif | |
| static inline bool within_margin(int value, int margin) | |
| { | |
| return ((unsigned int)(value + margin - 1) < (2 * margin - 1)); | |
| } | |
| static inline void update_load_add(struct load_weight *lw, unsigned long inc) | |
| { | |
| lw->weight += inc; | |
| lw->inv_weight = 0; | |
| } | |
| #define WMULT_CONST (~0U) | |
| #define WMULT_SHIFT 32 | |
| static void __update_inv_weight(struct load_weight *lw) | |
| { | |
| unsigned long w; | |
| if (likely(lw->inv_weight)) | |
| return; | |
| w = scale_load_down(lw->weight); | |
| if (BITS_PER_LONG > 32 && unlikely(w >= WMULT_CONST)) | |
| lw->inv_weight = 1; | |
| else if (unlikely(!w)) | |
| lw->inv_weight = WMULT_CONST; | |
| else | |
| lw->inv_weight = WMULT_CONST / w; | |
| } | |
| static u64 __calc_delta(u64 delta_exec, unsigned long weight, struct load_weight *lw) | |
| { | |
| u64 fact = scale_load_down(weight); | |
| u32 fact_hi = (u32)(fact >> 32); | |
| int shift = WMULT_SHIFT; | |
| int fs; | |
| __update_inv_weight(lw); | |
| if (unlikely(fact_hi)) { | |
| fs = fls(fact_hi); | |
| shift -= fs; | |
| fact >>= fs; | |
| } | |
| fact = mul_u32_u32(fact, lw->inv_weight); | |
| fact_hi = (u32)(fact >> 32); | |
| if (fact_hi) { | |
| fs = fls(fact_hi); | |
| shift -= fs; | |
| fact >>= fs; | |
| } | |
| return mul_u64_u32_shr(delta_exec, fact, shift); | |
| } | |
| static u64 __sched_period(unsigned long nr_running); | |
| #define for_each_sched_entity(se) \ | |
| for (; se; se = se->parent) | |
| #if IS_ENABLED(CONFIG_FAIR_GROUP_SCHED) | |
| static int cfs_rq_is_idle(struct cfs_rq *cfs_rq) | |
| { | |
| return cfs_rq->idle > 0; | |
| } | |
| static int se_is_idle(struct sched_entity *se) | |
| { | |
| if (entity_is_task(se)) | |
| return task_has_idle_policy(task_of(se)); | |
| return cfs_rq_is_idle(group_cfs_rq(se)); | |
| } | |
| #else | |
| static int cfs_rq_is_idle(struct cfs_rq *cfs_rq) | |
| { | |
| return 0; | |
| } | |
| static int se_is_idle(struct sched_entity *se) | |
| { | |
| return 0; | |
| } | |
| #endif | |
| static bool sched_idle_cfs_rq(struct cfs_rq *cfs_rq) | |
| { | |
| return cfs_rq->nr_running && | |
| cfs_rq->nr_running == cfs_rq->idle_nr_running; | |
| } | |
| u64 sched_slice(struct cfs_rq *cfs_rq, struct sched_entity *se) | |
| { | |
| unsigned int nr_running = cfs_rq->nr_running; | |
| struct sched_entity *init_se = se; | |
| unsigned int min_gran; | |
| u64 slice; | |
| if (sched_feat(ALT_PERIOD)) | |
| nr_running = rq_of(cfs_rq)->cfs.h_nr_running; | |
| slice = __sched_period(nr_running + !se->on_rq); | |
| for_each_sched_entity(se) { | |
| struct load_weight *load; | |
| struct load_weight lw; | |
| struct cfs_rq *qcfs_rq; | |
| qcfs_rq = cfs_rq_of(se); | |
| load = &qcfs_rq->load; | |
| if (unlikely(!se->on_rq)) { | |
| lw = qcfs_rq->load; | |
| update_load_add(&lw, se->load.weight); | |
| load = &lw; | |
| } | |
| slice = __calc_delta(slice, se->load.weight, load); | |
| } | |
| if (sched_feat(BASE_SLICE)) { | |
| if (se_is_idle(init_se) && !sched_idle_cfs_rq(cfs_rq)) | |
| min_gran = sysctl_sched_idle_min_granularity; | |
| else | |
| min_gran = sysctl_sched_min_granularity; | |
| slice = max_t(u64, slice, min_gran); | |
| } | |
| return slice; | |
| } | |
| void set_next_buddy(struct sched_entity *se) | |
| { | |
| for_each_sched_entity(se) { | |
| if (SCHED_WARN_ON(!se->on_rq)) | |
| return; | |
| if (se_is_idle(se)) | |
| return; | |
| cfs_rq_of(se)->next = se; | |
| } | |
| } | |
| static inline struct task_group *css_tg(struct cgroup_subsys_state *css) | |
| { | |
| return css ? container_of(css, struct task_group, css) : NULL; | |
| } | |
| static inline unsigned int | |
| uclamp_idle_value(struct rq *rq, enum uclamp_id clamp_id, | |
| unsigned int clamp_value) | |
| { | |
| /* | |
| * Avoid blocked utilization pushing up the frequency when we go | |
| * idle (which drops the max-clamp) by retaining the last known | |
| * max-clamp. | |
| */ | |
| if (clamp_id == UCLAMP_MAX) { | |
| rq->uclamp_flags |= UCLAMP_FLAG_IDLE; | |
| return clamp_value; | |
| } | |
| return uclamp_none(UCLAMP_MIN); | |
| } | |
| static inline void uclamp_idle_reset(struct rq *rq, enum uclamp_id clamp_id, | |
| unsigned int clamp_value) | |
| { | |
| /* Reset max-clamp retention only on idle exit */ | |
| if (!(rq->uclamp_flags & UCLAMP_FLAG_IDLE)) | |
| return; | |
| WRITE_ONCE(rq->uclamp[clamp_id].value, clamp_value); | |
| } | |
| /* | |
| * The effective clamp bucket index of a task depends on, by increasing | |
| * priority: | |
| * - the task specific clamp value, when explicitly requested from userspace | |
| * - the task group effective clamp value, for tasks not either in the root | |
| * group or in an autogroup | |
| * - the system default clamp value, defined by the sysadmin | |
| */ | |
| static inline struct uclamp_se | |
| uclamp_eff_get(struct task_struct *p, enum uclamp_id clamp_id) | |
| { | |
| struct uclamp_se uc_max = uclamp_default[clamp_id]; | |
| struct uclamp_se uc_eff; | |
| int ret; | |
| // This function will always return uc_eff | |
| rvh_uclamp_eff_get_pixel_mod(NULL, p, clamp_id, &uc_max, &uc_eff, &ret); | |
| return uc_eff; | |
| } | |
| static inline | |
| unsigned int uclamp_rq_max_value(struct rq *rq, enum uclamp_id clamp_id, | |
| unsigned int clamp_value) | |
| { | |
| struct uclamp_bucket *bucket = rq->uclamp[clamp_id].bucket; | |
| int bucket_id = UCLAMP_BUCKETS - 1; | |
| /* | |
| * Since both min and max clamps are max aggregated, find the | |
| * top most bucket with tasks in. | |
| */ | |
| for ( ; bucket_id >= 0; bucket_id--) { | |
| if (!bucket[bucket_id].tasks) | |
| continue; | |
| return bucket[bucket_id].value; | |
| } | |
| /* No tasks -- default clamp values */ | |
| return uclamp_idle_value(rq, clamp_id, clamp_value); | |
| } | |
| /* | |
| * When a task is enqueued on a rq, the clamp bucket currently defined by the | |
| * task's uclamp::bucket_id is refcounted on that rq. This also immediately | |
| * updates the rq's clamp value if required. | |
| * | |
| * Tasks can have a task-specific value requested from user-space, track | |
| * within each bucket the maximum value for tasks refcounted in it. | |
| * This "local max aggregation" allows to track the exact "requested" value | |
| * for each bucket when all its RUNNABLE tasks require the same clamp. | |
| */ | |
| inline void uclamp_rq_inc_id(struct rq *rq, struct task_struct *p, | |
| enum uclamp_id clamp_id) | |
| { | |
| struct uclamp_rq *uc_rq = &rq->uclamp[clamp_id]; | |
| struct uclamp_se *uc_se = &p->uclamp[clamp_id]; | |
| struct uclamp_bucket *bucket; | |
| lockdep_assert_rq_held(rq); | |
| if(SCHED_WARN_ON(!uclamp_is_used())) | |
| return; | |
| /* Update task effective clamp */ | |
| p->uclamp[clamp_id] = uclamp_eff_get(p, clamp_id); | |
| bucket = &uc_rq->bucket[uc_se->bucket_id]; | |
| bucket->tasks++; | |
| uc_se->active = true; | |
| uclamp_idle_reset(rq, clamp_id, uc_se->value); | |
| /* | |
| * Local max aggregation: rq buckets always track the max | |
| * "requested" clamp value of its RUNNABLE tasks. | |
| */ | |
| if (bucket->tasks == 1 || uc_se->value > bucket->value) | |
| bucket->value = uc_se->value; | |
| if (uc_se->value > READ_ONCE(uc_rq->value)) | |
| WRITE_ONCE(uc_rq->value, uc_se->value); | |
| } | |
| /* | |
| * When a task is dequeued from a rq, the clamp bucket refcounted by the task | |
| * is released. If this is the last task reference counting the rq's max | |
| * active clamp value, then the rq's clamp value is updated. | |
| * | |
| * Both refcounted tasks and rq's cached clamp values are expected to be | |
| * always valid. If it's detected they are not, as defensive programming, | |
| * enforce the expected state and warn. | |
| */ | |
| inline void uclamp_rq_dec_id(struct rq *rq, struct task_struct *p, | |
| enum uclamp_id clamp_id) | |
| { | |
| struct uclamp_rq *uc_rq = &rq->uclamp[clamp_id]; | |
| struct uclamp_se *uc_se = &p->uclamp[clamp_id]; | |
| struct uclamp_bucket *bucket; | |
| unsigned int bkt_clamp; | |
| unsigned int rq_clamp; | |
| lockdep_assert_rq_held(rq); | |
| if(SCHED_WARN_ON(!uclamp_is_used())) | |
| return; | |
| /* | |
| * If sched_uclamp_used was enabled after task @p was enqueued, | |
| * we could end up with unbalanced call to uclamp_rq_dec_id(). | |
| * | |
| * In this case the uc_se->active flag should be false since no uclamp | |
| * accounting was performed at enqueue time and we can just return | |
| * here. | |
| * | |
| * Need to be careful of the following enqeueue/dequeue ordering | |
| * problem too | |
| * | |
| * enqueue(taskA) | |
| * // sched_uclamp_used gets enabled | |
| * enqueue(taskB) | |
| * dequeue(taskA) | |
| * // Must not decrement bukcet->tasks here | |
| * dequeue(taskB) | |
| * | |
| * where we could end up with stale data in uc_se and | |
| * bucket[uc_se->bucket_id]. | |
| * | |
| * The following check here eliminates the possibility of such race. | |
| */ | |
| if (unlikely(!uc_se->active)) | |
| return; | |
| bucket = &uc_rq->bucket[uc_se->bucket_id]; | |
| SCHED_WARN_ON(!bucket->tasks); | |
| if (likely(bucket->tasks)) | |
| bucket->tasks--; | |
| uc_se->active = false; | |
| /* | |
| * Keep "local max aggregation" simple and accept to (possibly) | |
| * overboost some RUNNABLE tasks in the same bucket. | |
| * The rq clamp bucket value is reset to its base value whenever | |
| * there are no more RUNNABLE tasks refcounting it. | |
| */ | |
| if (likely(bucket->tasks)) | |
| return; | |
| rq_clamp = READ_ONCE(uc_rq->value); | |
| /* | |
| * Defensive programming: this should never happen. If it happens, | |
| * e.g. due to future modification, warn and fixup the expected value. | |
| */ | |
| SCHED_WARN_ON(bucket->value > rq_clamp); | |
| if (bucket->value >= rq_clamp) { | |
| bkt_clamp = uclamp_rq_max_value(rq, clamp_id, uc_se->value); | |
| WRITE_ONCE(uc_rq->value, bkt_clamp); | |
| } | |
| } | |
| static void | |
| prio_changed_fair(struct rq *rq, struct task_struct *p, int oldprio) | |
| { | |
| if (!task_on_rq_queued(p)) | |
| return; | |
| if (rq->cfs.nr_running == 1) | |
| return; | |
| /* | |
| * Reschedule if we are currently running on this runqueue and | |
| * our priority decreased, or if we are not currently running on | |
| * this runqueue and our priority is higher than the current's | |
| */ | |
| if (task_current(rq, p)) { | |
| if (p->prio > oldprio) | |
| resched_curr(rq); | |
| } else | |
| check_preempt_curr(rq, p, 0); | |
| } | |
| static inline unsigned long cfs_rq_load_avg(struct cfs_rq *cfs_rq) | |
| { | |
| return cfs_rq->avg.load_avg; | |
| } | |
| static inline unsigned long cpu_load(struct rq *rq) | |
| { | |
| return cfs_rq_load_avg(&rq->cfs); | |
| } | |
| /*****************************************************************************/ | |
| /* New Code Section */ | |
| /*****************************************************************************/ | |
| // This part of code is new for this kernel, which are mostly helper functions. | |
| static inline bool get_task_spreading(struct task_struct *p) | |
| { | |
| return vg[get_vendor_group(p)].task_spreading; | |
| } | |
| static inline bool get_auto_prefer_fit(struct task_struct *p) | |
| { | |
| return vg[get_vendor_group(p)].auto_prefer_fit && p->prio <= THREAD_PRIORITY_TOP_APP_BOOST; | |
| } | |
| #if IS_ENABLED(CONFIG_USE_GROUP_THROTTLE) | |
| static inline unsigned int get_task_group_throttle(struct task_struct *p) | |
| { | |
| #if IS_ENABLED(CONFIG_USE_VENDOR_GROUP_UTIL) | |
| return ug[get_utilization_group(p, get_vendor_group(p))].group_throttle; | |
| #else | |
| return vg[get_vendor_group(p)].group_throttle; | |
| #endif | |
| } | |
| #if !IS_ENABLED(CONFIG_USE_VENDOR_GROUP_UTIL) | |
| static inline unsigned int get_group_throttle(struct task_group *tg) | |
| { | |
| return vg[get_vendor_task_group_struct(tg)->group].group_throttle; | |
| } | |
| #endif | |
| #endif | |
| /* | |
| * If a task is in prefer_idle group, check if it could run on the cpu based on its prio and the | |
| * prefer_idle cpumask defined. | |
| */ | |
| static inline bool check_preferred_idle_mask(struct task_struct *p, int cpu) | |
| { | |
| int vendor_group = get_vendor_group(p); | |
| if (!get_prefer_idle(p)) | |
| return true; | |
| if (p->prio <= THREAD_PRIORITY_TOP_APP_BOOST) { | |
| return cpumask_test_cpu(cpu, &vg[vendor_group].preferred_idle_mask_high); | |
| } else if (p->prio <= DEFAULT_PRIO) { | |
| return cpumask_test_cpu(cpu, &vg[vendor_group].preferred_idle_mask_mid); | |
| } else { | |
| return cpumask_test_cpu(cpu, &vg[vendor_group].preferred_idle_mask_low); | |
| } | |
| } | |
| static inline const cpumask_t *get_preferred_idle_mask(struct task_struct *p) | |
| { | |
| int vendor_group = get_vendor_group(p); | |
| if (p->wake_q_count || get_adpf(p, false)) | |
| return cpu_possible_mask; | |
| if (p->prio <= THREAD_PRIORITY_TOP_APP_BOOST) { | |
| return &vg[vendor_group].preferred_idle_mask_high; | |
| } else if (p->prio <= DEFAULT_PRIO) { | |
| return &vg[vendor_group].preferred_idle_mask_mid; | |
| } else { | |
| return &vg[vendor_group].preferred_idle_mask_low; | |
| } | |
| } | |
| static inline cpumask_t *get_group_cfs_skip_mask(struct task_struct *p) | |
| { | |
| return &vg[get_vendor_group(p)].group_cfs_skip_mask; | |
| } | |
| void init_vendor_group_data(void) | |
| { | |
| int i; | |
| #if IS_ENABLED(CONFIG_USE_VENDOR_GROUP_UTIL) | |
| int j; | |
| struct rq *rq; | |
| int group; | |
| struct rq_flags rf; | |
| struct task_struct *p; | |
| u64 last_update_time; | |
| #endif | |
| for (i = 0; i < VG_MAX; i++) { | |
| INIT_LIST_HEAD(&vendor_group_list[i].list); | |
| raw_spin_lock_init(&vendor_group_list[i].lock); | |
| vendor_group_list[i].cur_iterator = NULL; | |
| mutex_init(&vendor_group_list[i].iter_mutex); | |
| } | |
| #if IS_ENABLED(CONFIG_USE_VENDOR_GROUP_UTIL) | |
| for (j = 0; j < pixel_cpu_num; j++) { | |
| rq = cpu_rq(j); | |
| for (i = 0; i < UG_MAX; i++) { | |
| raw_spin_lock_init(&vendor_cfs_util[i][j].lock); | |
| vendor_cfs_util[i][j].avg.util_avg = 0; | |
| vendor_cfs_util[i][j].avg.util_sum = 0; | |
| vendor_cfs_util[i][j].util_removed = 0; | |
| vendor_cfs_util[i][j].util_est = 0; | |
| } | |
| rq_lock_irqsave(rq, &rf); | |
| update_rq_clock(rq); | |
| last_update_time = rq_clock_pelt(rq); | |
| for (i = 0; i < UG_MAX; i++) | |
| vendor_cfs_util[i][j].avg.last_update_time = last_update_time; | |
| list_for_each_entry(p, &rq->cfs_tasks, se.group_node) { | |
| group = get_utilization_group(p, get_vendor_group(p)); | |
| vendor_cfs_util[group][j].avg.util_avg += READ_ONCE(p->se.avg.util_avg); | |
| vendor_cfs_util[group][j].avg.util_sum += READ_ONCE(p->se.avg.util_sum); | |
| vendor_cfs_util[group][j].util_est += _task_util_est(p); | |
| } | |
| rq_unlock_irqrestore(rq, &rf); | |
| } | |
| #endif | |
| } | |
| void init_pixel_em(void) | |
| { | |
| #if IS_ENABLED(CONFIG_PIXEL_EM) | |
| raw_spin_lock_init(&vendor_sched_pixel_em_lock); | |
| #endif | |
| mutex_init(&thermal_cap_mutex); | |
| } | |
| #if IS_ENABLED(CONFIG_USE_VENDOR_GROUP_UTIL) | |
| /* This function is called when tasks migrate among vendor groups */ | |
| void migrate_vendor_group_util(struct task_struct *p, unsigned int old, unsigned int new) | |
| { | |
| int cpu = task_cpu(p); | |
| unsigned long irqflags; | |
| unsigned long util_avg, util_sum; | |
| unsigned long util_est; | |
| int old_mapping, new_mapping; | |
| old_mapping = get_utilization_group(p, old); | |
| new_mapping = get_utilization_group(p, new); | |
| if (old_mapping == new_mapping) | |
| return; | |
| if (old_mapping == UG_BG) { | |
| util_avg = min(vendor_cfs_util[old_mapping][cpu].avg.util_avg, p->se.avg.util_avg); | |
| util_sum = min(vendor_cfs_util[old_mapping][cpu].avg.util_sum, p->se.avg.util_sum); | |
| } else { | |
| util_avg = p->se.avg.util_avg; | |
| util_sum = p->se.avg.util_sum; | |
| } | |
| if (likely(sched_feat(UTIL_EST))) | |
| util_est = min(vendor_cfs_util[old_mapping][cpu].util_est, _task_util_est(p)); | |
| //remove util from old group | |
| raw_spin_lock_irqsave(&vendor_cfs_util[old_mapping][cpu].lock, irqflags); | |
| if (old_mapping == UG_BG) { | |
| vendor_cfs_util[old_mapping][cpu].avg.util_avg -= util_avg; | |
| vendor_cfs_util[old_mapping][cpu].avg.util_sum -= util_sum; | |
| } | |
| if (likely(sched_feat(UTIL_EST))) { | |
| if (p->on_rq) | |
| vendor_cfs_util[old_mapping][cpu].util_est -= util_est; | |
| } | |
| raw_spin_unlock_irqrestore(&vendor_cfs_util[old_mapping][cpu].lock, irqflags); | |
| //move util to new group | |
| raw_spin_lock_irqsave(&vendor_cfs_util[new_mapping][cpu].lock, irqflags); | |
| if (new_mapping == UG_BG) { | |
| vendor_cfs_util[new_mapping][cpu].avg.util_avg += util_avg; | |
| vendor_cfs_util[new_mapping][cpu].avg.util_sum += util_sum; | |
| } | |
| if (likely(sched_feat(UTIL_EST))) { | |
| if (p->on_rq) | |
| vendor_cfs_util[new_mapping][cpu].util_est += util_est; | |
| } | |
| raw_spin_unlock_irqrestore(&vendor_cfs_util[new_mapping][cpu].lock, irqflags); | |
| } | |
| /* This function hooks to update_load_avg */ | |
| static inline void update_vendor_group_util(u64 now, struct cfs_rq *cfs_rq, | |
| struct sched_entity *se) | |
| { | |
| int curr_group = -1, se_group = get_utilization_group(task_of(se), | |
| get_vendor_group(task_of(se))); | |
| struct sched_avg *sa; | |
| unsigned long removed_util, irqflags; | |
| u32 divider; | |
| if (cfs_rq->curr && entity_is_task(cfs_rq->curr)) | |
| curr_group = get_utilization_group(task_of(cfs_rq->curr), | |
| get_vendor_group(task_of(cfs_rq->curr))); | |
| sa = &vendor_cfs_util[UG_BG][cfs_rq->rq->cpu].avg; | |
| if (vendor_cfs_util[UG_BG][cfs_rq->rq->cpu].util_removed) { | |
| removed_util = 0; | |
| divider = get_pelt_divider(sa); | |
| raw_spin_lock_irqsave(&vendor_cfs_util[UG_BG][cfs_rq->rq->cpu].lock, irqflags); | |
| swap(vendor_cfs_util[UG_BG][cfs_rq->rq->cpu].util_removed, removed_util); | |
| lsub_positive(&sa->util_avg, removed_util); | |
| lsub_positive(&sa->util_sum, removed_util * divider); | |
| sa->util_sum = max_t(unsigned long, sa->util_sum, sa->util_avg * PELT_MIN_DIVIDER); | |
| raw_spin_unlock_irqrestore(&vendor_cfs_util[UG_BG][cfs_rq->rq->cpu].lock, irqflags); | |
| } | |
| if (curr_group == UG_BG || se_group == UG_BG || sa->util_avg != 0 || sa->util_sum != 0) { | |
| raw_spin_lock_irqsave(&vendor_cfs_util[UG_BG][cfs_rq->rq->cpu].lock, irqflags); | |
| if (___update_load_sum(now, sa, curr_group == UG_BG, 0, curr_group == UG_BG)) | |
| ___update_load_avg(sa, 1); | |
| raw_spin_unlock_irqrestore(&vendor_cfs_util[UG_BG][cfs_rq->rq->cpu].lock, irqflags); | |
| } | |
| } | |
| /* This function hooks to update_blocked_fair */ | |
| static inline void update_vendor_group_util_blocked(u64 now, struct rq *rq) | |
| { | |
| unsigned long irqflags; | |
| struct sched_avg *sa; | |
| sa = &vendor_cfs_util[UG_BG][rq->cpu].avg; | |
| if ((available_idle_cpu(rq->cpu) || task_is_realtime(rq->curr)) && | |
| (sa->util_avg != 0 || sa->util_sum != 0)) { | |
| raw_spin_lock_irqsave(&vendor_cfs_util[UG_BG][rq->cpu].lock, irqflags); | |
| if (___update_load_sum(now, sa, 0, 0, 0)) | |
| ___update_load_avg(sa, 1); | |
| raw_spin_unlock_irqrestore(&vendor_cfs_util[UG_BG][rq->cpu].lock, irqflags); | |
| } | |
| } | |
| /* This function hooks to attach_entity_load_avg */ | |
| static inline void attach_vendor_group_util(struct cfs_rq *cfs_rq, struct sched_entity *se) | |
| { | |
| int group = get_utilization_group(task_of(se), get_vendor_group(task_of(se))); | |
| unsigned long irqflags; | |
| if (group == UG_BG) { | |
| raw_spin_lock_irqsave(&vendor_cfs_util[group][cfs_rq->rq->cpu].lock, irqflags); | |
| vendor_cfs_util[group][cfs_rq->rq->cpu].avg.util_avg += se->avg.util_avg; | |
| vendor_cfs_util[group][cfs_rq->rq->cpu].avg.util_sum += se->avg.util_sum; | |
| raw_spin_unlock_irqrestore(&vendor_cfs_util[group][cfs_rq->rq->cpu].lock, irqflags); | |
| } | |
| } | |
| /* This function hooks to detach_entity_load_avg */ | |
| static inline void detach_vendor_group_util(struct cfs_rq *cfs_rq, struct sched_entity *se) | |
| { | |
| int group = get_utilization_group(task_of(se), get_vendor_group(task_of(se))); | |
| unsigned long irqflags; | |
| if (group == UG_BG) { | |
| raw_spin_lock_irqsave(&vendor_cfs_util[group][cfs_rq->rq->cpu].lock, irqflags); | |
| lsub_positive(&vendor_cfs_util[group][cfs_rq->rq->cpu].avg.util_avg, | |
| se->avg.util_avg); | |
| lsub_positive(&vendor_cfs_util[group][cfs_rq->rq->cpu].avg.util_sum, | |
| se->avg.util_sum); | |
| vendor_cfs_util[group][cfs_rq->rq->cpu].avg.util_sum = max_t(unsigned long, | |
| vendor_cfs_util[group][cfs_rq->rq->cpu].avg.util_sum, | |
| vendor_cfs_util[group][cfs_rq->rq->cpu].avg.util_avg * PELT_MIN_DIVIDER); | |
| raw_spin_unlock_irqrestore(&vendor_cfs_util[group][cfs_rq->rq->cpu].lock, irqflags); | |
| } | |
| } | |
| /* | |
| * This function hooks to remove_entity_load_avg. It is called when tasks migrate to another cpu rq, | |
| * and we need to keep the removed util, which will be deduced from the prev rq in the next update | |
| * call on that rq. | |
| */ | |
| static inline void remove_vendor_group_util(struct cfs_rq *cfs_rq, struct sched_entity *se) | |
| { | |
| int group = get_utilization_group(task_of(se), get_vendor_group(task_of(se))); | |
| unsigned long irqflags; | |
| if (group == UG_BG) { | |
| raw_spin_lock_irqsave(&vendor_cfs_util[group][cfs_rq->rq->cpu].lock, irqflags); | |
| vendor_cfs_util[group][cfs_rq->rq->cpu].util_removed += se->avg.util_avg; | |
| raw_spin_unlock_irqrestore(&vendor_cfs_util[group][cfs_rq->rq->cpu].lock, irqflags); | |
| } | |
| } | |
| static inline unsigned long cpu_vendor_group_util(int cpu, bool with, struct task_struct *p) | |
| { | |
| int group = -1; | |
| unsigned long fg_group_util, util; | |
| #if IS_ENABLED(CONFIG_USE_GROUP_THROTTLE) | |
| unsigned long scale_cpu = arch_scale_cpu_capacity(cpu); | |
| #endif | |
| if (p) | |
| group = get_utilization_group(p, get_vendor_group(p)); | |
| // Calculate bg group util first. | |
| util = vendor_cfs_util[UG_BG][cpu].avg.util_avg; | |
| if (group == UG_BG) { | |
| if (with) { | |
| util += task_util(p); | |
| } else { | |
| lsub_positive(&util, task_util(p)); | |
| } | |
| } | |
| #if IS_ENABLED(CONFIG_USE_GROUP_THROTTLE) | |
| util = min_t(unsigned long, util, cap_scale(ug[UG_BG].group_throttle, scale_cpu)); | |
| #endif | |
| util = min_t(unsigned long, util, ug[UG_BG].uc_req[UCLAMP_MAX].value); | |
| // For fg group, we use root util - bg group util. | |
| fg_group_util = cpu_rq(cpu)->cfs.avg.util_avg; | |
| lsub_positive(&fg_group_util, vendor_cfs_util[UG_BG][cpu].avg.util_avg); | |
| if (group == UG_FG) { | |
| if (with) { | |
| fg_group_util += task_util(p); | |
| } else { | |
| lsub_positive(&fg_group_util, task_util(p)); | |
| } | |
| } | |
| #if IS_ENABLED(CONFIG_USE_GROUP_THROTTLE) | |
| fg_group_util = min_t(unsigned long, fg_group_util, | |
| cap_scale(ug[UG_FG].group_throttle, scale_cpu)); | |
| #endif | |
| fg_group_util = min_t(unsigned long, fg_group_util, | |
| ug[UG_FG].uc_req[UCLAMP_MAX].value); | |
| util += fg_group_util; | |
| return util; | |
| } | |
| static inline unsigned long cpu_vendor_group_util_est(int cpu, bool with, struct task_struct *p) | |
| { | |
| int i, group = -1; | |
| unsigned long group_util_est, util_est = 0; | |
| #if IS_ENABLED(CONFIG_USE_GROUP_THROTTLE) | |
| unsigned long scale_cpu = arch_scale_cpu_capacity(cpu);; | |
| #endif | |
| if (p) | |
| group = get_utilization_group(p, get_vendor_group(p)); | |
| for (i = 0; i < UG_MAX; i++) { | |
| group_util_est = vendor_cfs_util[i][cpu].util_est; | |
| if (group == i) { | |
| if (with) { | |
| group_util_est += _task_util_est(p); | |
| } else { | |
| lsub_positive(&group_util_est, _task_util_est(p)); | |
| } | |
| } | |
| #if IS_ENABLED(CONFIG_USE_GROUP_THROTTLE) | |
| group_util_est = min_t(unsigned long, group_util_est, | |
| cap_scale(ug[i].group_throttle, scale_cpu)); | |
| #endif | |
| group_util_est = min_t(unsigned long, group_util_est, | |
| ug[i].uc_req[UCLAMP_MAX].value); | |
| util_est += group_util_est; | |
| } | |
| return util_est; | |
| } | |
| #endif | |
| #if IS_ENABLED(CONFIG_UCLAMP_TASK) && IS_ENABLED(CONFIG_FAIR_GROUP_SCHED) | |
| #if IS_ENABLED(CONFIG_USE_VENDOR_GROUP_UTIL) | |
| unsigned long __always_inline cpu_util_cfs_group_mod(int cpu) | |
| { | |
| if (likely(sched_feat(UTIL_EST))) { | |
| return max(cpu_vendor_group_util(cpu, false, NULL), | |
| cpu_vendor_group_util_est(cpu, false, NULL)); | |
| } else { | |
| return cpu_vendor_group_util(cpu, false, NULL); | |
| } | |
| } | |
| #else | |
| static inline unsigned long cpu_util_cfs_group_mod_no_est(struct rq *rq) | |
| { | |
| struct cfs_rq *cfs_rq, *pos; | |
| unsigned long util = 0, unclamped_util = 0; | |
| struct task_group *tg; | |
| #if IS_ENABLED(CONFIG_USE_GROUP_THROTTLE) | |
| unsigned long scale_cpu = arch_scale_cpu_capacity(rq->cpu); | |
| #endif | |
| // cpu_util_cfs = root_util - subgroup_util_sum + throttled_subgroup_util_sum | |
| for_each_leaf_cfs_rq_safe(rq, cfs_rq, pos) { | |
| if (&rq->cfs != cfs_rq) { | |
| tg = cfs_rq->tg; | |
| unclamped_util += cfs_rq->avg.util_avg; | |
| #if IS_ENABLED(CONFIG_USE_GROUP_THROTTLE) | |
| util += min_t(unsigned long, READ_ONCE(cfs_rq->avg.util_avg), | |
| cap_scale(get_group_throttle(tg), scale_cpu)); | |
| #else | |
| util += READ_ONCE(cfs_rq->avg.util_avg); | |
| #endif | |
| } | |
| } | |
| util += max_t(int, READ_ONCE(rq->cfs.avg.util_avg) - unclamped_util, 0); | |
| return util; | |
| } | |
| unsigned long cpu_util_cfs_group_mod(int cpu) | |
| { | |
| struct rq *rq = cpu_rq(cpu); | |
| unsigned long util = cpu_util_cfs_group_mod_no_est(rq); | |
| if (sched_feat(UTIL_EST)) { | |
| // TODO: right now the limit of util_est is per task | |
| // consider to make it per group. | |
| util = max_t(unsigned long, util, | |
| READ_ONCE(rq->cfs.avg.util_est.enqueued)); | |
| } | |
| return util; | |
| } | |
| #endif | |
| #else | |
| #define cpu_util_cfs_group_mod cpu_util_cfs | |
| #endif | |
| unsigned long cpu_util(int cpu) | |
| { | |
| return min(cpu_util_cfs_group_mod(cpu), capacity_of(cpu)); | |
| } | |
| #if IS_ENABLED(CONFIG_USE_GROUP_THROTTLE) | |
| /* Similar to cpu_util_without but only count the task's group util contribution */ | |
| #if IS_ENABLED(CONFIG_USE_VENDOR_GROUP_UTIL) | |
| static inline unsigned long get_group_util(int cpu, struct task_struct *p, unsigned long max, | |
| bool subtract) | |
| { | |
| int group = get_utilization_group(p, get_vendor_group(p)); | |
| unsigned long util = cpu_rq(cpu)->cfs.avg.util_avg; | |
| if (group == UG_BG) { | |
| util = vendor_cfs_util[UG_BG][cpu].avg.util_avg; | |
| // For fg group, we use root util - bg group util | |
| } else if (group == UG_FG) { | |
| util = cpu_rq(cpu)->cfs.avg.util_avg; | |
| lsub_positive(&util, vendor_cfs_util[UG_BG][cpu].avg.util_avg); | |
| } else { | |
| SCHED_WARN_ON(1); | |
| return min(util, max); | |
| } | |
| if (subtract && (cpu == task_cpu(p) && READ_ONCE(p->se.avg.last_update_time))) | |
| lsub_positive(&util, task_util(p)); | |
| if (likely(sched_feat(UTIL_EST))) { | |
| unsigned long util_est = vendor_cfs_util[group][cpu].util_est; | |
| if (subtract && unlikely(task_on_rq_queued(p) || current == p)) | |
| lsub_positive(&util_est, _task_util_est(p)); | |
| util = max(util, util_est); | |
| } | |
| return min(util, max); | |
| } | |
| #else | |
| static unsigned long group_util_without(int cpu, struct task_struct *p, unsigned long max) | |
| { | |
| unsigned long util = READ_ONCE(task_group(p)->cfs_rq[cpu]->avg.util_avg); | |
| if (cpu != task_cpu(p) || !READ_ONCE(p->se.avg.last_update_time)) | |
| goto out; | |
| lsub_positive(&util, task_util(p)); | |
| out: | |
| return min_t(unsigned long, util, max); | |
| } | |
| #endif | |
| #endif | |
| static unsigned long cpu_util_without(int cpu, struct task_struct *p, unsigned long max) | |
| { | |
| #if IS_ENABLED(CONFIG_USE_VENDOR_GROUP_UTIL) | |
| unsigned long util; | |
| /* Task has no contribution or is new */ | |
| if (cpu != task_cpu(p) || !READ_ONCE(p->se.avg.last_update_time)) | |
| util = cpu_vendor_group_util(cpu, false, NULL); | |
| else | |
| util = cpu_vendor_group_util(cpu, false, p); | |
| if (likely(sched_feat(UTIL_EST))) { | |
| unsigned long estimated; | |
| if (unlikely(task_on_rq_queued(p) || current == p)) { | |
| estimated = cpu_vendor_group_util_est(cpu, false, p); | |
| } else { | |
| estimated = cpu_vendor_group_util_est(cpu, false, NULL); | |
| } | |
| util = max(util, estimated); | |
| } | |
| return min(util, max); | |
| #else | |
| struct cfs_rq *cfs_rq; | |
| unsigned long util; | |
| /* Task has no contribution or is new */ | |
| if (cpu != task_cpu(p) || !READ_ONCE(p->se.avg.last_update_time)) | |
| return cpu_util(cpu); | |
| cfs_rq = &cpu_rq(cpu)->cfs; | |
| util = cpu_util_cfs_group_mod_no_est(cpu_rq(cpu)); | |
| /* Discount task's util from CPU's util */ | |
| lsub_positive(&util, task_util(p)); | |
| /* | |
| * Covered cases: | |
| * | |
| * a) if *p is the only task sleeping on this CPU, then: | |
| * cpu_util (== task_util) > util_est (== 0) | |
| * and thus we return: | |
| * cpu_util_without = (cpu_util - task_util) = 0 | |
| * | |
| * b) if other tasks are SLEEPING on this CPU, which is now exiting | |
| * IDLE, then: | |
| * cpu_util >= task_util | |
| * cpu_util > util_est (== 0) | |
| * and thus we discount *p's blocked utilization to return: | |
| * cpu_util_without = (cpu_util - task_util) >= 0 | |
| * | |
| * c) if other tasks are RUNNABLE on that CPU and | |
| * util_est > cpu_util | |
| * then we use util_est since it returns a more restrictive | |
| * estimation of the spare capacity on that CPU, by just | |
| * considering the expected utilization of tasks already | |
| * runnable on that CPU. | |
| * | |
| * Cases a) and b) are covered by the above code, while case c) is | |
| * covered by the following code when estimated utilization is | |
| * enabled. | |
| */ | |
| if (sched_feat(UTIL_EST)) { | |
| unsigned int estimated = | |
| READ_ONCE(cfs_rq->avg.util_est.enqueued); | |
| /* | |
| * Despite the following checks we still have a small window | |
| * for a possible race, when an execl's select_task_rq_fair() | |
| * races with LB's detach_task(): | |
| * | |
| * detach_task() | |
| * p->on_rq = TASK_ON_RQ_MIGRATING; | |
| * ---------------------------------- A | |
| * deactivate_task() \ | |
| * dequeue_task() + RaceTime | |
| * util_est_dequeue() / | |
| * ---------------------------------- B | |
| * | |
| * The additional check on "current == p" it's required to | |
| * properly fix the execl regression and it helps in further | |
| * reducing the chances for the above race. | |
| */ | |
| if (unlikely(task_on_rq_queued(p) || current == p)) | |
| lsub_positive(&estimated, _task_util_est(p)); | |
| util = max_t(unsigned long, util, estimated); | |
| } | |
| /* | |
| * Utilization (estimated) can exceed the CPU capacity, thus let's | |
| * clamp to the maximum CPU capacity to ensure consistency with | |
| * the cpu_util call. | |
| */ | |
| return min_t(unsigned long, util, max); | |
| #endif | |
| } | |
| struct vendor_group_property *get_vendor_group_property(enum vendor_group group) | |
| { | |
| SCHED_WARN_ON(group < VG_SYSTEM || group >= VG_MAX); | |
| return &(vg[group]); | |
| } | |
| #if IS_ENABLED(CONFIG_USE_VENDOR_GROUP_UTIL) | |
| struct vendor_util_group_property *get_vendor_util_group_property(enum utilization_group group) | |
| { | |
| SCHED_WARN_ON(group < UG_BG || group >= UG_MAX); | |
| return &(ug[group]); | |
| } | |
| #endif | |
| static bool task_fits_capacity(struct task_struct *p, int cpu) | |
| { | |
| unsigned long uclamp_min = uclamp_eff_value_pixel_mod(p, UCLAMP_MIN); | |
| unsigned long uclamp_max = uclamp_eff_value_pixel_mod(p, UCLAMP_MAX); | |
| unsigned long task_util = task_util_est(p); | |
| if (cpu >= pixel_cluster_start_cpu[2]) | |
| return true; | |
| /* | |
| * Ignore uclamp if spreading the task | |
| */ | |
| if (get_task_spreading(p)) { | |
| uclamp_min = uclamp_none(UCLAMP_MIN); | |
| uclamp_max = uclamp_none(UCLAMP_MAX); | |
| } | |
| #if IS_ENABLED(CONFIG_USE_GROUP_THROTTLE) | |
| /* clamp task utilization against its per-cpu group limit */ | |
| task_util = min_t(unsigned long, task_util, cap_scale(get_task_group_throttle(p), | |
| arch_scale_cpu_capacity(cpu))); | |
| #endif | |
| uclamp_min = max(uclamp_min, get_vendor_task_struct(p)->iowait_boost); | |
| return util_fits_cpu(task_util, uclamp_min, uclamp_max, cpu); | |
| } | |
| void rvh_util_fits_cpu_pixel_mod(void *data, unsigned long util, unsigned long uclamp_min, | |
| unsigned long uclamp_max, int cpu, bool *fits, bool *done) | |
| { | |
| *fits = util_fits_cpu(util, uclamp_min, uclamp_max, cpu); | |
| *done = true; | |
| } | |
| /** | |
| * cpu_is_idle - is a given CPU idle for enqueuing work. | |
| * @cpu: the CPU in question. | |
| * | |
| * Return: 1 if the CPU is currently idle. 0 otherwise. | |
| */ | |
| int cpu_is_idle(int cpu) | |
| { | |
| if (available_idle_cpu(cpu) || sched_cpu_idle(cpu)) | |
| return 1; | |
| return 0; | |
| } | |
| static unsigned long cpu_util_next(int cpu, struct task_struct *p, int dst_cpu) | |
| { | |
| #if IS_ENABLED(CONFIG_USE_VENDOR_GROUP_UTIL) | |
| unsigned long util; | |
| if (task_cpu(p) == cpu && dst_cpu != cpu) | |
| util = cpu_vendor_group_util(cpu, false, p); | |
| else if (task_cpu(p) != cpu && dst_cpu == cpu) | |
| util = cpu_vendor_group_util(cpu, true, p); | |
| else | |
| util = cpu_vendor_group_util(cpu, false, NULL); | |
| if (likely(sched_feat(UTIL_EST))) { | |
| unsigned long estimated; | |
| if (dst_cpu == cpu) { | |
| estimated = cpu_vendor_group_util_est(cpu, true, p); | |
| } else { | |
| estimated = cpu_vendor_group_util_est(cpu, false, NULL); | |
| } | |
| util = max(util, estimated); | |
| } | |
| return min(util, capacity_of(cpu)); | |
| #else | |
| struct cfs_rq *cfs_rq, *pos; | |
| unsigned long util = 0, unclamped_util = 0; | |
| unsigned long util_est; | |
| struct task_group *tg; | |
| long delta = 0; | |
| struct rq *rq = cpu_rq(cpu); | |
| #if IS_ENABLED(CONFIG_USE_GROUP_THROTTLE) | |
| unsigned long scale_cpu = arch_scale_cpu_capacity(cpu); | |
| #endif | |
| if (task_cpu(p) == cpu && dst_cpu != cpu) | |
| delta = -task_util(p); | |
| else if (task_cpu(p) != cpu && dst_cpu == cpu) | |
| delta = task_util(p); | |
| // For leaf groups | |
| for_each_leaf_cfs_rq_safe(rq, cfs_rq, pos) { | |
| if (&rq->cfs != cfs_rq) { | |
| unsigned long group_util = 0; | |
| tg = cfs_rq->tg; | |
| if (p->se.cfs_rq->tg == tg) | |
| group_util = max_t(int, READ_ONCE(cfs_rq->avg.util_avg) + delta, 0); | |
| else | |
| group_util = READ_ONCE(cfs_rq->avg.util_avg); | |
| unclamped_util += READ_ONCE(cfs_rq->avg.util_avg); | |
| #if IS_ENABLED(CONFIG_USE_GROUP_THROTTLE) | |
| util += min_t(unsigned long, group_util, | |
| cap_scale(get_group_throttle(tg), scale_cpu)); | |
| #else | |
| util += group_util; | |
| #endif | |
| } | |
| } | |
| // For root group | |
| if (p->se.cfs_rq->tg == rq->cfs.tg) | |
| util = max_t(long, READ_ONCE(rq->cfs.avg.util_avg) - unclamped_util + util + delta, | |
| 0); | |
| else | |
| util = max_t(long, READ_ONCE(rq->cfs.avg.util_avg) - unclamped_util + util, 0); | |
| if (sched_feat(UTIL_EST)) { | |
| util_est = READ_ONCE(rq->cfs.avg.util_est.enqueued); | |
| if (dst_cpu == cpu) | |
| util_est += _task_util_est(p); | |
| util = max_t(unsigned long, util, util_est); | |
| } | |
| return min(util, capacity_of(cpu)); | |
| #endif | |
| } | |
| #if IS_ENABLED(CONFIG_PIXEL_EM) | |
| static inline unsigned long get_wakeup_energy(int cpu, int opp_level) | |
| { | |
| unsigned long energy = 0; | |
| struct pixel_idle_em *idle_em_snapshot; | |
| unsigned long irqflags; | |
| raw_spin_lock_irqsave(&vendor_sched_pixel_em_lock, irqflags); | |
| idle_em_snapshot = READ_ONCE(vendor_sched_pixel_idle_em); | |
| if (idle_em_snapshot) { | |
| energy = idle_em_snapshot->cpu_to_cluster[cpu]->idle_opps[opp_level].energy; | |
| } | |
| raw_spin_unlock_irqrestore(&vendor_sched_pixel_em_lock, irqflags); | |
| return energy; | |
| } | |
| #endif | |
| static inline unsigned long em_cpu_energy_pixel_mod(struct em_perf_domain *pd, | |
| unsigned long max_util, unsigned long sum_util, bool count_idle, | |
| int dst_cpu) | |
| { | |
| unsigned long freq, scale_cpu; | |
| struct em_perf_state *ps; | |
| int i, cpu; | |
| if (!sum_util) | |
| return 0; | |
| cpu = cpumask_first(to_cpumask(pd->cpus)); | |
| #if IS_ENABLED(CONFIG_PIXEL_EM) | |
| { | |
| unsigned long energy; | |
| struct pixel_em_profile **profile_ptr_snapshot; | |
| profile_ptr_snapshot = READ_ONCE(vendor_sched_pixel_em_profile); | |
| if (profile_ptr_snapshot) { | |
| struct pixel_em_profile *profile = READ_ONCE(*profile_ptr_snapshot); | |
| if (profile) { | |
| struct pixel_em_cluster *cluster = profile->cpu_to_cluster[cpu]; | |
| struct pixel_em_opp *max_opp; | |
| struct pixel_em_opp *opp; | |
| max_opp = &cluster->opps[cluster->num_opps - 1]; | |
| freq = map_util_freq_pixel_mod(max_util, | |
| max_opp->freq, | |
| max_opp->capacity, | |
| cpu); | |
| for (i = 0; i < cluster->num_opps; i++) { | |
| opp = &cluster->opps[i]; | |
| if (opp->freq >= freq) | |
| break; | |
| } | |
| energy = opp->cost * sum_util; | |
| if (count_idle) { | |
| unsigned long cur_freq = arch_scale_freq_capacity(cpu) * | |
| max_opp->freq >> SCHED_CAPACITY_SHIFT; | |
| for (i = 0; i < cluster->num_opps; i++) { | |
| opp = &cluster->opps[i]; | |
| if (opp->freq >= cur_freq) | |
| break; | |
| } | |
| if (check_add_overflow(energy, get_wakeup_energy(dst_cpu, i), &energy)) { | |
| WARN_ON(1); | |
| energy = UINT_MAX; | |
| } | |
| } | |
| return energy; | |
| } | |
| } | |
| } | |
| #endif | |
| scale_cpu = arch_scale_cpu_capacity(cpu); | |
| ps = &pd->table[pd->nr_perf_states - 1]; | |
| freq = map_util_freq_pixel_mod(max_util, ps->frequency, scale_cpu, cpu); | |
| for (i = 0; i < pd->nr_perf_states; i++) { | |
| ps = &pd->table[i]; | |
| if (ps->frequency >= freq && !(ps->flags & EM_PERF_STATE_INEFFICIENT)) | |
| break; | |
| } | |
| return ps->cost * sum_util / scale_cpu; | |
| } | |
| static long | |
| compute_energy(struct task_struct *p, int dst_cpu, struct perf_domain *pd, unsigned long exit_lat) | |
| { | |
| unsigned long max_util, util_cfs, cpu_util, cpu_cap; | |
| unsigned long sum_util, energy = 0; | |
| struct task_struct *tsk; | |
| int cpu; | |
| bool count_idle; | |
| for (; pd; pd = pd->next) { | |
| struct cpumask *pd_mask = perf_domain_span(pd); | |
| /* | |
| * The energy model mandates all the CPUs of a performance | |
| * domain have the same capacity. | |
| */ | |
| cpu_cap = arch_scale_cpu_capacity(cpumask_first(pd_mask)); | |
| max_util = sum_util = 0; | |
| /* | |
| * The capacity state of CPUs of the current rd can be driven by | |
| * CPUs of another rd if they belong to the same performance | |
| * domain. So, account for the utilization of these CPUs too | |
| * by masking pd with cpu_online_mask instead of the rd span. | |
| * | |
| * If an entire performance domain is outside of the current rd, | |
| * it will not appear in its pd list and will not be accounted | |
| * by compute_energy(). | |
| */ | |
| for_each_cpu_and(cpu, pd_mask, cpu_online_mask) { | |
| util_cfs = cpu_util_next(cpu, p, dst_cpu); | |
| /* | |
| * Busy time computation: utilization clamping is not | |
| * required since the ratio (sum_util / cpu_capacity) | |
| * is already enough to scale the EM reported power | |
| * consumption at the (eventually clamped) cpu_capacity. | |
| */ | |
| sum_util += schedutil_cpu_util_pixel_mod(cpu, util_cfs, cpu_cap, | |
| ENERGY_UTIL, NULL); | |
| /* | |
| * Performance domain frequency: utilization clamping | |
| * must be considered since it affects the selection | |
| * of the performance domain frequency. | |
| * NOTE: in case RT tasks are running, by default the | |
| * FREQUENCY_UTIL's utilization can be max OPP. | |
| */ | |
| tsk = cpu == dst_cpu ? p : NULL; | |
| cpu_util = schedutil_cpu_util_pixel_mod(cpu, util_cfs, cpu_cap, | |
| FREQUENCY_UTIL, tsk); | |
| max_util = max(max_util, cpu_util); | |
| } | |
| count_idle = cpumask_test_cpu(dst_cpu, pd_mask) && exit_lat > C1_EXIT_LATENCY; | |
| energy += em_cpu_energy_pixel_mod(pd->em_pd, max_util, sum_util, count_idle, | |
| dst_cpu); | |
| } | |
| trace_sched_compute_energy(p, dst_cpu, energy); | |
| return energy; | |
| } | |
| #if IS_ENABLED(CONFIG_USE_GROUP_THROTTLE) | |
| /* If a task_group is over its group limit on a particular CPU with margin considered */ | |
| #if IS_ENABLED(CONFIG_USE_VENDOR_GROUP_UTIL) | |
| static inline bool group_overutilized(int cpu, struct task_struct *p, unsigned long util) | |
| { | |
| unsigned long group_capacity = cap_scale(get_task_group_throttle(p), | |
| arch_scale_cpu_capacity(cpu)); | |
| return !fits_capacity(util, group_capacity, cpu); | |
| } | |
| #else | |
| static inline bool group_overutilized(int cpu, struct task_group *tg, unsigned long util) | |
| { | |
| unsigned long group_capacity = cap_scale(get_group_throttle(tg), | |
| arch_scale_cpu_capacity(cpu)); | |
| return !fits_capacity(util, group_capacity, cpu); | |
| } | |
| #endif | |
| #endif | |
| static void prio_changed(struct task_struct *p, int old_prio, int new_prio) | |
| { | |
| struct rq *rq; | |
| bool queued, running; | |
| rq = task_rq(p); | |
| update_rq_clock(rq); | |
| queued = task_on_rq_queued(p); | |
| running = task_current(rq, p); | |
| if (queued) | |
| p->sched_class->dequeue_task(rq, p, DEQUEUE_SAVE | DEQUEUE_NOCLOCK); | |
| if (running) | |
| put_prev_task(rq, p); | |
| reweight_task(p, new_prio - MAX_RT_PRIO); | |
| if (queued) | |
| p->sched_class->enqueue_task(rq, p, ENQUEUE_RESTORE | ENQUEUE_NOCLOCK); | |
| if (running) | |
| set_next_task(rq, p); | |
| prio_changed_fair(rq, p, old_prio); | |
| } | |
| void update_task_prio(struct task_struct *p, struct vendor_task_struct *vp, bool val) | |
| { | |
| int new_prio, old_prio; | |
| unsigned long irqflags; | |
| if (p->prio < MAX_RT_PRIO) | |
| return; | |
| if (val) { | |
| raw_spin_lock_irqsave(&vp->lock, irqflags); | |
| vp->orig_prio = p->static_prio; | |
| raw_spin_unlock_irqrestore(&vp->lock, irqflags); | |
| } | |
| old_prio = p->prio; | |
| if (val) { | |
| p->prio = NICE_TO_PRIO(MIN_NICE); | |
| } else { | |
| p->prio = vp->orig_prio; | |
| } | |
| new_prio = p->prio; | |
| if (old_prio != new_prio) | |
| prio_changed(p, old_prio, new_prio); | |
| } | |
| static void get_uclamp_on_nice(struct task_struct *p, enum uclamp_id clamp_id, | |
| unsigned int *value) | |
| { | |
| int group = get_vendor_group(p); | |
| if (clamp_id == UCLAMP_MIN) { | |
| if (!vg[group].uclamp_min_on_nice_enable) | |
| return; | |
| if (p->prio <= vg[group].uclamp_min_on_nice_high_prio) { | |
| *value = vg[group].uclamp_min_on_nice_high_value; | |
| } else if (p->prio <= vg[group].uclamp_min_on_nice_mid_prio) { | |
| *value = vg[group].uclamp_min_on_nice_mid_value; | |
| } else { | |
| *value = vg[group].uclamp_min_on_nice_low_value; | |
| } | |
| } else if (clamp_id == UCLAMP_MAX) { | |
| if (!vg[group].uclamp_max_on_nice_enable) | |
| return; | |
| if (p->prio <= vg[group].uclamp_max_on_nice_high_prio) { | |
| *value = vg[group].uclamp_max_on_nice_high_value; | |
| } else if (p->prio <= vg[group].uclamp_max_on_nice_mid_prio) { | |
| *value = vg[group].uclamp_max_on_nice_mid_value; | |
| } else { | |
| *value = vg[group].uclamp_max_on_nice_low_value; | |
| } | |
| } | |
| } | |
| /*****************************************************************************/ | |
| /* Modified Code Section */ | |
| /*****************************************************************************/ | |
| /* | |
| * This part of code is vendor hook functions, which modify or extend the original | |
| * functions. | |
| */ | |
| static u64 __sched_period(unsigned long nr_running) | |
| { | |
| unsigned int sched_nr_latency = DIV_ROUND_UP(sysctl_sched_latency, | |
| sysctl_sched_min_granularity); | |
| if (unlikely(nr_running > sched_nr_latency)) | |
| return nr_running * sysctl_sched_min_granularity; | |
| else | |
| return sysctl_sched_latency; | |
| } | |
| int find_energy_efficient_cpu(struct task_struct *p, int prev_cpu, | |
| cpumask_t *valid_mask) | |
| { | |
| struct root_domain *rd; | |
| struct perf_domain *pd; | |
| cpumask_t cm[CC_MAX] = { [0 ... CC_MAX-1] = {CPU_BITS_NONE} }; | |
| cpumask_t candidates = { CPU_BITS_NONE }, candidates_temp = { CPU_BITS_NONE }; | |
| int i, weight, best_energy_cpu = -1, this_cpu = smp_processor_id(); | |
| long cur_energy, best_energy = LONG_MAX; | |
| unsigned long p_util_min = uclamp_is_used() ? uclamp_eff_value_pixel_mod(p, UCLAMP_MIN) : 0; | |
| unsigned long p_util_max = uclamp_is_used() ? uclamp_eff_value_pixel_mod(p, UCLAMP_MAX) : 1024; | |
| unsigned long spare_cap, target_max_spare_cap = 0; | |
| unsigned long task_importance = ((p->prio <= DEFAULT_PRIO) ? p_util_min : 0) + p_util_max; | |
| unsigned int exit_lat, pd_best_exit_lat, best_exit_lat; | |
| bool is_idle, task_fits, util_fits; | |
| bool idle_target_found = false, importance_target_found = false; | |
| bool prefer_idle = get_prefer_idle(p); | |
| unsigned long capacity, wake_util, cpu_importance, pd_least_cpu_importantce; | |
| #if IS_ENABLED(CONFIG_USE_GROUP_THROTTLE) | |
| bool group_overutilize; | |
| unsigned long group_capacity, wake_group_util; | |
| #endif | |
| unsigned long pd_max_spare_cap, pd_max_unimportant_spare_cap, pd_max_packing_spare_cap, pd_max_spare_cap_running_rt; | |
| int pd_max_spare_cap_cpu, pd_best_idle_cpu, pd_most_unimportant_cpu, pd_best_packing_cpu, pd_max_spare_cap_running_rt_cpu; | |
| int most_spare_cap_cpu = -1, unimportant_max_spare_cap_cpu = -1, idle_max_cap_cpu = -1; | |
| int least_loaded_cpu = -1; | |
| struct cpuidle_state *idle_state; | |
| unsigned long unimportant_max_spare_cap = 0, idle_max_cap = 0; | |
| unsigned long cfs_load, min_load = ULONG_MAX; | |
| bool prefer_fit = false; | |
| const cpumask_t *preferred_idle_mask; | |
| rd = cpu_rq(this_cpu)->rd; | |
| rcu_read_lock(); | |
| pd = rcu_dereference(rd->pd); | |
| if (!pd) | |
| goto out; | |
| p_util_min = max(p_util_min, get_vendor_task_struct(p)->iowait_boost); | |
| if (get_prefer_fit(p) || get_auto_prefer_fit(p)) | |
| prefer_fit = true; | |
| for (; pd; pd = pd->next) { | |
| unsigned long util_min = p_util_min, util_max = p_util_max; | |
| unsigned long rq_util_min, rq_util_max; | |
| pd_max_spare_cap_running_rt = 0; | |
| pd_max_spare_cap = 0; | |
| pd_max_packing_spare_cap = 0; | |
| pd_max_unimportant_spare_cap = 0; | |
| pd_best_exit_lat = UINT_MAX; | |
| pd_max_spare_cap_running_rt_cpu = -1; | |
| pd_max_spare_cap_cpu = -1; | |
| pd_best_idle_cpu = -1; | |
| pd_most_unimportant_cpu = -1; | |
| pd_best_packing_cpu = -1; | |
| pd_least_cpu_importantce = SCHED_CAPACITY_SCALE << 2; | |
| for_each_cpu_and(i, perf_domain_span(pd), valid_mask ? valid_mask : p->cpus_ptr) { | |
| if (i >= pixel_cpu_num) | |
| break; | |
| if (!cpu_active(i)) | |
| continue; | |
| capacity = capacity_of(i); | |
| is_idle = cpu_is_idle(i); | |
| cpu_importance = READ_ONCE(cpu_rq(i)->uclamp[UCLAMP_MIN].value) + | |
| READ_ONCE(cpu_rq(i)->uclamp[UCLAMP_MAX].value); | |
| wake_util = cpu_util_without(i, p, capacity); | |
| #if IS_ENABLED(CONFIG_USE_GROUP_THROTTLE) | |
| group_capacity = cap_scale(get_task_group_throttle(p), | |
| arch_scale_cpu_capacity(i)); | |
| #if IS_ENABLED(CONFIG_USE_VENDOR_GROUP_UTIL) | |
| wake_group_util = get_group_util(i, p, group_capacity, true); | |
| group_overutilize = group_overutilized(i, p, wake_group_util); | |
| #else | |
| wake_group_util = group_util_without(i, p, group_capacity); | |
| group_overutilize = group_overutilized(i, task_group(p), wake_group_util); | |
| #endif | |
| spare_cap = min_t(unsigned long, capacity - wake_util, | |
| group_capacity - wake_group_util); | |
| #else | |
| spare_cap = capacity - wake_util; | |
| #endif | |
| task_fits = task_fits_capacity(p, i); | |
| exit_lat = 0; | |
| if (is_idle) { | |
| idle_state = idle_get_state(cpu_rq(i)); | |
| if (idle_state) | |
| exit_lat = idle_state->exit_latency; | |
| } | |
| #if IS_ENABLED(CONFIG_USE_GROUP_THROTTLE) | |
| if (trace_sched_cpu_util_cfs_enabled()) | |
| trace_sched_cpu_util_cfs(i, is_idle, exit_lat, cpu_importance, | |
| cpu_util(i), capacity, wake_util, | |
| group_capacity, wake_group_util, spare_cap, | |
| task_fits, group_overutilize); | |
| #else | |
| if (trace_sched_cpu_util_cfs_enabled()) | |
| trace_sched_cpu_util_cfs(i, is_idle, exit_lat, cpu_importance, | |
| cpu_util(i), capacity, wake_util, capacity, | |
| wake_util, spare_cap, task_fits, false); | |
| #endif | |
| /* | |
| * Consider auto_uclamp_max based on placing the task | |
| * on the ith cpu | |
| */ | |
| if (get_vendor_task_struct(p)->auto_uclamp_max_flags) | |
| util_max = p_util_max = sched_auto_uclamp_max[i]; | |
| /* | |
| * Skip CPUs that cannot satisfy the capacity request. | |
| * IOW, placing the task there would make the CPU | |
| * overutilized. Take uclamp into account to see how | |
| * much capacity we can get out of the CPU; this is | |
| * aligned with sched_cpu_util(). | |
| */ | |
| if (uclamp_is_used() && !uclamp_rq_is_idle(cpu_rq(i))) { | |
| /* | |
| * Open code uclamp_rq_util_with() except for | |
| * the clamp() part. Ie: apply max aggregation | |
| * only. util_fits_cpu() logic requires to | |
| * operate on non clamped util but must use the | |
| * max-aggregated uclamp_{min, max}. | |
| */ | |
| rq_util_min = uclamp_rq_get(cpu_rq(i), UCLAMP_MIN); | |
| rq_util_max = uclamp_rq_get(cpu_rq(i), UCLAMP_MAX); | |
| util_min = max(rq_util_min, p_util_min); | |
| util_max = max(rq_util_max, p_util_max); | |
| } | |
| util_fits = util_fits_cpu(wake_util, util_min, util_max, i); | |
| cfs_load = cpu_load(cpu_rq(i)); | |
| if (cfs_load < min_load) { | |
| min_load = cfs_load; | |
| least_loaded_cpu = i; | |
| } | |
| if (prefer_idle) { | |
| /* | |
| * For a cluster, the energy computation result will be the same for | |
| * idle cpus on that cluster, so we could save some computation by | |
| * just choosing 1 idle cpu for each cluster. | |
| * If there are multiple idle cpus, compare their c states (exit | |
| * latency). | |
| */ | |
| if (is_idle) { | |
| /* The first idle cpu will always fall into this case. */ | |
| if (exit_lat < pd_best_exit_lat) { | |
| pd_best_idle_cpu = i; | |
| pd_best_exit_lat = exit_lat; | |
| } else if (exit_lat == pd_best_exit_lat) { | |
| /* | |
| * A simple randomization, by choosing the first or | |
| * the last cpu if pd_best_idle_cpu != prev_cpu. | |
| */ | |
| if (i == prev_cpu || | |
| (pd_best_idle_cpu != prev_cpu && this_cpu % 2)) | |
| pd_best_idle_cpu = i; | |
| } | |
| idle_target_found = true; | |
| /* Only keep the highest cpu */ | |
| if (pd_best_idle_cpu > idle_max_cap_cpu) { | |
| idle_max_cap_cpu = pd_best_idle_cpu; | |
| idle_max_cap = capacity; | |
| } | |
| } | |
| if (idle_target_found) | |
| continue; | |
| if (prefer_fit && task_fits && cpu_curr(i)->prio < MAX_RT_PRIO) { | |
| if (spare_cap >= pd_max_spare_cap_running_rt) { | |
| pd_max_spare_cap_running_rt = spare_cap; | |
| pd_max_spare_cap_running_rt_cpu = i; | |
| } | |
| continue; | |
| } | |
| /* Find an unimportant cpu. */ | |
| if (task_importance > cpu_importance) { | |
| /* Choose the least important cpu*/ | |
| if (cpu_importance < pd_least_cpu_importantce) { | |
| pd_least_cpu_importantce = cpu_importance; | |
| pd_max_unimportant_spare_cap = spare_cap; | |
| pd_most_unimportant_cpu = i; | |
| importance_target_found = true; | |
| /* Only keep the highest cpu */ | |
| if (i > unimportant_max_spare_cap_cpu) { | |
| unimportant_max_spare_cap_cpu = i; | |
| unimportant_max_spare_cap = spare_cap; | |
| } | |
| /* | |
| * If cpu importance is the same, choose the one with higher | |
| * spare capacity. | |
| */ | |
| } else if (cpu_importance == pd_least_cpu_importantce) { | |
| if (spare_cap >= pd_max_unimportant_spare_cap) { | |
| pd_max_unimportant_spare_cap = spare_cap; | |
| pd_most_unimportant_cpu = i; | |
| importance_target_found = true; | |
| /* Only keep the highest cpu */ | |
| if (i > unimportant_max_spare_cap_cpu) { | |
| unimportant_max_spare_cap_cpu = i; | |
| unimportant_max_spare_cap = | |
| spare_cap; | |
| } | |
| } | |
| } | |
| } | |
| if (importance_target_found) | |
| continue; | |
| /* | |
| * Make srue prefer_fit task could find a candidate in high capacity | |
| * clusters. | |
| */ | |
| #if IS_ENABLED(CONFIG_USE_GROUP_THROTTLE) | |
| if (!prefer_fit && | |
| (group_overutilize || !util_fits)) | |
| continue; | |
| #else | |
| if (!prefer_fit && !util_fits) | |
| continue; | |
| #endif | |
| if (prefer_fit && !task_fits) | |
| continue; | |
| /* find max spare capacity cpu, used as backup */ | |
| if (spare_cap > target_max_spare_cap) { | |
| target_max_spare_cap = spare_cap; | |
| cpumask_clear(&cm[MAX_SPARE_CAP]); | |
| cpumask_set_cpu(i, &cm[MAX_SPARE_CAP]); | |
| } else if (spare_cap == target_max_spare_cap) { | |
| /* | |
| * When spare capacity is the same, clear the choice | |
| * randomly based on task_util. | |
| */ | |
| if ((task_util_est(p) % 2)) | |
| cpumask_clear(&cm[MAX_SPARE_CAP]); | |
| cpumask_set_cpu(i, &cm[MAX_SPARE_CAP]); | |
| } | |
| } else { /* Below path is for non-prefer idle case */ | |
| if (spare_cap > target_max_spare_cap) { | |
| target_max_spare_cap = spare_cap; | |
| most_spare_cap_cpu = i; | |
| } else if (spare_cap && spare_cap == target_max_spare_cap && | |
| task_util_est(p) % 2) { | |
| most_spare_cap_cpu = i; | |
| } | |
| #if IS_ENABLED(CONFIG_USE_GROUP_THROTTLE) | |
| if (group_overutilize || !util_fits) | |
| continue; | |
| #else | |
| if (!util_fits) | |
| continue; | |
| #endif | |
| if (!task_fits) | |
| continue; | |
| #if IS_ENABLED(CONFIG_USE_GROUP_THROTTLE) | |
| if (spare_cap < min_t(unsigned long, task_util_est(p), | |
| cap_scale(get_task_group_throttle(p), | |
| arch_scale_cpu_capacity(i)))) | |
| continue; | |
| #else | |
| if (spare_cap < task_util_est(p)) | |
| continue; | |
| #endif | |
| /* | |
| * Find the best packing CPU with the maximum spare capacity in | |
| * the performance domain | |
| */ | |
| if (vendor_sched_npi_packing && !is_idle && | |
| cpu_importance <= DEFAULT_IMPRATANCE_THRESHOLD && | |
| spare_cap > pd_max_packing_spare_cap && capacity_curr_of(i) >= | |
| ((cpu_util_next(i, p, i) + cpu_util_rt(cpu_rq(i))) * | |
| sched_capacity_margin[i]) >> SCHED_CAPACITY_SHIFT) { | |
| pd_max_packing_spare_cap = spare_cap; | |
| pd_best_packing_cpu = i; | |
| } | |
| if (pd_best_packing_cpu != -1) | |
| continue; | |
| /* | |
| * Find the CPU with the maximum spare capacity in | |
| * the performance domain | |
| */ | |
| if (spare_cap > pd_max_spare_cap) { | |
| pd_max_spare_cap = spare_cap; | |
| pd_max_spare_cap_cpu = i; | |
| pd_best_exit_lat = exit_lat; | |
| /* Candidates could be idle cpu, so compare their exit lat. */ | |
| } else if (spare_cap == pd_max_spare_cap) { | |
| if (exit_lat < pd_best_exit_lat) { | |
| pd_max_spare_cap_cpu = i; | |
| pd_best_exit_lat = exit_lat; | |
| } else if (exit_lat == pd_best_exit_lat) { | |
| /* | |
| * A simple randomization by choosing the first or | |
| * the last cpu if pd_max_spare_cap_cpu != prev_cpu. | |
| */ | |
| if (i == prev_cpu || | |
| (pd_max_spare_cap_cpu != prev_cpu && | |
| this_cpu % 2)) | |
| pd_max_spare_cap_cpu = i; | |
| } | |
| } | |
| } | |
| } | |
| if (pd_max_spare_cap_running_rt_cpu != -1) { | |
| cpumask_set_cpu(pd_max_spare_cap_running_rt_cpu, &cm[MAX_SPARE_CAP_RUNNING_RT]); | |
| } | |
| /* set the best_idle_cpu of each cluster */ | |
| if (pd_best_idle_cpu != -1) { | |
| if (task_fits) { | |
| cpumask_set_cpu(pd_best_idle_cpu, &cm[IDLE_FIT]); | |
| } else { | |
| cpumask_set_cpu(pd_best_idle_cpu, &cm[IDLE_UNFIT]); | |
| } | |
| } | |
| /* set the best_important_cpu of each cluster */ | |
| if (pd_most_unimportant_cpu != -1) { | |
| if (task_fits) { | |
| cpumask_set_cpu(pd_most_unimportant_cpu, &cm[UNIMPORTANT_FIT]); | |
| } else { | |
| cpumask_set_cpu(pd_most_unimportant_cpu, &cm[UNIMPORTANT_UNFIT]); | |
| } | |
| } | |
| /* set the packing cpu of max_spare_cap of each cluster */ | |
| if (pd_best_packing_cpu != -1) | |
| cpumask_set_cpu(pd_best_packing_cpu, &cm[PACKING]); | |
| /* set the max_spare_cap_cpu of each cluster */ | |
| if (pd_max_spare_cap_cpu != -1) | |
| cpumask_set_cpu(pd_max_spare_cap_cpu, &cm[MAX_SPARE_CAP]); | |
| } | |
| if (prefer_idle) { | |
| preferred_idle_mask = get_preferred_idle_mask(p); | |
| cpumask_or(&cm[IDLE_UNPREFERRED], &cm[IDLE_FIT], &cm[IDLE_UNFIT]); | |
| cpumask_andnot(&cm[IDLE_UNPREFERRED], &cm[IDLE_UNPREFERRED], preferred_idle_mask); | |
| // If there is no fit idle CPU in preferred_idle_mask, ignore it | |
| if (task_fits_capacity(p, cpumask_last(preferred_idle_mask))) | |
| cpumask_and(&cm[IDLE_FIT], &cm[IDLE_FIT], preferred_idle_mask); | |
| cpumask_and(&cm[IDLE_UNFIT], &cm[IDLE_UNFIT], preferred_idle_mask); | |
| } | |
| /* Assign candidates based on search order. */ | |
| if (prefer_fit) { | |
| if (!cpumask_empty(&cm[IDLE_FIT])) { | |
| cpumask_copy(&candidates, &cm[IDLE_FIT]); | |
| } else if (!cpumask_empty(&cm[UNIMPORTANT_FIT])) { | |
| cpumask_copy(&candidates, &cm[UNIMPORTANT_FIT]); | |
| } else if (idle_max_cap_cpu != -1 && unimportant_max_spare_cap_cpu == -1) { | |
| cpumask_set_cpu(idle_max_cap_cpu, &candidates); | |
| } else if (idle_max_cap_cpu == -1 && unimportant_max_spare_cap_cpu != -1) { | |
| cpumask_set_cpu(unimportant_max_spare_cap_cpu, &candidates); | |
| } else if (idle_max_cap_cpu != -1 && unimportant_max_spare_cap_cpu != -1) { | |
| if (idle_max_cap >= unimportant_max_spare_cap) | |
| cpumask_set_cpu(idle_max_cap_cpu, &candidates); | |
| else | |
| cpumask_set_cpu(unimportant_max_spare_cap_cpu, &candidates); | |
| } else if (!cpumask_empty(&cm[MAX_SPARE_CAP])) { | |
| cpumask_copy(&candidates, &cm[MAX_SPARE_CAP]); | |
| } else if (!cpumask_empty(&cm[MAX_SPARE_CAP_RUNNING_RT])){ | |
| cpumask_copy(&candidates, &cm[MAX_SPARE_CAP_RUNNING_RT]); | |
| } | |
| } else { | |
| if (!cpumask_empty(&cm[IDLE_FIT])) { | |
| cpumask_copy(&candidates, &cm[IDLE_FIT]); | |
| } else if (!cpumask_empty(&cm[IDLE_UNFIT])) { | |
| /* Assign biggest cpu core found for unfit case. */ | |
| cpumask_set_cpu(cpumask_last(&cm[IDLE_UNFIT]), &candidates); | |
| } else if (!cpumask_empty(&cm[UNIMPORTANT_FIT])) { | |
| cpumask_copy(&candidates, &cm[UNIMPORTANT_FIT]); | |
| } else if (!cpumask_empty(&cm[UNIMPORTANT_UNFIT])) { | |
| cpumask_set_cpu(cpumask_last(&cm[UNIMPORTANT_UNFIT]), &candidates); | |
| } else if (!cpumask_empty(&cm[IDLE_UNPREFERRED])) { | |
| cpumask_copy(&candidates, &cm[IDLE_UNPREFERRED]); | |
| } else if (!cpumask_empty(&cm[PACKING])) { | |
| cpumask_copy(&candidates, &cm[PACKING]); | |
| } else if (!cpumask_empty(&cm[MAX_SPARE_CAP])) { | |
| cpumask_copy(&candidates, &cm[MAX_SPARE_CAP]); | |
| } | |
| } | |
| cpumask_andnot(&candidates_temp, &candidates, get_group_cfs_skip_mask(p)); | |
| if (!cpumask_empty(&candidates_temp)) | |
| cpumask_copy(&candidates, &candidates_temp); | |
| weight = cpumask_weight(&candidates); | |
| best_energy_cpu = most_spare_cap_cpu != -1 ? most_spare_cap_cpu: least_loaded_cpu; | |
| /* Bail out if no candidate was found. */ | |
| if (weight == 0) | |
| goto out; | |
| /* Bail out if only 1 candidate was found. */ | |
| if (weight == 1) { | |
| best_energy_cpu = cpumask_first(&candidates); | |
| goto out; | |
| } | |
| /* Compute Energy */ | |
| best_exit_lat = UINT_MAX; | |
| pd = rcu_dereference(rd->pd); | |
| for_each_cpu(i, &candidates) { | |
| exit_lat = 0; | |
| if (cpu_is_idle(i)) { | |
| idle_state = idle_get_state(cpu_rq(i)); | |
| if (idle_state) | |
| exit_lat = idle_state->exit_latency; | |
| } | |
| cur_energy = compute_energy(p, i, pd, exit_lat); | |
| if (cur_energy < best_energy) { | |
| best_energy = cur_energy; | |
| best_energy_cpu = i; | |
| best_exit_lat = exit_lat; | |
| } else if (cur_energy == best_energy) { | |
| if (exit_lat < best_exit_lat) { | |
| best_energy_cpu = i; | |
| best_exit_lat = exit_lat; | |
| } else if (exit_lat == best_exit_lat) { | |
| /* Prefer prev cpu or this cpu. */ | |
| if (i == prev_cpu || | |
| (best_energy_cpu != prev_cpu && i == this_cpu)) { | |
| best_energy_cpu = i; | |
| } | |
| } | |
| } | |
| } | |
| out: | |
| rcu_read_unlock(); | |
| trace_sched_find_energy_efficient_cpu(p, prefer_idle, prefer_fit, | |
| task_importance, cm, best_energy_cpu); | |
| return best_energy_cpu; | |
| } | |
| #define IOWAIT_BOOST_MIN (SCHED_CAPACITY_SCALE / 8) | |
| void rvh_set_iowait_pixel_mod(void *data, struct task_struct *p, struct rq *rq, | |
| int *should_iowait_boost) | |
| { | |
| struct vendor_task_struct *vp = get_vendor_task_struct(p); | |
| struct vendor_rq_struct *vrq = get_vendor_rq_struct(task_rq(p)); | |
| bool boosted = uclamp_boosted_pixel_mod(p); | |
| if (p->in_iowait && boosted) { | |
| if (!vp->iowait_boost) | |
| vp->iowait_boost = IOWAIT_BOOST_MIN; | |
| else | |
| vp->iowait_boost = min_t(unsigned long, | |
| vp->iowait_boost << 1, | |
| sched_per_task_iowait_boost_max_value); | |
| } else { | |
| if (!boosted) { | |
| vp->iowait_boost = 0; | |
| } else { | |
| vp->iowait_boost >>= 1; | |
| if (vp->iowait_boost < IOWAIT_BOOST_MIN) | |
| vp->iowait_boost = 0; | |
| } | |
| } | |
| *should_iowait_boost = vp->iowait_boost; | |
| /* | |
| * If we should boost, then we will send an update to cpufreq governor | |
| * as soon as we return from here which will then take into account the | |
| * vrq->iowait_boost value we just updated. So the expectation is that | |
| * we can have one enqueue at a time and this value will be taken into | |
| * account by the governor immediately during this enqueue() call. | |
| * | |
| * Only exception if rate limit rejects the freq update; then we are | |
| * already out of luck anyway for this particular enqueue(). | |
| */ | |
| if (*should_iowait_boost) | |
| vrq->iowait_boost = vp->iowait_boost; | |
| } | |
| void rvh_cpu_overutilized_pixel_mod(void *data, int cpu, int *overutilized) | |
| { | |
| unsigned long rq_util_min = uclamp_rq_get(cpu_rq(cpu), UCLAMP_MIN); | |
| unsigned long rq_util_max = uclamp_rq_get(cpu_rq(cpu), UCLAMP_MAX); | |
| *overutilized = !util_fits_cpu(cpu_util(cpu), rq_util_min, rq_util_max, cpu); | |
| } | |
| /* | |
| * Caller must have called apply_dvfs_headroom() before doing the mapping to | |
| * frequency. | |
| */ | |
| unsigned long map_util_freq_pixel_mod(unsigned long util, unsigned long freq, | |
| unsigned long cap, int cpu) | |
| { | |
| freq = freq * util / cap; | |
| #if IS_ENABLED(CONFIG_PIXEL_EM) | |
| if (static_branch_likely(&skip_inefficient_opps_enable)) | |
| { | |
| struct pixel_em_profile **profile_ptr_snapshot; | |
| profile_ptr_snapshot = READ_ONCE(vendor_sched_pixel_em_profile); | |
| if (profile_ptr_snapshot) { | |
| struct pixel_em_profile *profile = READ_ONCE(*profile_ptr_snapshot); | |
| if (profile) { | |
| struct pixel_em_cluster *cluster = profile->cpu_to_cluster[cpu]; | |
| struct pixel_em_opp *opp; | |
| int i; | |
| freq = map_scaling_freq(cpu, freq); | |
| for (i = 0; i < cluster->num_opps; i++) { | |
| opp = &cluster->opps[i]; | |
| if (opp->freq >= freq && !opp->inefficient) | |
| break; | |
| } | |
| SCHED_WARN_ON(opp->inefficient); | |
| freq = opp->freq; | |
| } | |
| } | |
| } | |
| #endif | |
| return map_scaling_freq(cpu, freq); | |
| } | |
| static inline struct uclamp_se | |
| uclamp_tg_restrict_pixel_mod(struct task_struct *p, enum uclamp_id clamp_id) | |
| { | |
| struct uclamp_se uc_req = p->uclamp_req[clamp_id]; | |
| struct vendor_task_struct *vp = get_vendor_task_struct(p); | |
| struct vendor_inheritance_struct *vi = get_vendor_inheritance_struct(p); | |
| bool is_adpf = get_adpf(p, true); | |
| int i = 0; | |
| #if IS_ENABLED(CONFIG_UCLAMP_TASK_GROUP) | |
| unsigned int tg_min, tg_max, vnd_min, vnd_max, value; | |
| unsigned int nice_min = uclamp_none(UCLAMP_MIN), nice_max = uclamp_none(UCLAMP_MAX); | |
| unsigned int thermal_uclamp_max = thermal_cap[task_cpu(p)].uclamp_max; | |
| // Task prio specific restriction | |
| get_uclamp_on_nice(p, UCLAMP_MIN, &nice_min); | |
| get_uclamp_on_nice(p, UCLAMP_MAX, &nice_max); | |
| // Task group restriction | |
| /* | |
| * Tasks in autogroups or root task group should have uclamp_min is 0. | |
| * uclamp_min is defined as a LIMIT rather than a PROTECTION at that | |
| * level. | |
| */ | |
| if (task_group_is_autogroup(task_group(p))) | |
| tg_min = 0; | |
| else if (task_group(p) == &root_task_group) | |
| tg_min = 0; | |
| else | |
| tg_min = task_group(p)->uclamp[UCLAMP_MIN].value; | |
| tg_max = task_group(p)->uclamp[UCLAMP_MAX].value; | |
| // Vendor group restriction | |
| vnd_min = vg[vp->group].uc_req[UCLAMP_MIN].value; | |
| vnd_max = is_adpf ? | |
| uclamp_none(UCLAMP_MAX) : vg[vp->group].uc_req[UCLAMP_MAX].value; | |
| if (get_auto_uclamp_max(p) && !is_adpf) { | |
| vp->auto_uclamp_max_flags |= AUTO_UCLAMP_MAX_FLAG_GROUP; | |
| vnd_max = sched_auto_uclamp_max[task_cpu(p)]; | |
| } else { | |
| vp->auto_uclamp_max_flags &= ~AUTO_UCLAMP_MAX_FLAG_GROUP; | |
| } | |
| value = uc_req.value; | |
| value = clamp(value, max(nice_min, max(tg_min, vnd_min)), | |
| min(nice_max, min(tg_max, vnd_max))); | |
| // inherited uclamp restriction | |
| for (; i < VI_MAX; i++) | |
| value = clamp(value, vi->uclamp[i][UCLAMP_MIN], vi->uclamp[i][UCLAMP_MAX]); | |
| // prefer high capacity cpu | |
| if (clamp_id == UCLAMP_MIN && get_prefer_high_cap(p)) | |
| value = max(value, (unsigned int)capacity_orig_of(pixel_cluster_start_cpu[0]) + 1); | |
| /* Boost tasks during suspend/resume */ | |
| if (clamp_id == UCLAMP_MIN && in_suspend_resume) | |
| value = max(value, SCHED_CAPACITY_SCALE/4); | |
| // For uclamp min, if task has a valid per-task setting that is lower than or equal to its | |
| // group value, increase the final uclamp value by 1. This would have effect only on | |
| // importance metrics which is used in task placement, and little effect on cpufreq. | |
| if (clamp_id == UCLAMP_MIN && uc_req.value <= max(tg_min, vnd_min) && uc_req.user_defined | |
| && value < SCHED_CAPACITY_SCALE) | |
| value = value + 1; | |
| // adding 1 to ensure we can detect tasks that has | |
| // uclamp_max == thermal_uclamp_max in util_fits_cpu | |
| if (clamp_id == UCLAMP_MAX && thermal_uclamp_max != SCHED_CAPACITY_SCALE) { | |
| if (!is_adpf) | |
| value = min(value, thermal_uclamp_max + 1); | |
| else | |
| value = min(value, min_t(unsigned int, SCHED_CAPACITY_SCALE, thermal_uclamp_max * | |
| thermal_cap_margin[task_cpu(p)] >> SCHED_CAPACITY_SHIFT)); | |
| } | |
| // For low prio unthrottled task, reduce its uclamp.max by 1 which | |
| // would affect task importance in cpu_rq thus affect task placement. | |
| // It should have no effect in cpufreq. | |
| if (clamp_id == UCLAMP_MAX && p->prio > DEFAULT_PRIO) | |
| value = min_t(unsigned int, UCLAMP_BUCKET_DELTA * (UCLAMP_BUCKETS - 1) - 1, value); | |
| uc_req.value = value; | |
| uc_req.bucket_id = get_bucket_id(value); | |
| #endif | |
| return uc_req; | |
| } | |
| void rvh_uclamp_eff_get_pixel_mod(void *data, struct task_struct *p, enum uclamp_id clamp_id, | |
| struct uclamp_se *uclamp_max, struct uclamp_se *uclamp_eff, | |
| int *ret) | |
| { | |
| struct uclamp_se uc_req; | |
| *ret = 1; | |
| uc_req = uclamp_tg_restrict_pixel_mod(p, clamp_id); | |
| /* System default restrictions always apply */ | |
| if (unlikely(uc_req.value > uclamp_max->value)) { | |
| *uclamp_eff = *uclamp_max; | |
| return; | |
| } | |
| *uclamp_eff = uc_req; | |
| return; | |
| } | |
| void initialize_vendor_group_property(void) | |
| { | |
| int i; | |
| unsigned int min_val = 0; | |
| unsigned int max_val = SCHED_CAPACITY_SCALE; | |
| for (i = 0; i < VG_MAX; i++) { | |
| vg[i].prefer_idle = false; | |
| vg[i].prefer_high_cap = false; | |
| vg[i].task_spreading = false; | |
| vg[i].auto_prefer_fit = false; | |
| #if !IS_ENABLED(CONFIG_USE_VENDOR_GROUP_UTIL) | |
| vg[i].group_throttle = max_val; | |
| #endif | |
| cpumask_copy(&vg[i].preferred_idle_mask_low, cpu_possible_mask); | |
| cpumask_copy(&vg[i].preferred_idle_mask_mid, cpu_possible_mask); | |
| cpumask_copy(&vg[i].preferred_idle_mask_high, cpu_possible_mask); | |
| vg[i].uclamp_min_on_nice_low_value = uclamp_none(UCLAMP_MIN); | |
| vg[i].uclamp_min_on_nice_mid_value = uclamp_none(UCLAMP_MIN); | |
| vg[i].uclamp_min_on_nice_high_value = uclamp_none(UCLAMP_MIN); | |
| vg[i].uclamp_max_on_nice_low_value = uclamp_none(UCLAMP_MAX); | |
| vg[i].uclamp_max_on_nice_mid_value = uclamp_none(UCLAMP_MAX); | |
| vg[i].uclamp_max_on_nice_high_value = uclamp_none(UCLAMP_MAX); | |
| vg[i].uclamp_min_on_nice_low_prio = THREAD_PRIORITY_LOWEST; | |
| vg[i].uclamp_min_on_nice_mid_prio = THREAD_PRIORITY_BACKGROUND; | |
| vg[i].uclamp_min_on_nice_high_prio = DEFAULT_PRIO; | |
| vg[i].uclamp_max_on_nice_low_prio = THREAD_PRIORITY_LOWEST; | |
| vg[i].uclamp_max_on_nice_mid_prio = THREAD_PRIORITY_BACKGROUND; | |
| vg[i].uclamp_max_on_nice_high_prio = DEFAULT_PRIO; | |
| vg[i].uclamp_min_on_nice_enable = false; | |
| vg[i].uclamp_max_on_nice_enable = false; | |
| if (i == VG_SYSTEM) { | |
| vg[i].uc_req[UCLAMP_MIN].value = early_boot_boost_uclamp_min; | |
| } else { | |
| vg[i].uc_req[UCLAMP_MIN].value = min_val; | |
| } | |
| vg[i].uc_req[UCLAMP_MIN].bucket_id = get_bucket_id(vg[i].uc_req[UCLAMP_MIN].value); | |
| vg[i].uc_req[UCLAMP_MIN].user_defined = false; | |
| vg[i].uc_req[UCLAMP_MAX].value = max_val; | |
| vg[i].uc_req[UCLAMP_MAX].bucket_id = get_bucket_id(max_val); | |
| vg[i].uc_req[UCLAMP_MAX].user_defined = false; | |
| #if IS_ENABLED(CONFIG_USE_VENDOR_GROUP_UTIL) | |
| vg[i].ug = UG_AUTO; | |
| #endif | |
| vg[i].rampup_multiplier = 1; | |
| vg[i].disable_util_est = false; | |
| vg[i].qos_adpf_enable = false; | |
| vg[i].qos_prefer_idle_enable = false; | |
| vg[i].qos_prefer_fit_enable = false; | |
| vg[i].qos_boost_prio_enable = false; | |
| vg[i].qos_preempt_wakeup_enable = false; | |
| vg[i].qos_auto_uclamp_max_enable = false; | |
| vg[i].qos_prefer_high_cap_enable = false; | |
| vg[i].qos_rampup_multiplier_enable = false; | |
| } | |
| #if IS_ENABLED(CONFIG_USE_VENDOR_GROUP_UTIL) | |
| for (i = 0; i < UG_MAX; i++) { | |
| #if IS_ENABLED(CONFIG_USE_GROUP_THROTTLE) | |
| ug[i].group_throttle = max_val; | |
| #endif | |
| ug[i].uc_req[UCLAMP_MIN].value = min_val; | |
| ug[i].uc_req[UCLAMP_MAX].value = max_val; | |
| } | |
| #endif | |
| } | |
| void rvh_check_preempt_wakeup_pixel_mod(void *data, struct rq *rq, struct task_struct *p, | |
| bool *preempt, bool *nopreempt, int wake_flags, struct sched_entity *se, | |
| struct sched_entity *pse, int next_buddy_marked, unsigned int granularity) | |
| { | |
| if (!entity_is_task(se) || !entity_is_task(pse)) | |
| return; | |
| if(!get_preempt_wakeup(task_of(se)) && get_preempt_wakeup(task_of(pse))) { | |
| if (!next_buddy_marked) | |
| set_next_buddy(pse); | |
| *preempt = true; | |
| return; | |
| } | |
| } | |
| void rvh_util_est_update_pixel_mod(void *data, struct cfs_rq *cfs_rq, struct task_struct *p, | |
| bool task_sleep, int *ret) | |
| { | |
| long last_ewma_diff, last_enqueued_diff; | |
| struct util_est ue; | |
| *ret = 1; | |
| if (!sched_feat(UTIL_EST)) | |
| return; | |
| if (static_branch_likely(&auto_dvfs_headroom_enable)) { | |
| struct vendor_task_struct *vp = get_vendor_task_struct(p); | |
| unsigned int rampup_multiplier = get_rampup_multiplier(p); | |
| if (vg[get_vendor_group(p)].disable_util_est) { | |
| p->se.avg.util_est.enqueued = 0; | |
| p->se.avg.util_est.ewma = 0; | |
| return; | |
| } | |
| if (vp->ignore_util_est_update && rampup_multiplier) | |
| return; | |
| } | |
| /* | |
| * Skip update of task's estimated utilization when the task has not | |
| * yet completed an activation, e.g. being migrated. | |
| */ | |
| if (!task_sleep) | |
| return; | |
| /* | |
| * If the PELT values haven't changed since enqueue time, | |
| * skip the util_est update. | |
| */ | |
| ue = p->se.avg.util_est; | |
| if (!static_branch_likely(&auto_dvfs_headroom_enable)) { | |
| if (ue.enqueued & UTIL_AVG_UNCHANGED) | |
| return; | |
| } | |
| last_enqueued_diff = ue.enqueued; | |
| /* | |
| * Reset EWMA on utilization increases, the moving average is used only | |
| * to smooth utilization decreases. | |
| */ | |
| ue.enqueued = task_util(p); | |
| if (sched_feat(UTIL_EST_FASTUP)) { | |
| if (ue.ewma < ue.enqueued) { | |
| ue.ewma = ue.enqueued; | |
| goto done; | |
| } | |
| } | |
| /* | |
| * Skip update of task's estimated utilization when its members are | |
| * already ~1% close to its last activation value. | |
| */ | |
| last_ewma_diff = ue.enqueued - ue.ewma; | |
| last_enqueued_diff -= ue.enqueued; | |
| if (within_margin(last_ewma_diff, UTIL_EST_MARGIN)) { | |
| if (!within_margin(last_enqueued_diff, UTIL_EST_MARGIN)) | |
| goto done; | |
| return; | |
| } | |
| /* | |
| * To avoid overestimation of actual task utilization, skip updates if | |
| * we cannot grant there is idle time in this CPU. | |
| */ | |
| if (!static_branch_likely(&auto_dvfs_headroom_enable)) { | |
| if (task_util(p) > capacity_orig_of(cpu_of(rq_of(cfs_rq)))) | |
| return; | |
| } | |
| /* | |
| * Update Task's estimated utilization | |
| * | |
| * When *p completes an activation we can consolidate another sample | |
| * of the task size. This is done by storing the current PELT value | |
| * as ue.enqueued and by using this value to update the Exponential | |
| * Weighted Moving Average (EWMA): | |
| * | |
| * ewma(t) = w * task_util(p) + (1-w) * ewma(t-1) | |
| * = w * task_util(p) + ewma(t-1) - w * ewma(t-1) | |
| * = w * (task_util(p) - ewma(t-1)) + ewma(t-1) | |
| * = w * ( last_ewma_diff ) + ewma(t-1) | |
| * = w * (last_ewma_diff + ewma(t-1) / w) | |
| * | |
| * Where 'w' is the weight of new samples, which is configured to be | |
| * 0.25, thus making w=1/4 ( >>= UTIL_EST_WEIGHT_SHIFT) | |
| */ | |
| ue.ewma <<= UTIL_EST_WEIGHT_SHIFT; | |
| ue.ewma += last_ewma_diff; | |
| ue.ewma >>= UTIL_EST_WEIGHT_SHIFT; | |
| done: | |
| ue.enqueued |= UTIL_AVG_UNCHANGED; | |
| WRITE_ONCE(p->se.avg.util_est, ue); | |
| trace_sched_util_est_se_tp(&p->se); | |
| } | |
| #if !IS_ENABLED(CONFIG_USE_VENDOR_GROUP_UTIL) | |
| void rvh_cpu_cgroup_online_pixel_mod(void *data, struct cgroup_subsys_state *css) | |
| { | |
| struct vendor_task_group_struct *vtg; | |
| const char *name = css_tg(css)->css.cgroup->kn->name; | |
| vtg = get_vendor_task_group_struct(css_tg(css)); | |
| if (strcmp(name, "system") == 0) { | |
| vtg->group = VG_SYSTEM; | |
| } else if (strcmp(name, "top-app") == 0) { | |
| vtg->group = VG_TOPAPP; | |
| } else if (strcmp(name, "foreground") == 0) { | |
| vtg->group = VG_FOREGROUND; | |
| } else if (strcmp(name, "camera-daemon") == 0) { | |
| vtg->group = VG_CAMERA; | |
| } else if (strcmp(name, "background") == 0) { | |
| vtg->group = VG_BACKGROUND; | |
| } else if (strcmp(name, "system-background") == 0) { | |
| vtg->group = VG_SYSTEM_BACKGROUND; | |
| } else if (strcmp(name, "nnapi-hal") == 0) { | |
| vtg->group = VG_NNAPI_HAL; | |
| } else if (strcmp(name, "rt") == 0) { | |
| vtg->group = VG_RT; | |
| } else if (strcmp(name, "dex2oat") == 0) { | |
| vtg->group = VG_DEX2OAT; | |
| } else { | |
| vtg->group = VG_SYSTEM; | |
| } | |
| } | |
| #endif | |
| void rvh_post_init_entity_util_avg_pixel_mod(void *data, struct sched_entity *se) | |
| { | |
| if (!static_branch_likely(&auto_dvfs_headroom_enable)) { | |
| struct cfs_rq *cfs_rq = cfs_rq_of(se); | |
| struct sched_avg *sa = &se->avg; | |
| long cpu_scale = arch_scale_cpu_capacity(cpu_of(rq_of(cfs_rq))); | |
| if (cfs_rq->avg.util_avg == 0) { | |
| sa->util_avg = vendor_sched_util_post_init_scale * cpu_scale / SCHED_CAPACITY_SCALE; | |
| sa->runnable_avg = sa->util_avg; | |
| } | |
| } else { | |
| struct sched_avg *sa = &se->avg; | |
| sa->util_avg = 0; | |
| sa->runnable_avg = 0; | |
| sa->util_est.enqueued = 0 | UTIL_AVG_UNCHANGED; | |
| sa->util_est.ewma = 0; | |
| } | |
| } | |
| void vh_sched_uclamp_validate_pixel_mod(void *data, struct task_struct *tsk, | |
| const struct sched_attr *attr, bool user, | |
| int *ret, bool *done) | |
| { | |
| struct vendor_task_struct *vtsk = get_vendor_task_struct(tsk); | |
| if (attr->sched_util_max != AUTO_UCLAMP_MAX_MAGIC) { | |
| vtsk->auto_uclamp_max_flags &= ~AUTO_UCLAMP_MAX_FLAG_TASK; | |
| return; | |
| } | |
| *done = true; | |
| *ret = 0; | |
| } | |
| void vh_sched_setscheduler_uclamp_pixel_mod(void *data, struct task_struct *tsk, int clamp_id, | |
| unsigned int value) | |
| { | |
| struct vendor_task_struct *vtsk = get_vendor_task_struct(tsk); | |
| trace_sched_setscheduler_uclamp(tsk, clamp_id, value); | |
| if (trace_clock_set_rate_enabled()) { | |
| char trace_name[32] = {0}; | |
| scnprintf(trace_name, sizeof(trace_name), "%s_%d", | |
| clamp_id == UCLAMP_MIN ? "UCLAMP_MIN" : "UCLAMP_MAX", tsk->pid); | |
| trace_clock_set_rate(trace_name, value, raw_smp_processor_id()); | |
| } | |
| if (clamp_id == UCLAMP_MAX && value == AUTO_UCLAMP_MAX_MAGIC) { | |
| vtsk->auto_uclamp_max_flags |= AUTO_UCLAMP_MAX_FLAG_TASK; | |
| uclamp_se_set(&tsk->uclamp_req[UCLAMP_MAX], | |
| sched_auto_uclamp_max[task_cpu(tsk)], | |
| true); | |
| } | |
| update_misfit_status(tsk, task_rq(tsk)); | |
| } | |
| static inline void uclamp_fork_pixel_mod(struct task_struct *p, struct task_struct *orig) | |
| { | |
| enum uclamp_id clamp_id; | |
| if (likely(!get_adpf(orig, false) && !get_power_efficiency(p))) | |
| return; | |
| for_each_clamp_id(clamp_id) { | |
| uclamp_se_set(&p->uclamp_req[clamp_id], | |
| uclamp_none(clamp_id), false); | |
| } | |
| } | |
| void vh_dup_task_struct_pixel_mod(void *data, struct task_struct *tsk, struct task_struct *orig) | |
| { | |
| struct vendor_task_struct *v_tsk, *v_orig; | |
| if (wait_for_init) | |
| return; | |
| v_tsk = get_vendor_task_struct(tsk); | |
| v_orig = get_vendor_task_struct(orig); | |
| uclamp_fork_pixel_mod(tsk, orig); | |
| init_vendor_task_struct(v_tsk); | |
| v_tsk->group = v_orig->group; | |
| v_tsk->orig_prio = orig->static_prio; | |
| } | |
| void rvh_select_task_rq_fair_pixel_mod(void *data, struct task_struct *p, int prev_cpu, int sd_flag, | |
| int wake_flags, int *target_cpu) | |
| { | |
| int sync = (wake_flags & WF_SYNC) && !(current->flags & PF_EXITING); | |
| bool sync_wakeup = false, prefer_prev = false; | |
| int cpu; | |
| /* sync wake up */ | |
| cpu = smp_processor_id(); | |
| set_auto_prefer_high_cap(p, sync && cpu >= pixel_cluster_start_cpu[1]); | |
| if (sync && cpu_rq(cpu)->nr_running == 1 && cpumask_test_cpu(cpu, p->cpus_ptr) && | |
| task_fits_capacity(p, cpu)) { | |
| *target_cpu = cpu; | |
| sync_wakeup = true; | |
| goto out; | |
| } | |
| /* prefer prev cpu */ | |
| if (!cpumask_test_cpu(prev_cpu, &skip_prefer_prev_mask) && cpu_active(prev_cpu) && | |
| cpu_is_idle(prev_cpu) && task_fits_capacity(p, prev_cpu) && | |
| cpumask_test_cpu(prev_cpu, p->cpus_ptr) && check_preferred_idle_mask(p, prev_cpu) && | |
| !cpumask_test_cpu(prev_cpu, get_group_cfs_skip_mask(p))) { | |
| struct cpuidle_state *idle_state; | |
| unsigned int exit_lat = UINT_MAX; | |
| rcu_read_lock(); | |
| idle_state = idle_get_state(cpu_rq(prev_cpu)); | |
| if (sched_cpu_idle(prev_cpu)) | |
| exit_lat = 0; | |
| else if (idle_state) | |
| exit_lat = idle_state->exit_latency; | |
| rcu_read_unlock(); | |
| if (exit_lat <= C1_EXIT_LATENCY) { | |
| prefer_prev = true; | |
| *target_cpu = prev_cpu; | |
| goto out; | |
| } | |
| } | |
| if (sd_flag & SD_BALANCE_WAKE) { | |
| *target_cpu = find_energy_efficient_cpu(p, prev_cpu, NULL); | |
| } | |
| out: | |
| if (trace_sched_select_task_rq_fair_enabled()) | |
| trace_sched_select_task_rq_fair(p, task_util_est(p), | |
| sync_wakeup, get_adpf(p, true), prefer_prev, | |
| get_vendor_task_struct(p)->auto_prefer_high_cap, | |
| get_vendor_group(p), | |
| uclamp_eff_value_pixel_mod(p, UCLAMP_MIN), | |
| uclamp_eff_value_pixel_mod(p, UCLAMP_MAX), | |
| prev_cpu, *target_cpu); | |
| set_auto_prefer_high_cap(p, false); | |
| } | |
| void rvh_set_user_nice_locked_pixel_mod(void *data, struct task_struct *p, long *nice) | |
| { | |
| struct vendor_task_struct *vp; | |
| unsigned long irqflags; | |
| if (!vendor_sched_boost_adpf_prio) | |
| return; | |
| if (p->prio < MAX_RT_PRIO) | |
| return; | |
| vp = get_vendor_task_struct(p); | |
| if (get_boost_prio(p)) { | |
| raw_spin_lock_irqsave(&vp->lock, irqflags); | |
| p->normal_prio = p->static_prio = vp->orig_prio = NICE_TO_PRIO(*nice); | |
| raw_spin_unlock_irqrestore(&vp->lock, irqflags); | |
| if (unlikely(p->prio != NICE_TO_PRIO(MIN_NICE))) { | |
| p->prio = NICE_TO_PRIO(MIN_NICE); | |
| prio_changed(p, vp->orig_prio, p->prio); | |
| } | |
| } | |
| } | |
| void rvh_setscheduler_pixel_mod(void *data, struct task_struct *p) | |
| { | |
| struct vendor_task_struct *vp; | |
| unsigned long irqflags; | |
| if (!vendor_sched_boost_adpf_prio) | |
| return; | |
| if (p->prio < MAX_RT_PRIO) | |
| return; | |
| vp = get_vendor_task_struct(p); | |
| if (get_boost_prio(p)) { | |
| raw_spin_lock_irqsave(&vp->lock, irqflags); | |
| vp->orig_prio = p->static_prio; | |
| raw_spin_unlock_irqrestore(&vp->lock, irqflags); | |
| if (unlikely(p->prio != NICE_TO_PRIO(MIN_NICE))) { | |
| p->prio = NICE_TO_PRIO(MIN_NICE); | |
| reweight_task(p, p->prio - MAX_RT_PRIO); | |
| } | |
| } | |
| } | |
| static struct task_struct *detach_important_task(struct rq *src_rq, int dst_cpu) | |
| { | |
| struct task_struct *p = NULL, *best_task = NULL, *backup = NULL, | |
| *backup_ui = NULL, *backup_unfit = NULL; | |
| lockdep_assert_held(&src_rq->__lock); | |
| list_for_each_entry_reverse(p, &src_rq->cfs_tasks, se.group_node) { | |
| bool is_ui = false, is_boost = false; | |
| if (!cpumask_test_cpu(dst_cpu, p->cpus_ptr)) | |
| continue; | |
| if (task_on_cpu(src_rq, p)) | |
| continue; | |
| if (!get_prefer_idle(p)) | |
| continue; | |
| if (get_adpf(p, true)) | |
| is_ui = true; | |
| else if (uclamp_eff_value_pixel_mod(p, UCLAMP_MIN) > 0) | |
| is_boost = true; | |
| if (!is_ui && !is_boost) | |
| continue; | |
| /* | |
| * Do not pull tasks in skip mask unless it is ADPF task. | |
| */ | |
| if(!is_ui) | |
| continue; | |
| if (task_fits_capacity(p, dst_cpu)) { | |
| if (!task_fits_capacity(p, src_rq->cpu)) { | |
| // if task is fit for new cpu but not old cpu | |
| // stop if we found an ADPF UI task | |
| // use it as backup if we found a boost task | |
| if (is_ui) { | |
| best_task = p; | |
| break; | |
| } | |
| backup = p; | |
| } else { | |
| if (is_ui) { | |
| backup_ui = p; | |
| continue; | |
| } | |
| if (!backup) | |
| backup = p; | |
| } | |
| } else { | |
| // if new idle is not capable, use it as backup but not for UI task. | |
| if (!is_ui) | |
| backup_unfit = p; | |
| } | |
| } | |
| if (best_task) | |
| p = best_task; | |
| else if (backup_ui) | |
| p = backup_ui; | |
| else if (backup) | |
| p = backup; | |
| else if (backup_unfit) | |
| p = backup_unfit; | |
| else | |
| p = NULL; | |
| if (p) { | |
| /* detach_task */ | |
| deactivate_task(src_rq, p, DEQUEUE_NOCLOCK); | |
| set_task_cpu(p, dst_cpu); | |
| if (backup_unfit) | |
| cpu_rq(dst_cpu)->misfit_task_load = p->se.avg.load_avg; | |
| else | |
| cpu_rq(dst_cpu)->misfit_task_load = 0; | |
| } | |
| return p; | |
| } | |
| /* | |
| * In our newidle_balance, We ignore update next_interval, which could lead to | |
| * the next tick might being triggered prematurely. but that should be fine since | |
| * this is should not be happening often enough. | |
| */ | |
| void sched_newidle_balance_pixel_mod(void *data, struct rq *this_rq, struct rq_flags *rf, | |
| int *pulled_task, int *done) | |
| { | |
| int cpu; | |
| struct rq *src_rq; | |
| struct task_struct *p = NULL; | |
| struct rq_flags src_rf; | |
| int this_cpu = this_rq->cpu; | |
| struct vendor_rq_struct *this_vrq = get_vendor_rq_struct(this_rq); | |
| struct vendor_rq_struct *src_vrq; | |
| if (SCHED_WARN_ON(atomic_read(&this_vrq->num_adpf_tasks))) | |
| atomic_set(&this_vrq->num_adpf_tasks, 0); | |
| /* | |
| * We must set idle_stamp _before_ calling idle_balance(), such that we | |
| * measure the duration of idle_balance() as idle time. | |
| */ | |
| this_rq->idle_stamp = rq_clock(this_rq); | |
| /* | |
| * Do not pull tasks towards !active CPUs... | |
| */ | |
| if (!cpu_active(this_cpu)) | |
| return; | |
| /* | |
| * This is OK, because current is on_cpu, which avoids it being picked | |
| * for load-balance and preemption/IRQs are still disabled avoiding | |
| * further scheduler activity on it and we're being very careful to | |
| * re-start the picking loop. | |
| */ | |
| rq_unpin_lock(this_rq, rf); | |
| raw_spin_rq_unlock(this_rq); | |
| this_cpu = this_rq->cpu; | |
| for_each_cpu(cpu, cpu_active_mask) { | |
| int cpu_importnace = READ_ONCE(cpu_rq(cpu)->uclamp[UCLAMP_MIN].value) + | |
| READ_ONCE(cpu_rq(cpu)->uclamp[UCLAMP_MAX].value); | |
| if (cpu == this_cpu) | |
| continue; | |
| src_rq = cpu_rq(cpu); | |
| src_vrq = get_vendor_rq_struct(src_rq); | |
| /* | |
| * Don't bother if no latency sensitive tasks on src_rq or if | |
| * there's only one. | |
| * | |
| * If there are too many other tasks on the rq but a single | |
| * latency_sensitive one, we should rely on skip_next_buddy() | |
| * and better wake up placement logic to avoid this situation | |
| * first. We can consider pulling here too in the future if we | |
| * find there are corner cases hard to fix otherwise. | |
| * | |
| * If something with a high nice value is stealing time, then | |
| * we should ask questions about why nice value is set so high. | |
| * | |
| * If an RT task is stealing time, then we should ask why wake | |
| * up path placed the two in the same CPU. We have an avoidance | |
| * strategy for this. | |
| */ | |
| if (atomic_read(&src_vrq->num_adpf_tasks) <= 1) | |
| continue; | |
| rq_lock_irqsave(src_rq, &src_rf); | |
| update_rq_clock(src_rq); | |
| if (src_rq->active_balance) { | |
| rq_unlock_irqrestore(src_rq, &src_rf); | |
| continue; | |
| } | |
| if (cpu_is_idle(cpu)) { | |
| rq_unlock_irqrestore(src_rq, &src_rf); | |
| continue; | |
| } | |
| /* src_cpu is just waken up by tasks */ | |
| if (src_rq->curr == src_rq->idle) { | |
| rq_unlock_irqrestore(src_rq, &src_rf); | |
| continue; | |
| } | |
| /* we assume rt task will release cpu soon */ | |
| if (src_rq->curr->prio < MAX_RT_PRIO) { | |
| rq_unlock_irqrestore(src_rq, &src_rf); | |
| continue; | |
| } | |
| if (cpu_importnace <= DEFAULT_IMPRATANCE_THRESHOLD || !src_rq->cfs.nr_running) { | |
| rq_unlock_irqrestore(src_rq, &src_rf); | |
| continue; | |
| } | |
| rcu_read_lock(); | |
| p = detach_important_task(src_rq, this_cpu); | |
| rq_unlock(src_rq, &src_rf); | |
| if (p) { | |
| attach_one_task(this_rq, p); | |
| local_irq_restore(src_rf.flags); | |
| rcu_read_unlock(); | |
| break; | |
| } | |
| local_irq_restore(src_rf.flags); | |
| rcu_read_unlock(); | |
| } | |
| raw_spin_rq_lock(this_rq); | |
| /* | |
| * While browsing the domains, we released the rq lock, a task could | |
| * have been enqueued in the meantime. Since we're not going idle, | |
| * pretend we pulled a task. | |
| */ | |
| if (this_rq->cfs.h_nr_running && !*pulled_task) | |
| *pulled_task = 1; | |
| /* Is there a task of a high priority class? */ | |
| if (this_rq->nr_running != this_rq->cfs.h_nr_running) | |
| *pulled_task = -1; | |
| if (*pulled_task) | |
| this_rq->idle_stamp = 0; | |
| if (*pulled_task != 0) { | |
| *done = 1; | |
| /* TODO: need implement update_blocked_averages */ | |
| } | |
| rq_repin_lock(this_rq, rf); | |
| return; | |
| } | |
| void rvh_can_migrate_task_pixel_mod(void *data, struct task_struct *mp, | |
| int dst_cpu, int *can_migrate) | |
| { | |
| struct vendor_rq_struct *vrq = get_vendor_rq_struct(cpu_rq(dst_cpu)); | |
| if (!get_prefer_idle(mp)) | |
| return; | |
| if (atomic_read(&vrq->num_adpf_tasks)) | |
| *can_migrate = 0; | |
| if (cpumask_test_cpu(dst_cpu, get_group_cfs_skip_mask(mp))) | |
| *can_migrate = 0; | |
| } | |
| #if IS_ENABLED(CONFIG_USE_VENDOR_GROUP_UTIL) | |
| void rvh_attach_entity_load_avg_pixel_mod(void *data, struct cfs_rq *cfs_rq, | |
| struct sched_entity *se) | |
| { | |
| if (entity_is_task(se)) | |
| attach_vendor_group_util(cfs_rq, se); | |
| } | |
| void rvh_detach_entity_load_avg_pixel_mod(void *data, struct cfs_rq *cfs_rq, | |
| struct sched_entity *se) | |
| { | |
| if (entity_is_task(se)) | |
| detach_vendor_group_util(cfs_rq, se); | |
| } | |
| void rvh_update_load_avg_pixel_mod(void *data, u64 now, struct cfs_rq *cfs_rq, | |
| struct sched_entity *se) | |
| { | |
| if (entity_is_task(se)) | |
| update_vendor_group_util(now, cfs_rq, se); | |
| } | |
| void rvh_remove_entity_load_avg_pixel_mod(void *data, struct cfs_rq *cfs_rq, | |
| struct sched_entity *se) | |
| { | |
| if (entity_is_task(se)) | |
| remove_vendor_group_util(cfs_rq, se); | |
| } | |
| void rvh_update_blocked_fair_pixel_mod(void *data, struct rq *rq) | |
| { | |
| update_vendor_group_util_blocked(rq_clock_pelt(rq), rq); | |
| } | |
| #endif | |
| void rvh_enqueue_task_fair_pixel_mod(void *data, struct rq *rq, struct task_struct *p, int flags) | |
| { | |
| struct vendor_task_struct *vp = get_vendor_task_struct(p); | |
| bool force_cpufreq_update = false; | |
| unsigned long irqflags; | |
| if (!static_branch_unlikely(&enqueue_dequeue_ready)) | |
| return; | |
| if (!task_on_rq_migrating(p)) { | |
| vp->prev_sum_exec_runtime = p->se.sum_exec_runtime; | |
| vp->util_enqueued = task_util(p); | |
| vp->ignore_util_est_update = true; | |
| /* | |
| * If the utilization is rising, keep accounting for delta_exec | |
| * so that we can catch up with a bursty task appropriately. | |
| * | |
| * If the utilization is stable, it is hard to know whether | |
| * this is due to the slow update rate (at top end util will | |
| * grow much slower) or due to the fact it has settled. If it | |
| * started to drop that's an indication the task is settling | |
| * and we can stop accounting for delta exec. | |
| * | |
| * We need to ensure util_est_update() doesn't mess up the | |
| * util_est we're building up, so set a flag to ignore it. But | |
| * allow us to latch back to util_avg once we have settled. | |
| */ | |
| if (vp->util_enqueued < vp->prev_util_enqueued) { | |
| vp->delta_exec = 0; | |
| vp->ignore_util_est_update = false; | |
| } | |
| } | |
| if (get_adpf(p, true)) | |
| inc_adpf_counter(p, rq); | |
| #if IS_ENABLED(CONFIG_USE_VENDOR_GROUP_UTIL) | |
| if (likely(sched_feat(UTIL_EST))) { | |
| int group = get_utilization_group(p, get_vendor_group(p)); | |
| raw_spin_lock_irqsave(&vendor_cfs_util[group][rq->cpu].lock, irqflags); | |
| vendor_cfs_util[group][rq->cpu].util_est += _task_util_est(p); | |
| raw_spin_unlock_irqrestore(&vendor_cfs_util[group][rq->cpu].lock, irqflags); | |
| } | |
| #endif | |
| /* Can only process uclamp after sched_slice() was updated */ | |
| if (uclamp_is_used()) | |
| force_cpufreq_update = apply_uclamp_filters(rq, p); | |
| /* | |
| * If we have applied the uclamp filter, we'll unconditionally request | |
| * a frequency update which should take new changes into account. | |
| */ | |
| if (uclamp_is_used() && force_cpufreq_update) | |
| cpufreq_update_util(rq, SCHED_PIXEL_FORCE_UPDATE); | |
| } | |
| void rvh_dequeue_task_fair_pixel_mod(void *data, struct rq *rq, struct task_struct *p, int flags) | |
| { | |
| struct vendor_task_struct *vp = get_vendor_task_struct(p); | |
| struct vendor_rq_struct *vrq = get_vendor_rq_struct(rq); | |
| unsigned long irqflags; | |
| if (!static_branch_unlikely(&enqueue_dequeue_ready)) | |
| return; | |
| if (!task_on_rq_migrating(p)) { | |
| vp->prev_sum_exec_runtime = p->se.sum_exec_runtime; | |
| vp->prev_util_enqueued = vp->util_enqueued; | |
| } | |
| if (get_adpf(p, true)) | |
| dec_adpf_counter(p, rq); | |
| #if IS_ENABLED(CONFIG_USE_VENDOR_GROUP_UTIL) | |
| if (likely(sched_feat(UTIL_EST))) { | |
| int group = get_utilization_group(p, get_vendor_group(p)); | |
| raw_spin_lock_irqsave(&vendor_cfs_util[group][rq->cpu].lock, irqflags); | |
| if (!rq->cfs.h_nr_running) | |
| vendor_cfs_util[group][rq->cpu].util_est = 0; | |
| else | |
| lsub_positive(&vendor_cfs_util[group][rq->cpu].util_est, | |
| _task_util_est(p)); | |
| raw_spin_unlock_irqrestore(&vendor_cfs_util[group][rq->cpu].lock, irqflags); | |
| } | |
| #endif | |
| /* Resetting uclamp filter is handled in dequeue_task() */ | |
| /* | |
| * If this task has boosted the rq then clear the boost once it no | |
| * longer runs. | |
| * | |
| * No aggregation done here. Several tasks enqueued at the same | |
| * time will be hit by the rate limit. | |
| */ | |
| if (vp->iowait_boost == vrq->iowait_boost) | |
| vrq->iowait_boost = 0; | |
| } | |
| void vh_sched_resume_end(void *data, void *unused) | |
| { | |
| max_load_balance_interval = vh_sched_max_load_balance_interval; | |
| sysctl_sched_min_granularity = vh_sched_min_granularity_ns; | |
| sysctl_sched_wakeup_granularity = vh_sched_wakeup_granularity_ns; | |
| sysctl_sched_latency = vh_sched_latency_ns; | |
| } | |
| static int find_target_cap(unsigned int freq, unsigned int cpu) | |
| { | |
| struct pixel_em_profile **profile_ptr_snapshot, *profile; | |
| struct pixel_em_cluster *em_cluster; | |
| struct task_struct *g, *p; | |
| int target_cap = 0, i; | |
| profile_ptr_snapshot = READ_ONCE(vendor_sched_pixel_em_profile); | |
| if (!profile_ptr_snapshot) { | |
| pr_err("Pixel EM profile not found\n"); | |
| return -EINVAL; | |
| } | |
| profile = READ_ONCE(*profile_ptr_snapshot); | |
| if (!profile) { | |
| pr_err("Pixel EM profile not found\n"); | |
| return -EINVAL; | |
| } | |
| em_cluster = profile->cpu_to_cluster[cpu]; | |
| if (freq >= em_cluster->opps[em_cluster->num_opps - 1].freq) | |
| target_cap = SCHED_CAPACITY_SCALE; | |
| else { | |
| for (i = 0; i < em_cluster->num_opps; i++) { | |
| struct pixel_em_opp *opp = &em_cluster->opps[i]; | |
| if (opp->freq >= freq) { | |
| /* use -3 to make sure the various conversion logic | |
| * which can end up rounding up or down by 1 | |
| * doesn't lead to wrong results. | |
| */ | |
| target_cap = opp->capacity - 3; | |
| break; | |
| } | |
| } | |
| } | |
| if (target_cap <= 0) | |
| return -EINVAL; | |
| for_each_cpu(cpu, &em_cluster->cpus) { | |
| pr_debug("updating CPU:%d uclamp value to :%u freq value to:%u \n", | |
| cpu, target_cap, freq); | |
| thermal_cap[cpu].uclamp_max = target_cap; | |
| thermal_cap[cpu].freq = freq; | |
| } | |
| rcu_read_lock(); | |
| for_each_process_thread(g, p) { | |
| if (!task_on_rq_queued(p)) | |
| continue; | |
| if (!cpumask_test_cpu(task_cpu(p), &em_cluster->cpus)) | |
| continue; | |
| uclamp_update_active(p, UCLAMP_MAX); | |
| } | |
| rcu_read_unlock(); | |
| return 0; | |
| } | |
| /** | |
| * sched_thermal_freq_cap() - Thermal cooling device will use this API | |
| * to place a max frequency cap for a cluster. Ideally the first CPU in that cluster will | |
| * be given as input. Thermal will expect the freq cap be applied to all CPUs in | |
| * that cluster. | |
| * | |
| * @cpu - Any cpu that belongs to a cluster. | |
| * @freq - CPU frequency cap that needs to be applied to all CPUs in that cluster. | |
| * Return: 0 on success or error value. | |
| */ | |
| int sched_thermal_freq_cap(unsigned int cpu, unsigned int freq) | |
| { | |
| int ret; | |
| mutex_lock(&thermal_cap_mutex); | |
| ret = find_target_cap(freq, cpu); | |
| mutex_unlock(&thermal_cap_mutex); | |
| return ret; | |
| } | |
| EXPORT_SYMBOL_GPL(sched_thermal_freq_cap); | |
| /** | |
| * update_thermal_freq_cap() - pixel_em will use this API to update thermal cap (ulcamp min) | |
| * and keep original capped max frequency. | |
| * @cpu - Any cpu whose energy model will be changed by pixel_em. | |
| */ | |
| void update_thermal_freq_cap(unsigned int cpu) | |
| { | |
| if (cpu >= nr_cpu_ids) | |
| return; | |
| mutex_lock(&thermal_cap_mutex); | |
| WARN_ON(!find_target_cap(thermal_cap[cpu].freq, cpu)); | |
| mutex_unlock(&thermal_cap_mutex); | |
| } | |
| EXPORT_SYMBOL_GPL(update_thermal_freq_cap); | |
| void update_task_real_cap(struct task_struct *p) | |
| { | |
| struct vendor_task_struct *vp = get_vendor_task_struct(p); | |
| u64 now = sched_clock(); | |
| u64 dur_ns = now - vp->real_cap_update_ns; | |
| unsigned long irqflags; | |
| raw_spin_lock_irqsave(&vp->lock, irqflags); | |
| if (vp->real_cap_avg) { | |
| u64 workload = vp->real_cap_avg * (vp->real_cap_total_ns >> 10) | |
| + (dur_ns >> 10) * capacity_curr_of(task_cpu(p)); | |
| vp->real_cap_total_ns += dur_ns; | |
| vp->real_cap_avg = workload / (vp->real_cap_total_ns >> 10); | |
| } else { | |
| vp->real_cap_total_ns = dur_ns; | |
| vp->real_cap_avg = capacity_curr_of(task_cpu(p)); | |
| } | |
| vp->real_cap_update_ns = now; | |
| raw_spin_unlock_irqrestore(&vp->lock, irqflags); | |
| if (trace_clock_set_rate_enabled()) { | |
| char trace_name[32] = {0}; | |
| scnprintf(trace_name, sizeof(trace_name), "%d_REAL_CAP_AVG", (int)(p->pid)); | |
| trace_clock_set_rate(trace_name, vp->real_cap_avg, raw_smp_processor_id()); | |
| } | |
| } | |
| EXPORT_SYMBOL_GPL(update_task_real_cap); |