@@ -662,6 +662,10 @@ struct task_struct {
unsigned long wakee_flip_decay_ts;
struct task_struct *last_wakee;
+ /* Delayed placement */
+ struct hrtimer delay_placement_timer;
+ int delay_placement_cpu;
+
/*
* recent_used_cpu is initially set as the last CPU used by a task
* that wakes affine another task. Waker/wakee relationships can
@@ -52,6 +52,9 @@ int sched_proc_update_handler(struct ctl_table *table, int write,
void *buffer, size_t *length, loff_t *ppos);
#endif
+extern __read_mostly unsigned int sysctl_sched_delayed_placement;
+extern __read_mostly unsigned int sysctl_sched_lowfreq;
+
/*
* control realtime throttling:
*
@@ -3217,6 +3217,33 @@ int sysctl_schedstats(struct ctl_table *table, int write, void *buffer,
static inline void init_schedstats(void) {}
#endif /* CONFIG_SCHEDSTATS */
+static enum hrtimer_restart delayed_placement_fn(struct hrtimer *data)
+{
+ struct task_struct *p = container_of(data, struct task_struct,
+ delay_placement_timer);
+ struct rq *rq;
+ struct rq_flags rf;
+ bool queued, running;
+
+ /*
+ * If, by chance, p was already migrated to this cpu, no need to do
+ * anything. This can happen because of load balancing for example.
+ */
+ if (task_cpu(p) == p->delay_placement_cpu)
+ return HRTIMER_NORESTART;
+
+ rq = task_rq_lock(p, &rf);
+
+ queued = task_on_rq_queued(p);
+ running = task_current(rq, p);
+ if (queued && !running)
+ rq = __migrate_task(rq, &rf, p, p->delay_placement_cpu);
+
+ task_rq_unlock(rq, p, &rf);
+
+ return HRTIMER_NORESTART;
+}
+
/*
* fork()/clone()-time setup:
*/
@@ -3299,6 +3326,11 @@ int sched_fork(unsigned long clone_flags, struct task_struct *p)
plist_node_init(&p->pushable_tasks, MAX_PRIO);
RB_CLEAR_NODE(&p->pushable_dl_tasks);
#endif
+
+ hrtimer_init(&p->delay_placement_timer, CLOCK_MONOTONIC,
+ HRTIMER_MODE_REL);
+ p->delay_placement_timer.function = delayed_placement_fn;
+
return 0;
}
@@ -84,6 +84,15 @@ static unsigned int normalized_sysctl_sched_wakeup_granularity = 1000000UL;
const_debug unsigned int sysctl_sched_migration_cost = 500000UL;
+/*
+ * After fork, exec or wakeup, thread placement is delayed. This option gives
+ * the delay in nanoseconds.
+ *
+ * (default: 50us)
+ */
+unsigned int sysctl_sched_delayed_placement = 50000;
+unsigned int sysctl_sched_lowfreq;
+
int sched_thermal_decay_shift;
static int __init setup_sched_thermal_decay_shift(char *str)
{
@@ -6656,6 +6665,13 @@ static int find_energy_efficient_cpu(struct task_struct *p, int prev_cpu)
return -1;
}
+static bool is_cpu_low_freq(int cpu)
+{
+ if (!sysctl_sched_lowfreq)
+ return false;
+ return cpu_rq(cpu)->freq <= sysctl_sched_lowfreq;
+}
+
/*
* select_task_rq_fair: Select target runqueue for the waking task in domains
* that have the 'sd_flag' flag set. In practice, this is SD_BALANCE_WAKE,
@@ -6683,7 +6699,7 @@ select_task_rq_fair(struct task_struct *p, int prev_cpu, int sd_flag, int wake_f
if (sched_energy_enabled()) {
new_cpu = find_energy_efficient_cpu(p, prev_cpu);
if (new_cpu >= 0)
- return new_cpu;
+ goto local;
new_cpu = prev_cpu;
}
@@ -6724,6 +6740,28 @@ select_task_rq_fair(struct task_struct *p, int prev_cpu, int sd_flag, int wake_f
}
rcu_read_unlock();
+local:
+ if (!is_cpu_low_freq(new_cpu))
+ goto end;
+ /*
+ * If fork/wake/exec, trigger an interrupt in 50us (default) to eventually steal the thread
+ * and place the thread locally.
+ */
+ if (new_cpu == task_cpu(p))
+ goto end;
+
+ if (sd_flag & (SD_BALANCE_FORK | SD_BALANCE_WAKE | SD_BALANCE_EXEC)) {
+ p->delay_placement_cpu = new_cpu;
+ new_cpu = task_cpu(current);
+
+ /* Arm timer in 50us */
+ hrtimer_start(&p->delay_placement_timer,
+ ktime_set(0,
+ sysctl_sched_delayed_placement),
+ HRTIMER_MODE_REL);
+ }
+
+end:
return new_cpu;
}
@@ -7085,6 +7123,8 @@ done: __maybe_unused;
if (hrtick_enabled(rq))
hrtick_start_fair(rq, p);
+ hrtimer_try_to_cancel(&p->delay_placement_timer);
+
update_misfit_status(p, rq);
return p;
@@ -2018,6 +2018,9 @@ extern void check_preempt_curr(struct rq *rq, struct task_struct *p, int flags);
extern const_debug unsigned int sysctl_sched_nr_migrate;
extern const_debug unsigned int sysctl_sched_migration_cost;
+extern unsigned int sysctl_sched_delayed_placement;
+extern unsigned int sysctl_sched_lowfreq;
+
#ifdef CONFIG_SCHED_HRTICK
/*
@@ -1712,6 +1712,20 @@ static struct ctl_table kern_table[] = {
.mode = 0644,
.proc_handler = proc_dointvec,
},
+ {
+ .procname = "sched_delayed_placement",
+ .data = &sysctl_sched_delayed_placement,
+ .maxlen = sizeof(unsigned int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ },
+ {
+ .procname = "sched_lowfreq",
+ .data = &sysctl_sched_lowfreq,
+ .maxlen = sizeof(unsigned int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ },
#ifdef CONFIG_SCHEDSTATS
{
.procname = "sched_schedstats",