forked from TencentOS/TencentOS-kernel
1447 lines
32 KiB
C
1447 lines
32 KiB
C
/*
|
|
* Copyright (C) 2019 Tencent Ltd. All rights reserved.
|
|
*
|
|
* File Name : batch.c
|
|
* Author :
|
|
* Date : 2019-12-26
|
|
* Descriptor:
|
|
*/
|
|
|
|
#include <linux/sysctl.h>
|
|
#include <linux/latencytop.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/cpumask.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/profile.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/mempolicy.h>
|
|
#include <linux/migrate.h>
|
|
#include <linux/task_work.h>
|
|
#include <linux/sched/batch.h>
|
|
|
|
#include <trace/events/sched.h>
|
|
#include "batch.h"
|
|
#include "sched.h"
|
|
#include "fair.h"
|
|
|
|
void set_bt_load_weight(struct task_struct *p)
|
|
{
|
|
int prio = p->static_prio - MIN_BT_PRIO;
|
|
struct load_weight *load = &p->bt.load;
|
|
|
|
load->weight = scale_load(sched_prio_to_weight[prio]);
|
|
load->inv_weight = sched_prio_to_wmult[prio];
|
|
}
|
|
|
|
const struct sched_class bt_sched_class;
|
|
|
|
static inline struct task_struct *bt_task_of(struct sched_entity *bt_se)
|
|
{
|
|
return container_of(bt_se, struct task_struct, bt);
|
|
}
|
|
|
|
static inline struct rq *rq_of_bt_rq(struct bt_rq *bt_rq)
|
|
{
|
|
return container_of(bt_rq, struct rq, bt);
|
|
}
|
|
|
|
#define bt_entity_is_task(bt) 1
|
|
|
|
#define for_each_sched_bt_entity(bt) \
|
|
for (; bt; bt = NULL)
|
|
|
|
static inline struct bt_rq *task_bt_rq(struct task_struct *p)
|
|
{
|
|
return &task_rq(p)->bt;
|
|
}
|
|
|
|
static inline struct bt_rq *bt_rq_of(struct sched_entity *bt_se)
|
|
{
|
|
struct task_struct *p = bt_task_of(bt_se);
|
|
struct rq *rq = task_rq(p);
|
|
|
|
return &rq->bt;
|
|
}
|
|
|
|
static inline struct sched_entity *parent_bt_entity(struct sched_entity *bt)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
/**************************************************************
|
|
* Scheduling class tree data structure manipulation methods:
|
|
*/
|
|
|
|
static inline int bt_entity_before(struct sched_entity *a,
|
|
struct sched_entity *b)
|
|
{
|
|
return (s64)(a->vruntime - b->vruntime) < 0;
|
|
}
|
|
|
|
static void update_bt_min_vruntime(struct bt_rq *bt_rq)
|
|
{
|
|
struct sched_entity *curr = bt_rq->curr;
|
|
struct rb_node *leftmost = rb_first_cached(&bt_rq->tasks_timeline);
|
|
|
|
u64 vruntime = bt_rq->min_vruntime;
|
|
|
|
if (curr) {
|
|
if (curr->on_rq)
|
|
vruntime = bt_rq->curr->vruntime;
|
|
else
|
|
curr = NULL;
|
|
}
|
|
|
|
if (leftmost) {
|
|
struct sched_entity *bt_se = rb_entry(leftmost,
|
|
struct sched_entity,
|
|
run_node);
|
|
|
|
if (!curr)
|
|
vruntime = bt_se->vruntime;
|
|
else
|
|
vruntime = min_vruntime(vruntime, bt_se->vruntime);
|
|
}
|
|
|
|
/* ensure we never gain time by being placed backwards. */
|
|
bt_rq->min_vruntime = max_vruntime(bt_rq->min_vruntime, vruntime);
|
|
#ifndef CONFIG_64BIT
|
|
/* memory barrior for writting */
|
|
smp_wmb();
|
|
bt_rq->min_vruntime_copy = bt_rq->min_vruntime;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Enqueue an entity into the rb-tree:
|
|
*/
|
|
static void __enqueue_bt_entity(struct bt_rq *bt_rq, struct sched_entity *bt_se)
|
|
{
|
|
struct rb_node **link = &bt_rq->tasks_timeline.rb_root.rb_node;
|
|
struct rb_node *parent = NULL;
|
|
struct sched_entity *entry;
|
|
int leftmost = 1;
|
|
|
|
/*
|
|
* Find the right place in the rbtree:
|
|
*/
|
|
while (*link) {
|
|
parent = *link;
|
|
entry = rb_entry(parent, struct sched_entity, run_node);
|
|
/*
|
|
* We dont care about collisions. Nodes with
|
|
* the same key stay together.
|
|
*/
|
|
if (bt_entity_before(bt_se, entry)) {
|
|
link = &parent->rb_left;
|
|
} else {
|
|
link = &parent->rb_right;
|
|
leftmost = 0;
|
|
}
|
|
}
|
|
|
|
rb_link_node(&bt_se->run_node, parent, link);
|
|
rb_insert_color_cached(&bt_se->run_node,
|
|
&bt_rq->tasks_timeline, leftmost);
|
|
}
|
|
|
|
static void __dequeue_bt_entity(struct bt_rq *bt_rq, struct sched_entity *bt_se)
|
|
{
|
|
rb_erase_cached(&bt_se->run_node, &bt_rq->tasks_timeline);
|
|
}
|
|
|
|
struct sched_entity *__pick_first_bt_entity(struct bt_rq *bt_rq)
|
|
{
|
|
struct rb_node *left = rb_first_cached(&bt_rq->tasks_timeline);
|
|
|
|
if (!left)
|
|
return NULL;
|
|
|
|
return rb_entry(left, struct sched_entity, run_node);
|
|
}
|
|
|
|
static struct sched_entity *__pick_next_bt_entity(struct sched_entity *bt_se)
|
|
{
|
|
struct rb_node *next = rb_next(&bt_se->run_node);
|
|
|
|
if (!next)
|
|
return NULL;
|
|
|
|
return rb_entry(next, struct sched_entity, run_node);
|
|
}
|
|
|
|
#ifdef CONFIG_SCHED_DEBUG
|
|
struct sched_entity *__pick_last_bt_entity(struct bt_rq *bt_rq)
|
|
{
|
|
struct rb_node *last = rb_last(&bt_rq->tasks_timeline.rb_root);
|
|
|
|
if (!last)
|
|
return NULL;
|
|
|
|
return rb_entry(last, struct sched_entity, run_node);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* delta /= w
|
|
*/
|
|
static inline unsigned long
|
|
calc_delta_bt(unsigned long delta, struct sched_entity *bt_se)
|
|
{
|
|
if (unlikely(bt_se->load.weight != NICE_0_LOAD))
|
|
delta = __calc_delta(delta, NICE_0_LOAD, &bt_se->load);
|
|
|
|
return delta;
|
|
}
|
|
|
|
/*
|
|
* We calculate the wall-time slice from the period by taking a part
|
|
* proportional to the weight.
|
|
*
|
|
* s = p*P[w/rw]
|
|
*/
|
|
static u64 sched_bt_slice(struct bt_rq *bt_rq, struct sched_entity *se)
|
|
{
|
|
u64 slice = __sched_period(bt_rq->nr_running + !se->on_rq);
|
|
|
|
for_each_sched_bt_entity(se) {
|
|
struct load_weight *load;
|
|
struct load_weight lw;
|
|
|
|
bt_rq = bt_rq_of(se);
|
|
load = &bt_rq->load;
|
|
|
|
if (unlikely(!se->on_rq)) {
|
|
lw = bt_rq->load;
|
|
|
|
update_load_add(&lw, se->load.weight);
|
|
load = &lw;
|
|
}
|
|
slice = __calc_delta(slice, se->load.weight, load);
|
|
}
|
|
return slice;
|
|
}
|
|
|
|
/*
|
|
* We calculate the vruntime slice of a to-be-inserted task.
|
|
*
|
|
* vs = s/w
|
|
*/
|
|
static u64 sched_bt_vslice(struct bt_rq *bt_rq, struct sched_entity *se)
|
|
{
|
|
return calc_delta_bt(sched_bt_slice(bt_rq, se), se);
|
|
}
|
|
|
|
/*
|
|
* Update the current task's runtime bt_statistics. Skip current tasks that
|
|
* are not in our scheduling class.
|
|
*/
|
|
static inline void
|
|
__update_curr_bt(struct bt_rq *bt_rq, struct sched_entity *curr,
|
|
unsigned long delta_exec)
|
|
{
|
|
unsigned long delta_exec_weighted;
|
|
|
|
schedstat_set(curr->bt_statistics->exec_max,
|
|
max((u64)delta_exec, curr->bt_statistics->exec_max));
|
|
|
|
curr->sum_exec_runtime += delta_exec;
|
|
schedstat_add(bt_rq->exec_clock, delta_exec);
|
|
delta_exec_weighted = calc_delta_bt(delta_exec, curr);
|
|
|
|
curr->vruntime += delta_exec_weighted;
|
|
update_bt_min_vruntime(bt_rq);
|
|
}
|
|
|
|
static void update_curr_bt(struct bt_rq *bt_rq)
|
|
{
|
|
struct sched_entity *curr = bt_rq->curr;
|
|
u64 now = rq_of_bt_rq(bt_rq)->clock_task;
|
|
unsigned long delta_exec;
|
|
|
|
if (unlikely(!curr))
|
|
return;
|
|
|
|
/*
|
|
* Get the amount of time the current task was running
|
|
* since the last time we changed load (this cannot
|
|
* overflow on 32 bits):
|
|
*/
|
|
delta_exec = (unsigned long)(now - curr->exec_start);
|
|
if (unlikely((s64)delta_exec <= 0))
|
|
return;
|
|
|
|
__update_curr_bt(bt_rq, curr, delta_exec);
|
|
curr->exec_start = now;
|
|
|
|
if (bt_entity_is_task(curr)) {
|
|
struct task_struct *curtask = bt_task_of(curr);
|
|
|
|
trace_sched_stat_runtime(curtask, delta_exec, curr->vruntime);
|
|
cpuacct_charge(curtask, delta_exec);
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
update_stats_wait_start_bt(struct bt_rq *bt_rq, struct sched_entity *bt_se)
|
|
{
|
|
schedstat_set(bt_se->bt_statistics->wait_start,
|
|
rq_of_bt_rq(bt_rq)->clock);
|
|
}
|
|
|
|
/*
|
|
* Task is being enqueued - update stats:
|
|
*/
|
|
static void
|
|
update_stats_enqueue_bt(struct bt_rq *bt_rq, struct sched_entity *se)
|
|
{
|
|
/*
|
|
* Are we enqueueing a waiting task? (for current tasks
|
|
* a dequeue/enqueue event is a NOP)
|
|
*/
|
|
if (se != bt_rq->curr)
|
|
update_stats_wait_start_bt(bt_rq, se);
|
|
}
|
|
|
|
static void
|
|
update_stats_wait_end_bt(struct bt_rq *bt_rq, struct sched_entity *se)
|
|
{
|
|
schedstat_set(se->bt_statistics->wait_max,
|
|
max(se->bt_statistics->wait_max,
|
|
rq_of_bt_rq(bt_rq)->clock - se->bt_statistics->wait_start));
|
|
schedstat_set(se->bt_statistics->wait_count,
|
|
se->bt_statistics->wait_count + 1);
|
|
schedstat_set(se->bt_statistics->wait_sum,
|
|
se->bt_statistics->wait_sum +
|
|
rq_of_bt_rq(bt_rq)->clock - se->bt_statistics->wait_start);
|
|
#ifdef CONFIG_SCHEDSTATS
|
|
if (bt_entity_is_task(se)) {
|
|
trace_sched_stat_wait(bt_task_of(se),
|
|
rq_of_bt_rq(bt_rq)->clock - se->bt_statistics->wait_start);
|
|
}
|
|
#endif
|
|
schedstat_set(se->bt_statistics->wait_start, 0);
|
|
}
|
|
|
|
static inline void
|
|
update_stats_dequeue_bt(struct bt_rq *bt_rq, struct sched_entity *se)
|
|
{
|
|
/*
|
|
* Mark the end of the wait period if dequeueing a
|
|
* waiting task:
|
|
*/
|
|
if (se != bt_rq->curr)
|
|
update_stats_wait_end_bt(bt_rq, se);
|
|
}
|
|
|
|
/*
|
|
* We are picking a new current task - update its stats:
|
|
*/
|
|
static inline void
|
|
update_stats_curr_start_bt(struct bt_rq *bt_rq, struct sched_entity *se)
|
|
{
|
|
/*
|
|
* We are starting a new run period:
|
|
*/
|
|
se->exec_start = rq_clock_task(rq_of_bt_rq(bt_rq));
|
|
}
|
|
|
|
static void
|
|
account_bt_entity_enqueue(struct bt_rq *bt_rq, struct sched_entity *se)
|
|
{
|
|
update_load_add(&bt_rq->load, se->load.weight);
|
|
|
|
bt_rq->nr_running++;
|
|
}
|
|
|
|
static void
|
|
account_bt_entity_dequeue(struct bt_rq *bt_rq, struct sched_entity *se)
|
|
{
|
|
update_load_sub(&bt_rq->load, se->load.weight);
|
|
|
|
bt_rq->nr_running--;
|
|
}
|
|
|
|
static void enqueue_bt_sleeper(struct bt_rq *bt_rq, struct sched_entity *se)
|
|
{
|
|
#if defined(CONFIG_SCHEDSTATS) || defined(CONFIG_LATENCYTOP)
|
|
struct task_struct *tsk = NULL;
|
|
|
|
if (bt_entity_is_task(se))
|
|
tsk = bt_task_of(se);
|
|
|
|
if (se->bt_statistics->sleep_start) {
|
|
u64 delta = rq_of_bt_rq(bt_rq)->clock - se->bt_statistics->sleep_start;
|
|
|
|
if ((s64)delta < 0)
|
|
delta = 0;
|
|
|
|
#ifdef CONFIG_SCHEDSTATS
|
|
if (unlikely(delta > se->bt_statistics->sleep_max))
|
|
se->bt_statistics->sleep_max = delta;
|
|
#endif
|
|
|
|
se->bt_statistics->sleep_start = 0;
|
|
#ifdef CONFIG_SCHEDSTATS
|
|
se->bt_statistics->sum_sleep_runtime += delta;
|
|
#endif
|
|
|
|
if (tsk) {
|
|
account_scheduler_latency(tsk, delta >> 10, 1);
|
|
#ifdef CONFIG_SCHEDSTATS
|
|
trace_sched_stat_sleep(tsk, delta);
|
|
#endif
|
|
}
|
|
}
|
|
if (se->bt_statistics->block_start) {
|
|
u64 delta = rq_of_bt_rq(bt_rq)->clock - se->bt_statistics->block_start;
|
|
|
|
if ((s64)delta < 0)
|
|
delta = 0;
|
|
|
|
#ifdef CONFIG_SCHEDSTATS
|
|
if (unlikely(delta > se->bt_statistics->block_max))
|
|
se->bt_statistics->block_max = delta;
|
|
#endif
|
|
|
|
se->bt_statistics->block_start = 0;
|
|
#ifdef CONFIG_SCHEDSTATS
|
|
se->bt_statistics->sum_sleep_runtime += delta;
|
|
#endif
|
|
|
|
if (tsk) {
|
|
#ifdef CONFIG_SCHEDSTATS
|
|
if (tsk->in_iowait) {
|
|
se->bt_statistics->iowait_sum += delta;
|
|
se->bt_statistics->iowait_count++;
|
|
trace_sched_stat_iowait(tsk, delta);
|
|
}
|
|
#endif
|
|
|
|
trace_sched_stat_blocked(tsk, delta);
|
|
|
|
/*
|
|
* Blocking time is in units of nanosecs, so shift by
|
|
* 20 to get a milliseconds-range estimation of the
|
|
* amount of time that the task spent sleeping:
|
|
*/
|
|
if (unlikely(prof_on == SLEEP_PROFILING)) {
|
|
profile_hits(SLEEP_PROFILING,
|
|
(void *)get_wchan(tsk),
|
|
delta >> 20);
|
|
}
|
|
account_scheduler_latency(tsk, delta >> 10, 0);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void check_bt_spread(struct bt_rq *bt_rq, struct sched_entity *se)
|
|
{
|
|
#ifdef CONFIG_SCHED_DEBUG
|
|
s64 d = se->vruntime - bt_rq->min_vruntime;
|
|
|
|
if (d < 0)
|
|
d = -d;
|
|
|
|
if (d > 3*sysctl_sched_latency)
|
|
schedstat_inc(bt_rq->nr_spread_over);
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
place_bt_entity(struct bt_rq *bt_rq, struct sched_entity *se, int initial)
|
|
{
|
|
u64 vruntime = bt_rq->min_vruntime;
|
|
|
|
/*
|
|
* The 'current' period is already promised to the current tasks,
|
|
* however the extra weight of the new task will slow them down a
|
|
* little, place the new task so that it fits in the slot that
|
|
* stays open at the end.
|
|
*/
|
|
if (initial && sched_feat(START_DEBIT))
|
|
vruntime += sched_bt_vslice(bt_rq, se);
|
|
|
|
/* sleeps up to a single latency don't count. */
|
|
if (!initial) {
|
|
unsigned long thresh = sysctl_sched_latency;
|
|
|
|
/*
|
|
* Halve their sleep time's effect, to allow
|
|
* for a gentler effect of sleepers:
|
|
*/
|
|
if (sched_feat(GENTLE_FAIR_SLEEPERS))
|
|
thresh >>= 1;
|
|
|
|
vruntime -= thresh;
|
|
}
|
|
|
|
/* ensure we never gain time by being placed backwards. */
|
|
se->vruntime = max_vruntime(se->vruntime, vruntime);
|
|
}
|
|
|
|
static void
|
|
enqueue_bt_entity(struct bt_rq *bt_rq, struct sched_entity *se, int flags)
|
|
{
|
|
/*
|
|
* Update the normalized vruntime before updating min_vruntime
|
|
* through callig update_curr().
|
|
*/
|
|
if (!(flags & ENQUEUE_WAKEUP) || (flags & ENQUEUE_MIGRATED))
|
|
se->vruntime += bt_rq->min_vruntime;
|
|
|
|
/*
|
|
* Update run-time bt_statistics of the 'current'.
|
|
*/
|
|
update_curr_bt(bt_rq);
|
|
account_bt_entity_enqueue(bt_rq, se);
|
|
|
|
if (flags & ENQUEUE_WAKEUP) {
|
|
place_bt_entity(bt_rq, se, 0);
|
|
enqueue_bt_sleeper(bt_rq, se);
|
|
}
|
|
|
|
update_stats_enqueue_bt(bt_rq, se);
|
|
check_bt_spread(bt_rq, se);
|
|
if (se != bt_rq->curr)
|
|
__enqueue_bt_entity(bt_rq, se);
|
|
se->on_rq = 1;
|
|
}
|
|
|
|
static void __clear_buddies_last_bt(struct sched_entity *se)
|
|
{
|
|
for_each_sched_bt_entity(se) {
|
|
struct bt_rq *bt_rq = bt_rq_of(se);
|
|
|
|
if (bt_rq->last == se)
|
|
bt_rq->last = NULL;
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void __clear_buddies_next_bt(struct sched_entity *se)
|
|
{
|
|
for_each_sched_bt_entity(se) {
|
|
struct bt_rq *bt_rq = bt_rq_of(se);
|
|
|
|
if (bt_rq->next == se)
|
|
bt_rq->next = NULL;
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void __clear_buddies_skip_bt(struct sched_entity *se)
|
|
{
|
|
for_each_sched_bt_entity(se) {
|
|
struct bt_rq *bt_rq = bt_rq_of(se);
|
|
|
|
if (bt_rq->skip == se)
|
|
bt_rq->skip = NULL;
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void clear_buddies_bt(struct bt_rq *bt_rq, struct sched_entity *se)
|
|
{
|
|
if (bt_rq->last == se)
|
|
__clear_buddies_last_bt(se);
|
|
|
|
if (bt_rq->next == se)
|
|
__clear_buddies_next_bt(se);
|
|
|
|
if (bt_rq->skip == se)
|
|
__clear_buddies_skip_bt(se);
|
|
}
|
|
|
|
static void
|
|
dequeue_bt_entity(struct bt_rq *bt_rq, struct sched_entity *se, int flags)
|
|
{
|
|
/*
|
|
* Update run-time bt_statistics of the 'current'.
|
|
*/
|
|
update_curr_bt(bt_rq);
|
|
|
|
update_stats_dequeue_bt(bt_rq, se);
|
|
if (flags & DEQUEUE_SLEEP) {
|
|
#if defined(CONFIG_SCHEDSTATS) || defined(CONFIG_LATENCYTOP)
|
|
if (bt_entity_is_task(se)) {
|
|
struct task_struct *tsk = bt_task_of(se);
|
|
|
|
if (tsk->state & TASK_INTERRUPTIBLE)
|
|
se->bt_statistics->sleep_start = rq_of_bt_rq(bt_rq)->clock;
|
|
if (tsk->state & TASK_UNINTERRUPTIBLE)
|
|
se->bt_statistics->block_start = rq_of_bt_rq(bt_rq)->clock;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
clear_buddies_bt(bt_rq, se);
|
|
|
|
if (se != bt_rq->curr)
|
|
__dequeue_bt_entity(bt_rq, se);
|
|
se->on_rq = 0;
|
|
account_bt_entity_dequeue(bt_rq, se);
|
|
|
|
/*
|
|
* Normalize the entity after updating the min_vruntime because the
|
|
* update can refer to the ->curr item and we need to reflect this
|
|
* movement in our normalized position.
|
|
*/
|
|
if (!(flags & DEQUEUE_SLEEP))
|
|
se->vruntime -= bt_rq->min_vruntime;
|
|
|
|
update_bt_min_vruntime(bt_rq);
|
|
}
|
|
|
|
/*
|
|
* Preempt the current task with a newly woken task if needed:
|
|
*/
|
|
static void
|
|
check_preempt_tick_bt(struct bt_rq *bt_rq, struct sched_entity *curr)
|
|
{
|
|
unsigned long ideal_runtime, delta_exec;
|
|
struct sched_entity *se;
|
|
s64 delta;
|
|
|
|
ideal_runtime = sched_bt_slice(bt_rq, curr);
|
|
delta_exec = curr->sum_exec_runtime - curr->prev_sum_exec_runtime;
|
|
if (delta_exec > ideal_runtime) {
|
|
resched_curr(rq_of_bt_rq(bt_rq));
|
|
/*
|
|
* The current task ran long enough, ensure it doesn't get
|
|
* re-elected due to buddy favours.
|
|
*/
|
|
clear_buddies_bt(bt_rq, curr);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Ensure that a task that missed wakeup preemption by a
|
|
* narrow margin doesn't have to wait for a full slice.
|
|
* This also mitigates buddy induced latencies under load.
|
|
*/
|
|
if (delta_exec < sysctl_sched_min_granularity)
|
|
return;
|
|
|
|
se = __pick_first_bt_entity(bt_rq);
|
|
delta = curr->vruntime - se->vruntime;
|
|
|
|
if (delta < 0)
|
|
return;
|
|
|
|
if (delta > ideal_runtime)
|
|
resched_curr(rq_of_bt_rq(bt_rq));
|
|
}
|
|
|
|
static void
|
|
set_next_bt_entity(struct bt_rq *bt_rq, struct sched_entity *se)
|
|
{
|
|
/* 'current' is not kept within the tree. */
|
|
if (se->on_rq) {
|
|
/*
|
|
* Any task has to be enqueued before it get to execute on
|
|
* a CPU. So account for the time it spent waiting on the
|
|
* runqueue.
|
|
*/
|
|
update_stats_wait_end_bt(bt_rq, se);
|
|
__dequeue_bt_entity(bt_rq, se);
|
|
}
|
|
|
|
update_stats_curr_start_bt(bt_rq, se);
|
|
bt_rq->curr = se;
|
|
#ifdef CONFIG_SCHEDSTATS
|
|
/*
|
|
* Track our maximum slice length, if the CPU's load is at
|
|
* least twice that of our own weight (i.e. dont track it
|
|
* when there are only lesser-weight tasks around):
|
|
*/
|
|
if (bt_rq->load.weight >= 2*se->load.weight) {
|
|
se->bt_statistics->slice_max = max(se->bt_statistics->slice_max,
|
|
se->sum_exec_runtime - se->prev_sum_exec_runtime);
|
|
}
|
|
#endif
|
|
se->prev_sum_exec_runtime = se->sum_exec_runtime;
|
|
}
|
|
|
|
static int
|
|
wakeup_preempt_bt_entity(struct sched_entity *curr, struct sched_entity *se);
|
|
|
|
/*
|
|
* Pick the next process, keeping these things in mind, in this order:
|
|
* 1) keep things fair between processes/task groups
|
|
* 2) pick the "next" process, since someone really wants that to run
|
|
* 3) pick the "last" process, for cache locality
|
|
* 4) do not run the "skip" process, if something else is available
|
|
*/
|
|
static struct sched_entity *pick_next_bt_entity(struct bt_rq *bt_rq)
|
|
{
|
|
struct sched_entity *se = __pick_first_bt_entity(bt_rq);
|
|
struct sched_entity *left = se;
|
|
|
|
/*
|
|
* Avoid running the skip buddy, if running something else can
|
|
* be done without getting too unfair.
|
|
*/
|
|
if (bt_rq->skip == se) {
|
|
struct sched_entity *second = __pick_next_bt_entity(se);
|
|
|
|
if (second && wakeup_preempt_bt_entity(second, left) < 1)
|
|
se = second;
|
|
}
|
|
|
|
/*
|
|
* Prefer last buddy, try to return the CPU to a preempted task.
|
|
*/
|
|
if (bt_rq->last && wakeup_preempt_bt_entity(bt_rq->last, left) < 1)
|
|
se = bt_rq->last;
|
|
|
|
/*
|
|
* Someone really wants this to run. If it's not unfair, run it.
|
|
*/
|
|
if (bt_rq->next && wakeup_preempt_bt_entity(bt_rq->next, left) < 1)
|
|
se = bt_rq->next;
|
|
|
|
clear_buddies_bt(bt_rq, se);
|
|
|
|
return se;
|
|
}
|
|
|
|
static void put_prev_bt_entity(struct bt_rq *bt_rq, struct sched_entity *prev)
|
|
{
|
|
/*
|
|
* If still on the runqueue then deactivate_task()
|
|
* was not called and update_curr() has to be done:
|
|
*/
|
|
if (prev->on_rq)
|
|
update_curr_bt(bt_rq);
|
|
|
|
check_bt_spread(bt_rq, prev);
|
|
if (prev->on_rq) {
|
|
update_stats_wait_start_bt(bt_rq, prev);
|
|
/* Put 'current' back into the tree. */
|
|
__enqueue_bt_entity(bt_rq, prev);
|
|
}
|
|
bt_rq->curr = NULL;
|
|
}
|
|
|
|
static void
|
|
bt_entity_tick(struct bt_rq *bt_rq, struct sched_entity *curr, int queued)
|
|
{
|
|
/*
|
|
* Update run-time bt_statistics of the 'current'.
|
|
*/
|
|
update_curr_bt(bt_rq);
|
|
|
|
#ifdef CONFIG_SCHED_HRTICK
|
|
/*
|
|
* queued ticks are scheduled to match the slice, so don't bother
|
|
* validating it and just reschedule.
|
|
*/
|
|
if (queued) {
|
|
resched_curr(rq_of_bt_rq(bt_rq));
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
if (bt_rq->nr_running > 1)
|
|
check_preempt_tick_bt(bt_rq, curr);
|
|
}
|
|
|
|
/*
|
|
* The enqueue_task method is called before nr_running is
|
|
* increased. Here we update the fair scheduling stats and
|
|
* then put the task into the rbtree:
|
|
*/
|
|
static void
|
|
enqueue_task_bt(struct rq *rq, struct task_struct *p, int flags)
|
|
{
|
|
struct bt_rq *bt_rq;
|
|
struct sched_entity *se = &p->bt;
|
|
|
|
for_each_sched_bt_entity(se) {
|
|
if (se->on_rq)
|
|
break;
|
|
bt_rq = bt_rq_of(se);
|
|
enqueue_bt_entity(bt_rq, se, flags);
|
|
|
|
bt_rq->h_nr_running++;
|
|
|
|
flags = ENQUEUE_WAKEUP;
|
|
}
|
|
|
|
if (!se) {
|
|
rq->bt_nr_running++;
|
|
add_nr_running(rq, 1);
|
|
}
|
|
}
|
|
|
|
static void set_next_buddy_bt(struct sched_entity *se);
|
|
|
|
/*
|
|
* The dequeue_task method is called before nr_running is
|
|
* decreased. We remove the task from the rbtree and
|
|
* update the fair scheduling stats:
|
|
*/
|
|
static void dequeue_task_bt(struct rq *rq, struct task_struct *p, int flags)
|
|
{
|
|
struct bt_rq *bt_rq;
|
|
struct sched_entity *se = &p->bt;
|
|
int task_sleep = flags & DEQUEUE_SLEEP;
|
|
|
|
for_each_sched_bt_entity(se) {
|
|
bt_rq = bt_rq_of(se);
|
|
dequeue_bt_entity(bt_rq, se, flags);
|
|
|
|
bt_rq->h_nr_running--;
|
|
|
|
/* Don't dequeue parent if it has other entities besides us */
|
|
if (bt_rq->load.weight) {
|
|
/*
|
|
* Bias pick_next to pick a task from this cfs_rq, as
|
|
* p is sleeping when it is within its sched_slice.
|
|
*/
|
|
if (task_sleep && parent_bt_entity(se))
|
|
set_next_buddy_bt(parent_bt_entity(se));
|
|
|
|
/* avoid re-evaluating load for this entity */
|
|
se = parent_bt_entity(se);
|
|
break;
|
|
}
|
|
flags |= DEQUEUE_SLEEP;
|
|
}
|
|
|
|
if (!se) {
|
|
sub_nr_running(rq, 1);
|
|
rq->bt_nr_running--;
|
|
}
|
|
}
|
|
|
|
#ifdef CONFIG_SMP
|
|
/**
|
|
* idle_cpu - is a given cpu idle currently?
|
|
* @cpu: the processor in question.
|
|
*/
|
|
static int idle_cpu_bt(int cpu)
|
|
{
|
|
struct rq *rq = cpu_rq(cpu);
|
|
|
|
if (rq->curr != rq->idle)
|
|
return 0;
|
|
|
|
if (rq->nr_running)
|
|
return 0;
|
|
|
|
#ifdef CONFIG_SMP
|
|
if (!llist_empty(&rq->wake_list))
|
|
return 0;
|
|
#endif
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int select_idle_sibling_bt(struct task_struct *p, int target)
|
|
{
|
|
struct sched_domain *sd;
|
|
struct sched_group *sg;
|
|
int i = task_cpu(p);
|
|
|
|
if (idle_cpu_bt(target))
|
|
return target;
|
|
|
|
/*
|
|
* If the prevous cpu is cache affine and idle, don't be stupid.
|
|
*/
|
|
if (i != target && cpus_share_cache(i, target) && idle_cpu_bt(i))
|
|
return i;
|
|
|
|
/*
|
|
* Otherwise, iterate the domains and find an elegible idle cpu.
|
|
*/
|
|
sd = rcu_dereference(per_cpu(sd_llc, target));
|
|
for_each_lower_domain(sd) {
|
|
sg = sd->groups;
|
|
do {
|
|
if (!cpumask_intersects(sched_group_cpus(sg),
|
|
tsk_cpus_allowed(p)))
|
|
goto next;
|
|
|
|
for_each_cpu(i, sched_group_cpus(sg)) {
|
|
if (i == target || !idle_cpu_bt(i))
|
|
goto next;
|
|
}
|
|
|
|
target = cpumask_first_and(sched_group_cpus(sg),
|
|
tsk_cpus_allowed(p));
|
|
goto done;
|
|
next:
|
|
sg = sg->next;
|
|
} while (sg != sd->groups);
|
|
}
|
|
done:
|
|
return target;
|
|
}
|
|
|
|
static int select_idle_cpu(struct task_struct *p, int target)
|
|
{
|
|
struct sched_domain *sd;
|
|
struct sched_group *sg;
|
|
int i = task_cpu(p);
|
|
|
|
if (idle_cpu_bt(target))
|
|
return target;
|
|
|
|
/*
|
|
* If the prevous cpu is cache affine and idle, don't be stupid.
|
|
*/
|
|
if (i != target && cpus_share_cache(i, target) && idle_cpu_bt(i))
|
|
return i;
|
|
|
|
/*
|
|
* Otherwise, iterate the domains and find an elegible idle cpu.
|
|
*/
|
|
sd = rcu_dereference(per_cpu(sd_llc, target));
|
|
for_each_lower_domain(sd) {
|
|
sg = sd->groups;
|
|
do {
|
|
if (!cpumask_intersects(sched_group_cpus(sg),
|
|
tsk_cpus_allowed(p)))
|
|
goto next;
|
|
|
|
for_each_cpu(i, sched_group_cpus(sg)) {
|
|
if (idle_cpu_bt(i) && cpumask_test_cpu(i, tsk_cpus_allowed(p))) {
|
|
target = i;
|
|
goto done;
|
|
}
|
|
}
|
|
next:
|
|
sg = sg->next;
|
|
} while (sg != sd->groups);
|
|
}
|
|
|
|
done:
|
|
return target;
|
|
}
|
|
|
|
|
|
static int
|
|
select_task_rq_bt(struct task_struct *p, int prev_cpu, int sd_flag, int wake_flags)
|
|
{
|
|
struct sched_domain *tmp, *affine_sd = NULL, *sd = NULL;
|
|
int cpu = smp_processor_id();
|
|
int new_cpu = prev_cpu;
|
|
int want_affine = 0;
|
|
|
|
if (p->nr_cpus_allowed == 1)
|
|
return prev_cpu;
|
|
|
|
if (sd_flag & SD_BALANCE_WAKE) {
|
|
if (cpumask_test_cpu(cpu, tsk_cpus_allowed(p)))
|
|
want_affine = 1;
|
|
new_cpu = prev_cpu;
|
|
}
|
|
|
|
rcu_read_lock();
|
|
for_each_domain(cpu, tmp) {
|
|
if (!(tmp->flags & SD_LOAD_BALANCE))
|
|
continue;
|
|
|
|
/*
|
|
* If both cpu and prev_cpu are part of this domain,
|
|
* cpu is a valid SD_WAKE_AFFINE target.
|
|
*/
|
|
if (want_affine && (tmp->flags & SD_WAKE_AFFINE) &&
|
|
cpumask_test_cpu(prev_cpu, sched_domain_span(tmp))) {
|
|
affine_sd = tmp;
|
|
break;
|
|
}
|
|
|
|
if (tmp->flags & sd_flag)
|
|
sd = tmp;
|
|
}
|
|
|
|
if (affine_sd) {
|
|
new_cpu = select_idle_sibling_bt(p, prev_cpu);
|
|
goto unlock;
|
|
}
|
|
|
|
new_cpu = select_idle_cpu(p, prev_cpu);
|
|
|
|
unlock:
|
|
rcu_read_unlock();
|
|
|
|
return new_cpu;
|
|
}
|
|
#endif
|
|
|
|
static unsigned long
|
|
wakeup_gran_bt(struct sched_entity *curr, struct sched_entity *se)
|
|
{
|
|
unsigned long gran = sysctl_sched_wakeup_granularity;
|
|
|
|
/*
|
|
* Since its curr running now, convert the gran from real-time
|
|
* to virtual-time in his units.
|
|
*
|
|
* By using 'se' instead of 'curr' we penalize light tasks, so
|
|
* they get preempted easier. That is, if 'se' < 'curr' then
|
|
* the resulting gran will be larger, therefore penalizing the
|
|
* lighter, if otoh 'se' > 'curr' then the resulting gran will
|
|
* be smaller, again penalizing the lighter task.
|
|
*
|
|
* This is especially important for buddies when the leftmost
|
|
* task is higher priority than the buddy.
|
|
*/
|
|
return calc_delta_bt(gran, se);
|
|
}
|
|
|
|
/*
|
|
* Should 'se' preempt 'curr'.
|
|
*
|
|
* |s1
|
|
* |s2
|
|
* |s3
|
|
* g
|
|
* |<--->|c
|
|
*
|
|
* w(c, s1) = -1
|
|
* w(c, s2) = 0
|
|
* w(c, s3) = 1
|
|
*
|
|
*/
|
|
static int
|
|
wakeup_preempt_bt_entity(struct sched_entity *curr, struct sched_entity *se)
|
|
{
|
|
s64 gran, vdiff = curr->vruntime - se->vruntime;
|
|
|
|
if (vdiff <= 0)
|
|
return -1;
|
|
|
|
gran = wakeup_gran_bt(curr, se);
|
|
if (vdiff > gran)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void set_last_buddy_bt(struct sched_entity *se)
|
|
{
|
|
if (bt_entity_is_task(se))
|
|
return;
|
|
|
|
for_each_sched_bt_entity(se)
|
|
bt_rq_of(se)->last = se;
|
|
}
|
|
|
|
static void set_next_buddy_bt(struct sched_entity *se)
|
|
{
|
|
if (bt_entity_is_task(se))
|
|
return;
|
|
|
|
for_each_sched_bt_entity(se)
|
|
bt_rq_of(se)->next = se;
|
|
}
|
|
|
|
static void set_skip_buddy_bt(struct sched_entity *se)
|
|
{
|
|
for_each_sched_bt_entity(se)
|
|
bt_rq_of(se)->skip = se;
|
|
}
|
|
|
|
/*
|
|
* Preempt the current task with a newly woken task if needed:
|
|
*/
|
|
static void check_preempt_wakeup_bt(struct rq *rq, struct task_struct *p, int wake_flags)
|
|
{
|
|
struct task_struct *curr = rq->curr;
|
|
struct sched_entity *se = &curr->bt, *pse = &p->bt;
|
|
struct bt_rq *bt_rq = task_bt_rq(curr);
|
|
int scale = bt_rq->nr_running >= sched_nr_latency;
|
|
int next_buddy_marked = 0;
|
|
|
|
if (unlikely(se == pse))
|
|
return;
|
|
|
|
if (sched_feat(NEXT_BUDDY) && scale && !(wake_flags & WF_FORK)) {
|
|
set_next_buddy_bt(pse);
|
|
next_buddy_marked = 1;
|
|
}
|
|
|
|
/*
|
|
* We can come here with TIF_NEED_RESCHED already set from new task
|
|
* wake up path.
|
|
*
|
|
* Note: this also catches the edge-case of curr being in a throttled
|
|
* group (e.g. via set_curr_task), since update_curr() (in the
|
|
* enqueue of curr) will have resulted in resched being set. This
|
|
* prevents us from potentially nominating it as a false LAST_BUDDY
|
|
* below.
|
|
*/
|
|
if (test_tsk_need_resched(curr))
|
|
return;
|
|
|
|
/* BT tasks are by definition preempted by non-bt tasks. */
|
|
if (likely(p->policy < SCHED_BT))
|
|
goto preempt;
|
|
|
|
if (!sched_feat(WAKEUP_PREEMPTION))
|
|
return;
|
|
|
|
update_curr_bt(bt_rq_of(se));
|
|
BUG_ON(!pse);
|
|
if (wakeup_preempt_bt_entity(se, pse) == 1) {
|
|
/*
|
|
* Bias pick_next to pick the sched entity that is
|
|
* triggering this preemption.
|
|
*/
|
|
if (!next_buddy_marked)
|
|
set_next_buddy_bt(pse);
|
|
goto preempt;
|
|
}
|
|
|
|
return;
|
|
|
|
preempt:
|
|
resched_curr(rq);
|
|
/*
|
|
* Only set the backward buddy when the current task is still
|
|
* on the rq. This can happen when a wakeup gets interleaved
|
|
* with schedule on the ->pre_schedule() or idle_balance()
|
|
* point, either of which can * drop the rq lock.
|
|
*
|
|
* Also, during early boot the idle thread is in the fair class,
|
|
* for obvious reasons its a bad idea to schedule back to it.
|
|
*/
|
|
if (unlikely(!se->on_rq || curr == rq->idle))
|
|
return;
|
|
|
|
if (sched_feat(LAST_BUDDY) && scale && bt_entity_is_task(se))
|
|
set_last_buddy_bt(se);
|
|
}
|
|
|
|
static struct task_struct *pick_next_task_bt(struct rq *rq, struct task_struct *prev, struct rq_flags *rf)
|
|
{
|
|
struct task_struct *p;
|
|
struct bt_rq *bt_rq;
|
|
struct sched_entity *se;
|
|
int new_tasks = -1;
|
|
|
|
bt_rq = &rq->bt;
|
|
again:
|
|
if (!bt_rq->nr_running)
|
|
goto idle;
|
|
|
|
put_prev_task(rq, prev);
|
|
|
|
se = pick_next_bt_entity(bt_rq);
|
|
set_next_bt_entity(bt_rq, se);
|
|
|
|
p = bt_task_of(se);
|
|
|
|
return p;
|
|
|
|
idle:
|
|
new_tasks = idle_balance(rq, rf);
|
|
|
|
/*
|
|
* Because idle_balance() releases (and re-acquires) rq->lock, it is
|
|
* possible for any higher priority task to appear. In that case we
|
|
* must re-start the pick_next_entity() loop.
|
|
*/
|
|
if (new_tasks < 0)
|
|
return RETRY_TASK;
|
|
|
|
if (new_tasks > 0)
|
|
goto again;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Account for a descheduled task:
|
|
*/
|
|
static void put_prev_task_bt(struct rq *rq, struct task_struct *prev)
|
|
{
|
|
struct sched_entity *se = &prev->bt;
|
|
struct bt_rq *bt_rq;
|
|
|
|
for_each_sched_bt_entity(se) {
|
|
bt_rq = bt_rq_of(se);
|
|
put_prev_bt_entity(bt_rq, se);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* sched_yield() is very simple
|
|
*
|
|
* The magic of dealing with the ->skip buddy is in pick_next_entity.
|
|
*/
|
|
static void yield_task_bt(struct rq *rq)
|
|
{
|
|
struct task_struct *curr = rq->curr;
|
|
struct bt_rq *bt_rq = task_bt_rq(curr);
|
|
struct sched_entity *se = &curr->bt;
|
|
|
|
/*
|
|
* Are we the only task in the tree?
|
|
*/
|
|
if (unlikely(rq->bt_nr_running == 1))
|
|
return;
|
|
|
|
clear_buddies_bt(bt_rq, se);
|
|
|
|
update_rq_clock(rq);
|
|
/*
|
|
* Update run-time bt_statistics of the 'current'.
|
|
*/
|
|
update_curr_bt(bt_rq);
|
|
/*
|
|
* Tell update_rq_clock() that we've just updated,
|
|
* so we don't do microscopic update in schedule()
|
|
* and double the fastpath cost.
|
|
*/
|
|
// rq->skip_clock_update = 1;
|
|
|
|
set_skip_buddy_bt(se);
|
|
}
|
|
|
|
static bool yield_to_task_bt(struct rq *rq, struct task_struct *p, bool preempt)
|
|
{
|
|
struct sched_entity *se = &p->bt;
|
|
|
|
if (!se->on_rq)
|
|
return false;
|
|
|
|
/* Tell the scheduler that we'd really like pse to run next. */
|
|
set_next_buddy_bt(se);
|
|
|
|
yield_task_bt(rq);
|
|
|
|
return true;
|
|
}
|
|
|
|
#ifdef CONFIG_SMP
|
|
static void rq_online_bt(struct rq *rq)
|
|
{
|
|
update_sysctl();
|
|
}
|
|
|
|
static void rq_offline_bt(struct rq *rq)
|
|
{
|
|
update_sysctl();
|
|
}
|
|
|
|
#endif /* CONFIG_SMP */
|
|
|
|
/*
|
|
* scheduler tick hitting a task of our scheduling class:
|
|
*/
|
|
static void task_tick_bt(struct rq *rq, struct task_struct *curr, int queued)
|
|
{
|
|
struct bt_rq *bt_rq;
|
|
struct sched_entity *se = &curr->bt;
|
|
|
|
for_each_sched_bt_entity(se) {
|
|
bt_rq = bt_rq_of(se);
|
|
bt_entity_tick(bt_rq, se, queued);
|
|
}
|
|
|
|
if (static_branch_unlikely(&sched_numa_balancing))
|
|
task_tick_numa(rq, curr);
|
|
}
|
|
|
|
/*
|
|
* called on fork with the child task as argument from the parent's context
|
|
* - child not yet on the tasklist
|
|
* - preemption disabled
|
|
*/
|
|
static void task_fork_bt(struct task_struct *p)
|
|
{
|
|
struct bt_rq *bt_rq;
|
|
struct sched_entity *se = &p->bt, *curr;
|
|
int this_cpu = smp_processor_id();
|
|
struct rq *rq = this_rq();
|
|
unsigned long flags;
|
|
|
|
raw_spin_lock_irqsave(&rq->lock, flags);
|
|
|
|
update_rq_clock(rq);
|
|
|
|
bt_rq = task_bt_rq(current);
|
|
curr = bt_rq->curr;
|
|
|
|
/*
|
|
* Not only the cpu but also the task_group of the parent might have
|
|
* been changed after parent->se.parent,cfs_rq were copied to
|
|
* child->se.parent,cfs_rq. So call __set_task_cpu() to make those
|
|
* of child point to valid ones.
|
|
*/
|
|
rcu_read_lock();
|
|
__set_task_cpu(p, this_cpu);
|
|
rcu_read_unlock();
|
|
|
|
update_curr_bt(bt_rq);
|
|
|
|
if (curr)
|
|
se->vruntime = curr->vruntime;
|
|
place_bt_entity(bt_rq, se, 1);
|
|
|
|
if (sysctl_sched_child_runs_first && curr && bt_entity_before(curr, se)) {
|
|
/*
|
|
* Upon rescheduling, sched_class::put_prev_task() will place
|
|
* 'current' within the tree based on its new key value.
|
|
*/
|
|
swap(curr->vruntime, se->vruntime);
|
|
resched_curr(rq);
|
|
}
|
|
|
|
se->vruntime -= bt_rq->min_vruntime;
|
|
|
|
raw_spin_unlock_irqrestore(&rq->lock, flags);
|
|
}
|
|
|
|
/*
|
|
* Priority of the task has changed. Check to see if we preempt
|
|
* the current task.
|
|
*/
|
|
static void
|
|
prio_changed_bt(struct rq *rq, struct task_struct *p, int oldprio)
|
|
{
|
|
if (!p->bt.on_rq)
|
|
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 (rq->curr == p) {
|
|
if (p->prio > oldprio)
|
|
resched_curr(rq);
|
|
} else
|
|
check_preempt_curr(rq, p, 0);
|
|
}
|
|
|
|
static void switched_from_bt(struct rq *rq, struct task_struct *p)
|
|
{
|
|
struct sched_entity *se = &p->bt;
|
|
struct bt_rq *bt_rq = bt_rq_of(se);
|
|
|
|
/*
|
|
* Ensure the task's vruntime is normalized, so that when it's
|
|
* switched back to the fair class the enqueue_entity(.flags=0) will
|
|
* do the right thing.
|
|
*
|
|
* If it's on_rq, then the dequeue_entity(.flags=0) will already
|
|
* have normalized the vruntime, if it's !on_rq, then only when
|
|
* the task is sleeping will it still have non-normalized vruntime.
|
|
*/
|
|
if (!p->on_rq && p->state != TASK_RUNNING) {
|
|
/*
|
|
* Fix up our vruntime so that the current sleep doesn't
|
|
* cause 'unlimited' sleep bonus.
|
|
*/
|
|
place_bt_entity(bt_rq, se, 0);
|
|
se->vruntime -= bt_rq->min_vruntime;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* We switched to the sched_fair class.
|
|
*/
|
|
static void switched_to_bt(struct rq *rq, struct task_struct *p)
|
|
{
|
|
BUG_ON(!bt_prio(p->static_prio));
|
|
|
|
// attach_task_bt_rq(p);
|
|
|
|
if (!p->bt.on_rq)
|
|
return;
|
|
|
|
/*
|
|
* We were most likely switched from sched_rt, so
|
|
* kick off the schedule if running, otherwise just see
|
|
* if we can still preempt the current task.
|
|
*/
|
|
if (rq->curr == p)
|
|
resched_curr(rq);
|
|
else
|
|
check_preempt_curr(rq, p, 0);
|
|
}
|
|
|
|
/* Account for a task changing its policy or group.
|
|
*
|
|
* This routine is mostly called to set cfs_rq->curr field when a task
|
|
* migrates between groups/classes.
|
|
*/
|
|
static void set_curr_task_bt(struct rq *rq)
|
|
{
|
|
struct sched_entity *se = &rq->curr->bt;
|
|
|
|
for_each_sched_bt_entity(se) {
|
|
struct bt_rq *bt_rq = bt_rq_of(se);
|
|
|
|
set_next_bt_entity(bt_rq, se);
|
|
}
|
|
}
|
|
|
|
void init_bt_rq(struct bt_rq *bt_rq)
|
|
{
|
|
bt_rq->tasks_timeline.rb_root = RB_ROOT;
|
|
bt_rq->tasks_timeline.rb_leftmost = NULL;
|
|
bt_rq->min_vruntime = (u64)(-(1LL << 20));
|
|
#ifndef CONFIG_64BIT
|
|
bt_rq->min_vruntime_copy = bt_rq->min_vruntime;
|
|
#endif
|
|
}
|
|
|
|
static unsigned int get_rr_interval_bt(struct rq *rq, struct task_struct *task)
|
|
{
|
|
struct sched_entity *se = &task->bt;
|
|
unsigned int rr_interval = 0;
|
|
|
|
/*
|
|
* Time slice is 0 for SCHED_OTHER tasks that are on an otherwise
|
|
* idle runqueue:
|
|
*/
|
|
if (rq->bt.load.weight)
|
|
rr_interval = NS_TO_JIFFIES(sched_bt_slice(bt_rq_of(se), se));
|
|
|
|
return rr_interval;
|
|
}
|
|
|
|
/*
|
|
* All the scheduling class methods:
|
|
*/
|
|
const struct sched_class bt_sched_class = {
|
|
.next = &idle_sched_class,
|
|
.enqueue_task = enqueue_task_bt,
|
|
.dequeue_task = dequeue_task_bt,
|
|
.yield_task = yield_task_bt,
|
|
.yield_to_task = yield_to_task_bt,
|
|
|
|
.check_preempt_curr = check_preempt_wakeup_bt,
|
|
|
|
.pick_next_task = pick_next_task_bt,
|
|
.put_prev_task = put_prev_task_bt,
|
|
|
|
#ifdef CONFIG_SMP
|
|
.select_task_rq = select_task_rq_bt,
|
|
.rq_online = rq_online_bt,
|
|
.rq_offline = rq_offline_bt,
|
|
#endif
|
|
|
|
.set_curr_task = set_curr_task_bt,
|
|
.task_tick = task_tick_bt,
|
|
.task_fork = task_fork_bt,
|
|
|
|
.prio_changed = prio_changed_bt,
|
|
.switched_from = switched_from_bt,
|
|
.switched_to = switched_to_bt,
|
|
|
|
.get_rr_interval = get_rr_interval_bt,
|
|
};
|