From 8dc40f6f881de162da338fd7593469f06bb63fdc Mon Sep 17 00:00:00 2001 From: Alessio Balsini Date: Tue, 13 Mar 2018 18:35:00 +0000 Subject: [PATCH 1/5] sched/deadline: Implement "runtime overrun signal" support This patch adds the possibility of getting the delivery of a SIGXCPU signal whenever there is a runtime overrun. The request is done through the sched_flags field within the sched_attr structure. Forward port of https://lkml.org/lkml/2009/10/16/170 Tested-by: Mathieu Poirier Signed-off-by: Juri Lelli Signed-off-by: Claudio Scordino Signed-off-by: Luca Abeni Signed-off-by: Peter Zijlstra (Intel) Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: Tommaso Cucinotta Link: http://lkml.kernel.org/r/1513077024-25461-1-git-send-email-claudio@evidence.eu.com Signed-off-by: Ingo Molnar --- include/linux/sched.h | 1 + include/uapi/linux/sched.h | 8 +++++++- kernel/sched/core.c | 6 ++---- kernel/sched/deadline.c | 6 ++++++ kernel/time/posix-cpu-timers.c | 18 ++++++++++++++++++ 5 files changed, 34 insertions(+), 5 deletions(-) diff --git a/include/linux/sched.h b/include/linux/sched.h index 16dfa4084e1763..1a5bbb0b3bace6 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1511,6 +1511,7 @@ struct sched_dl_entity { * code. */ int dl_throttled, dl_boosted, dl_yielded, dl_non_contending; + int dl_overrun; /* * Bandwidth enforcement timer. Each -deadline task has its diff --git a/include/uapi/linux/sched.h b/include/uapi/linux/sched.h index 39827e30061357..8544cab36debc2 100644 --- a/include/uapi/linux/sched.h +++ b/include/uapi/linux/sched.h @@ -49,6 +49,12 @@ */ #define SCHED_FLAG_RESET_ON_FORK 0x01 #define SCHED_FLAG_RECLAIM 0x02 -#define SCHED_FLAG_SPECIAL 0x04 +#define SCHED_FLAG_DL_OVERRUN 0x04 +#define SCHED_FLAG_SPECIAL 0x08 + +#define SCHED_FLAG_ALL (SCHED_FLAG_RESET_ON_FORK | \ + SCHED_FLAG_RECLAIM | \ + SCHED_FLAG_SPECIAL | \ + SCHED_FLAG_DL_OVERRUN) #endif /* _UAPI_LINUX_SCHED_H */ diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 10324a516bc080..028d474c9d5da8 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -2233,6 +2233,7 @@ void __dl_clear_params(struct task_struct *p) dl_se->dl_throttled = 0; dl_se->dl_yielded = 0; dl_se->dl_non_contending = 0; + dl_se->dl_overrun = 0; } /* @@ -4119,10 +4120,7 @@ static int __sched_setscheduler(struct task_struct *p, return -EINVAL; } - if (attr->sched_flags & - ~(SCHED_FLAG_RESET_ON_FORK | - SCHED_FLAG_RECLAIM | - SCHED_FLAG_SPECIAL)) + if (attr->sched_flags & ~(SCHED_FLAG_ALL)) return -EINVAL; /* diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c index b7c6c9791bf656..f3a06384d3cc65 100644 --- a/kernel/sched/deadline.c +++ b/kernel/sched/deadline.c @@ -914,6 +914,12 @@ static void update_curr_dl(struct rq *rq) throttle: if (dl_runtime_exceeded(dl_se) || dl_se->dl_yielded) { dl_se->dl_throttled = 1; + + /* If requested, inform the user about runtime overruns. */ + if (dl_runtime_exceeded(dl_se) && + (dl_se->flags & SCHED_FLAG_DL_OVERRUN)) + dl_se->dl_overrun = 1; + __dequeue_task_dl(rq, curr, 0); if (unlikely(dl_se->dl_boosted || !start_dl_timer(curr))) enqueue_task_dl(rq, curr, ENQUEUE_REPLENISH); diff --git a/kernel/time/posix-cpu-timers.c b/kernel/time/posix-cpu-timers.c index 404eb294b2277a..26682fa916d147 100644 --- a/kernel/time/posix-cpu-timers.c +++ b/kernel/time/posix-cpu-timers.c @@ -12,6 +12,7 @@ #include #include #include +#include /* * Called after updating RLIMIT_CPU to run cpu timer and update @@ -851,6 +852,14 @@ check_timers_list(struct list_head *timers, return 0; } +static inline void check_dl_overrun(struct task_struct *tsk) +{ + if (tsk->dl.dl_overrun) { + tsk->dl.dl_overrun = 0; + __group_send_sig_info(SIGXCPU, SEND_SIG_PRIV, tsk); + } +} + /* * Check for any per-thread CPU timers that have fired and move them off * the tsk->cpu_timers[N] list onto the firing list. Here we update the @@ -865,6 +874,9 @@ static void check_thread_timers(struct task_struct *tsk, unsigned long long expires; unsigned long soft; + if (dl_task(tsk)) + check_dl_overrun(tsk); + /* * If cputime_expires is zero, then there are no active * per thread CPU timers. @@ -969,6 +981,9 @@ static void check_process_timers(struct task_struct *tsk, struct task_cputime cputime; unsigned long soft; + if (dl_task(tsk)) + check_dl_overrun(tsk); + /* * If cputimer is not running, then there are no active * process wide timers (POSIX 1.b, itimers, RLIMIT_CPU). @@ -1175,6 +1190,9 @@ static inline int fastpath_timer_check(struct task_struct *tsk) return 1; } + if (dl_task(tsk) && tsk->dl.dl_overrun) + return 1; + return 0; } From 384f7e65db97e15e301f5d94e1c0a2f011591fa8 Mon Sep 17 00:00:00 2001 From: Alessio Balsini Date: Mon, 22 Jan 2018 17:35:19 +0000 Subject: [PATCH 2/5] sched/deadline: force the removal of the bandwidth constraint [ DO NOT MERGE ] By forcing the removal of the bandwidth check, SCHED_DEADLINE will be able (also) to accept tasks also when the affinity is not equal to the whole root domain. --- kernel/sched/sched.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index f02ae598482fff..0114e55a39f9b9 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -186,7 +186,7 @@ struct dl_bandwidth { static inline int dl_bandwidth_enabled(void) { - return sysctl_sched_rt_runtime >= 0; + return 0; } extern struct dl_bw *dl_bw_of(int i); From 4059df432879e852b2873e6b1a609de7f283bc50 Mon Sep 17 00:00:00 2001 From: Alessio Balsini Date: Wed, 14 Feb 2018 14:57:00 +0000 Subject: [PATCH 3/5] sched/deadline: sched_getattr returns relative dl task information [ DO NOT MERGE ] If the task calls a sched_getattr on its own tid with flags = 1, the dl runqueue status is updated and the current runtime and absolute deadline values are returned. --- kernel/sched/core.c | 25 ++++++++++++++++++++----- kernel/sched/deadline.c | 2 +- kernel/sched/sched.h | 2 ++ 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 028d474c9d5da8..54d98516b13f8f 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -4007,14 +4007,29 @@ static void __setscheduler(struct rq *rq, struct task_struct *p, } static void -__getparam_dl(struct task_struct *p, struct sched_attr *attr) +__getparam_dl(struct task_struct *p, struct sched_attr *attr, unsigned int flags) { struct sched_dl_entity *dl_se = &p->dl; attr->sched_priority = p->rt_priority; - attr->sched_runtime = dl_se->dl_runtime; - attr->sched_deadline = dl_se->dl_deadline; + + if (flags == 1 && p == current) { + update_curr_dl(task_rq(p)); + + /* + * sched_runtime can never be negative because, since this + * operation can be performed by the task on its own + * sched_attr, if the bandwidth is <= 0, then the task is + * throttled and therefore cannot perform the syscall. + */ + attr->sched_runtime = dl_se->runtime; + attr->sched_deadline = dl_se->deadline; + } else { + attr->sched_runtime = dl_se->dl_runtime; + attr->sched_deadline = dl_se->dl_deadline; + } attr->sched_period = dl_se->dl_period; + attr->sched_flags = dl_se->flags; } @@ -4681,7 +4696,7 @@ SYSCALL_DEFINE4(sched_getattr, pid_t, pid, struct sched_attr __user *, uattr, int retval; if (!uattr || pid < 0 || size > PAGE_SIZE || - size < SCHED_ATTR_SIZE_VER0 || flags) + size < SCHED_ATTR_SIZE_VER0) return -EINVAL; rcu_read_lock(); @@ -4698,7 +4713,7 @@ SYSCALL_DEFINE4(sched_getattr, pid_t, pid, struct sched_attr __user *, uattr, if (p->sched_reset_on_fork) attr.sched_flags |= SCHED_FLAG_RESET_ON_FORK; if (task_has_dl_policy(p)) - __getparam_dl(p, &attr); + __getparam_dl(p, &attr, flags); else if (task_has_rt_policy(p)) attr.sched_priority = p->rt_priority; else diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c index f3a06384d3cc65..5065e26c2a499c 100644 --- a/kernel/sched/deadline.c +++ b/kernel/sched/deadline.c @@ -866,7 +866,7 @@ u64 grub_reclaim(u64 delta, struct rq *rq, u64 u) * Update the current task's runtime statistics (provided it is still * a -deadline task and has not been removed from the dl_rq). */ -static void update_curr_dl(struct rq *rq) +void update_curr_dl(struct rq *rq) { struct task_struct *curr = rq->curr; struct sched_dl_entity *dl_se = &curr->dl; diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 0114e55a39f9b9..796c39eb9a877c 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -84,6 +84,8 @@ static inline void update_cpu_load_active(struct rq *this_rq) { } */ #define RUNTIME_INF ((u64)~0ULL) +void update_curr_dl(struct rq *rq); + static inline int idle_policy(int policy) { return policy == SCHED_IDLE; From 905eb94e5b1121fab1fb1730efad02476a8b043c Mon Sep 17 00:00:00 2001 From: Alessio Balsini Date: Tue, 27 Mar 2018 11:50:24 +0100 Subject: [PATCH 4/5] sched/cpufreq_schedutil: worker kthread with proper dl parameters Use valid dl parameters for sugov kthread, in such a way that priority inheritance does not cause problems. Use its special flag to not count its bandwidth. --- kernel/sched/cpufreq_schedutil.c | 10 +++++++--- kernel/sched/deadline.c | 12 ++++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c index d4e91064b72139..dacbe0d42f23b9 100644 --- a/kernel/sched/cpufreq_schedutil.c +++ b/kernel/sched/cpufreq_schedutil.c @@ -542,9 +542,13 @@ static int sugov_kthread_create(struct sugov_policy *sg_policy) .sched_flags = SCHED_FLAG_SPECIAL, .sched_nice = 0, .sched_priority = 0, - .sched_runtime = 0, - .sched_deadline = 0, - .sched_period = 0, + /* + * Fake (unused) bandwidth; workaround to "fix" + * priority inheritance. + */ + .sched_runtime = 1000000, + .sched_deadline = 10000000, + .sched_period = 10000000, }; struct cpufreq_policy *policy = sg_policy->policy; int ret; diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c index 5065e26c2a499c..7b344095b6d64a 100644 --- a/kernel/sched/deadline.c +++ b/kernel/sched/deadline.c @@ -48,6 +48,9 @@ void add_running_bw(struct sched_dl_entity *dl_se, struct dl_rq *dl_rq) { u64 old = dl_rq->running_bw; + if (unlikely(dl_entity_is_special(dl_se))) + return; + lockdep_assert_held(&(rq_of_dl_rq(dl_rq))->lock); dl_rq->running_bw += dl_se->dl_bw; WARN_ON(dl_rq->running_bw < old); /* overflow */ @@ -61,6 +64,9 @@ void sub_running_bw(struct sched_dl_entity *dl_se, struct dl_rq *dl_rq) { u64 old = dl_rq->running_bw; + if (unlikely(dl_entity_is_special(dl_se))) + return; + lockdep_assert_held(&(rq_of_dl_rq(dl_rq))->lock); dl_rq->running_bw -= dl_se->dl_bw; WARN_ON(dl_rq->running_bw > old); /* underflow */ @@ -75,6 +81,9 @@ void add_rq_bw(struct sched_dl_entity *dl_se, struct dl_rq *dl_rq) { u64 old = dl_rq->this_bw; + if (unlikely(dl_entity_is_special(dl_se))) + return; + lockdep_assert_held(&(rq_of_dl_rq(dl_rq))->lock); dl_rq->this_bw += dl_se->dl_bw; WARN_ON(dl_rq->this_bw < old); /* overflow */ @@ -85,6 +94,9 @@ void sub_rq_bw(struct sched_dl_entity *dl_se, struct dl_rq *dl_rq) { u64 old = dl_rq->this_bw; + if (unlikely(dl_entity_is_special(dl_se))) + return; + lockdep_assert_held(&(rq_of_dl_rq(dl_rq))->lock); dl_rq->this_bw -= dl_se->dl_bw; WARN_ON(dl_rq->this_bw > old); /* underflow */ From 005e23a63c0cc7c423f948d311bb59da87b3b858 Mon Sep 17 00:00:00 2001 From: Alessio Balsini Date: Tue, 27 Mar 2018 15:45:54 +0100 Subject: [PATCH 5/5] sched/core: always dequeue dl task when changing parameters The optimization of updating task parameters without moving the task improves the performance for rt tasks, but causes problems in the bandwidth accounting for dl tasks. More specifically, dl tasks need to be enqueued and dequeued to properly update the bandwidth of the runqueue, so this workaround disables that optimization for dl tasks. --- kernel/sched/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 54d98516b13f8f..a07639e4953f7d 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -4294,7 +4294,7 @@ static int __sched_setscheduler(struct task_struct *p, p->sched_reset_on_fork = reset_on_fork; oldprio = p->prio; - if (pi) { + if (!dl_task(p) && pi) { /* * Take priority boosted tasks into account. If the new * effective priority is unchanged, we just store the new