浏览代码

sched: prevent wakeup over-scheduling

Prevent wakeup over-scheduling.  Once a task has been preempted by a
task of the same or lower priority, it becomes ineligible for repeated
preemption by same until it has been ticked, or slept.  Instead, the
task is marked for preemption at the next tick.  Tasks of higher
priority still preempt immediately.

Signed-off-by: Mike Galbraith <efault@gmx.de>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Mike Galbraith 17 年之前
父节点
当前提交
95938a35c5
共有 3 个文件被更改,包括 15 次插入4 次删除
  1. 1 0
      include/linux/sched.h
  2. 3 1
      kernel/sched.c
  3. 11 3
      kernel/sched_fair.c

+ 1 - 0
include/linux/sched.h

@@ -912,6 +912,7 @@ struct sched_entity {
 	struct load_weight	load;		/* for load-balancing */
 	struct load_weight	load;		/* for load-balancing */
 	struct rb_node		run_node;
 	struct rb_node		run_node;
 	unsigned int		on_rq;
 	unsigned int		on_rq;
+	int			peer_preempt;
 
 
 	u64			exec_start;
 	u64			exec_start;
 	u64			sum_exec_runtime;
 	u64			sum_exec_runtime;

+ 3 - 1
kernel/sched.c

@@ -445,6 +445,7 @@ enum {
 	SCHED_FEAT_TREE_AVG             = 4,
 	SCHED_FEAT_TREE_AVG             = 4,
 	SCHED_FEAT_APPROX_AVG           = 8,
 	SCHED_FEAT_APPROX_AVG           = 8,
 	SCHED_FEAT_WAKEUP_PREEMPT	= 16,
 	SCHED_FEAT_WAKEUP_PREEMPT	= 16,
+	SCHED_FEAT_PREEMPT_RESTRICT	= 32,
 };
 };
 
 
 const_debug unsigned int sysctl_sched_features =
 const_debug unsigned int sysctl_sched_features =
@@ -452,7 +453,8 @@ const_debug unsigned int sysctl_sched_features =
 		SCHED_FEAT_START_DEBIT		*1 |
 		SCHED_FEAT_START_DEBIT		*1 |
 		SCHED_FEAT_TREE_AVG		*0 |
 		SCHED_FEAT_TREE_AVG		*0 |
 		SCHED_FEAT_APPROX_AVG		*0 |
 		SCHED_FEAT_APPROX_AVG		*0 |
-		SCHED_FEAT_WAKEUP_PREEMPT	*1;
+		SCHED_FEAT_WAKEUP_PREEMPT	*1 |
+		SCHED_FEAT_PREEMPT_RESTRICT	*1;
 
 
 #define sched_feat(x) (sysctl_sched_features & SCHED_FEAT_##x)
 #define sched_feat(x) (sysctl_sched_features & SCHED_FEAT_##x)
 
 

+ 11 - 3
kernel/sched_fair.c

@@ -526,6 +526,7 @@ dequeue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int sleep)
 
 
 	update_stats_dequeue(cfs_rq, se);
 	update_stats_dequeue(cfs_rq, se);
 	if (sleep) {
 	if (sleep) {
+		se->peer_preempt = 0;
 #ifdef CONFIG_SCHEDSTATS
 #ifdef CONFIG_SCHEDSTATS
 		if (entity_is_task(se)) {
 		if (entity_is_task(se)) {
 			struct task_struct *tsk = task_of(se);
 			struct task_struct *tsk = task_of(se);
@@ -553,8 +554,10 @@ check_preempt_tick(struct cfs_rq *cfs_rq, struct sched_entity *curr)
 
 
 	ideal_runtime = sched_slice(cfs_rq, curr);
 	ideal_runtime = sched_slice(cfs_rq, curr);
 	delta_exec = curr->sum_exec_runtime - curr->prev_sum_exec_runtime;
 	delta_exec = curr->sum_exec_runtime - curr->prev_sum_exec_runtime;
-	if (delta_exec > ideal_runtime)
+	if (delta_exec > ideal_runtime ||
+			(sched_feat(PREEMPT_RESTRICT) && curr->peer_preempt))
 		resched_task(rq_of(cfs_rq)->curr);
 		resched_task(rq_of(cfs_rq)->curr);
+	curr->peer_preempt = 0;
 }
 }
 
 
 static void
 static void
@@ -839,8 +842,12 @@ static void check_preempt_wakeup(struct rq *rq, struct task_struct *p)
 		if (unlikely(se->load.weight != NICE_0_LOAD))
 		if (unlikely(se->load.weight != NICE_0_LOAD))
 			gran = calc_delta_fair(gran, &se->load);
 			gran = calc_delta_fair(gran, &se->load);
 
 
-		if (delta > gran)
-			resched_task(curr);
+		if (delta > gran) {
+			int now = !sched_feat(PREEMPT_RESTRICT);
+
+			if (now || p->prio < curr->prio || !se->peer_preempt++)
+				resched_task(curr);
+		}
 	}
 	}
 }
 }
 
 
@@ -1034,6 +1041,7 @@ static void task_new_fair(struct rq *rq, struct task_struct *p)
 	check_spread(cfs_rq, curr);
 	check_spread(cfs_rq, curr);
 	__enqueue_entity(cfs_rq, se);
 	__enqueue_entity(cfs_rq, se);
 	account_entity_enqueue(cfs_rq, se);
 	account_entity_enqueue(cfs_rq, se);
+	se->peer_preempt = 0;
 	resched_task(rq->curr);
 	resched_task(rq->curr);
 }
 }