diff options
| -rw-r--r-- | include/linux/sched.h | 1 | ||||
| -rw-r--r-- | kernel/sched/core.c | 30 | ||||
| -rw-r--r-- | kernel/sched/idle_task.c | 1 | ||||
| -rw-r--r-- | kernel/sched/sched.h | 10 | ||||
| -rw-r--r-- | kernel/time/tick-sched.c | 7 | 
5 files changed, 49 insertions, 0 deletions
diff --git a/include/linux/sched.h b/include/linux/sched.h index ebf7095158a..af008d7bad5 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1862,6 +1862,7 @@ static inline void wake_up_nohz_cpu(int cpu) { }  #ifdef CONFIG_NO_HZ_FULL  extern bool sched_can_stop_tick(void); +extern u64 scheduler_tick_max_deferment(void);  #else  static inline bool sched_can_stop_tick(void) { return false; }  #endif diff --git a/kernel/sched/core.c b/kernel/sched/core.c index e94842d4400..3bdf986a091 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -2736,8 +2736,35 @@ void scheduler_tick(void)  	rq->idle_balance = idle_cpu(cpu);  	trigger_load_balance(rq, cpu);  #endif +	rq_last_tick_reset(rq);  } +#ifdef CONFIG_NO_HZ_FULL +/** + * scheduler_tick_max_deferment + * + * Keep at least one tick per second when a single + * active task is running because the scheduler doesn't + * yet completely support full dynticks environment. + * + * This makes sure that uptime, CFS vruntime, load + * balancing, etc... continue to move forward, even + * with a very low granularity. + */ +u64 scheduler_tick_max_deferment(void) +{ +	struct rq *rq = this_rq(); +	unsigned long next, now = ACCESS_ONCE(jiffies); + +	next = rq->last_sched_tick + HZ; + +	if (time_before_eq(next, now)) +		return 0; + +	return jiffies_to_usecs(next - now) * NSEC_PER_USEC; +} +#endif +  notrace unsigned long get_parent_ip(unsigned long addr)  {  	if (in_lock_functions(addr)) { @@ -6993,6 +7020,9 @@ void __init sched_init(void)  #ifdef CONFIG_NO_HZ_COMMON  		rq->nohz_flags = 0;  #endif +#ifdef CONFIG_NO_HZ_FULL +		rq->last_sched_tick = 0; +#endif  #endif  		init_rq_hrtick(rq);  		atomic_set(&rq->nr_iowait, 0); diff --git a/kernel/sched/idle_task.c b/kernel/sched/idle_task.c index b8ce7732834..d8da01008d3 100644 --- a/kernel/sched/idle_task.c +++ b/kernel/sched/idle_task.c @@ -17,6 +17,7 @@ select_task_rq_idle(struct task_struct *p, int sd_flag, int flags)  static void pre_schedule_idle(struct rq *rq, struct task_struct *prev)  {  	idle_exit_fair(rq); +	rq_last_tick_reset(rq);  }  static void post_schedule_idle(struct rq *rq) diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 24dc2989774..ce39224d615 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -410,6 +410,9 @@ struct rq {  	u64 nohz_stamp;  	unsigned long nohz_flags;  #endif +#ifdef CONFIG_NO_HZ_FULL +	unsigned long last_sched_tick; +#endif  	int skip_clock_update;  	/* capture load from *all* tasks on this cpu: */ @@ -1090,6 +1093,13 @@ static inline void dec_nr_running(struct rq *rq)  	rq->nr_running--;  } +static inline void rq_last_tick_reset(struct rq *rq) +{ +#ifdef CONFIG_NO_HZ_FULL +	rq->last_sched_tick = jiffies; +#endif +} +  extern void update_rq_clock(struct rq *rq);  extern void activate_task(struct rq *rq, struct task_struct *p, int flags); diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c index 1c9f53b2ddb..07929c63357 100644 --- a/kernel/time/tick-sched.c +++ b/kernel/time/tick-sched.c @@ -600,6 +600,13 @@ static ktime_t tick_nohz_stop_sched_tick(struct tick_sched *ts,  			time_delta = KTIME_MAX;  		} +#ifdef CONFIG_NO_HZ_FULL +		if (!ts->inidle) { +			time_delta = min(time_delta, +					 scheduler_tick_max_deferment()); +		} +#endif +  		/*  		 * calculate the expiry time for the next timer wheel  		 * timer. delta_jiffies >= NEXT_TIMER_MAX_DELTA signals  |