sched/bt: cgroup support and ignore cpu binding for offline task

This commit add cpu.offline to cpu cgroup, echo 1 > cpu.offline would
convert all tasks under this cgroup to offline task. Beside, a new
sysctl sysctl_sched_bt_ignore_cpubind is added, which makes offline
tasks ignoring CPU binding and running on any CPU.

Signed-off-by: Xiaoming Gao <newtongao@tencent.com>
Signed-off-by: Hua Liu <shookliu@tencent.com>
Signed-off-by: Xiaogguang Chen <xiaoggchen@tencent.com>
Signed-off-by: Zhiguang Peng <zgpeng@tencent.com>
Signed-off-by: Bin Fan <tombinfan@tencent.com>
Signed-off-by: He Chen <heddchen@tencent.com>
This commit is contained in:
He Chen 2020-06-11 10:46:30 +08:00 committed by Xiaoming Gao
parent 92f20d99a7
commit de8ae6bfd2
10 changed files with 289 additions and 27 deletions

View File

@ -92,6 +92,7 @@ enum {
CFTYPE_WORLD_WRITABLE = (1 << 4), /* (DON'T USE FOR NEW FILES) S_IWUGO */
#ifdef CONFIG_BT_SCHED
CFTYPE_BT_SHARES = (1 << 5), /* BT-sched shares */
CFTYPE_BT_PRIVATE = (1 << 6),
#endif
/* internal flags, do not use outside cgroup core proper */

View File

@ -60,6 +60,7 @@ extern int sysctl_sched_rt_runtime;
#ifdef CONFIG_BT_SCHED
extern unsigned int sysctl_sched_bt_period;
extern int sysctl_sched_bt_runtime;
extern unsigned int sysctl_sched_bt_ignore_cpubind;
extern unsigned int sysctl_idle_balance_bt_cost;
extern unsigned int sysctl_sched_bt_granularity_ns;
extern unsigned int sysctl_sched_bt_load_fair;

View File

@ -100,6 +100,7 @@ static int kernel_init(void *);
extern void init_IRQ(void);
extern void fork_init(void);
extern void radix_tree_init(void);
extern void init_offline_cpu_control(void);
/*
* Debug helper: via this flag we know that we are in 'early bootup code'
@ -1007,6 +1008,7 @@ static int __ref kernel_init(void *unused)
int ret;
kernel_init_freeable();
init_offline_cpu_control();
/* need to finish all async __init code before freeing the memory */
async_synchronize_full();
ftrace_free_init_mem();

View File

@ -54,6 +54,7 @@
#include <linux/proc_ns.h>
#include <linux/nsproxy.h>
#include <linux/file.h>
#include <linux/sched/batch.h>
#include <net/sock.h>
#define CREATE_TRACE_POINTS
@ -80,6 +81,8 @@ EXPORT_SYMBOL_GPL(cgroup_mutex);
EXPORT_SYMBOL_GPL(css_set_lock);
#endif
extern unsigned int offlinegroup_enabled;
/*
* Protects cgroup_idr and css_idr so that IDs can be released without
* grabbing cgroup_mutex.
@ -3526,6 +3529,12 @@ restart:
continue;
if ((cft->flags & CFTYPE_ONLY_ON_ROOT) && cgroup_parent(cgrp))
continue;
if ((cft->flags & CFTYPE_BT_SHARES) && !sched_bt_on)
continue;
if ((cft->flags & CFTYPE_BT_PRIVATE) && !sched_bt_on)
continue;
if ((cft->flags & CFTYPE_BT_PRIVATE) && !offlinegroup_enabled)
continue;
if (is_add) {
ret = cgroup_add_file(css, cgrp, cft);

View File

@ -5,11 +5,13 @@
#include <linux/seq_file.h>
#include <linux/kallsyms.h>
#include <linux/utsname.h>
#include <linux/sched.h>
#include <linux/security.h>
#include <linux/export.h>
#include <linux/nospec.h>
unsigned int __read_mostly sysctl_sched_autogroup_enabled = 1;
unsigned int __read_mostly offlinegroup_enabled = 0;
static struct autogroup autogroup_default;
static atomic_t autogroup_seq_nr;
@ -207,6 +209,14 @@ static int __init setup_autogroup(char *str)
__setup("noautogroup", setup_autogroup);
static int __init setup_offlinegroup(char *str)
{
if (sched_bt_on)
offlinegroup_enabled = 1;
return 1;
}
__setup("offline_group", setup_offlinegroup);
#ifdef CONFIG_PROC_FS
int proc_sched_autogroup_set_nice(struct task_struct *p, int nice)

View File

@ -19,6 +19,10 @@
#include <linux/task_work.h>
#include <linux/hrtimer.h>
#include <linux/sched/batch.h>
#include <linux/proc_fs.h>
#include <linux/kfifo.h>
#include <linux/seq_file.h>
#include <asm/uaccess.h>
#include <trace/events/sched.h>
#include "sched.h"
@ -35,10 +39,13 @@ void set_bt_load_weight(struct task_struct *p)
load->inv_weight = sched_prio_to_wmult[prio];
}
extern unsigned int offlinegroup_enabled;
const struct sched_class bt_sched_class;
unsigned int sysctl_idle_balance_bt_cost = 300000UL;
unsigned int sysctl_sched_bt_granularity_ns = 4000000;
unsigned int sysctl_sched_bt_load_fair = 1;
void * bt_cpu_control_set = 0;
unsigned int sysctl_sched_bt_ignore_cpubind = 0;
/*
* sd_lb_stats_bt - Structure to store the statistics of a sched_domain
@ -385,7 +392,8 @@ static void start_bandwidth_timer(struct hrtimer *period_timer, ktime_t period)
static void start_bt_bandwidth(struct bt_bandwidth *bt_b)
{
if (!bt_bandwidth_enabled() || bt_b->bt_runtime == RUNTIME_INF)
if (!offlinegroup_enabled &&
(!bt_bandwidth_enabled() || bt_b->bt_runtime == RUNTIME_INF))
return;
if (hrtimer_active(&bt_b->bt_period_timer))
@ -636,7 +644,7 @@ static int balance_bt_runtime(struct bt_rq *bt_rq)
{
int more = 0;
if (!sched_feat(BT_RUNTIME_SHARE))
if (offlinegroup_enabled || !sched_feat(BT_RUNTIME_SHARE))
return more;
if (bt_rq->bt_time > bt_rq->bt_runtime) {
@ -797,7 +805,8 @@ static int do_sched_bt_period_timer(struct bt_bandwidth *bt_b, int overrun)
raw_spin_unlock(&rq->lock);
}
if (!throttled && (!bt_bandwidth_enabled() || bt_b->bt_runtime == RUNTIME_INF))
if (!throttled && !offlinegroup_enabled &&
(!bt_bandwidth_enabled() || bt_b->bt_runtime == RUNTIME_INF))
idle = 1;
if (idle)
@ -829,22 +838,26 @@ static int sched_bt_runtime_exceeded(struct bt_rq *bt_rq)
* Don't actually throttle groups that have no runtime assigned
* but accrue some time due to boosting.
*/
if (likely(bt_b->bt_runtime)) {
static bool once = false;
if (!offlinegroup_enabled) {
if (likely(bt_b->bt_runtime)) {
static bool once = false;
throttle_bt_rq(bt_rq);
throttle_bt_rq(bt_rq);
if (!once) {
once = true;
printk_deferred("sched: BT throttling activated\n");
if (!once) {
once = true;
printk_deferred("sched: BT throttling activated\n");
}
} else {
/*
* In case we did anyway, make it go away,
* replenishment is a joke, since it will replenish us
* with exactly 0 ns.
*/
bt_rq->bt_time = 0;
}
} else {
/*
* In case we did anyway, make it go away,
* replenishment is a joke, since it will replenish us
* with exactly 0 ns.
*/
bt_rq->bt_time = 0;
throttle_bt_rq(bt_rq);
}
if (bt_rq_throttled(bt_rq)) {
@ -861,7 +874,8 @@ static void
account_bt_rq_runtime(struct bt_rq *bt_rq,
unsigned long delta_exec)
{
if (!bt_bandwidth_enabled() || sched_bt_runtime(bt_rq) == RUNTIME_INF)
if (!offlinegroup_enabled &&
(!bt_bandwidth_enabled() || sched_bt_runtime(bt_rq) == RUNTIME_INF))
return;
raw_spin_lock(&bt_rq->bt_runtime_lock);
@ -2506,12 +2520,19 @@ select_task_rq_bt(struct task_struct *p, int prev_cpu, int sd_flag, int wake_fla
int cpu = smp_processor_id();
int new_cpu = prev_cpu;
int want_affine = 0;
bool check_cpumask;
if (p->nr_cpus_allowed == 1)
if (offlinegroup_enabled && sysctl_sched_bt_ignore_cpubind)
check_cpumask = false;
else
check_cpumask = true;
if (check_cpumask && p->nr_cpus_allowed == 1)
return prev_cpu;
if (sd_flag & SD_BALANCE_WAKE) {
if (cpumask_test_cpu(cpu, tsk_cpus_allowed(p)))
if (!check_cpumask ||
(check_cpumask && cpumask_test_cpu(cpu, tsk_cpus_allowed(p))))
want_affine = 1;
new_cpu = prev_cpu;
}
@ -2866,6 +2887,8 @@ static bool yield_to_task_bt(struct rq *rq, struct task_struct *p, bool preempt)
static
int can_migrate_bt_task(struct task_struct *p, struct lb_env *env)
{
bool check_cpumask;
/*
* We do not migrate tasks that are:
* 1) throttled_lb_pair, or
@ -2873,7 +2896,12 @@ int can_migrate_bt_task(struct task_struct *p, struct lb_env *env)
* 3) running (obviously), or
* 4) are cache-hot on their current CPU.
*/
if (!cpumask_test_cpu(env->dst_cpu, tsk_cpus_allowed(p))) {
if (offlinegroup_enabled && sysctl_sched_bt_ignore_cpubind)
check_cpumask = false;
else
check_cpumask = true;
if (check_cpumask && !cpumask_test_cpu(env->dst_cpu, tsk_cpus_allowed(p))) {
int cpu;
schedstat_inc(p->se.bt_statistics->nr_failed_migrations_affine);
@ -3949,6 +3977,13 @@ static int load_balance_bt(int this_cpu, struct rq *this_rq,
.cpus = cpus,
};
bool check_cpumask;
if (offlinegroup_enabled && sysctl_sched_bt_ignore_cpubind)
check_cpumask = true;
else
check_cpumask = false;
/*
* For NEWLY_IDLE load_balancing, we don't need to consider
* other cpus in our group
@ -4088,8 +4123,8 @@ more_balance:
* if the curr task on busiest cpu can't be
* moved to this_cpu
*/
if (!cpumask_test_cpu(this_cpu,
tsk_cpus_allowed(busiest->curr))) {
if (check_cpumask &&
!cpumask_test_cpu(this_cpu, tsk_cpus_allowed(busiest->curr))) {
raw_spin_unlock_irqrestore(&busiest->lock,
flags);
env.flags |= LBF_ALL_PINNED;
@ -4901,10 +4936,9 @@ static int sched_bt_global_constraints(void)
if (sysctl_sched_bt_period <= 0)
return -EINVAL;
/*
* There's always some BT tasks in the root group
* -- migration, kstopmachine etc..
*/
if (offlinegroup_enabled)
return 0;
if (sysctl_sched_bt_runtime == 0)
return -EINVAL;
@ -4950,3 +4984,80 @@ int sched_bt_handler(struct ctl_table *table, int write,
return ret;
}
static int offline_proc_show(struct seq_file *m, void *v) {
unsigned long * n = (unsigned long*)m->private;
seq_printf(m, "%lu\n", *n);
return 0;
}
static int offline_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, offline_proc_show, PDE_DATA(inode));
}
#define OFFLINE_NUMBUF 8
static ssize_t offline_proc_write(struct file *file, const char __user *ubuf,
size_t cnt, loff_t *ppos)
{
unsigned long * start = (unsigned long *)bt_cpu_control_set;
unsigned long *to = (unsigned long *)PDE_DATA(file_inode(file));
char buffer[OFFLINE_NUMBUF];
int cpu = to - start;
struct bt_rq *bt_rq;
unsigned long tmp;
int cpn;
cpn = min((int)OFFLINE_NUMBUF, (int)cnt);
if (copy_from_user(buffer, ubuf, cpn))
return -EFAULT;
buffer[cpn - 1] = '\0';
if (kstrtoul(buffer, 0, &tmp) || tmp > 100)
return -EINVAL;
*to = tmp;
bt_rq = &cpu_rq(cpu)->bt;
raw_spin_lock(&bt_rq->bt_runtime_lock);
bt_rq->bt_runtime = (u64)sysctl_sched_bt_period * NSEC_PER_USEC * tmp / 100;
raw_spin_unlock(&bt_rq->bt_runtime_lock);
return cnt;
}
static const struct file_operations info_fops = {
.open = offline_proc_open,
.read = seq_read,
.write = offline_proc_write,
.llseek = seq_lseek,
.release = single_release,
};
void init_offline_cpu_control(void)
{
int i, nr = num_online_cpus();
char buffer[20] = "offline";
struct proc_dir_entry * dir;
if(!offlinegroup_enabled)
return;
dir= proc_mkdir(buffer, NULL);
if (!dir)
return;
bt_cpu_control_set = kmalloc(sizeof(unsigned long)*nr, GFP_KERNEL);
if(!bt_cpu_control_set)
return;
for(i=0; i<nr; i++) {
sprintf(buffer,"%s%d","cpu",i);
proc_create_data(buffer, 0400, dir, &info_fops, (void *)((unsigned long *)bt_cpu_control_set + i));
(*((unsigned long*)bt_cpu_control_set+i)) = 100;
}
return;
}

View File

@ -42,6 +42,8 @@
#include "../workqueue_internal.h"
#include "../smpboot.h"
extern unsigned int offlinegroup_enabled;
extern unsigned int sysctl_sched_bt_ignore_cpubind;
#define CREATE_TRACE_POINTS
#include <trace/events/sched.h>
@ -1020,8 +1022,16 @@ struct migration_arg {
static struct rq *__migrate_task(struct rq *rq, struct rq_flags *rf,
struct task_struct *p, int dest_cpu)
{
bool check_cpumask;
if (offlinegroup_enabled && sysctl_sched_bt_ignore_cpubind &&
(p->sched_class == &bt_sched_class))
check_cpumask = false;
else
check_cpumask = true;
/* Affinity changed (again). */
if (!is_cpu_allowed(p, dest_cpu))
if (!check_cpumask && !is_cpu_allowed(p, dest_cpu))
return rq;
update_rq_clock(rq);
@ -1605,6 +1615,10 @@ int select_task_rq(struct task_struct *p, int cpu, int sd_flags, int wake_flags)
else
cpu = cpumask_any(&p->cpus_allowed);
if (offlinegroup_enabled && sysctl_sched_bt_ignore_cpubind &&
(p->sched_class == &bt_sched_class))
return cpu;
/*
* In order not to call set_task_cpu() on a blocking task we need
* to rely on ttwu() to place the task on a valid ->cpus_allowed
@ -4675,6 +4689,7 @@ do_sched_setscheduler(pid_t pid, int policy, struct sched_param __user *param)
struct sched_param lparam;
struct task_struct *p;
int retval;
struct task_group *tg;
if (!param || pid < 0)
return -EINVAL;
@ -4684,8 +4699,21 @@ do_sched_setscheduler(pid_t pid, int policy, struct sched_param __user *param)
rcu_read_lock();
retval = -ESRCH;
p = find_process_by_pid(pid);
if (p != NULL)
if (p != NULL) {
if (offlinegroup_enabled) {
tg = container_of(task_css_check(p, cpu_cgrp_id, false),
struct task_group, css);
if (tg->parent && tg->offline && policy != SCHED_BT &&
policy != SCHED_RR && policy != SCHED_FIFO)
goto out;
if (tg->parent && !tg->offline && policy == SCHED_BT)
goto out;
}
retval = sched_setscheduler(p, policy, &lparam);
}
out:
rcu_read_unlock();
return retval;
@ -6647,6 +6675,7 @@ void sched_offline_group(struct task_group *tg)
static void sched_change_group(struct task_struct *tsk, int type)
{
struct task_group *tg;
struct rq *rq = task_rq(tsk);
/*
* All callers are synchronized by task_rq_lock(); we do not use RCU
@ -6658,6 +6687,34 @@ static void sched_change_group(struct task_struct *tsk, int type)
tg = autogroup_task_group(tsk, tg);
tsk->sched_task_group = tg;
/* No need to re-setcheduler when fork or exit a task */
if (offlinegroup_enabled && !rt_task(tsk) &&
!(tsk->flags & PF_EXITING) && (type != TASK_SET_GROUP)) {
struct sched_attr attr = {
.sched_priority = 0,
};
if (tg->offline) {
attr.sched_nice = PRIO_TO_BT_NICE(tsk->static_prio);
attr.sched_policy = SCHED_BT;
} else {
attr.sched_nice = PRIO_TO_NICE(tsk->static_prio);
attr.sched_policy = SCHED_NORMAL;
}
/*
* FIXME: __setscheduler before task_change_group would
* lead to missing prev cfs_rq/bt_rq stats updating.
* Otherwise, putting __setscheduler after task_change_group
* is not right either, which would miss next cfs_rq/bt_rq
* stats updating.
* In fact, __setscheduler should be right before set_task_rq
* in task_change_group callback, but if so, the code would be
* messed up.
*/
__setscheduler(rq, tsk, &attr, 0);
}
#if defined(CONFIG_FAIR_GROUP_SCHED) || defined (CONFIG_BT_GROUP_SCHED)
if (tsk->sched_class->task_change_group)
tsk->sched_class->task_change_group(tsk, type);
@ -6721,6 +6778,9 @@ cpu_cgroup_css_alloc(struct cgroup_subsys_state *parent_css)
if (IS_ERR(tg))
return ERR_PTR(-ENOMEM);
if (offlinegroup_enabled)
tg->offline = parent->offline;
return &tg->css;
}
@ -7093,6 +7153,53 @@ static u64 cpu_bt_shares_read_u64(struct cgroup_subsys_state *css,
return (u64) scale_load_down(tg->bt_shares);
}
static int cpu_offline_write_uint(struct cgroup_subsys_state *css,
struct cftype *cftype, u64 offline_input)
{
struct css_task_iter it;
struct task_struct *tsk;
struct task_group *tg;
struct sched_param param;
int pid, sched_class;
int err;
tg = css_tg(css);
if (!tg->se[0])
return -EINVAL;
if (tg->offline == !!offline_input)
return 0;
if (!tg->offline && offline_input) {
sched_class = SCHED_BT;
} else if (tg->offline && !offline_input) {
sched_class = SCHED_NORMAL;
} else
return 0;
tg->offline = !!offline_input;
param.sched_priority = 0;
css_task_iter_start(css, 0, &it);
while ((tsk = css_task_iter_next(&it))) {
pid = task_tgid_vnr(tsk);
if (pid > 0 && !rt_task(tsk)) {
err=sched_setscheduler(tsk, sched_class, &param);
}
}
css_task_iter_end(&it);
return 0;
}
static u64 cpu_offline_read_uint(struct cgroup_subsys_state *css,
struct cftype *cft)
{
struct task_group *tg = css_tg(css);
return tg->offline;
}
#endif
#ifdef CONFIG_RT_GROUP_SCHED
@ -7132,9 +7239,16 @@ static struct cftype cpu_files[] = {
#ifdef CONFIG_BT_GROUP_SCHED
{
.name = "bt_shares",
.flags = CFTYPE_BT_SHARES,
.read_u64 = cpu_bt_shares_read_u64,
.write_u64 = cpu_bt_shares_write_u64,
},
{
.name = "offline",
.flags = CFTYPE_BT_PRIVATE,
.read_u64 = cpu_offline_read_uint,
.write_u64 = cpu_offline_write_uint,
},
#endif
#ifdef CONFIG_CFS_BANDWIDTH
{

View File

@ -519,6 +519,7 @@ static struct cftype files[] = {
#ifdef CONFIG_BT_SCHED
{
.name = "bt_usage_percpu",
.flags = CFTYPE_BT_SHARES,
.seq_show = bt_cpuacct_percpu_seq_show,
},
#endif
@ -541,6 +542,7 @@ static struct cftype files[] = {
#ifdef CONFIG_BT_SCHED
{
.name = "bt_stat",
.flags = CFTYPE_BT_SHARES,
.seq_show = bt_cpuacct_stats_show,
},
#endif

View File

@ -6,6 +6,7 @@
#include <linux/sched/topology.h>
#include <linux/sched/rt.h>
#include <linux/sched/deadline.h>
#include <linux/sched/batch.h>
#include <linux/sched/clock.h>
#include <linux/sched/wake_q.h>
#include <linux/sched/signal.h>
@ -372,6 +373,8 @@ struct task_group {
#endif
struct cfs_bandwidth cfs_bandwidth;
unsigned long offline;
};
#ifdef CONFIG_FAIR_GROUP_SCHED

View File

@ -571,6 +571,15 @@ static struct ctl_table kern_table[] = {
.mode = 0644,
.proc_handler = proc_dointvec,
},
{
.procname = "sched_bt_ignore_cpubind",
.data = &sysctl_sched_bt_ignore_cpubind,
.maxlen = sizeof(unsigned int),
.mode = 0644,
.proc_handler = proc_dointvec_minmax,
.extra1 = &zero,
.extra2 = &one,
},
#ifdef CONFIG_INTEL_RDT
{
.procname = "sched_bt_rdt_cache_percent",