2020-09-24 01:49:20 -04:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
|
|
|
/*
|
|
|
|
* Copyright (c) 2019-2021, The Linux Foundation. All rights reserved.
|
|
|
|
*/
|
|
|
|
#include <linux/irq.h>
|
|
|
|
#include <linux/delay.h>
|
|
|
|
#include <trace/events/sched.h>
|
|
|
|
|
|
|
|
#include "qc_vas.h"
|
|
|
|
|
|
|
|
#ifdef CONFIG_SCHED_WALT
|
|
|
|
/* 1ms default for 20ms window size scaled to 1024 */
|
|
|
|
unsigned int sysctl_sched_min_task_util_for_boost = 51;
|
|
|
|
/* 0.68ms default for 20ms window size scaled to 1024 */
|
|
|
|
unsigned int sysctl_sched_min_task_util_for_colocation = 35;
|
|
|
|
|
|
|
|
int
|
|
|
|
kick_active_balance(struct rq *rq, struct task_struct *p, int new_cpu)
|
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
int rc = 0;
|
|
|
|
|
|
|
|
/* Invoke active balance to force migrate currently running task */
|
|
|
|
raw_spin_lock_irqsave(&rq->lock, flags);
|
|
|
|
if (!rq->active_balance) {
|
|
|
|
rq->active_balance = 1;
|
|
|
|
rq->push_cpu = new_cpu;
|
|
|
|
get_task_struct(p);
|
|
|
|
rq->wrq.push_task = p;
|
|
|
|
rc = 1;
|
|
|
|
}
|
|
|
|
raw_spin_unlock_irqrestore(&rq->lock, flags);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct walt_rotate_work {
|
|
|
|
struct work_struct w;
|
|
|
|
struct task_struct *src_task;
|
|
|
|
struct task_struct *dst_task;
|
|
|
|
int src_cpu;
|
|
|
|
int dst_cpu;
|
|
|
|
};
|
|
|
|
|
|
|
|
DEFINE_PER_CPU(struct walt_rotate_work, walt_rotate_works);
|
|
|
|
|
|
|
|
void walt_rotate_work_func(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct walt_rotate_work *wr = container_of(work,
|
|
|
|
struct walt_rotate_work, w);
|
2021-06-30 10:57:44 -04:00
|
|
|
struct rq *src_rq = cpu_rq(wr->src_cpu), *dst_rq = cpu_rq(wr->dst_cpu);
|
|
|
|
unsigned long flags;
|
2020-09-24 01:49:20 -04:00
|
|
|
|
|
|
|
migrate_swap(wr->src_task, wr->dst_task, wr->dst_cpu, wr->src_cpu);
|
|
|
|
|
|
|
|
put_task_struct(wr->src_task);
|
|
|
|
put_task_struct(wr->dst_task);
|
|
|
|
|
2021-06-30 10:57:44 -04:00
|
|
|
local_irq_save(flags);
|
|
|
|
double_rq_lock(src_rq, dst_rq);
|
|
|
|
|
|
|
|
dst_rq->active_balance = 0;
|
|
|
|
src_rq->active_balance = 0;
|
|
|
|
|
|
|
|
double_rq_unlock(src_rq, dst_rq);
|
|
|
|
local_irq_restore(flags);
|
|
|
|
|
2020-09-24 01:49:20 -04:00
|
|
|
clear_reserved(wr->src_cpu);
|
|
|
|
clear_reserved(wr->dst_cpu);
|
|
|
|
}
|
|
|
|
|
|
|
|
void walt_rotate_work_init(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for_each_possible_cpu(i) {
|
|
|
|
struct walt_rotate_work *wr = &per_cpu(walt_rotate_works, i);
|
|
|
|
|
|
|
|
INIT_WORK(&wr->w, walt_rotate_work_func);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#define WALT_ROTATION_THRESHOLD_NS 16000000
|
|
|
|
void walt_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 walt_rotate_work *wr = NULL;
|
|
|
|
|
|
|
|
if (!walt_rotation_enabled)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!is_min_capacity_cpu(src_cpu))
|
|
|
|
return;
|
|
|
|
|
|
|
|
wc = sched_ktime_clock();
|
|
|
|
for_each_possible_cpu(i) {
|
|
|
|
struct rq *rq = cpu_rq(i);
|
|
|
|
|
|
|
|
if (!is_min_capacity_cpu(i))
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (is_reserved(i))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!rq->misfit_task_load || rq->curr->sched_class !=
|
|
|
|
&fair_sched_class)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
wait = wc - rq->curr->wts.last_enqueued_ts;
|
|
|
|
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 (is_min_capacity_cpu(i))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (is_reserved(i))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (rq->curr->sched_class != &fair_sched_class)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (rq->nr_running > 1)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
run = wc - rq->curr->wts.last_enqueued_ts;
|
|
|
|
|
|
|
|
if (run < WALT_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);
|
2021-06-30 10:57:44 -04:00
|
|
|
if (dst_rq->curr->sched_class == &fair_sched_class &&
|
|
|
|
!src_rq->active_balance && !dst_rq->active_balance) {
|
2020-09-24 01:49:20 -04:00
|
|
|
get_task_struct(src_rq->curr);
|
|
|
|
get_task_struct(dst_rq->curr);
|
|
|
|
|
|
|
|
mark_reserved(src_cpu);
|
|
|
|
mark_reserved(dst_cpu);
|
|
|
|
wr = &per_cpu(walt_rotate_works, src_cpu);
|
|
|
|
|
|
|
|
wr->src_task = src_rq->curr;
|
|
|
|
wr->dst_task = dst_rq->curr;
|
|
|
|
|
|
|
|
wr->src_cpu = src_cpu;
|
|
|
|
wr->dst_cpu = dst_cpu;
|
2021-06-30 10:57:44 -04:00
|
|
|
dst_rq->active_balance = 1;
|
|
|
|
src_rq->active_balance = 1;
|
2020-09-24 01:49:20 -04:00
|
|
|
}
|
2021-06-30 10:57:44 -04:00
|
|
|
|
2020-09-24 01:49:20 -04:00
|
|
|
double_rq_unlock(src_rq, dst_rq);
|
|
|
|
|
|
|
|
if (wr)
|
|
|
|
queue_work_on(src_cpu, system_highpri_wq, &wr->w);
|
|
|
|
}
|
|
|
|
|
|
|
|
DEFINE_RAW_SPINLOCK(migration_lock);
|
|
|
|
void check_for_migration(struct rq *rq, struct task_struct *p)
|
|
|
|
{
|
|
|
|
int active_balance;
|
|
|
|
int new_cpu = -1;
|
|
|
|
int prev_cpu = task_cpu(p);
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (rq->misfit_task_load) {
|
|
|
|
if (rq->curr->state != TASK_RUNNING ||
|
|
|
|
rq->curr->nr_cpus_allowed == 1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (walt_rotation_enabled) {
|
|
|
|
raw_spin_lock(&migration_lock);
|
|
|
|
walt_check_for_rotation(rq);
|
|
|
|
raw_spin_unlock(&migration_lock);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
raw_spin_lock(&migration_lock);
|
|
|
|
rcu_read_lock();
|
|
|
|
new_cpu = find_energy_efficient_cpu(p, prev_cpu, 0, 1);
|
|
|
|
rcu_read_unlock();
|
|
|
|
if ((new_cpu >= 0) && (new_cpu != prev_cpu) &&
|
|
|
|
(capacity_orig_of(new_cpu) > capacity_orig_of(prev_cpu))) {
|
|
|
|
active_balance = kick_active_balance(rq, p, new_cpu);
|
|
|
|
if (active_balance) {
|
|
|
|
mark_reserved(new_cpu);
|
|
|
|
raw_spin_unlock(&migration_lock);
|
|
|
|
ret = stop_one_cpu_nowait(prev_cpu,
|
|
|
|
active_load_balance_cpu_stop, rq,
|
|
|
|
&rq->active_balance_work);
|
|
|
|
if (!ret)
|
|
|
|
clear_reserved(new_cpu);
|
|
|
|
else
|
|
|
|
wake_up_if_idle(new_cpu);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
raw_spin_unlock(&migration_lock);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int sched_init_task_load_show(struct seq_file *m, void *v)
|
|
|
|
{
|
|
|
|
struct inode *inode = m->private;
|
|
|
|
struct task_struct *p;
|
|
|
|
|
|
|
|
p = get_proc_task(inode);
|
|
|
|
if (!p)
|
|
|
|
return -ESRCH;
|
|
|
|
|
|
|
|
seq_printf(m, "%d\n", sched_get_init_task_load(p));
|
|
|
|
|
|
|
|
put_task_struct(p);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
ssize_t
|
|
|
|
sched_init_task_load_write(struct file *file, const char __user *buf,
|
|
|
|
size_t count, loff_t *offset)
|
|
|
|
{
|
|
|
|
struct inode *inode = file_inode(file);
|
|
|
|
struct task_struct *p;
|
|
|
|
char buffer[PROC_NUMBUF];
|
|
|
|
int init_task_load, err;
|
|
|
|
|
|
|
|
memset(buffer, 0, sizeof(buffer));
|
|
|
|
if (count > sizeof(buffer) - 1)
|
|
|
|
count = sizeof(buffer) - 1;
|
|
|
|
if (copy_from_user(buffer, buf, count)) {
|
|
|
|
err = -EFAULT;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = kstrtoint(strstrip(buffer), 0, &init_task_load);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
p = get_proc_task(inode);
|
|
|
|
if (!p)
|
|
|
|
return -ESRCH;
|
|
|
|
|
|
|
|
err = sched_set_init_task_load(p, init_task_load);
|
|
|
|
|
|
|
|
put_task_struct(p);
|
|
|
|
|
|
|
|
out:
|
|
|
|
return err < 0 ? err : count;
|
|
|
|
}
|
|
|
|
|
|
|
|
int sched_init_task_load_open(struct inode *inode, struct file *filp)
|
|
|
|
{
|
|
|
|
return single_open(filp, sched_init_task_load_show, inode);
|
|
|
|
}
|
|
|
|
|
|
|
|
int sched_group_id_show(struct seq_file *m, void *v)
|
|
|
|
{
|
|
|
|
struct inode *inode = m->private;
|
|
|
|
struct task_struct *p;
|
|
|
|
|
|
|
|
p = get_proc_task(inode);
|
|
|
|
if (!p)
|
|
|
|
return -ESRCH;
|
|
|
|
|
|
|
|
seq_printf(m, "%d\n", sched_get_group_id(p));
|
|
|
|
|
|
|
|
put_task_struct(p);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
ssize_t
|
|
|
|
sched_group_id_write(struct file *file, const char __user *buf,
|
|
|
|
size_t count, loff_t *offset)
|
|
|
|
{
|
|
|
|
struct inode *inode = file_inode(file);
|
|
|
|
struct task_struct *p;
|
|
|
|
char buffer[PROC_NUMBUF];
|
|
|
|
int group_id, err;
|
|
|
|
|
|
|
|
memset(buffer, 0, sizeof(buffer));
|
|
|
|
if (count > sizeof(buffer) - 1)
|
|
|
|
count = sizeof(buffer) - 1;
|
|
|
|
if (copy_from_user(buffer, buf, count)) {
|
|
|
|
err = -EFAULT;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = kstrtoint(strstrip(buffer), 0, &group_id);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
p = get_proc_task(inode);
|
|
|
|
if (!p)
|
|
|
|
return -ESRCH;
|
|
|
|
|
|
|
|
err = sched_set_group_id(p, group_id);
|
|
|
|
|
|
|
|
put_task_struct(p);
|
|
|
|
|
|
|
|
out:
|
|
|
|
return err < 0 ? err : count;
|
|
|
|
}
|
|
|
|
|
|
|
|
int sched_group_id_open(struct inode *inode, struct file *filp)
|
|
|
|
{
|
|
|
|
return single_open(filp, sched_group_id_show, inode);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_SMP
|
|
|
|
/*
|
|
|
|
* Print out various scheduling related per-task fields:
|
|
|
|
*/
|
|
|
|
int sched_wake_up_idle_show(struct seq_file *m, void *v)
|
|
|
|
{
|
|
|
|
struct inode *inode = m->private;
|
|
|
|
struct task_struct *p;
|
|
|
|
|
|
|
|
p = get_proc_task(inode);
|
|
|
|
if (!p)
|
|
|
|
return -ESRCH;
|
|
|
|
|
|
|
|
seq_printf(m, "%d\n", sched_get_wake_up_idle(p));
|
|
|
|
|
|
|
|
put_task_struct(p);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
ssize_t
|
|
|
|
sched_wake_up_idle_write(struct file *file, const char __user *buf,
|
|
|
|
size_t count, loff_t *offset)
|
|
|
|
{
|
|
|
|
struct inode *inode = file_inode(file);
|
|
|
|
struct task_struct *p;
|
|
|
|
char buffer[PROC_NUMBUF];
|
|
|
|
int wake_up_idle, err;
|
|
|
|
|
|
|
|
memset(buffer, 0, sizeof(buffer));
|
|
|
|
if (count > sizeof(buffer) - 1)
|
|
|
|
count = sizeof(buffer) - 1;
|
|
|
|
if (copy_from_user(buffer, buf, count)) {
|
|
|
|
err = -EFAULT;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = kstrtoint(strstrip(buffer), 0, &wake_up_idle);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
p = get_proc_task(inode);
|
|
|
|
if (!p)
|
|
|
|
return -ESRCH;
|
|
|
|
|
|
|
|
err = sched_set_wake_up_idle(p, wake_up_idle);
|
|
|
|
|
|
|
|
put_task_struct(p);
|
|
|
|
|
|
|
|
out:
|
|
|
|
return err < 0 ? err : count;
|
|
|
|
}
|
|
|
|
|
|
|
|
int sched_wake_up_idle_open(struct inode *inode, struct file *filp)
|
|
|
|
{
|
|
|
|
return single_open(filp, sched_wake_up_idle_show, inode);
|
|
|
|
}
|
|
|
|
|
|
|
|
int group_balance_cpu_not_isolated(struct sched_group *sg)
|
|
|
|
{
|
|
|
|
cpumask_t cpus;
|
|
|
|
|
|
|
|
cpumask_and(&cpus, sched_group_span(sg), group_balance_mask(sg));
|
|
|
|
cpumask_andnot(&cpus, &cpus, cpu_isolated_mask);
|
|
|
|
return cpumask_first(&cpus);
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_SMP */
|
|
|
|
|
|
|
|
#ifdef CONFIG_PROC_SYSCTL
|
|
|
|
static void sched_update_updown_migrate_values(bool up)
|
|
|
|
{
|
|
|
|
int i = 0, cpu;
|
|
|
|
struct walt_sched_cluster *cluster;
|
|
|
|
int cap_margin_levels = num_sched_clusters - 1;
|
|
|
|
|
|
|
|
if (cap_margin_levels > 1) {
|
|
|
|
/*
|
|
|
|
* No need to worry about CPUs in last cluster
|
|
|
|
* if there are more than 2 clusters in the system
|
|
|
|
*/
|
|
|
|
for_each_sched_cluster(cluster) {
|
|
|
|
for_each_cpu(cpu, &cluster->cpus) {
|
|
|
|
if (up)
|
|
|
|
sched_capacity_margin_up[cpu] =
|
|
|
|
sysctl_sched_capacity_margin_up[i];
|
|
|
|
else
|
|
|
|
sched_capacity_margin_down[cpu] =
|
|
|
|
sysctl_sched_capacity_margin_down[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (++i >= cap_margin_levels)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for_each_possible_cpu(cpu) {
|
|
|
|
if (up)
|
|
|
|
sched_capacity_margin_up[cpu] =
|
|
|
|
sysctl_sched_capacity_margin_up[0];
|
|
|
|
else
|
|
|
|
sched_capacity_margin_down[cpu] =
|
|
|
|
sysctl_sched_capacity_margin_down[0];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int sched_updown_migrate_handler(struct ctl_table *table, int write,
|
|
|
|
void __user *buffer, size_t *lenp,
|
|
|
|
loff_t *ppos)
|
|
|
|
{
|
|
|
|
int ret, i;
|
|
|
|
unsigned int *data = (unsigned int *)table->data;
|
|
|
|
unsigned int *old_val;
|
|
|
|
static DEFINE_MUTEX(mutex);
|
|
|
|
int cap_margin_levels = num_sched_clusters ? num_sched_clusters - 1 : 0;
|
|
|
|
|
|
|
|
if (cap_margin_levels <= 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
mutex_lock(&mutex);
|
|
|
|
|
|
|
|
if (table->maxlen != (sizeof(unsigned int) * cap_margin_levels))
|
|
|
|
table->maxlen = sizeof(unsigned int) * cap_margin_levels;
|
|
|
|
|
|
|
|
if (!write) {
|
|
|
|
ret = proc_douintvec_capacity(table, write, buffer, lenp, ppos);
|
|
|
|
goto unlock_mutex;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Cache the old values so that they can be restored
|
|
|
|
* if either the write fails (for example out of range values)
|
|
|
|
* or the downmigrate and upmigrate are not in sync.
|
|
|
|
*/
|
|
|
|
old_val = kzalloc(table->maxlen, GFP_KERNEL);
|
|
|
|
if (!old_val) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto unlock_mutex;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(old_val, data, table->maxlen);
|
|
|
|
|
|
|
|
ret = proc_douintvec_capacity(table, write, buffer, lenp, ppos);
|
|
|
|
|
|
|
|
if (ret) {
|
|
|
|
memcpy(data, old_val, table->maxlen);
|
|
|
|
goto free_old_val;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < cap_margin_levels; i++) {
|
|
|
|
if (sysctl_sched_capacity_margin_up[i] >
|
|
|
|
sysctl_sched_capacity_margin_down[i]) {
|
|
|
|
memcpy(data, old_val, table->maxlen);
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto free_old_val;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sched_update_updown_migrate_values(data ==
|
|
|
|
&sysctl_sched_capacity_margin_up[0]);
|
|
|
|
|
|
|
|
free_old_val:
|
|
|
|
kfree(old_val);
|
|
|
|
unlock_mutex:
|
|
|
|
mutex_unlock(&mutex);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_PROC_SYSCTL */
|
|
|
|
|
|
|
|
int sched_isolate_count(const cpumask_t *mask, bool include_offline)
|
|
|
|
{
|
|
|
|
cpumask_t count_mask = CPU_MASK_NONE;
|
|
|
|
|
|
|
|
if (include_offline) {
|
|
|
|
cpumask_complement(&count_mask, cpu_online_mask);
|
|
|
|
cpumask_or(&count_mask, &count_mask, cpu_isolated_mask);
|
|
|
|
cpumask_and(&count_mask, &count_mask, mask);
|
|
|
|
} else {
|
|
|
|
cpumask_and(&count_mask, mask, cpu_isolated_mask);
|
|
|
|
}
|
|
|
|
|
|
|
|
return cpumask_weight(&count_mask);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_HOTPLUG_CPU
|
|
|
|
static int do_isolation_work_cpu_stop(void *data)
|
|
|
|
{
|
|
|
|
unsigned int cpu = smp_processor_id();
|
|
|
|
struct rq *rq = cpu_rq(cpu);
|
|
|
|
struct rq_flags rf;
|
|
|
|
|
|
|
|
local_irq_disable();
|
|
|
|
|
|
|
|
irq_migrate_all_off_this_cpu();
|
|
|
|
|
|
|
|
sched_ttwu_pending();
|
|
|
|
|
|
|
|
/* Update our root-domain */
|
|
|
|
rq_lock(rq, &rf);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Temporarily mark the rq as offline. This will allow us to
|
|
|
|
* move tasks off the CPU.
|
|
|
|
*/
|
|
|
|
if (rq->rd) {
|
|
|
|
BUG_ON(!cpumask_test_cpu(cpu, rq->rd->span));
|
|
|
|
set_rq_offline(rq);
|
|
|
|
}
|
|
|
|
|
|
|
|
migrate_tasks(rq, &rf, false);
|
|
|
|
|
|
|
|
if (rq->rd)
|
|
|
|
set_rq_online(rq);
|
|
|
|
rq_unlock(rq, &rf);
|
|
|
|
|
|
|
|
clear_walt_request(cpu);
|
|
|
|
local_irq_enable();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int do_unisolation_work_cpu_stop(void *data)
|
|
|
|
{
|
|
|
|
watchdog_enable(smp_processor_id());
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void sched_update_group_capacities(int cpu)
|
|
|
|
{
|
|
|
|
struct sched_domain *sd;
|
|
|
|
|
|
|
|
mutex_lock(&sched_domains_mutex);
|
|
|
|
rcu_read_lock();
|
|
|
|
|
|
|
|
for_each_domain(cpu, sd) {
|
|
|
|
int balance_cpu = group_balance_cpu(sd->groups);
|
|
|
|
|
|
|
|
init_sched_groups_capacity(cpu, sd);
|
|
|
|
/*
|
|
|
|
* Need to ensure this is also called with balancing
|
|
|
|
* cpu.
|
|
|
|
*/
|
|
|
|
if (cpu != balance_cpu)
|
|
|
|
init_sched_groups_capacity(balance_cpu, sd);
|
|
|
|
}
|
|
|
|
|
|
|
|
rcu_read_unlock();
|
|
|
|
mutex_unlock(&sched_domains_mutex);
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned int cpu_isolation_vote[NR_CPUS];
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 1) CPU is isolated and cpu is offlined:
|
|
|
|
* Unisolate the core.
|
|
|
|
* 2) CPU is not isolated and CPU is offlined:
|
|
|
|
* No action taken.
|
|
|
|
* 3) CPU is offline and request to isolate
|
|
|
|
* Request ignored.
|
|
|
|
* 4) CPU is offline and isolated:
|
|
|
|
* Not a possible state.
|
|
|
|
* 5) CPU is online and request to isolate
|
|
|
|
* Normal case: Isolate the CPU
|
|
|
|
* 6) CPU is not isolated and comes back online
|
|
|
|
* Nothing to do
|
|
|
|
*
|
|
|
|
* Note: The client calling sched_isolate_cpu() is repsonsible for ONLY
|
|
|
|
* calling sched_unisolate_cpu() on a CPU that the client previously isolated.
|
|
|
|
* Client is also responsible for unisolating when a core goes offline
|
|
|
|
* (after CPU is marked offline).
|
|
|
|
*/
|
|
|
|
int sched_isolate_cpu(int cpu)
|
|
|
|
{
|
|
|
|
struct rq *rq;
|
|
|
|
cpumask_t avail_cpus;
|
|
|
|
int ret_code = 0;
|
|
|
|
u64 start_time = 0;
|
|
|
|
|
|
|
|
if (trace_sched_isolate_enabled())
|
|
|
|
start_time = sched_clock();
|
|
|
|
|
|
|
|
cpu_maps_update_begin();
|
|
|
|
|
|
|
|
cpumask_andnot(&avail_cpus, cpu_online_mask, cpu_isolated_mask);
|
|
|
|
|
|
|
|
if (cpu < 0 || cpu >= nr_cpu_ids || !cpu_possible(cpu) ||
|
|
|
|
!cpu_online(cpu) || cpu >= NR_CPUS) {
|
|
|
|
ret_code = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
rq = cpu_rq(cpu);
|
|
|
|
|
|
|
|
if (++cpu_isolation_vote[cpu] > 1)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
/* We cannot isolate ALL cpus in the system */
|
|
|
|
if (cpumask_weight(&avail_cpus) == 1) {
|
|
|
|
--cpu_isolation_vote[cpu];
|
|
|
|
ret_code = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* There is a race between watchdog being enabled by hotplug and
|
|
|
|
* core isolation disabling the watchdog. When a CPU is hotplugged in
|
|
|
|
* and the hotplug lock has been released the watchdog thread might
|
|
|
|
* not have run yet to enable the watchdog.
|
|
|
|
* We have to wait for the watchdog to be enabled before proceeding.
|
|
|
|
*/
|
|
|
|
if (!watchdog_configured(cpu)) {
|
|
|
|
msleep(20);
|
|
|
|
if (!watchdog_configured(cpu)) {
|
|
|
|
--cpu_isolation_vote[cpu];
|
|
|
|
ret_code = -EBUSY;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
set_cpu_isolated(cpu, true);
|
|
|
|
cpumask_clear_cpu(cpu, &avail_cpus);
|
|
|
|
|
|
|
|
/* Migrate timers */
|
|
|
|
smp_call_function_any(&avail_cpus, hrtimer_quiesce_cpu, &cpu, 1);
|
|
|
|
smp_call_function_any(&avail_cpus, timer_quiesce_cpu, &cpu, 1);
|
|
|
|
|
|
|
|
watchdog_disable(cpu);
|
|
|
|
irq_lock_sparse();
|
|
|
|
stop_cpus(cpumask_of(cpu), do_isolation_work_cpu_stop, 0);
|
|
|
|
irq_unlock_sparse();
|
|
|
|
|
|
|
|
calc_load_migrate(rq);
|
|
|
|
update_max_interval();
|
|
|
|
sched_update_group_capacities(cpu);
|
|
|
|
|
|
|
|
out:
|
|
|
|
cpu_maps_update_done();
|
|
|
|
trace_sched_isolate(cpu, cpumask_bits(cpu_isolated_mask)[0],
|
|
|
|
start_time, 1);
|
|
|
|
return ret_code;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Note: The client calling sched_isolate_cpu() is repsonsible for ONLY
|
|
|
|
* calling sched_unisolate_cpu() on a CPU that the client previously isolated.
|
|
|
|
* Client is also responsible for unisolating when a core goes offline
|
|
|
|
* (after CPU is marked offline).
|
|
|
|
*/
|
|
|
|
int sched_unisolate_cpu_unlocked(int cpu)
|
|
|
|
{
|
|
|
|
int ret_code = 0;
|
|
|
|
u64 start_time = 0;
|
|
|
|
|
|
|
|
if (cpu < 0 || cpu >= nr_cpu_ids || !cpu_possible(cpu)
|
|
|
|
|| cpu >= NR_CPUS) {
|
|
|
|
ret_code = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (trace_sched_isolate_enabled())
|
|
|
|
start_time = sched_clock();
|
|
|
|
|
|
|
|
if (!cpu_isolation_vote[cpu]) {
|
|
|
|
ret_code = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (--cpu_isolation_vote[cpu])
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
set_cpu_isolated(cpu, false);
|
|
|
|
update_max_interval();
|
|
|
|
sched_update_group_capacities(cpu);
|
|
|
|
|
|
|
|
if (cpu_online(cpu)) {
|
|
|
|
stop_cpus(cpumask_of(cpu), do_unisolation_work_cpu_stop, 0);
|
|
|
|
|
|
|
|
/* Kick CPU to immediately do load balancing */
|
|
|
|
if (!atomic_fetch_or(NOHZ_KICK_MASK, nohz_flags(cpu)))
|
|
|
|
smp_send_reschedule(cpu);
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
trace_sched_isolate(cpu, cpumask_bits(cpu_isolated_mask)[0],
|
|
|
|
start_time, 0);
|
|
|
|
return ret_code;
|
|
|
|
}
|
|
|
|
|
|
|
|
int sched_unisolate_cpu(int cpu)
|
|
|
|
{
|
|
|
|
int ret_code;
|
|
|
|
|
|
|
|
cpu_maps_update_begin();
|
|
|
|
ret_code = sched_unisolate_cpu_unlocked(cpu);
|
|
|
|
cpu_maps_update_done();
|
|
|
|
return ret_code;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Remove a task from the runqueue and pretend that it's migrating. This
|
|
|
|
* should prevent migrations for the detached task and disallow further
|
|
|
|
* changes to tsk_cpus_allowed.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
detach_one_task_core(struct task_struct *p, struct rq *rq,
|
|
|
|
struct list_head *tasks)
|
|
|
|
{
|
|
|
|
lockdep_assert_held(&rq->lock);
|
|
|
|
|
|
|
|
p->on_rq = TASK_ON_RQ_MIGRATING;
|
|
|
|
deactivate_task(rq, p, 0);
|
|
|
|
list_add(&p->se.group_node, tasks);
|
|
|
|
}
|
|
|
|
|
|
|
|
void attach_tasks_core(struct list_head *tasks, struct rq *rq)
|
|
|
|
{
|
|
|
|
struct task_struct *p;
|
|
|
|
|
|
|
|
lockdep_assert_held(&rq->lock);
|
|
|
|
|
|
|
|
while (!list_empty(tasks)) {
|
|
|
|
p = list_first_entry(tasks, struct task_struct, se.group_node);
|
|
|
|
list_del_init(&p->se.group_node);
|
|
|
|
|
|
|
|
BUG_ON(task_rq(p) != rq);
|
|
|
|
activate_task(rq, p, 0);
|
|
|
|
p->on_rq = TASK_ON_RQ_QUEUED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_HOTPLUG_CPU */
|
|
|
|
#endif /* CONFIG_SCHED_WALT */
|