forked from mirrors/linux
		
	Merge branch 'sched/core' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip into for-6.12
Pull tip/sched/core to resolve the following four conflicts. While 2-4 are simple context conflicts, 1 is a bit subtle and easy to resolve incorrectly. 1.2c8d046d5d("sched: Add normal_policy()") vs.faa42d2941("sched/fair: Make SCHED_IDLE entity be preempted in strict hierarchy") The former converts direct test on p->policy to use the helper normal_policy(). The latter moves the p->policy test to a different location. Resolve by converting the test on p->plicy in the new location to use normal_policy(). 2.a7a9fc5492("sched_ext: Add boilerplate for extensible scheduler class") vs.a110a81c52("sched/deadline: Deferrable dl server") Both add calls to put_prev_task_idle() and set_next_task_idle(). Simple context conflict. Resolve by taking changes from both. 3.a7a9fc5492("sched_ext: Add boilerplate for extensible scheduler class") vs.c245910049("sched/core: Add clearing of ->dl_server in put_prev_task_balance()") The former changes for_each_class() itertion to use for_each_active_class(). The latter moves away the adjacent dl_server handling code. Simple context conflict. Resolve by taking changes from both. 4.60c27fb59f("sched_ext: Implement sched_ext_ops.cpu_online/offline()") vs.31b164e2e4("sched/smt: Introduce sched_smt_present_inc/dec() helper")2f02735412("sched/core: Introduce sched_set_rq_on/offline() helper") The former adds scx_rq_deactivate() call. The latter two change code around it. Simple context conflict. Resolve by taking changes from both. Signed-off-by: Tejun Heo <tj@kernel.org>
This commit is contained in:
		
						commit
						0df340ceae
					
				
					 11 changed files with 895 additions and 282 deletions
				
			
		|  | @ -641,12 +641,26 @@ struct sched_dl_entity { | |||
| 	 * | ||||
| 	 * @dl_overrun tells if the task asked to be informed about runtime | ||||
| 	 * overruns. | ||||
| 	 * | ||||
| 	 * @dl_server tells if this is a server entity. | ||||
| 	 * | ||||
| 	 * @dl_defer tells if this is a deferred or regular server. For | ||||
| 	 * now only defer server exists. | ||||
| 	 * | ||||
| 	 * @dl_defer_armed tells if the deferrable server is waiting | ||||
| 	 * for the replenishment timer to activate it. | ||||
| 	 * | ||||
| 	 * @dl_defer_running tells if the deferrable server is actually | ||||
| 	 * running, skipping the defer phase. | ||||
| 	 */ | ||||
| 	unsigned int			dl_throttled      : 1; | ||||
| 	unsigned int			dl_yielded        : 1; | ||||
| 	unsigned int			dl_non_contending : 1; | ||||
| 	unsigned int			dl_overrun	  : 1; | ||||
| 	unsigned int			dl_server         : 1; | ||||
| 	unsigned int			dl_defer	  : 1; | ||||
| 	unsigned int			dl_defer_armed	  : 1; | ||||
| 	unsigned int			dl_defer_running  : 1; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Bandwidth enforcement timer. Each -deadline task has its | ||||
|  | @ -674,7 +688,8 @@ struct sched_dl_entity { | |||
| 	 */ | ||||
| 	struct rq			*rq; | ||||
| 	dl_server_has_tasks_f		server_has_tasks; | ||||
| 	dl_server_pick_f		server_pick; | ||||
| 	dl_server_pick_f		server_pick_next; | ||||
| 	dl_server_pick_f		server_pick_task; | ||||
| 
 | ||||
| #ifdef CONFIG_RT_MUTEXES | ||||
| 	/*
 | ||||
|  |  | |||
|  | @ -163,6 +163,9 @@ static inline int __task_prio(const struct task_struct *p) | |||
| 	if (p->sched_class == &stop_sched_class) /* trumps deadline */ | ||||
| 		return -2; | ||||
| 
 | ||||
| 	if (p->dl_server) | ||||
| 		return -1; /* deadline */ | ||||
| 
 | ||||
| 	if (rt_prio(p->prio)) /* includes deadline */ | ||||
| 		return p->prio; /* [-1, 99] */ | ||||
| 
 | ||||
|  | @ -195,8 +198,24 @@ static inline bool prio_less(const struct task_struct *a, | |||
| 	if (-pb < -pa) | ||||
| 		return false; | ||||
| 
 | ||||
| 	if (pa == -1) /* dl_prio() doesn't work because of stop_class above */ | ||||
| 		return !dl_time_before(a->dl.deadline, b->dl.deadline); | ||||
| 	if (pa == -1) { /* dl_prio() doesn't work because of stop_class above */ | ||||
| 		const struct sched_dl_entity *a_dl, *b_dl; | ||||
| 
 | ||||
| 		a_dl = &a->dl; | ||||
| 		/*
 | ||||
| 		 * Since,'a' and 'b' can be CFS tasks served by DL server, | ||||
| 		 * __task_prio() can return -1 (for DL) even for those. In that | ||||
| 		 * case, get to the dl_server's DL entity. | ||||
| 		 */ | ||||
| 		if (a->dl_server) | ||||
| 			a_dl = a->dl_server; | ||||
| 
 | ||||
| 		b_dl = &b->dl; | ||||
| 		if (b->dl_server) | ||||
| 			b_dl = b->dl_server; | ||||
| 
 | ||||
| 		return !dl_time_before(a_dl->deadline, b_dl->deadline); | ||||
| 	} | ||||
| 
 | ||||
| 	if (pa == MAX_RT_PRIO + MAX_NICE)	/* fair */ | ||||
| 		return cfs_prio_less(a, b, in_fi); | ||||
|  | @ -1280,7 +1299,7 @@ bool sched_can_stop_tick(struct rq *rq) | |||
| 	 * dequeued by migrating while the constrained task continues to run. | ||||
| 	 * E.g. going from 2->1 without going through pick_next_task(). | ||||
| 	 */ | ||||
| 	if (sched_feat(HZ_BW) && __need_bw_check(rq, rq->curr)) { | ||||
| 	if (__need_bw_check(rq, rq->curr)) { | ||||
| 		if (cfs_task_bw_constrained(rq->curr)) | ||||
| 			return false; | ||||
| 	} | ||||
|  | @ -2255,6 +2274,12 @@ void migrate_disable(void) | |||
| 	struct task_struct *p = current; | ||||
| 
 | ||||
| 	if (p->migration_disabled) { | ||||
| #ifdef CONFIG_DEBUG_PREEMPT | ||||
| 		/*
 | ||||
| 		 *Warn about overflow half-way through the range. | ||||
| 		 */ | ||||
| 		WARN_ON_ONCE((s16)p->migration_disabled < 0); | ||||
| #endif | ||||
| 		p->migration_disabled++; | ||||
| 		return; | ||||
| 	} | ||||
|  | @ -2273,14 +2298,20 @@ void migrate_enable(void) | |||
| 		.flags     = SCA_MIGRATE_ENABLE, | ||||
| 	}; | ||||
| 
 | ||||
| #ifdef CONFIG_DEBUG_PREEMPT | ||||
| 	/*
 | ||||
| 	 * Check both overflow from migrate_disable() and superfluous | ||||
| 	 * migrate_enable(). | ||||
| 	 */ | ||||
| 	if (WARN_ON_ONCE((s16)p->migration_disabled <= 0)) | ||||
| 		return; | ||||
| #endif | ||||
| 
 | ||||
| 	if (p->migration_disabled > 1) { | ||||
| 		p->migration_disabled--; | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	if (WARN_ON_ONCE(!p->migration_disabled)) | ||||
| 		return; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Ensure stop_task runs either before or after this, and that | ||||
| 	 * __set_cpus_allowed_ptr(SCA_MIGRATE_ENABLE) doesn't schedule(). | ||||
|  | @ -4737,7 +4768,7 @@ void wake_up_new_task(struct task_struct *p) | |||
| 	update_rq_clock(rq); | ||||
| 	post_init_entity_util_avg(p); | ||||
| 
 | ||||
| 	activate_task(rq, p, ENQUEUE_NOCLOCK); | ||||
| 	activate_task(rq, p, ENQUEUE_NOCLOCK | ENQUEUE_INITIAL); | ||||
| 	trace_sched_wakeup_new(p); | ||||
| 	wakeup_preempt(rq, p, WF_FORK); | ||||
| #ifdef CONFIG_SMP | ||||
|  | @ -5855,6 +5886,14 @@ static void put_prev_task_balance(struct rq *rq, struct task_struct *prev, | |||
| #endif | ||||
| 
 | ||||
| 	put_prev_task(rq, prev); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * We've updated @prev and no longer need the server link, clear it. | ||||
| 	 * Must be done before ->pick_next_task() because that can (re)set | ||||
| 	 * ->dl_server. | ||||
| 	 */ | ||||
| 	if (prev->dl_server) | ||||
| 		prev->dl_server = NULL; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  | @ -5888,6 +5927,13 @@ __pick_next_task(struct rq *rq, struct task_struct *prev, struct rq_flags *rf) | |||
| 			p = pick_next_task_idle(rq); | ||||
| 		} | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * This is a normal CFS pick, but the previous could be a DL pick. | ||||
| 		 * Clear it as previous is no longer picked. | ||||
| 		 */ | ||||
| 		if (prev->dl_server) | ||||
| 			prev->dl_server = NULL; | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * This is the fast path; it cannot be a DL server pick; | ||||
| 		 * therefore even if @p == @prev, ->dl_server must be NULL. | ||||
|  | @ -5901,14 +5947,6 @@ __pick_next_task(struct rq *rq, struct task_struct *prev, struct rq_flags *rf) | |||
| restart: | ||||
| 	put_prev_task_balance(rq, prev, rf); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * We've updated @prev and no longer need the server link, clear it. | ||||
| 	 * Must be done before ->pick_next_task() because that can (re)set | ||||
| 	 * ->dl_server. | ||||
| 	 */ | ||||
| 	if (prev->dl_server) | ||||
| 		prev->dl_server = NULL; | ||||
| 
 | ||||
| 	for_each_active_class(class) { | ||||
| 		p = class->pick_next_task(rq); | ||||
| 		if (p) { | ||||
|  | @ -7925,6 +7963,30 @@ void set_rq_offline(struct rq *rq) | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| static inline void sched_set_rq_online(struct rq *rq, int cpu) | ||||
| { | ||||
| 	struct rq_flags rf; | ||||
| 
 | ||||
| 	rq_lock_irqsave(rq, &rf); | ||||
| 	if (rq->rd) { | ||||
| 		BUG_ON(!cpumask_test_cpu(cpu, rq->rd->span)); | ||||
| 		set_rq_online(rq); | ||||
| 	} | ||||
| 	rq_unlock_irqrestore(rq, &rf); | ||||
| } | ||||
| 
 | ||||
| static inline void sched_set_rq_offline(struct rq *rq, int cpu) | ||||
| { | ||||
| 	struct rq_flags rf; | ||||
| 
 | ||||
| 	rq_lock_irqsave(rq, &rf); | ||||
| 	if (rq->rd) { | ||||
| 		BUG_ON(!cpumask_test_cpu(cpu, rq->rd->span)); | ||||
| 		set_rq_offline(rq); | ||||
| 	} | ||||
| 	rq_unlock_irqrestore(rq, &rf); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * used to mark begin/end of suspend/resume: | ||||
|  */ | ||||
|  | @ -7975,10 +8037,25 @@ static int cpuset_cpu_inactive(unsigned int cpu) | |||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static inline void sched_smt_present_inc(int cpu) | ||||
| { | ||||
| #ifdef CONFIG_SCHED_SMT | ||||
| 	if (cpumask_weight(cpu_smt_mask(cpu)) == 2) | ||||
| 		static_branch_inc_cpuslocked(&sched_smt_present); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| static inline void sched_smt_present_dec(int cpu) | ||||
| { | ||||
| #ifdef CONFIG_SCHED_SMT | ||||
| 	if (cpumask_weight(cpu_smt_mask(cpu)) == 2) | ||||
| 		static_branch_dec_cpuslocked(&sched_smt_present); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| int sched_cpu_activate(unsigned int cpu) | ||||
| { | ||||
| 	struct rq *rq = cpu_rq(cpu); | ||||
| 	struct rq_flags rf; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Clear the balance_push callback and prepare to schedule | ||||
|  | @ -7986,13 +8063,10 @@ int sched_cpu_activate(unsigned int cpu) | |||
| 	 */ | ||||
| 	balance_push_set(cpu, false); | ||||
| 
 | ||||
| #ifdef CONFIG_SCHED_SMT | ||||
| 	/*
 | ||||
| 	 * When going up, increment the number of cores with SMT present. | ||||
| 	 */ | ||||
| 	if (cpumask_weight(cpu_smt_mask(cpu)) == 2) | ||||
| 		static_branch_inc_cpuslocked(&sched_smt_present); | ||||
| #endif | ||||
| 	sched_smt_present_inc(cpu); | ||||
| 	set_cpu_active(cpu, true); | ||||
| 
 | ||||
| 	if (sched_smp_initialized) { | ||||
|  | @ -8012,12 +8086,7 @@ int sched_cpu_activate(unsigned int cpu) | |||
| 	 * 2) At runtime, if cpuset_cpu_active() fails to rebuild the | ||||
| 	 *    domains. | ||||
| 	 */ | ||||
| 	rq_lock_irqsave(rq, &rf); | ||||
| 	if (rq->rd) { | ||||
| 		BUG_ON(!cpumask_test_cpu(cpu, rq->rd->span)); | ||||
| 		set_rq_online(rq); | ||||
| 	} | ||||
| 	rq_unlock_irqrestore(rq, &rf); | ||||
| 	sched_set_rq_online(rq, cpu); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
|  | @ -8025,7 +8094,6 @@ int sched_cpu_activate(unsigned int cpu) | |||
| int sched_cpu_deactivate(unsigned int cpu) | ||||
| { | ||||
| 	struct rq *rq = cpu_rq(cpu); | ||||
| 	struct rq_flags rf; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	/*
 | ||||
|  | @ -8056,22 +8124,16 @@ int sched_cpu_deactivate(unsigned int cpu) | |||
| 	 */ | ||||
| 	synchronize_rcu(); | ||||
| 
 | ||||
| 	rq_lock_irqsave(rq, &rf); | ||||
| 	if (rq->rd) { | ||||
| 		BUG_ON(!cpumask_test_cpu(cpu, rq->rd->span)); | ||||
| 		set_rq_offline(rq); | ||||
| 	} | ||||
| 	rq_unlock_irqrestore(rq, &rf); | ||||
| 	sched_set_rq_offline(rq, cpu); | ||||
| 
 | ||||
| 	scx_rq_deactivate(rq); | ||||
| 
 | ||||
| #ifdef CONFIG_SCHED_SMT | ||||
| 	/*
 | ||||
| 	 * When going down, decrement the number of cores with SMT present. | ||||
| 	 */ | ||||
| 	if (cpumask_weight(cpu_smt_mask(cpu)) == 2) | ||||
| 		static_branch_dec_cpuslocked(&sched_smt_present); | ||||
| 	sched_smt_present_dec(cpu); | ||||
| 
 | ||||
| #ifdef CONFIG_SCHED_SMT | ||||
| 	sched_core_cpu_deactivate(cpu); | ||||
| #endif | ||||
| 
 | ||||
|  | @ -8081,6 +8143,8 @@ int sched_cpu_deactivate(unsigned int cpu) | |||
| 	sched_update_numa(cpu, false); | ||||
| 	ret = cpuset_cpu_inactive(cpu); | ||||
| 	if (ret) { | ||||
| 		sched_smt_present_inc(cpu); | ||||
| 		sched_set_rq_online(rq, cpu); | ||||
| 		balance_push_set(cpu, false); | ||||
| 		set_cpu_active(cpu, true); | ||||
| 		sched_update_numa(cpu, true); | ||||
|  | @ -8290,8 +8354,6 @@ void __init sched_init(void) | |||
| #endif /* CONFIG_RT_GROUP_SCHED */ | ||||
| 	} | ||||
| 
 | ||||
| 	init_rt_bandwidth(&def_rt_bandwidth, global_rt_period(), global_rt_runtime()); | ||||
| 
 | ||||
| #ifdef CONFIG_SMP | ||||
| 	init_defrootdomain(); | ||||
| #endif | ||||
|  | @ -8346,8 +8408,13 @@ void __init sched_init(void) | |||
| 		init_tg_cfs_entry(&root_task_group, &rq->cfs, NULL, i, NULL); | ||||
| #endif /* CONFIG_FAIR_GROUP_SCHED */ | ||||
| 
 | ||||
| 		rq->rt.rt_runtime = def_rt_bandwidth.rt_runtime; | ||||
| #ifdef CONFIG_RT_GROUP_SCHED | ||||
| 		/*
 | ||||
| 		 * This is required for init cpu because rt.c:__enable_runtime() | ||||
| 		 * starts working after scheduler_running, which is not the case | ||||
| 		 * yet. | ||||
| 		 */ | ||||
| 		rq->rt.rt_runtime = global_rt_runtime(); | ||||
| 		init_tg_rt_entry(&root_task_group, &rq->rt, NULL, i, NULL); | ||||
| #endif | ||||
| #ifdef CONFIG_SMP | ||||
|  | @ -8379,6 +8446,7 @@ void __init sched_init(void) | |||
| #endif /* CONFIG_SMP */ | ||||
| 		hrtick_rq_init(rq); | ||||
| 		atomic_set(&rq->nr_iowait, 0); | ||||
| 		fair_server_init(rq); | ||||
| 
 | ||||
| #ifdef CONFIG_SCHED_CORE | ||||
| 		rq->core = rq; | ||||
|  |  | |||
|  | @ -582,6 +582,12 @@ void cputime_adjust(struct task_cputime *curr, struct prev_cputime *prev, | |||
| 	} | ||||
| 
 | ||||
| 	stime = mul_u64_u64_div_u64(stime, rtime, stime + utime); | ||||
| 	/*
 | ||||
| 	 * Because mul_u64_u64_div_u64() can approximate on some | ||||
| 	 * achitectures; enforce the constraint that: a*b/(b+c) <= a. | ||||
| 	 */ | ||||
| 	if (unlikely(stime > rtime)) | ||||
| 		stime = rtime; | ||||
| 
 | ||||
| update: | ||||
| 	/*
 | ||||
|  |  | |||
|  | @ -320,19 +320,12 @@ void sub_running_bw(struct sched_dl_entity *dl_se, struct dl_rq *dl_rq) | |||
| 		__sub_running_bw(dl_se->dl_bw, dl_rq); | ||||
| } | ||||
| 
 | ||||
| static void dl_change_utilization(struct task_struct *p, u64 new_bw) | ||||
| static void dl_rq_change_utilization(struct rq *rq, struct sched_dl_entity *dl_se, u64 new_bw) | ||||
| { | ||||
| 	struct rq *rq; | ||||
| 	if (dl_se->dl_non_contending) { | ||||
| 		sub_running_bw(dl_se, &rq->dl); | ||||
| 		dl_se->dl_non_contending = 0; | ||||
| 
 | ||||
| 	WARN_ON_ONCE(p->dl.flags & SCHED_FLAG_SUGOV); | ||||
| 
 | ||||
| 	if (task_on_rq_queued(p)) | ||||
| 		return; | ||||
| 
 | ||||
| 	rq = task_rq(p); | ||||
| 	if (p->dl.dl_non_contending) { | ||||
| 		sub_running_bw(&p->dl, &rq->dl); | ||||
| 		p->dl.dl_non_contending = 0; | ||||
| 		/*
 | ||||
| 		 * If the timer handler is currently running and the | ||||
| 		 * timer cannot be canceled, inactive_task_timer() | ||||
|  | @ -340,13 +333,25 @@ static void dl_change_utilization(struct task_struct *p, u64 new_bw) | |||
| 		 * will not touch the rq's active utilization, | ||||
| 		 * so we are still safe. | ||||
| 		 */ | ||||
| 		if (hrtimer_try_to_cancel(&p->dl.inactive_timer) == 1) | ||||
| 			put_task_struct(p); | ||||
| 		if (hrtimer_try_to_cancel(&dl_se->inactive_timer) == 1) { | ||||
| 			if (!dl_server(dl_se)) | ||||
| 				put_task_struct(dl_task_of(dl_se)); | ||||
| 		} | ||||
| 	} | ||||
| 	__sub_rq_bw(p->dl.dl_bw, &rq->dl); | ||||
| 	__sub_rq_bw(dl_se->dl_bw, &rq->dl); | ||||
| 	__add_rq_bw(new_bw, &rq->dl); | ||||
| } | ||||
| 
 | ||||
| static void dl_change_utilization(struct task_struct *p, u64 new_bw) | ||||
| { | ||||
| 	WARN_ON_ONCE(p->dl.flags & SCHED_FLAG_SUGOV); | ||||
| 
 | ||||
| 	if (task_on_rq_queued(p)) | ||||
| 		return; | ||||
| 
 | ||||
| 	dl_rq_change_utilization(task_rq(p), &p->dl, new_bw); | ||||
| } | ||||
| 
 | ||||
| static void __dl_clear_params(struct sched_dl_entity *dl_se); | ||||
| 
 | ||||
| /*
 | ||||
|  | @ -771,6 +776,15 @@ static inline void replenish_dl_new_period(struct sched_dl_entity *dl_se, | |||
| 	/* for non-boosted task, pi_of(dl_se) == dl_se */ | ||||
| 	dl_se->deadline = rq_clock(rq) + pi_of(dl_se)->dl_deadline; | ||||
| 	dl_se->runtime = pi_of(dl_se)->dl_runtime; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * If it is a deferred reservation, and the server | ||||
| 	 * is not handling an starvation case, defer it. | ||||
| 	 */ | ||||
| 	if (dl_se->dl_defer & !dl_se->dl_defer_running) { | ||||
| 		dl_se->dl_throttled = 1; | ||||
| 		dl_se->dl_defer_armed = 1; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  | @ -809,6 +823,9 @@ static inline void setup_new_dl_entity(struct sched_dl_entity *dl_se) | |||
| 	replenish_dl_new_period(dl_se, rq); | ||||
| } | ||||
| 
 | ||||
| static int start_dl_timer(struct sched_dl_entity *dl_se); | ||||
| static bool dl_entity_overflow(struct sched_dl_entity *dl_se, u64 t); | ||||
| 
 | ||||
| /*
 | ||||
|  * Pure Earliest Deadline First (EDF) scheduling does not deal with the | ||||
|  * possibility of a entity lasting more than what it declared, and thus | ||||
|  | @ -837,9 +854,18 @@ static void replenish_dl_entity(struct sched_dl_entity *dl_se) | |||
| 	/*
 | ||||
| 	 * This could be the case for a !-dl task that is boosted. | ||||
| 	 * Just go with full inherited parameters. | ||||
| 	 * | ||||
| 	 * Or, it could be the case of a deferred reservation that | ||||
| 	 * was not able to consume its runtime in background and | ||||
| 	 * reached this point with current u > U. | ||||
| 	 * | ||||
| 	 * In both cases, set a new period. | ||||
| 	 */ | ||||
| 	if (dl_se->dl_deadline == 0) | ||||
| 		replenish_dl_new_period(dl_se, rq); | ||||
| 	if (dl_se->dl_deadline == 0 || | ||||
| 	    (dl_se->dl_defer_armed && dl_entity_overflow(dl_se, rq_clock(rq)))) { | ||||
| 		dl_se->deadline = rq_clock(rq) + pi_of(dl_se)->dl_deadline; | ||||
| 		dl_se->runtime = pi_of(dl_se)->dl_runtime; | ||||
| 	} | ||||
| 
 | ||||
| 	if (dl_se->dl_yielded && dl_se->runtime > 0) | ||||
| 		dl_se->runtime = 0; | ||||
|  | @ -873,6 +899,44 @@ static void replenish_dl_entity(struct sched_dl_entity *dl_se) | |||
| 		dl_se->dl_yielded = 0; | ||||
| 	if (dl_se->dl_throttled) | ||||
| 		dl_se->dl_throttled = 0; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * If this is the replenishment of a deferred reservation, | ||||
| 	 * clear the flag and return. | ||||
| 	 */ | ||||
| 	if (dl_se->dl_defer_armed) { | ||||
| 		dl_se->dl_defer_armed = 0; | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * A this point, if the deferred server is not armed, and the deadline | ||||
| 	 * is in the future, if it is not running already, throttle the server | ||||
| 	 * and arm the defer timer. | ||||
| 	 */ | ||||
| 	if (dl_se->dl_defer && !dl_se->dl_defer_running && | ||||
| 	    dl_time_before(rq_clock(dl_se->rq), dl_se->deadline - dl_se->runtime)) { | ||||
| 		if (!is_dl_boosted(dl_se) && dl_se->server_has_tasks(dl_se)) { | ||||
| 
 | ||||
| 			/*
 | ||||
| 			 * Set dl_se->dl_defer_armed and dl_throttled variables to | ||||
| 			 * inform the start_dl_timer() that this is a deferred | ||||
| 			 * activation. | ||||
| 			 */ | ||||
| 			dl_se->dl_defer_armed = 1; | ||||
| 			dl_se->dl_throttled = 1; | ||||
| 			if (!start_dl_timer(dl_se)) { | ||||
| 				/*
 | ||||
| 				 * If for whatever reason (delays), a previous timer was | ||||
| 				 * queued but not serviced, cancel it and clean the | ||||
| 				 * deferrable server variables intended for start_dl_timer(). | ||||
| 				 */ | ||||
| 				hrtimer_try_to_cancel(&dl_se->dl_timer); | ||||
| 				dl_se->dl_defer_armed = 0; | ||||
| 				dl_se->dl_throttled = 0; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  | @ -1023,6 +1087,15 @@ static void update_dl_entity(struct sched_dl_entity *dl_se) | |||
| 		} | ||||
| 
 | ||||
| 		replenish_dl_new_period(dl_se, rq); | ||||
| 	} else if (dl_server(dl_se) && dl_se->dl_defer) { | ||||
| 		/*
 | ||||
| 		 * The server can still use its previous deadline, so check if | ||||
| 		 * it left the dl_defer_running state. | ||||
| 		 */ | ||||
| 		if (!dl_se->dl_defer_running) { | ||||
| 			dl_se->dl_defer_armed = 1; | ||||
| 			dl_se->dl_throttled = 1; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -1055,8 +1128,21 @@ static int start_dl_timer(struct sched_dl_entity *dl_se) | |||
| 	 * We want the timer to fire at the deadline, but considering | ||||
| 	 * that it is actually coming from rq->clock and not from | ||||
| 	 * hrtimer's time base reading. | ||||
| 	 * | ||||
| 	 * The deferred reservation will have its timer set to | ||||
| 	 * (deadline - runtime). At that point, the CBS rule will decide | ||||
| 	 * if the current deadline can be used, or if a replenishment is | ||||
| 	 * required to avoid add too much pressure on the system | ||||
| 	 * (current u > U). | ||||
| 	 */ | ||||
| 	act = ns_to_ktime(dl_next_period(dl_se)); | ||||
| 	if (dl_se->dl_defer_armed) { | ||||
| 		WARN_ON_ONCE(!dl_se->dl_throttled); | ||||
| 		act = ns_to_ktime(dl_se->deadline - dl_se->runtime); | ||||
| 	} else { | ||||
| 		/* act = deadline - rel-deadline + period */ | ||||
| 		act = ns_to_ktime(dl_next_period(dl_se)); | ||||
| 	} | ||||
| 
 | ||||
| 	now = hrtimer_cb_get_time(timer); | ||||
| 	delta = ktime_to_ns(now) - rq_clock(rq); | ||||
| 	act = ktime_add_ns(act, delta); | ||||
|  | @ -1106,6 +1192,62 @@ static void __push_dl_task(struct rq *rq, struct rq_flags *rf) | |||
| #endif | ||||
| } | ||||
| 
 | ||||
| /* a defer timer will not be reset if the runtime consumed was < dl_server_min_res */ | ||||
| static const u64 dl_server_min_res = 1 * NSEC_PER_MSEC; | ||||
| 
 | ||||
| static enum hrtimer_restart dl_server_timer(struct hrtimer *timer, struct sched_dl_entity *dl_se) | ||||
| { | ||||
| 	struct rq *rq = rq_of_dl_se(dl_se); | ||||
| 	u64 fw; | ||||
| 
 | ||||
| 	scoped_guard (rq_lock, rq) { | ||||
| 		struct rq_flags *rf = &scope.rf; | ||||
| 
 | ||||
| 		if (!dl_se->dl_throttled || !dl_se->dl_runtime) | ||||
| 			return HRTIMER_NORESTART; | ||||
| 
 | ||||
| 		sched_clock_tick(); | ||||
| 		update_rq_clock(rq); | ||||
| 
 | ||||
| 		if (!dl_se->dl_runtime) | ||||
| 			return HRTIMER_NORESTART; | ||||
| 
 | ||||
| 		if (!dl_se->server_has_tasks(dl_se)) { | ||||
| 			replenish_dl_entity(dl_se); | ||||
| 			return HRTIMER_NORESTART; | ||||
| 		} | ||||
| 
 | ||||
| 		if (dl_se->dl_defer_armed) { | ||||
| 			/*
 | ||||
| 			 * First check if the server could consume runtime in background. | ||||
| 			 * If so, it is possible to push the defer timer for this amount | ||||
| 			 * of time. The dl_server_min_res serves as a limit to avoid | ||||
| 			 * forwarding the timer for a too small amount of time. | ||||
| 			 */ | ||||
| 			if (dl_time_before(rq_clock(dl_se->rq), | ||||
| 					   (dl_se->deadline - dl_se->runtime - dl_server_min_res))) { | ||||
| 
 | ||||
| 				/* reset the defer timer */ | ||||
| 				fw = dl_se->deadline - rq_clock(dl_se->rq) - dl_se->runtime; | ||||
| 
 | ||||
| 				hrtimer_forward_now(timer, ns_to_ktime(fw)); | ||||
| 				return HRTIMER_RESTART; | ||||
| 			} | ||||
| 
 | ||||
| 			dl_se->dl_defer_running = 1; | ||||
| 		} | ||||
| 
 | ||||
| 		enqueue_dl_entity(dl_se, ENQUEUE_REPLENISH); | ||||
| 
 | ||||
| 		if (!dl_task(dl_se->rq->curr) || dl_entity_preempt(dl_se, &dl_se->rq->curr->dl)) | ||||
| 			resched_curr(rq); | ||||
| 
 | ||||
| 		__push_dl_task(rq, rf); | ||||
| 	} | ||||
| 
 | ||||
| 	return HRTIMER_NORESTART; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * This is the bandwidth enforcement timer callback. If here, we know | ||||
|  * a task is not on its dl_rq, since the fact that the timer was running | ||||
|  | @ -1128,28 +1270,8 @@ static enum hrtimer_restart dl_task_timer(struct hrtimer *timer) | |||
| 	struct rq_flags rf; | ||||
| 	struct rq *rq; | ||||
| 
 | ||||
| 	if (dl_server(dl_se)) { | ||||
| 		struct rq *rq = rq_of_dl_se(dl_se); | ||||
| 		struct rq_flags rf; | ||||
| 
 | ||||
| 		rq_lock(rq, &rf); | ||||
| 		if (dl_se->dl_throttled) { | ||||
| 			sched_clock_tick(); | ||||
| 			update_rq_clock(rq); | ||||
| 
 | ||||
| 			if (dl_se->server_has_tasks(dl_se)) { | ||||
| 				enqueue_dl_entity(dl_se, ENQUEUE_REPLENISH); | ||||
| 				resched_curr(rq); | ||||
| 				__push_dl_task(rq, &rf); | ||||
| 			} else { | ||||
| 				replenish_dl_entity(dl_se); | ||||
| 			} | ||||
| 
 | ||||
| 		} | ||||
| 		rq_unlock(rq, &rf); | ||||
| 
 | ||||
| 		return HRTIMER_NORESTART; | ||||
| 	} | ||||
| 	if (dl_server(dl_se)) | ||||
| 		return dl_server_timer(timer, dl_se); | ||||
| 
 | ||||
| 	p = dl_task_of(dl_se); | ||||
| 	rq = task_rq_lock(p, &rf); | ||||
|  | @ -1319,22 +1441,10 @@ static u64 grub_reclaim(u64 delta, struct rq *rq, struct sched_dl_entity *dl_se) | |||
| 	return (delta * u_act) >> BW_SHIFT; | ||||
| } | ||||
| 
 | ||||
| static inline void | ||||
| update_stats_dequeue_dl(struct dl_rq *dl_rq, struct sched_dl_entity *dl_se, | ||||
|                         int flags); | ||||
| static void update_curr_dl_se(struct rq *rq, struct sched_dl_entity *dl_se, s64 delta_exec) | ||||
| s64 dl_scaled_delta_exec(struct rq *rq, struct sched_dl_entity *dl_se, s64 delta_exec) | ||||
| { | ||||
| 	s64 scaled_delta_exec; | ||||
| 
 | ||||
| 	if (unlikely(delta_exec <= 0)) { | ||||
| 		if (unlikely(dl_se->dl_yielded)) | ||||
| 			goto throttle; | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	if (dl_entity_is_special(dl_se)) | ||||
| 		return; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * For tasks that participate in GRUB, we implement GRUB-PA: the | ||||
| 	 * spare reclaimed bandwidth is used to clock down frequency. | ||||
|  | @ -1353,8 +1463,64 @@ static void update_curr_dl_se(struct rq *rq, struct sched_dl_entity *dl_se, s64 | |||
| 		scaled_delta_exec = cap_scale(scaled_delta_exec, scale_cpu); | ||||
| 	} | ||||
| 
 | ||||
| 	return scaled_delta_exec; | ||||
| } | ||||
| 
 | ||||
| static inline void | ||||
| update_stats_dequeue_dl(struct dl_rq *dl_rq, struct sched_dl_entity *dl_se, | ||||
| 			int flags); | ||||
| static void update_curr_dl_se(struct rq *rq, struct sched_dl_entity *dl_se, s64 delta_exec) | ||||
| { | ||||
| 	s64 scaled_delta_exec; | ||||
| 
 | ||||
| 	if (unlikely(delta_exec <= 0)) { | ||||
| 		if (unlikely(dl_se->dl_yielded)) | ||||
| 			goto throttle; | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	if (dl_server(dl_se) && dl_se->dl_throttled && !dl_se->dl_defer) | ||||
| 		return; | ||||
| 
 | ||||
| 	if (dl_entity_is_special(dl_se)) | ||||
| 		return; | ||||
| 
 | ||||
| 	scaled_delta_exec = dl_scaled_delta_exec(rq, dl_se, delta_exec); | ||||
| 
 | ||||
| 	dl_se->runtime -= scaled_delta_exec; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * The fair server can consume its runtime while throttled (not queued/ | ||||
| 	 * running as regular CFS). | ||||
| 	 * | ||||
| 	 * If the server consumes its entire runtime in this state. The server | ||||
| 	 * is not required for the current period. Thus, reset the server by | ||||
| 	 * starting a new period, pushing the activation. | ||||
| 	 */ | ||||
| 	if (dl_se->dl_defer && dl_se->dl_throttled && dl_runtime_exceeded(dl_se)) { | ||||
| 		/*
 | ||||
| 		 * If the server was previously activated - the starving condition | ||||
| 		 * took place, it this point it went away because the fair scheduler | ||||
| 		 * was able to get runtime in background. So return to the initial | ||||
| 		 * state. | ||||
| 		 */ | ||||
| 		dl_se->dl_defer_running = 0; | ||||
| 
 | ||||
| 		hrtimer_try_to_cancel(&dl_se->dl_timer); | ||||
| 
 | ||||
| 		replenish_dl_new_period(dl_se, dl_se->rq); | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * Not being able to start the timer seems problematic. If it could not | ||||
| 		 * be started for whatever reason, we need to "unthrottle" the DL server | ||||
| 		 * and queue right away. Otherwise nothing might queue it. That's similar | ||||
| 		 * to what enqueue_dl_entity() does on start_dl_timer==0. For now, just warn. | ||||
| 		 */ | ||||
| 		WARN_ON_ONCE(!start_dl_timer(dl_se)); | ||||
| 
 | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| throttle: | ||||
| 	if (dl_runtime_exceeded(dl_se) || dl_se->dl_yielded) { | ||||
| 		dl_se->dl_throttled = 1; | ||||
|  | @ -1381,6 +1547,14 @@ static void update_curr_dl_se(struct rq *rq, struct sched_dl_entity *dl_se, s64 | |||
| 			resched_curr(rq); | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * The fair server (sole dl_server) does not account for real-time | ||||
| 	 * workload because it is running fair work. | ||||
| 	 */ | ||||
| 	if (dl_se == &rq->fair_server) | ||||
| 		return; | ||||
| 
 | ||||
| #ifdef CONFIG_RT_GROUP_SCHED | ||||
| 	/*
 | ||||
| 	 * Because -- for now -- we share the rt bandwidth, we need to | ||||
| 	 * account our runtime there too, otherwise actual rt tasks | ||||
|  | @ -1405,34 +1579,157 @@ static void update_curr_dl_se(struct rq *rq, struct sched_dl_entity *dl_se, s64 | |||
| 			rt_rq->rt_time += delta_exec; | ||||
| 		raw_spin_unlock(&rt_rq->rt_runtime_lock); | ||||
| 	} | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * In the non-defer mode, the idle time is not accounted, as the | ||||
|  * server provides a guarantee. | ||||
|  * | ||||
|  * If the dl_server is in defer mode, the idle time is also considered | ||||
|  * as time available for the fair server, avoiding a penalty for the | ||||
|  * rt scheduler that did not consumed that time. | ||||
|  */ | ||||
| void dl_server_update_idle_time(struct rq *rq, struct task_struct *p) | ||||
| { | ||||
| 	s64 delta_exec, scaled_delta_exec; | ||||
| 
 | ||||
| 	if (!rq->fair_server.dl_defer) | ||||
| 		return; | ||||
| 
 | ||||
| 	/* no need to discount more */ | ||||
| 	if (rq->fair_server.runtime < 0) | ||||
| 		return; | ||||
| 
 | ||||
| 	delta_exec = rq_clock_task(rq) - p->se.exec_start; | ||||
| 	if (delta_exec < 0) | ||||
| 		return; | ||||
| 
 | ||||
| 	scaled_delta_exec = dl_scaled_delta_exec(rq, &rq->fair_server, delta_exec); | ||||
| 
 | ||||
| 	rq->fair_server.runtime -= scaled_delta_exec; | ||||
| 
 | ||||
| 	if (rq->fair_server.runtime < 0) { | ||||
| 		rq->fair_server.dl_defer_running = 0; | ||||
| 		rq->fair_server.runtime = 0; | ||||
| 	} | ||||
| 
 | ||||
| 	p->se.exec_start = rq_clock_task(rq); | ||||
| } | ||||
| 
 | ||||
| void dl_server_update(struct sched_dl_entity *dl_se, s64 delta_exec) | ||||
| { | ||||
| 	update_curr_dl_se(dl_se->rq, dl_se, delta_exec); | ||||
| 	/* 0 runtime = fair server disabled */ | ||||
| 	if (dl_se->dl_runtime) | ||||
| 		update_curr_dl_se(dl_se->rq, dl_se, delta_exec); | ||||
| } | ||||
| 
 | ||||
| void dl_server_start(struct sched_dl_entity *dl_se) | ||||
| { | ||||
| 	struct rq *rq = dl_se->rq; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * XXX: the apply do not work fine at the init phase for the | ||||
| 	 * fair server because things are not yet set. We need to improve | ||||
| 	 * this before getting generic. | ||||
| 	 */ | ||||
| 	if (!dl_server(dl_se)) { | ||||
| 		u64 runtime =  50 * NSEC_PER_MSEC; | ||||
| 		u64 period = 1000 * NSEC_PER_MSEC; | ||||
| 
 | ||||
| 		dl_server_apply_params(dl_se, runtime, period, 1); | ||||
| 
 | ||||
| 		dl_se->dl_server = 1; | ||||
| 		dl_se->dl_defer = 1; | ||||
| 		setup_new_dl_entity(dl_se); | ||||
| 	} | ||||
| 
 | ||||
| 	if (!dl_se->dl_runtime) | ||||
| 		return; | ||||
| 
 | ||||
| 	enqueue_dl_entity(dl_se, ENQUEUE_WAKEUP); | ||||
| 	if (!dl_task(dl_se->rq->curr) || dl_entity_preempt(dl_se, &rq->curr->dl)) | ||||
| 		resched_curr(dl_se->rq); | ||||
| } | ||||
| 
 | ||||
| void dl_server_stop(struct sched_dl_entity *dl_se) | ||||
| { | ||||
| 	if (!dl_se->dl_runtime) | ||||
| 		return; | ||||
| 
 | ||||
| 	dequeue_dl_entity(dl_se, DEQUEUE_SLEEP); | ||||
| 	hrtimer_try_to_cancel(&dl_se->dl_timer); | ||||
| 	dl_se->dl_defer_armed = 0; | ||||
| 	dl_se->dl_throttled = 0; | ||||
| } | ||||
| 
 | ||||
| void dl_server_init(struct sched_dl_entity *dl_se, struct rq *rq, | ||||
| 		    dl_server_has_tasks_f has_tasks, | ||||
| 		    dl_server_pick_f pick) | ||||
| 		    dl_server_pick_f pick_next, | ||||
| 		    dl_server_pick_f pick_task) | ||||
| { | ||||
| 	dl_se->rq = rq; | ||||
| 	dl_se->server_has_tasks = has_tasks; | ||||
| 	dl_se->server_pick = pick; | ||||
| 	dl_se->server_pick_next = pick_next; | ||||
| 	dl_se->server_pick_task = pick_task; | ||||
| } | ||||
| 
 | ||||
| void __dl_server_attach_root(struct sched_dl_entity *dl_se, struct rq *rq) | ||||
| { | ||||
| 	u64 new_bw = dl_se->dl_bw; | ||||
| 	int cpu = cpu_of(rq); | ||||
| 	struct dl_bw *dl_b; | ||||
| 
 | ||||
| 	dl_b = dl_bw_of(cpu_of(rq)); | ||||
| 	guard(raw_spinlock)(&dl_b->lock); | ||||
| 
 | ||||
| 	if (!dl_bw_cpus(cpu)) | ||||
| 		return; | ||||
| 
 | ||||
| 	__dl_add(dl_b, new_bw, dl_bw_cpus(cpu)); | ||||
| } | ||||
| 
 | ||||
| int dl_server_apply_params(struct sched_dl_entity *dl_se, u64 runtime, u64 period, bool init) | ||||
| { | ||||
| 	u64 old_bw = init ? 0 : to_ratio(dl_se->dl_period, dl_se->dl_runtime); | ||||
| 	u64 new_bw = to_ratio(period, runtime); | ||||
| 	struct rq *rq = dl_se->rq; | ||||
| 	int cpu = cpu_of(rq); | ||||
| 	struct dl_bw *dl_b; | ||||
| 	unsigned long cap; | ||||
| 	int retval = 0; | ||||
| 	int cpus; | ||||
| 
 | ||||
| 	dl_b = dl_bw_of(cpu); | ||||
| 	guard(raw_spinlock)(&dl_b->lock); | ||||
| 
 | ||||
| 	cpus = dl_bw_cpus(cpu); | ||||
| 	cap = dl_bw_capacity(cpu); | ||||
| 
 | ||||
| 	if (__dl_overflow(dl_b, cap, old_bw, new_bw)) | ||||
| 		return -EBUSY; | ||||
| 
 | ||||
| 	if (init) { | ||||
| 		__add_rq_bw(new_bw, &rq->dl); | ||||
| 		__dl_add(dl_b, new_bw, cpus); | ||||
| 	} else { | ||||
| 		__dl_sub(dl_b, dl_se->dl_bw, cpus); | ||||
| 		__dl_add(dl_b, new_bw, cpus); | ||||
| 
 | ||||
| 		dl_rq_change_utilization(rq, dl_se, new_bw); | ||||
| 	} | ||||
| 
 | ||||
| 	dl_se->dl_runtime = runtime; | ||||
| 	dl_se->dl_deadline = period; | ||||
| 	dl_se->dl_period = period; | ||||
| 
 | ||||
| 	dl_se->runtime = 0; | ||||
| 	dl_se->deadline = 0; | ||||
| 
 | ||||
| 	dl_se->dl_bw = to_ratio(dl_se->dl_period, dl_se->dl_runtime); | ||||
| 	dl_se->dl_density = to_ratio(dl_se->dl_deadline, dl_se->dl_runtime); | ||||
| 
 | ||||
| 	return retval; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  | @ -1735,7 +2032,7 @@ enqueue_dl_entity(struct sched_dl_entity *dl_se, int flags) | |||
| 	 * be counted in the active utilization; hence, we need to call | ||||
| 	 * add_running_bw(). | ||||
| 	 */ | ||||
| 	if (dl_se->dl_throttled && !(flags & ENQUEUE_REPLENISH)) { | ||||
| 	if (!dl_se->dl_defer && dl_se->dl_throttled && !(flags & ENQUEUE_REPLENISH)) { | ||||
| 		if (flags & ENQUEUE_WAKEUP) | ||||
| 			task_contending(dl_se, flags); | ||||
| 
 | ||||
|  | @ -1757,6 +2054,25 @@ enqueue_dl_entity(struct sched_dl_entity *dl_se, int flags) | |||
| 		setup_new_dl_entity(dl_se); | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * If the reservation is still throttled, e.g., it got replenished but is a | ||||
| 	 * deferred task and still got to wait, don't enqueue. | ||||
| 	 */ | ||||
| 	if (dl_se->dl_throttled && start_dl_timer(dl_se)) | ||||
| 		return; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * We're about to enqueue, make sure we're not ->dl_throttled! | ||||
| 	 * In case the timer was not started, say because the defer time | ||||
| 	 * has passed, mark as not throttled and mark unarmed. | ||||
| 	 * Also cancel earlier timers, since letting those run is pointless. | ||||
| 	 */ | ||||
| 	if (dl_se->dl_throttled) { | ||||
| 		hrtimer_try_to_cancel(&dl_se->dl_timer); | ||||
| 		dl_se->dl_defer_armed = 0; | ||||
| 		dl_se->dl_throttled = 0; | ||||
| 	} | ||||
| 
 | ||||
| 	__enqueue_dl_entity(dl_se); | ||||
| } | ||||
| 
 | ||||
|  | @ -2086,7 +2402,12 @@ static struct sched_dl_entity *pick_next_dl_entity(struct dl_rq *dl_rq) | |||
| 	return __node_2_dle(left); | ||||
| } | ||||
| 
 | ||||
| static struct task_struct *pick_task_dl(struct rq *rq) | ||||
| /*
 | ||||
|  * __pick_next_task_dl - Helper to pick the next -deadline task to run. | ||||
|  * @rq: The runqueue to pick the next task from. | ||||
|  * @peek: If true, just peek at the next task. Only relevant for dlserver. | ||||
|  */ | ||||
| static struct task_struct *__pick_next_task_dl(struct rq *rq, bool peek) | ||||
| { | ||||
| 	struct sched_dl_entity *dl_se; | ||||
| 	struct dl_rq *dl_rq = &rq->dl; | ||||
|  | @ -2100,7 +2421,10 @@ static struct task_struct *pick_task_dl(struct rq *rq) | |||
| 	WARN_ON_ONCE(!dl_se); | ||||
| 
 | ||||
| 	if (dl_server(dl_se)) { | ||||
| 		p = dl_se->server_pick(dl_se); | ||||
| 		if (IS_ENABLED(CONFIG_SMP) && peek) | ||||
| 			p = dl_se->server_pick_task(dl_se); | ||||
| 		else | ||||
| 			p = dl_se->server_pick_next(dl_se); | ||||
| 		if (!p) { | ||||
| 			WARN_ON_ONCE(1); | ||||
| 			dl_se->dl_yielded = 1; | ||||
|  | @ -2115,11 +2439,18 @@ static struct task_struct *pick_task_dl(struct rq *rq) | |||
| 	return p; | ||||
| } | ||||
| 
 | ||||
| #ifdef CONFIG_SMP | ||||
| static struct task_struct *pick_task_dl(struct rq *rq) | ||||
| { | ||||
| 	return __pick_next_task_dl(rq, true); | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| static struct task_struct *pick_next_task_dl(struct rq *rq) | ||||
| { | ||||
| 	struct task_struct *p; | ||||
| 
 | ||||
| 	p = pick_task_dl(rq); | ||||
| 	p = __pick_next_task_dl(rq, false); | ||||
| 	if (!p) | ||||
| 		return p; | ||||
| 
 | ||||
|  |  | |||
|  | @ -333,8 +333,165 @@ static const struct file_operations sched_debug_fops = { | |||
| 	.release	= seq_release, | ||||
| }; | ||||
| 
 | ||||
| enum dl_param { | ||||
| 	DL_RUNTIME = 0, | ||||
| 	DL_PERIOD, | ||||
| }; | ||||
| 
 | ||||
| static unsigned long fair_server_period_max = (1 << 22) * NSEC_PER_USEC; /* ~4 seconds */ | ||||
| static unsigned long fair_server_period_min = (100) * NSEC_PER_USEC;     /* 100 us */ | ||||
| 
 | ||||
| static ssize_t sched_fair_server_write(struct file *filp, const char __user *ubuf, | ||||
| 				       size_t cnt, loff_t *ppos, enum dl_param param) | ||||
| { | ||||
| 	long cpu = (long) ((struct seq_file *) filp->private_data)->private; | ||||
| 	struct rq *rq = cpu_rq(cpu); | ||||
| 	u64 runtime, period; | ||||
| 	size_t err; | ||||
| 	int retval; | ||||
| 	u64 value; | ||||
| 
 | ||||
| 	err = kstrtoull_from_user(ubuf, cnt, 10, &value); | ||||
| 	if (err) | ||||
| 		return err; | ||||
| 
 | ||||
| 	scoped_guard (rq_lock_irqsave, rq) { | ||||
| 		runtime  = rq->fair_server.dl_runtime; | ||||
| 		period = rq->fair_server.dl_period; | ||||
| 
 | ||||
| 		switch (param) { | ||||
| 		case DL_RUNTIME: | ||||
| 			if (runtime == value) | ||||
| 				break; | ||||
| 			runtime = value; | ||||
| 			break; | ||||
| 		case DL_PERIOD: | ||||
| 			if (value == period) | ||||
| 				break; | ||||
| 			period = value; | ||||
| 			break; | ||||
| 		} | ||||
| 
 | ||||
| 		if (runtime > period || | ||||
| 		    period > fair_server_period_max || | ||||
| 		    period < fair_server_period_min) { | ||||
| 			return  -EINVAL; | ||||
| 		} | ||||
| 
 | ||||
| 		if (rq->cfs.h_nr_running) { | ||||
| 			update_rq_clock(rq); | ||||
| 			dl_server_stop(&rq->fair_server); | ||||
| 		} | ||||
| 
 | ||||
| 		retval = dl_server_apply_params(&rq->fair_server, runtime, period, 0); | ||||
| 		if (retval) | ||||
| 			cnt = retval; | ||||
| 
 | ||||
| 		if (!runtime) | ||||
| 			printk_deferred("Fair server disabled in CPU %d, system may crash due to starvation.\n", | ||||
| 					cpu_of(rq)); | ||||
| 
 | ||||
| 		if (rq->cfs.h_nr_running) | ||||
| 			dl_server_start(&rq->fair_server); | ||||
| 	} | ||||
| 
 | ||||
| 	*ppos += cnt; | ||||
| 	return cnt; | ||||
| } | ||||
| 
 | ||||
| static size_t sched_fair_server_show(struct seq_file *m, void *v, enum dl_param param) | ||||
| { | ||||
| 	unsigned long cpu = (unsigned long) m->private; | ||||
| 	struct rq *rq = cpu_rq(cpu); | ||||
| 	u64 value; | ||||
| 
 | ||||
| 	switch (param) { | ||||
| 	case DL_RUNTIME: | ||||
| 		value = rq->fair_server.dl_runtime; | ||||
| 		break; | ||||
| 	case DL_PERIOD: | ||||
| 		value = rq->fair_server.dl_period; | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| 	seq_printf(m, "%llu\n", value); | ||||
| 	return 0; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| static ssize_t | ||||
| sched_fair_server_runtime_write(struct file *filp, const char __user *ubuf, | ||||
| 				size_t cnt, loff_t *ppos) | ||||
| { | ||||
| 	return sched_fair_server_write(filp, ubuf, cnt, ppos, DL_RUNTIME); | ||||
| } | ||||
| 
 | ||||
| static int sched_fair_server_runtime_show(struct seq_file *m, void *v) | ||||
| { | ||||
| 	return sched_fair_server_show(m, v, DL_RUNTIME); | ||||
| } | ||||
| 
 | ||||
| static int sched_fair_server_runtime_open(struct inode *inode, struct file *filp) | ||||
| { | ||||
| 	return single_open(filp, sched_fair_server_runtime_show, inode->i_private); | ||||
| } | ||||
| 
 | ||||
| static const struct file_operations fair_server_runtime_fops = { | ||||
| 	.open		= sched_fair_server_runtime_open, | ||||
| 	.write		= sched_fair_server_runtime_write, | ||||
| 	.read		= seq_read, | ||||
| 	.llseek		= seq_lseek, | ||||
| 	.release	= single_release, | ||||
| }; | ||||
| 
 | ||||
| static ssize_t | ||||
| sched_fair_server_period_write(struct file *filp, const char __user *ubuf, | ||||
| 			       size_t cnt, loff_t *ppos) | ||||
| { | ||||
| 	return sched_fair_server_write(filp, ubuf, cnt, ppos, DL_PERIOD); | ||||
| } | ||||
| 
 | ||||
| static int sched_fair_server_period_show(struct seq_file *m, void *v) | ||||
| { | ||||
| 	return sched_fair_server_show(m, v, DL_PERIOD); | ||||
| } | ||||
| 
 | ||||
| static int sched_fair_server_period_open(struct inode *inode, struct file *filp) | ||||
| { | ||||
| 	return single_open(filp, sched_fair_server_period_show, inode->i_private); | ||||
| } | ||||
| 
 | ||||
| static const struct file_operations fair_server_period_fops = { | ||||
| 	.open		= sched_fair_server_period_open, | ||||
| 	.write		= sched_fair_server_period_write, | ||||
| 	.read		= seq_read, | ||||
| 	.llseek		= seq_lseek, | ||||
| 	.release	= single_release, | ||||
| }; | ||||
| 
 | ||||
| static struct dentry *debugfs_sched; | ||||
| 
 | ||||
| static void debugfs_fair_server_init(void) | ||||
| { | ||||
| 	struct dentry *d_fair; | ||||
| 	unsigned long cpu; | ||||
| 
 | ||||
| 	d_fair = debugfs_create_dir("fair_server", debugfs_sched); | ||||
| 	if (!d_fair) | ||||
| 		return; | ||||
| 
 | ||||
| 	for_each_possible_cpu(cpu) { | ||||
| 		struct dentry *d_cpu; | ||||
| 		char buf[32]; | ||||
| 
 | ||||
| 		snprintf(buf, sizeof(buf), "cpu%lu", cpu); | ||||
| 		d_cpu = debugfs_create_dir(buf, d_fair); | ||||
| 
 | ||||
| 		debugfs_create_file("runtime", 0644, d_cpu, (void *) cpu, &fair_server_runtime_fops); | ||||
| 		debugfs_create_file("period", 0644, d_cpu, (void *) cpu, &fair_server_period_fops); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static __init int sched_init_debug(void) | ||||
| { | ||||
| 	struct dentry __maybe_unused *numa; | ||||
|  | @ -374,6 +531,8 @@ static __init int sched_init_debug(void) | |||
| 
 | ||||
| 	debugfs_create_file("debug", 0444, debugfs_sched, NULL, &sched_debug_fops); | ||||
| 
 | ||||
| 	debugfs_fair_server_init(); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| late_initcall(sched_init_debug); | ||||
|  | @ -641,8 +800,6 @@ void print_cfs_rq(struct seq_file *m, int cpu, struct cfs_rq *cfs_rq) | |||
| 	SEQ_printf(m, "\n"); | ||||
| 	SEQ_printf(m, "cfs_rq[%d]:\n", cpu); | ||||
| #endif | ||||
| 	SEQ_printf(m, "  .%-30s: %Ld.%06ld\n", "exec_clock", | ||||
| 			SPLIT_NS(cfs_rq->exec_clock)); | ||||
| 
 | ||||
| 	raw_spin_rq_lock_irqsave(rq, flags); | ||||
| 	root = __pick_root_entity(cfs_rq); | ||||
|  | @ -669,8 +826,6 @@ void print_cfs_rq(struct seq_file *m, int cpu, struct cfs_rq *cfs_rq) | |||
| 			SPLIT_NS(right_vruntime)); | ||||
| 	spread = right_vruntime - left_vruntime; | ||||
| 	SEQ_printf(m, "  .%-30s: %Ld.%06ld\n", "spread", SPLIT_NS(spread)); | ||||
| 	SEQ_printf(m, "  .%-30s: %d\n", "nr_spread_over", | ||||
| 			cfs_rq->nr_spread_over); | ||||
| 	SEQ_printf(m, "  .%-30s: %d\n", "nr_running", cfs_rq->nr_running); | ||||
| 	SEQ_printf(m, "  .%-30s: %d\n", "h_nr_running", cfs_rq->h_nr_running); | ||||
| 	SEQ_printf(m, "  .%-30s: %d\n", "idle_nr_running", | ||||
|  | @ -730,9 +885,12 @@ void print_rt_rq(struct seq_file *m, int cpu, struct rt_rq *rt_rq) | |||
| 	SEQ_printf(m, "  .%-30s: %Ld.%06ld\n", #x, SPLIT_NS(rt_rq->x)) | ||||
| 
 | ||||
| 	PU(rt_nr_running); | ||||
| 
 | ||||
| #ifdef CONFIG_RT_GROUP_SCHED | ||||
| 	P(rt_throttled); | ||||
| 	PN(rt_time); | ||||
| 	PN(rt_runtime); | ||||
| #endif | ||||
| 
 | ||||
| #undef PN | ||||
| #undef PU | ||||
|  |  | |||
|  | @ -511,7 +511,7 @@ static int cfs_rq_is_idle(struct cfs_rq *cfs_rq) | |||
| 
 | ||||
| static int se_is_idle(struct sched_entity *se) | ||||
| { | ||||
| 	return 0; | ||||
| 	return task_has_idle_policy(task_of(se)); | ||||
| } | ||||
| 
 | ||||
| #endif	/* CONFIG_FAIR_GROUP_SCHED */ | ||||
|  | @ -1156,12 +1156,13 @@ s64 update_curr_common(struct rq *rq) | |||
| static void update_curr(struct cfs_rq *cfs_rq) | ||||
| { | ||||
| 	struct sched_entity *curr = cfs_rq->curr; | ||||
| 	struct rq *rq = rq_of(cfs_rq); | ||||
| 	s64 delta_exec; | ||||
| 
 | ||||
| 	if (unlikely(!curr)) | ||||
| 		return; | ||||
| 
 | ||||
| 	delta_exec = update_curr_se(rq_of(cfs_rq), curr); | ||||
| 	delta_exec = update_curr_se(rq, curr); | ||||
| 	if (unlikely(delta_exec <= 0)) | ||||
| 		return; | ||||
| 
 | ||||
|  | @ -1169,8 +1170,19 @@ static void update_curr(struct cfs_rq *cfs_rq) | |||
| 	update_deadline(cfs_rq, curr); | ||||
| 	update_min_vruntime(cfs_rq); | ||||
| 
 | ||||
| 	if (entity_is_task(curr)) | ||||
| 		update_curr_task(task_of(curr), delta_exec); | ||||
| 	if (entity_is_task(curr)) { | ||||
| 		struct task_struct *p = task_of(curr); | ||||
| 
 | ||||
| 		update_curr_task(p, delta_exec); | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * Any fair task that runs outside of fair_server should | ||||
| 		 * account against fair_server such that it can account for | ||||
| 		 * this time and possibly avoid running this period. | ||||
| 		 */ | ||||
| 		if (p->dl_server != &rq->fair_server) | ||||
| 			dl_server_update(&rq->fair_server, delta_exec); | ||||
| 	} | ||||
| 
 | ||||
| 	account_cfs_rq_runtime(cfs_rq, delta_exec); | ||||
| } | ||||
|  | @ -5766,6 +5778,7 @@ static bool throttle_cfs_rq(struct cfs_rq *cfs_rq) | |||
| 	struct cfs_bandwidth *cfs_b = tg_cfs_bandwidth(cfs_rq->tg); | ||||
| 	struct sched_entity *se; | ||||
| 	long task_delta, idle_task_delta, dequeue = 1; | ||||
| 	long rq_h_nr_running = rq->cfs.h_nr_running; | ||||
| 
 | ||||
| 	raw_spin_lock(&cfs_b->lock); | ||||
| 	/* This will start the period timer if necessary */ | ||||
|  | @ -5837,6 +5850,9 @@ static bool throttle_cfs_rq(struct cfs_rq *cfs_rq) | |||
| 	/* At this point se is NULL and we are at root level*/ | ||||
| 	sub_nr_running(rq, task_delta); | ||||
| 
 | ||||
| 	/* Stop the fair server if throttling resulted in no runnable tasks */ | ||||
| 	if (rq_h_nr_running && !rq->cfs.h_nr_running) | ||||
| 		dl_server_stop(&rq->fair_server); | ||||
| done: | ||||
| 	/*
 | ||||
| 	 * Note: distribution will already see us throttled via the | ||||
|  | @ -5855,6 +5871,7 @@ void unthrottle_cfs_rq(struct cfs_rq *cfs_rq) | |||
| 	struct cfs_bandwidth *cfs_b = tg_cfs_bandwidth(cfs_rq->tg); | ||||
| 	struct sched_entity *se; | ||||
| 	long task_delta, idle_task_delta; | ||||
| 	long rq_h_nr_running = rq->cfs.h_nr_running; | ||||
| 
 | ||||
| 	se = cfs_rq->tg->se[cpu_of(rq)]; | ||||
| 
 | ||||
|  | @ -5924,6 +5941,10 @@ void unthrottle_cfs_rq(struct cfs_rq *cfs_rq) | |||
| 			goto unthrottle_throttle; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Start the fair server if un-throttling resulted in new runnable tasks */ | ||||
| 	if (!rq_h_nr_running && rq->cfs.h_nr_running) | ||||
| 		dl_server_start(&rq->fair_server); | ||||
| 
 | ||||
| 	/* At this point se is NULL and we are at root level*/ | ||||
| 	add_nr_running(rq, task_delta); | ||||
| 
 | ||||
|  | @ -6556,7 +6577,7 @@ static void sched_fair_update_stop_tick(struct rq *rq, struct task_struct *p) | |||
| { | ||||
| 	int cpu = cpu_of(rq); | ||||
| 
 | ||||
| 	if (!sched_feat(HZ_BW) || !cfs_bandwidth_used()) | ||||
| 	if (!cfs_bandwidth_used()) | ||||
| 		return; | ||||
| 
 | ||||
| 	if (!tick_nohz_full_cpu(cpu)) | ||||
|  | @ -6751,6 +6772,7 @@ enqueue_task_fair(struct rq *rq, struct task_struct *p, int flags) | |||
| 	struct sched_entity *se = &p->se; | ||||
| 	int idle_h_nr_running = task_has_idle_policy(p); | ||||
| 	int task_new = !(flags & ENQUEUE_WAKEUP); | ||||
| 	int rq_h_nr_running = rq->cfs.h_nr_running; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * The code below (indirectly) updates schedutil which looks at | ||||
|  | @ -6805,6 +6827,13 @@ enqueue_task_fair(struct rq *rq, struct task_struct *p, int flags) | |||
| 			goto enqueue_throttle; | ||||
| 	} | ||||
| 
 | ||||
| 	if (!rq_h_nr_running && rq->cfs.h_nr_running) { | ||||
| 		/* Account for idle runtime */ | ||||
| 		if (!rq->nr_running) | ||||
| 			dl_server_update_idle_time(rq, rq->curr); | ||||
| 		dl_server_start(&rq->fair_server); | ||||
| 	} | ||||
| 
 | ||||
| 	/* At this point se is NULL and we are at root level*/ | ||||
| 	add_nr_running(rq, 1); | ||||
| 
 | ||||
|  | @ -6845,6 +6874,7 @@ static void dequeue_task_fair(struct rq *rq, struct task_struct *p, int flags) | |||
| 	int task_sleep = flags & DEQUEUE_SLEEP; | ||||
| 	int idle_h_nr_running = task_has_idle_policy(p); | ||||
| 	bool was_sched_idle = sched_idle_rq(rq); | ||||
| 	int rq_h_nr_running = rq->cfs.h_nr_running; | ||||
| 
 | ||||
| 	util_est_dequeue(&rq->cfs, p); | ||||
| 
 | ||||
|  | @ -6899,6 +6929,9 @@ static void dequeue_task_fair(struct rq *rq, struct task_struct *p, int flags) | |||
| 	/* At this point se is NULL and we are at root level*/ | ||||
| 	sub_nr_running(rq, 1); | ||||
| 
 | ||||
| 	if (rq_h_nr_running && !rq->cfs.h_nr_running) | ||||
| 		dl_server_stop(&rq->fair_server); | ||||
| 
 | ||||
| 	/* balance early to pull high priority tasks */ | ||||
| 	if (unlikely(!was_sched_idle && sched_idle_rq(rq))) | ||||
| 		rq->next_balance = jiffies; | ||||
|  | @ -8382,16 +8415,7 @@ static void check_preempt_wakeup_fair(struct rq *rq, struct task_struct *p, int | |||
| 	if (test_tsk_need_resched(curr)) | ||||
| 		return; | ||||
| 
 | ||||
| 	/* Idle tasks are by definition preempted by non-idle tasks. */ | ||||
| 	if (unlikely(task_has_idle_policy(curr)) && | ||||
| 	    likely(!task_has_idle_policy(p))) | ||||
| 		goto preempt; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Batch and idle tasks do not preempt non-idle tasks (their preemption | ||||
| 	 * is driven by the tick): | ||||
| 	 */ | ||||
| 	if (unlikely(!normal_policy(p->policy)) || !sched_feat(WAKEUP_PREEMPTION)) | ||||
| 	if (!sched_feat(WAKEUP_PREEMPTION)) | ||||
| 		return; | ||||
| 
 | ||||
| 	find_matching_se(&se, &pse); | ||||
|  | @ -8401,7 +8425,7 @@ static void check_preempt_wakeup_fair(struct rq *rq, struct task_struct *p, int | |||
| 	pse_is_idle = se_is_idle(pse); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Preempt an idle group in favor of a non-idle group (and don't preempt | ||||
| 	 * Preempt an idle entity in favor of a non-idle entity (and don't preempt | ||||
| 	 * in the inverse case). | ||||
| 	 */ | ||||
| 	if (cse_is_idle && !pse_is_idle) | ||||
|  | @ -8409,9 +8433,14 @@ static void check_preempt_wakeup_fair(struct rq *rq, struct task_struct *p, int | |||
| 	if (cse_is_idle != pse_is_idle) | ||||
| 		return; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * BATCH and IDLE tasks do not preempt others. | ||||
| 	 */ | ||||
| 	if (unlikely(!normal_policy(p->policy))) | ||||
| 		return; | ||||
| 
 | ||||
| 	cfs_rq = cfs_rq_of(se); | ||||
| 	update_curr(cfs_rq); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * XXX pick_eevdf(cfs_rq) != se ? | ||||
| 	 */ | ||||
|  | @ -8453,6 +8482,14 @@ static struct task_struct *pick_task_fair(struct rq *rq) | |||
| 		cfs_rq = group_cfs_rq(se); | ||||
| 	} while (cfs_rq); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * This can be called from directly from CFS's ->pick_task() or indirectly | ||||
| 	 * from DL's ->pick_task when fair server is enabled. In the indirect case, | ||||
| 	 * DL will set ->dl_server just after this function is called, so its Ok to | ||||
| 	 * clear. In the direct case, we are picking directly so we must clear it. | ||||
| 	 */ | ||||
| 	task_of(se)->dl_server = NULL; | ||||
| 
 | ||||
| 	return task_of(se); | ||||
| } | ||||
| #endif | ||||
|  | @ -8607,6 +8644,36 @@ static struct task_struct *__pick_next_task_fair(struct rq *rq) | |||
| 	return pick_next_task_fair(rq, NULL, NULL); | ||||
| } | ||||
| 
 | ||||
| static bool fair_server_has_tasks(struct sched_dl_entity *dl_se) | ||||
| { | ||||
| 	return !!dl_se->rq->cfs.nr_running; | ||||
| } | ||||
| 
 | ||||
| static struct task_struct *fair_server_pick_task(struct sched_dl_entity *dl_se) | ||||
| { | ||||
| #ifdef CONFIG_SMP | ||||
| 	return pick_task_fair(dl_se->rq); | ||||
| #else | ||||
| 	return NULL; | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| static struct task_struct *fair_server_pick_next(struct sched_dl_entity *dl_se) | ||||
| { | ||||
| 	return pick_next_task_fair(dl_se->rq, NULL, NULL); | ||||
| } | ||||
| 
 | ||||
| void fair_server_init(struct rq *rq) | ||||
| { | ||||
| 	struct sched_dl_entity *dl_se = &rq->fair_server; | ||||
| 
 | ||||
| 	init_dl_entity(dl_se); | ||||
| 
 | ||||
| 	dl_server_init(dl_se, rq, fair_server_has_tasks, fair_server_pick_next, | ||||
| 		       fair_server_pick_task); | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Account for a descheduled task: | ||||
|  */ | ||||
|  | @ -12693,22 +12760,7 @@ static void task_tick_fair(struct rq *rq, struct task_struct *curr, int queued) | |||
|  */ | ||||
| static void task_fork_fair(struct task_struct *p) | ||||
| { | ||||
| 	struct sched_entity *se = &p->se, *curr; | ||||
| 	struct cfs_rq *cfs_rq; | ||||
| 	struct rq *rq = this_rq(); | ||||
| 	struct rq_flags rf; | ||||
| 
 | ||||
| 	rq_lock(rq, &rf); | ||||
| 	update_rq_clock(rq); | ||||
| 
 | ||||
| 	set_task_max_allowed_capacity(p); | ||||
| 
 | ||||
| 	cfs_rq = task_cfs_rq(current); | ||||
| 	curr = cfs_rq->curr; | ||||
| 	if (curr) | ||||
| 		update_curr(cfs_rq); | ||||
| 	place_entity(cfs_rq, se, ENQUEUE_INITIAL); | ||||
| 	rq_unlock(rq, &rf); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  |  | |||
|  | @ -85,5 +85,3 @@ SCHED_FEAT(WA_BIAS, true) | |||
| SCHED_FEAT(UTIL_EST, true) | ||||
| 
 | ||||
| SCHED_FEAT(LATENCY_WARN, false) | ||||
| 
 | ||||
| SCHED_FEAT(HZ_BW, true) | ||||
|  |  | |||
|  | @ -452,6 +452,7 @@ static void wakeup_preempt_idle(struct rq *rq, struct task_struct *p, int flags) | |||
| 
 | ||||
| static void put_prev_task_idle(struct rq *rq, struct task_struct *prev) | ||||
| { | ||||
| 	dl_server_update_idle_time(rq, prev); | ||||
| 	scx_update_idle(rq, false); | ||||
| } | ||||
| 
 | ||||
|  | @ -460,6 +461,7 @@ static void set_next_task_idle(struct rq *rq, struct task_struct *next, bool fir | |||
| 	update_idle_core(rq); | ||||
| 	scx_update_idle(rq, true); | ||||
| 	schedstat_inc(rq->sched_goidle); | ||||
| 	next->se.exec_start = rq_clock_task(rq); | ||||
| } | ||||
| 
 | ||||
| #ifdef CONFIG_SMP | ||||
|  |  | |||
|  | @ -8,10 +8,6 @@ int sched_rr_timeslice = RR_TIMESLICE; | |||
| /* More than 4 hours if BW_SHIFT equals 20. */ | ||||
| static const u64 max_rt_runtime = MAX_BW; | ||||
| 
 | ||||
| static int do_sched_rt_period_timer(struct rt_bandwidth *rt_b, int overrun); | ||||
| 
 | ||||
| struct rt_bandwidth def_rt_bandwidth; | ||||
| 
 | ||||
| /*
 | ||||
|  * period over which we measure -rt task CPU usage in us. | ||||
|  * default: 1s | ||||
|  | @ -66,6 +62,40 @@ static int __init sched_rt_sysctl_init(void) | |||
| late_initcall(sched_rt_sysctl_init); | ||||
| #endif | ||||
| 
 | ||||
| void init_rt_rq(struct rt_rq *rt_rq) | ||||
| { | ||||
| 	struct rt_prio_array *array; | ||||
| 	int i; | ||||
| 
 | ||||
| 	array = &rt_rq->active; | ||||
| 	for (i = 0; i < MAX_RT_PRIO; i++) { | ||||
| 		INIT_LIST_HEAD(array->queue + i); | ||||
| 		__clear_bit(i, array->bitmap); | ||||
| 	} | ||||
| 	/* delimiter for bitsearch: */ | ||||
| 	__set_bit(MAX_RT_PRIO, array->bitmap); | ||||
| 
 | ||||
| #if defined CONFIG_SMP | ||||
| 	rt_rq->highest_prio.curr = MAX_RT_PRIO-1; | ||||
| 	rt_rq->highest_prio.next = MAX_RT_PRIO-1; | ||||
| 	rt_rq->overloaded = 0; | ||||
| 	plist_head_init(&rt_rq->pushable_tasks); | ||||
| #endif /* CONFIG_SMP */ | ||||
| 	/* We start is dequeued state, because no RT tasks are queued */ | ||||
| 	rt_rq->rt_queued = 0; | ||||
| 
 | ||||
| #ifdef CONFIG_RT_GROUP_SCHED | ||||
| 	rt_rq->rt_time = 0; | ||||
| 	rt_rq->rt_throttled = 0; | ||||
| 	rt_rq->rt_runtime = 0; | ||||
| 	raw_spin_lock_init(&rt_rq->rt_runtime_lock); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| #ifdef CONFIG_RT_GROUP_SCHED | ||||
| 
 | ||||
| static int do_sched_rt_period_timer(struct rt_bandwidth *rt_b, int overrun); | ||||
| 
 | ||||
| static enum hrtimer_restart sched_rt_period_timer(struct hrtimer *timer) | ||||
| { | ||||
| 	struct rt_bandwidth *rt_b = | ||||
|  | @ -130,35 +160,6 @@ static void start_rt_bandwidth(struct rt_bandwidth *rt_b) | |||
| 	do_start_rt_bandwidth(rt_b); | ||||
| } | ||||
| 
 | ||||
| void init_rt_rq(struct rt_rq *rt_rq) | ||||
| { | ||||
| 	struct rt_prio_array *array; | ||||
| 	int i; | ||||
| 
 | ||||
| 	array = &rt_rq->active; | ||||
| 	for (i = 0; i < MAX_RT_PRIO; i++) { | ||||
| 		INIT_LIST_HEAD(array->queue + i); | ||||
| 		__clear_bit(i, array->bitmap); | ||||
| 	} | ||||
| 	/* delimiter for bit-search: */ | ||||
| 	__set_bit(MAX_RT_PRIO, array->bitmap); | ||||
| 
 | ||||
| #if defined CONFIG_SMP | ||||
| 	rt_rq->highest_prio.curr = MAX_RT_PRIO-1; | ||||
| 	rt_rq->highest_prio.next = MAX_RT_PRIO-1; | ||||
| 	rt_rq->overloaded = 0; | ||||
| 	plist_head_init(&rt_rq->pushable_tasks); | ||||
| #endif /* CONFIG_SMP */ | ||||
| 	/* We start is dequeued state, because no RT tasks are queued */ | ||||
| 	rt_rq->rt_queued = 0; | ||||
| 
 | ||||
| 	rt_rq->rt_time = 0; | ||||
| 	rt_rq->rt_throttled = 0; | ||||
| 	rt_rq->rt_runtime = 0; | ||||
| 	raw_spin_lock_init(&rt_rq->rt_runtime_lock); | ||||
| } | ||||
| 
 | ||||
| #ifdef CONFIG_RT_GROUP_SCHED | ||||
| static void destroy_rt_bandwidth(struct rt_bandwidth *rt_b) | ||||
| { | ||||
| 	hrtimer_cancel(&rt_b->rt_period_timer); | ||||
|  | @ -195,7 +196,6 @@ void unregister_rt_sched_group(struct task_group *tg) | |||
| { | ||||
| 	if (tg->rt_se) | ||||
| 		destroy_rt_bandwidth(&tg->rt_bandwidth); | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| void free_rt_sched_group(struct task_group *tg) | ||||
|  | @ -253,8 +253,7 @@ int alloc_rt_sched_group(struct task_group *tg, struct task_group *parent) | |||
| 	if (!tg->rt_se) | ||||
| 		goto err; | ||||
| 
 | ||||
| 	init_rt_bandwidth(&tg->rt_bandwidth, | ||||
| 			ktime_to_ns(def_rt_bandwidth.rt_period), 0); | ||||
| 	init_rt_bandwidth(&tg->rt_bandwidth, ktime_to_ns(global_rt_period()), 0); | ||||
| 
 | ||||
| 	for_each_possible_cpu(i) { | ||||
| 		rt_rq = kzalloc_node(sizeof(struct rt_rq), | ||||
|  | @ -604,70 +603,6 @@ static inline struct rt_bandwidth *sched_rt_bandwidth(struct rt_rq *rt_rq) | |||
| 	return &rt_rq->tg->rt_bandwidth; | ||||
| } | ||||
| 
 | ||||
| #else /* !CONFIG_RT_GROUP_SCHED */ | ||||
| 
 | ||||
| static inline u64 sched_rt_runtime(struct rt_rq *rt_rq) | ||||
| { | ||||
| 	return rt_rq->rt_runtime; | ||||
| } | ||||
| 
 | ||||
| static inline u64 sched_rt_period(struct rt_rq *rt_rq) | ||||
| { | ||||
| 	return ktime_to_ns(def_rt_bandwidth.rt_period); | ||||
| } | ||||
| 
 | ||||
| typedef struct rt_rq *rt_rq_iter_t; | ||||
| 
 | ||||
| #define for_each_rt_rq(rt_rq, iter, rq) \ | ||||
| 	for ((void) iter, rt_rq = &rq->rt; rt_rq; rt_rq = NULL) | ||||
| 
 | ||||
| #define for_each_sched_rt_entity(rt_se) \ | ||||
| 	for (; rt_se; rt_se = NULL) | ||||
| 
 | ||||
| static inline struct rt_rq *group_rt_rq(struct sched_rt_entity *rt_se) | ||||
| { | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| static inline void sched_rt_rq_enqueue(struct rt_rq *rt_rq) | ||||
| { | ||||
| 	struct rq *rq = rq_of_rt_rq(rt_rq); | ||||
| 
 | ||||
| 	if (!rt_rq->rt_nr_running) | ||||
| 		return; | ||||
| 
 | ||||
| 	enqueue_top_rt_rq(rt_rq); | ||||
| 	resched_curr(rq); | ||||
| } | ||||
| 
 | ||||
| static inline void sched_rt_rq_dequeue(struct rt_rq *rt_rq) | ||||
| { | ||||
| 	dequeue_top_rt_rq(rt_rq, rt_rq->rt_nr_running); | ||||
| } | ||||
| 
 | ||||
| static inline int rt_rq_throttled(struct rt_rq *rt_rq) | ||||
| { | ||||
| 	return rt_rq->rt_throttled; | ||||
| } | ||||
| 
 | ||||
| static inline const struct cpumask *sched_rt_period_mask(void) | ||||
| { | ||||
| 	return cpu_online_mask; | ||||
| } | ||||
| 
 | ||||
| static inline | ||||
| struct rt_rq *sched_rt_period_rt_rq(struct rt_bandwidth *rt_b, int cpu) | ||||
| { | ||||
| 	return &cpu_rq(cpu)->rt; | ||||
| } | ||||
| 
 | ||||
| static inline struct rt_bandwidth *sched_rt_bandwidth(struct rt_rq *rt_rq) | ||||
| { | ||||
| 	return &def_rt_bandwidth; | ||||
| } | ||||
| 
 | ||||
| #endif /* CONFIG_RT_GROUP_SCHED */ | ||||
| 
 | ||||
| bool sched_rt_bandwidth_account(struct rt_rq *rt_rq) | ||||
| { | ||||
| 	struct rt_bandwidth *rt_b = sched_rt_bandwidth(rt_rq); | ||||
|  | @ -859,7 +794,7 @@ static int do_sched_rt_period_timer(struct rt_bandwidth *rt_b, int overrun) | |||
| 	const struct cpumask *span; | ||||
| 
 | ||||
| 	span = sched_rt_period_mask(); | ||||
| #ifdef CONFIG_RT_GROUP_SCHED | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * FIXME: isolated CPUs should really leave the root task group, | ||||
| 	 * whether they are isolcpus or were isolated via cpusets, lest | ||||
|  | @ -871,7 +806,7 @@ static int do_sched_rt_period_timer(struct rt_bandwidth *rt_b, int overrun) | |||
| 	 */ | ||||
| 	if (rt_b == &root_task_group.rt_bandwidth) | ||||
| 		span = cpu_online_mask; | ||||
| #endif | ||||
| 
 | ||||
| 	for_each_cpu(i, span) { | ||||
| 		int enqueue = 0; | ||||
| 		struct rt_rq *rt_rq = sched_rt_period_rt_rq(rt_b, i); | ||||
|  | @ -938,18 +873,6 @@ static int do_sched_rt_period_timer(struct rt_bandwidth *rt_b, int overrun) | |||
| 	return idle; | ||||
| } | ||||
| 
 | ||||
| static inline int rt_se_prio(struct sched_rt_entity *rt_se) | ||||
| { | ||||
| #ifdef CONFIG_RT_GROUP_SCHED | ||||
| 	struct rt_rq *rt_rq = group_rt_rq(rt_se); | ||||
| 
 | ||||
| 	if (rt_rq) | ||||
| 		return rt_rq->highest_prio.curr; | ||||
| #endif | ||||
| 
 | ||||
| 	return rt_task_of(rt_se)->prio; | ||||
| } | ||||
| 
 | ||||
| static int sched_rt_runtime_exceeded(struct rt_rq *rt_rq) | ||||
| { | ||||
| 	u64 runtime = sched_rt_runtime(rt_rq); | ||||
|  | @ -993,6 +916,72 @@ static int sched_rt_runtime_exceeded(struct rt_rq *rt_rq) | |||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| #else /* !CONFIG_RT_GROUP_SCHED */ | ||||
| 
 | ||||
| typedef struct rt_rq *rt_rq_iter_t; | ||||
| 
 | ||||
| #define for_each_rt_rq(rt_rq, iter, rq) \ | ||||
| 	for ((void) iter, rt_rq = &rq->rt; rt_rq; rt_rq = NULL) | ||||
| 
 | ||||
| #define for_each_sched_rt_entity(rt_se) \ | ||||
| 	for (; rt_se; rt_se = NULL) | ||||
| 
 | ||||
| static inline struct rt_rq *group_rt_rq(struct sched_rt_entity *rt_se) | ||||
| { | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| static inline void sched_rt_rq_enqueue(struct rt_rq *rt_rq) | ||||
| { | ||||
| 	struct rq *rq = rq_of_rt_rq(rt_rq); | ||||
| 
 | ||||
| 	if (!rt_rq->rt_nr_running) | ||||
| 		return; | ||||
| 
 | ||||
| 	enqueue_top_rt_rq(rt_rq); | ||||
| 	resched_curr(rq); | ||||
| } | ||||
| 
 | ||||
| static inline void sched_rt_rq_dequeue(struct rt_rq *rt_rq) | ||||
| { | ||||
| 	dequeue_top_rt_rq(rt_rq, rt_rq->rt_nr_running); | ||||
| } | ||||
| 
 | ||||
| static inline int rt_rq_throttled(struct rt_rq *rt_rq) | ||||
| { | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| static inline const struct cpumask *sched_rt_period_mask(void) | ||||
| { | ||||
| 	return cpu_online_mask; | ||||
| } | ||||
| 
 | ||||
| static inline | ||||
| struct rt_rq *sched_rt_period_rt_rq(struct rt_bandwidth *rt_b, int cpu) | ||||
| { | ||||
| 	return &cpu_rq(cpu)->rt; | ||||
| } | ||||
| 
 | ||||
| #ifdef CONFIG_SMP | ||||
| static void __enable_runtime(struct rq *rq) { } | ||||
| static void __disable_runtime(struct rq *rq) { } | ||||
| #endif | ||||
| 
 | ||||
| #endif /* CONFIG_RT_GROUP_SCHED */ | ||||
| 
 | ||||
| static inline int rt_se_prio(struct sched_rt_entity *rt_se) | ||||
| { | ||||
| #ifdef CONFIG_RT_GROUP_SCHED | ||||
| 	struct rt_rq *rt_rq = group_rt_rq(rt_se); | ||||
| 
 | ||||
| 	if (rt_rq) | ||||
| 		return rt_rq->highest_prio.curr; | ||||
| #endif | ||||
| 
 | ||||
| 	return rt_task_of(rt_se)->prio; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Update the current task's runtime statistics. Skip current tasks that | ||||
|  * are not in our scheduling class. | ||||
|  | @ -1000,7 +989,6 @@ static int sched_rt_runtime_exceeded(struct rt_rq *rt_rq) | |||
| static void update_curr_rt(struct rq *rq) | ||||
| { | ||||
| 	struct task_struct *curr = rq->curr; | ||||
| 	struct sched_rt_entity *rt_se = &curr->rt; | ||||
| 	s64 delta_exec; | ||||
| 
 | ||||
| 	if (curr->sched_class != &rt_sched_class) | ||||
|  | @ -1010,6 +998,9 @@ static void update_curr_rt(struct rq *rq) | |||
| 	if (unlikely(delta_exec <= 0)) | ||||
| 		return; | ||||
| 
 | ||||
| #ifdef CONFIG_RT_GROUP_SCHED | ||||
| 	struct sched_rt_entity *rt_se = &curr->rt; | ||||
| 
 | ||||
| 	if (!rt_bandwidth_enabled()) | ||||
| 		return; | ||||
| 
 | ||||
|  | @ -1028,6 +1019,7 @@ static void update_curr_rt(struct rq *rq) | |||
| 				do_start_rt_bandwidth(sched_rt_bandwidth(rt_rq)); | ||||
| 		} | ||||
| 	} | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| static void | ||||
|  | @ -1184,7 +1176,6 @@ dec_rt_group(struct sched_rt_entity *rt_se, struct rt_rq *rt_rq) | |||
| static void | ||||
| inc_rt_group(struct sched_rt_entity *rt_se, struct rt_rq *rt_rq) | ||||
| { | ||||
| 	start_rt_bandwidth(&def_rt_bandwidth); | ||||
| } | ||||
| 
 | ||||
| static inline | ||||
|  | @ -2912,19 +2903,6 @@ int sched_rt_can_attach(struct task_group *tg, struct task_struct *tsk) | |||
| #ifdef CONFIG_SYSCTL | ||||
| static int sched_rt_global_constraints(void) | ||||
| { | ||||
| 	unsigned long flags; | ||||
| 	int i; | ||||
| 
 | ||||
| 	raw_spin_lock_irqsave(&def_rt_bandwidth.rt_runtime_lock, flags); | ||||
| 	for_each_possible_cpu(i) { | ||||
| 		struct rt_rq *rt_rq = &cpu_rq(i)->rt; | ||||
| 
 | ||||
| 		raw_spin_lock(&rt_rq->rt_runtime_lock); | ||||
| 		rt_rq->rt_runtime = global_rt_runtime(); | ||||
| 		raw_spin_unlock(&rt_rq->rt_runtime_lock); | ||||
| 	} | ||||
| 	raw_spin_unlock_irqrestore(&def_rt_bandwidth.rt_runtime_lock, flags); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| #endif /* CONFIG_SYSCTL */ | ||||
|  | @ -2944,12 +2922,6 @@ static int sched_rt_global_validate(void) | |||
| 
 | ||||
| static void sched_rt_do_global(void) | ||||
| { | ||||
| 	unsigned long flags; | ||||
| 
 | ||||
| 	raw_spin_lock_irqsave(&def_rt_bandwidth.rt_runtime_lock, flags); | ||||
| 	def_rt_bandwidth.rt_runtime = global_rt_runtime(); | ||||
| 	def_rt_bandwidth.rt_period = ns_to_ktime(global_rt_period()); | ||||
| 	raw_spin_unlock_irqrestore(&def_rt_bandwidth.rt_runtime_lock, flags); | ||||
| } | ||||
| 
 | ||||
| static int sched_rt_handler(const struct ctl_table *table, int write, void *buffer, | ||||
|  |  | |||
|  | @ -362,7 +362,7 @@ extern bool __checkparam_dl(const struct sched_attr *attr); | |||
| extern bool dl_param_changed(struct task_struct *p, const struct sched_attr *attr); | ||||
| extern int  dl_cpuset_cpumask_can_shrink(const struct cpumask *cur, const struct cpumask *trial); | ||||
| extern int  dl_bw_check_overflow(int cpu); | ||||
| 
 | ||||
| extern s64 dl_scaled_delta_exec(struct rq *rq, struct sched_dl_entity *dl_se, s64 delta_exec); | ||||
| /*
 | ||||
|  * SCHED_DEADLINE supports servers (nested scheduling) with the following | ||||
|  * interface: | ||||
|  | @ -388,7 +388,15 @@ extern void dl_server_start(struct sched_dl_entity *dl_se); | |||
| extern void dl_server_stop(struct sched_dl_entity *dl_se); | ||||
| extern void dl_server_init(struct sched_dl_entity *dl_se, struct rq *rq, | ||||
| 		    dl_server_has_tasks_f has_tasks, | ||||
| 		    dl_server_pick_f pick); | ||||
| 		    dl_server_pick_f pick_next, | ||||
| 		    dl_server_pick_f pick_task); | ||||
| 
 | ||||
| extern void dl_server_update_idle_time(struct rq *rq, | ||||
| 		    struct task_struct *p); | ||||
| extern void fair_server_init(struct rq *rq); | ||||
| extern void __dl_server_attach_root(struct sched_dl_entity *dl_se, struct rq *rq); | ||||
| extern int dl_server_apply_params(struct sched_dl_entity *dl_se, | ||||
| 		    u64 runtime, u64 period, bool init); | ||||
| 
 | ||||
| #ifdef CONFIG_CGROUP_SCHED | ||||
| 
 | ||||
|  | @ -631,7 +639,6 @@ struct cfs_rq { | |||
| 	s64			avg_vruntime; | ||||
| 	u64			avg_load; | ||||
| 
 | ||||
| 	u64			exec_clock; | ||||
| 	u64			min_vruntime; | ||||
| #ifdef CONFIG_SCHED_CORE | ||||
| 	unsigned int		forceidle_seq; | ||||
|  | @ -651,10 +658,6 @@ struct cfs_rq { | |||
| 	struct sched_entity	*curr; | ||||
| 	struct sched_entity	*next; | ||||
| 
 | ||||
| #ifdef	CONFIG_SCHED_DEBUG | ||||
| 	unsigned int		nr_spread_over; | ||||
| #endif | ||||
| 
 | ||||
| #ifdef CONFIG_SMP | ||||
| 	/*
 | ||||
| 	 * CFS load tracking | ||||
|  | @ -794,13 +797,13 @@ struct rt_rq { | |||
| #endif /* CONFIG_SMP */ | ||||
| 	int			rt_queued; | ||||
| 
 | ||||
| #ifdef CONFIG_RT_GROUP_SCHED | ||||
| 	int			rt_throttled; | ||||
| 	u64			rt_time; | ||||
| 	u64			rt_runtime; | ||||
| 	/* Nests inside the rq lock: */ | ||||
| 	raw_spinlock_t		rt_runtime_lock; | ||||
| 
 | ||||
| #ifdef CONFIG_RT_GROUP_SCHED | ||||
| 	unsigned int		rt_nr_boosted; | ||||
| 
 | ||||
| 	struct rq		*rq; | ||||
|  | @ -1110,6 +1113,8 @@ struct rq { | |||
| 	struct scx_rq		scx; | ||||
| #endif | ||||
| 
 | ||||
| 	struct sched_dl_entity	fair_server; | ||||
| 
 | ||||
| #ifdef CONFIG_FAIR_GROUP_SCHED | ||||
| 	/* list of leaf cfs_rq on this CPU: */ | ||||
| 	struct list_head	leaf_cfs_rq_list; | ||||
|  | @ -1224,7 +1229,6 @@ struct rq { | |||
| 	/* latency stats */ | ||||
| 	struct sched_info	rq_sched_info; | ||||
| 	unsigned long long	rq_cpu_time; | ||||
| 	/* could above be rq->cfs_rq.exec_clock + rq->rt_rq.rt_runtime ? */ | ||||
| 
 | ||||
| 	/* sys_sched_yield() stats */ | ||||
| 	unsigned int		yld_count; | ||||
|  | @ -2619,7 +2623,6 @@ extern void init_sched_fair_class(void); | |||
| extern void resched_curr(struct rq *rq); | ||||
| extern void resched_cpu(int cpu); | ||||
| 
 | ||||
| extern struct rt_bandwidth def_rt_bandwidth; | ||||
| extern void init_rt_bandwidth(struct rt_bandwidth *rt_b, u64 period, u64 runtime); | ||||
| extern bool sched_rt_bandwidth_account(struct rt_rq *rt_rq); | ||||
| 
 | ||||
|  |  | |||
|  | @ -516,6 +516,14 @@ void rq_attach_root(struct rq *rq, struct root_domain *rd) | |||
| 	if (cpumask_test_cpu(rq->cpu, cpu_active_mask)) | ||||
| 		set_rq_online(rq); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Because the rq is not a task, dl_add_task_root_domain() did not | ||||
| 	 * move the fair server bw to the rd if it already started. | ||||
| 	 * Add it now. | ||||
| 	 */ | ||||
| 	if (rq->fair_server.dl_server) | ||||
| 		__dl_server_attach_root(&rq->fair_server, rq); | ||||
| 
 | ||||
| 	rq_unlock_irqrestore(rq, &rf); | ||||
| 
 | ||||
| 	if (old_rd) | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Tejun Heo
						Tejun Heo