blob: f9df27899f745cd40b6cb2afc8ce13b5363dea2f [file]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2021 MediaTek Inc.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/io.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/cpumask.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/timekeeping.h>
#include <linux/energy_model.h>
#include <trace/hooks/topology.h>
#include <trace/hooks/sched.h>
#include <sched/sched.h>
#include "sched_sys_common.h"
#include "eas_plus.h"
#include "eas_trace.h"
DEFINE_PER_CPU(struct task_rotate_work, task_rotate_works);
bool big_task_rotation_enable = true;
int sched_min_cap_orig_cpu = -1;
#define TASK_ROTATION_THRESHOLD_NS 6000000
#define HEAVY_TASK_NUM 4
unsigned int capacity_margin = 1280;
struct task_rotate_work {
struct work_struct w;
struct task_struct *src_task;
struct task_struct *dst_task;
int src_cpu;
int dst_cpu;
};
int is_reserved(int cpu)
{
struct rq *rq = cpu_rq(cpu);
int reserved = 0;
struct rq_flags rf;
rq_lock(rq, &rf);
reserved = rq->active_balance;
rq_unlock(rq, &rf);
return reserved;
}
bool is_min_capacity_cpu(int cpu)
{
if (capacity_orig_of(cpu) == capacity_orig_of(sched_min_cap_orig_cpu))
return true;
return false;
}
bool is_max_capacity_cpu(int cpu)
{
return capacity_orig_of(cpu) == SCHED_CAPACITY_SCALE;
}
static void task_rotate_work_func(struct work_struct *work)
{
struct task_rotate_work *wr = container_of(work,
struct task_rotate_work, w);
int ret = -1;
struct rq *src_rq, *dst_rq;
ret = migrate_swap(wr->src_task, wr->dst_task,
task_cpu(wr->dst_task), task_cpu(wr->src_task));
if (ret == 0) {
trace_sched_big_task_rotation(wr->src_cpu, wr->dst_cpu,
wr->src_task->pid,
wr->dst_task->pid,
true);
}
put_task_struct(wr->src_task);
put_task_struct(wr->dst_task);
src_rq = cpu_rq(wr->src_cpu);
dst_rq = cpu_rq(wr->dst_cpu);
local_irq_disable();
double_rq_lock(src_rq, dst_rq);
src_rq->active_balance = 0;
dst_rq->active_balance = 0;
double_rq_unlock(src_rq, dst_rq);
local_irq_enable();
}
void task_rotate_work_init(void)
{
int i;
for_each_possible_cpu(i) {
struct task_rotate_work *wr = &per_cpu(task_rotate_works, i);
INIT_WORK(&wr->w, task_rotate_work_func);
}
}
void task_rotate_init(void)
{
int i, min_cap_orig_cpu = -1;
unsigned long min_orig_cap = ULONG_MAX;
/* find min_cap cpu */
for_each_possible_cpu(i) {
if (capacity_orig_of(i) >= min_orig_cap)
continue;
min_orig_cap = capacity_orig_of(i);
min_cap_orig_cpu = i;
}
if (min_cap_orig_cpu >= 0) {
sched_min_cap_orig_cpu = min_cap_orig_cpu;
pr_info("scheduler: min_cap_orig_cpu = %d\n",
sched_min_cap_orig_cpu);
} else
pr_info("scheduler: can not find min_cap_orig_cpu\n");
/* init rotate work */
task_rotate_work_init();
}
void task_check_for_rotation(struct rq *src_rq)
{
u64 wc, wait, max_wait = 0, run, max_run = 0;
int deserved_cpu = nr_cpu_ids, dst_cpu = nr_cpu_ids;
int i, src_cpu = cpu_of(src_rq);
struct rq *dst_rq;
struct task_rotate_work *wr = NULL;
int heavy_task = 0;
int force = 0;
if (!big_task_rotation_enable)
return;
if (is_max_capacity_cpu(src_cpu))
return;
if (cpu_paused(src_cpu))
return;
for_each_possible_cpu(i) {
struct rq *rq = cpu_rq(i);
struct task_struct *curr_task = rq->curr;
if (curr_task &&
!task_fits_capacity(curr_task, cpu_rq(i)->cpu_capacity))
heavy_task += 1;
if (heavy_task >= HEAVY_TASK_NUM)
break;
}
if (heavy_task < HEAVY_TASK_NUM)
return;
wc = ktime_get_raw_ns();
for_each_possible_cpu(i) {
struct rq *rq = cpu_rq(i);
if (cpu_paused(i))
continue;
if (!is_min_capacity_cpu(i))
continue;
if (!rq->misfit_task_load ||
(rq->curr->policy != SCHED_NORMAL))
continue;
if (is_reserved(i))
continue;
wait = wc - rq->curr->android_vendor_data1[3];
if (wait > max_wait) {
max_wait = wait;
deserved_cpu = i;
}
}
if (deserved_cpu != src_cpu)
return;
for_each_possible_cpu(i) {
struct rq *rq = cpu_rq(i);
if (cpu_paused(i))
continue;
if (capacity_orig_of(i) <= capacity_orig_of(src_cpu))
continue;
if (rq->curr->policy != SCHED_NORMAL)
continue;
if (rq->nr_running > 1)
continue;
if (is_reserved(i))
continue;
run = wc - rq->curr->android_vendor_data1[3];
if (run < TASK_ROTATION_THRESHOLD_NS)
continue;
if (run > max_run) {
max_run = run;
dst_cpu = i;
}
}
if (dst_cpu == nr_cpu_ids)
return;
dst_rq = cpu_rq(dst_cpu);
double_rq_lock(src_rq, dst_rq);
if (dst_rq->curr->policy == SCHED_NORMAL) {
if (!cpumask_test_cpu(dst_cpu,
src_rq->curr->cpus_ptr) ||
!cpumask_test_cpu(src_cpu,
dst_rq->curr->cpus_ptr)) {
double_rq_unlock(src_rq, dst_rq);
return;
}
if (cpu_paused(src_cpu) || cpu_paused(dst_cpu))
return;
if (!src_rq->active_balance && !dst_rq->active_balance) {
src_rq->active_balance = 1;
dst_rq->active_balance = 1;
get_task_struct(src_rq->curr);
get_task_struct(dst_rq->curr);
wr = &per_cpu(task_rotate_works, src_cpu);
wr->src_task = src_rq->curr;
wr->dst_task = dst_rq->curr;
wr->src_cpu = src_rq->cpu;
wr->dst_cpu = dst_rq->cpu;
force = 1;
}
}
double_rq_unlock(src_rq, dst_rq);
if (force) {
queue_work_on(src_cpu, system_highpri_wq, &wr->w);
trace_sched_big_task_rotation(wr->src_cpu, wr->dst_cpu,
wr->src_task->pid, wr->dst_task->pid,
false);
}
}
void set_big_task_rotation(bool enable)
{
big_task_rotation_enable = enable;
}
EXPORT_SYMBOL_GPL(set_big_task_rotation);
void rotat_after_enqueue_task(void __always_unused *data, struct rq *rq,
struct task_struct *p)
{
p->android_vendor_data1[3] = ktime_get_raw_ns();
}
void rotat_task_stats(void __always_unused *data,
struct task_struct *p)
{
p->android_vendor_data1[3] = ktime_get_raw_ns();
}
void rotat_task_newtask(void __always_unused *data,
struct task_struct *p, unsigned long clone_flags)
{
p->android_vendor_data1[3] = 0;
}