[RFC,V3,6/6] sched/fair: Implement starvation monitor

Message ID bd9977efff8cc3e002c4b2db02f611167905a99f.1686239016.git.bristot@kernel.org
State New
Headers
Series SCHED_DEADLINE server infrastructure |

Commit Message

Daniel Bristot de Oliveira June 8, 2023, 3:58 p.m. UTC
  From: Juri Lelli <juri.lelli@redhat.com>

Starting deadline server for lower priority classes right away when
first task is enqueued might break guarantees, as tasks belonging to
intermediate priority classes could be uselessly preempted. E.g., a well
behaving (non hog) FIFO task can be preempted by NORMAL tasks even if
there are still CPU cycles available for NORMAL tasks to run, as they'll
be running inside the fair deadline server for some period of time.

To prevent this issue, implement a starvation monitor mechanism that
starts the deadline server only if a (fair in this case) task hasn't
been scheduled for some interval of time after it has been enqueued.
Use pick/put functions to manage starvation monitor status.

Signed-off-by: Juri Lelli <juri.lelli@redhat.com>
Signed-off-by: Daniel Bristot de Oliveira <bristot@kernel.org>
---
 kernel/sched/fair.c  | 57 ++++++++++++++++++++++++++++++++++++++++++--
 kernel/sched/sched.h |  4 ++++
 2 files changed, 59 insertions(+), 2 deletions(-)
  

Comments

Joel Fernandes June 12, 2023, 1:57 a.m. UTC | #1
Hello,

On Thu, Jun 8, 2023 at 11:58 AM Daniel Bristot de Oliveira
<bristot@kernel.org> wrote:
>
> From: Juri Lelli <juri.lelli@redhat.com>
>
> Starting deadline server for lower priority classes right away when
> first task is enqueued might break guarantees, as tasks belonging to
> intermediate priority classes could be uselessly preempted. E.g., a well
> behaving (non hog) FIFO task can be preempted by NORMAL tasks even if
> there are still CPU cycles available for NORMAL tasks to run, as they'll
> be running inside the fair deadline server for some period of time.
>
> To prevent this issue, implement a starvation monitor mechanism that
> starts the deadline server only if a (fair in this case) task hasn't
> been scheduled for some interval of time after it has been enqueued.
> Use pick/put functions to manage starvation monitor status.

Me and Vineeth were discussing that another way of resolving this
issue is to use a DL-server for RT as well, and then using a smaller
deadline  for RT. That way the RT is more likely to be selected due to
its earlier deadline/period.

Another approach could be to implement the 0-laxity scheduling as a
general SCHED_DEADLINE feature, perhaps through a flag. And allow DL
tasks to opt-in to 0-laxity scheduling unless there are idle cycles.
And then opt-in the feature for the CFS deadline server task.

Lastly, if the goal is to remove RT throttling code eventually, are
you also planning to remove RT group scheduling as well? Are there
users of RT group scheduling that might be impacted? On the other
hand, RT throttling / group scheduling code can be left as it is
(perhaps documenting it as deprecated) and the server stuff can be
implemented via a CONFIG option.

 - Joel

> Signed-off-by: Juri Lelli <juri.lelli@redhat.com>
> Signed-off-by: Daniel Bristot de Oliveira <bristot@kernel.org>
> ---
>  kernel/sched/fair.c  | 57 ++++++++++++++++++++++++++++++++++++++++++--
>  kernel/sched/sched.h |  4 ++++
>  2 files changed, 59 insertions(+), 2 deletions(-)
>
> diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
> index f493f05c1f84..75eadd85e2b3 100644
> --- a/kernel/sched/fair.c
> +++ b/kernel/sched/fair.c
> @@ -6315,6 +6315,53 @@ static int sched_idle_cpu(int cpu)
>  }
>  #endif
>
> +
> +static void fair_server_watchdog(struct timer_list *list)
> +{
> +       struct rq *rq = container_of(list, struct rq, fair_server_wd);
> +       struct rq_flags rf;
> +
> +       rq_lock_irqsave(rq, &rf);
> +       rq->fair_server_wd_running = 0;
> +
> +       if (!rq->cfs.h_nr_running)
> +               goto out;
> +
> +       update_rq_clock(rq);
> +       dl_server_start(&rq->fair_server);
> +       rq->fair_server_active = 1;
> +       resched_curr(rq);
> +
> +out:
> +       rq_unlock_irqrestore(rq, &rf);
> +}
> +
> +static inline void fair_server_watchdog_start(struct rq *rq)
> +{
> +       if (rq->fair_server_wd_running || rq->fair_server_active)
> +               return;
> +
> +       timer_setup(&rq->fair_server_wd, fair_server_watchdog, 0);
> +       rq->fair_server_wd.expires = jiffies + FAIR_SERVER_WATCHDOG_INTERVAL;
> +       add_timer_on(&rq->fair_server_wd, cpu_of(rq));
> +       rq->fair_server_active = 0;
> +       rq->fair_server_wd_running = 1;
> +}
> +
> +static inline void fair_server_watchdog_stop(struct rq *rq, bool stop_server)
> +{
> +       if (!rq->fair_server_wd_running && !stop_server)
> +               return;
> +
> +       del_timer(&rq->fair_server_wd);
> +       rq->fair_server_wd_running = 0;
> +
> +       if (stop_server && rq->fair_server_active) {
> +               dl_server_stop(&rq->fair_server);
> +               rq->fair_server_active = 0;
> +       }
> +}
> +
>  /*
>   * The enqueue_task method is called before nr_running is
>   * increased. Here we update the fair scheduling stats and
> @@ -6337,7 +6384,7 @@ enqueue_task_fair(struct rq *rq, struct task_struct *p, int flags)
>         util_est_enqueue(&rq->cfs, p);
>
>         if (!rq->cfs.h_nr_running)
> -               dl_server_start(&rq->fair_server);
> +               fair_server_watchdog_start(rq);
>
>         /*
>          * If in_iowait is set, the code below may not trigger any cpufreq
> @@ -6484,7 +6531,7 @@ static void dequeue_task_fair(struct rq *rq, struct task_struct *p, int flags)
>
>  dequeue_throttle:
>         if (!rq->cfs.h_nr_running)
> -               dl_server_stop(&rq->fair_server);
> +               fair_server_watchdog_stop(rq, true);
>
>         util_est_update(&rq->cfs, p, task_sleep);
>         hrtick_update(rq);
> @@ -8193,6 +8240,7 @@ done: __maybe_unused;
>                 hrtick_start_fair(rq, p);
>
>         update_misfit_status(p, rq);
> +       fair_server_watchdog_stop(rq, false);
>
>         return p;
>
> @@ -8248,6 +8296,8 @@ void fair_server_init(struct rq *rq)
>         dl_se->dl_period = 20 * TICK_NSEC;
>
>         dl_server_init(dl_se, rq, fair_server_has_tasks, fair_server_pick);
> +
> +       rq->fair_server_wd_running = 0;
>  }
>
>  /*
> @@ -8262,6 +8312,9 @@ static void put_prev_task_fair(struct rq *rq, struct task_struct *prev)
>                 cfs_rq = cfs_rq_of(se);
>                 put_prev_entity(cfs_rq, se);
>         }
> +
> +       if (rq->cfs.h_nr_running)
> +               fair_server_watchdog_start(rq);
>  }
>
>  /*
> diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
> index d4a7c0823c53..cab5d2b1e71f 100644
> --- a/kernel/sched/sched.h
> +++ b/kernel/sched/sched.h
> @@ -353,6 +353,7 @@ 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);
>
> +#define FAIR_SERVER_WATCHDOG_INTERVAL (HZ >> 1)
>  extern void fair_server_init(struct rq *);
>
>  #ifdef CONFIG_CGROUP_SCHED
> @@ -1018,6 +1019,9 @@ struct rq {
>         struct dl_rq            dl;
>
>         struct sched_dl_entity  fair_server;
> +       int                     fair_server_active;
> +       struct timer_list       fair_server_wd;
> +       int                     fair_server_wd_running;
>
>  #ifdef CONFIG_FAIR_GROUP_SCHED
>         /* list of leaf cfs_rq on this CPU: */
> --
> 2.40.1
>
  
Daniel Bristot de Oliveira June 12, 2023, 2:45 p.m. UTC | #2
On 6/12/23 03:57, Joel Fernandes wrote:
> Hello,
> 
> On Thu, Jun 8, 2023 at 11:58 AM Daniel Bristot de Oliveira
> <bristot@kernel.org> wrote:
>>
>> From: Juri Lelli <juri.lelli@redhat.com>
>>
>> Starting deadline server for lower priority classes right away when
>> first task is enqueued might break guarantees, as tasks belonging to
>> intermediate priority classes could be uselessly preempted. E.g., a well
>> behaving (non hog) FIFO task can be preempted by NORMAL tasks even if
>> there are still CPU cycles available for NORMAL tasks to run, as they'll
>> be running inside the fair deadline server for some period of time.
>>
>> To prevent this issue, implement a starvation monitor mechanism that
>> starts the deadline server only if a (fair in this case) task hasn't
>> been scheduled for some interval of time after it has been enqueued.
>> Use pick/put functions to manage starvation monitor status.
> 
> Me and Vineeth were discussing that another way of resolving this
> issue is to use a DL-server for RT as well, and then using a smaller
> deadline  for RT. That way the RT is more likely to be selected due to
> its earlier deadline/period.

It would not be that different from what we have now.

One of the problems of throttling nowadays is that it accounts for a large window
of time, and any "imprecision" can cause the mechanism not to work as expected.

For example, we work on a fully-isolated CPU scenario, where some very sporadic
workload can be placed on the isolated CPU because of per-cpu kernel activities,
e.g., kworkers... We need to let them run, but for a minimal amount of time, for
instance, 20 us, to bound the interference.

The current mechanism does not give this precision because the IRQ accounting
does not account for runtime for the rt throttling (which makes sense). So the
RT throttling has the 20 us stolen from IRQs and keeps running. The same will
happen if we swap the current mechanism with a DL server for the RT.

Also, thinking about short deadlines to fake a fixed priority is... not starting
well. A fixed-priority higher instance is not a property of a deadline-based
scheduler, and Linux has a fixed-priority hierarchy: STOP -> DL -> RT -> CFS...
It is simple, and that is good.

That is why it is better to boost CFS instead of throttling RT. By boosting
CFS, you do not need a server for RT, and we account for anything on top of CFS
for free (IRQ/DL/FIFO...).

> 
> Another approach could be to implement the 0-laxity scheduling as a
> general SCHED_DEADLINE feature, perhaps through a flag. And allow DL
> tasks to opt-in to 0-laxity scheduling unless there are idle cycles.
> And then opt-in the feature for the CFS deadline server task.

A 0-laxity scheduler is not as simple as it sounds, as the priority also depends
on the "C" (runtime, generally WCET), which is hard to find and embeds
pessimism. Also, having such a feature would make other mechanisms harder, as
well as debugging things. For example, proxy-execution or a more precise
schedulability test...

In a paper, the scheduler alone is the solution. In real life, the solution
for problems like locking is as fundamental as the scheduler. We need to keep
things simple to expand on these other topics as well.

So, I do not think we need all the drawbacks of a mixed solution to just fix
the throttling problem, and EDF is more capable and explored for the
general case.

With this patch's idea (and expansions), we can fix the throttling problem
without breaking other behaviors like scheduling order...

> 
> Lastly, if the goal is to remove RT throttling code eventually, are
> you also planning to remove RT group scheduling as well? Are there
> users of RT group scheduling that might be impacted? On the other
> hand, RT throttling / group scheduling code can be left as it is
> (perhaps documenting it as deprecated) and the server stuff can be
> implemented via a CONFIG option.

I think that the idea is to have the DL servers eventually replace the group
schedule. But I also believe that it is better to start by solving the
throttling and then moving to other constructions on top of the mechanism.

-- Daniel
> 
>  - Joel
> 
>> Signed-off-by: Juri Lelli <juri.lelli@redhat.com>
>> Signed-off-by: Daniel Bristot de Oliveira <bristot@kernel.org>
  
Joel Fernandes June 12, 2023, 8:35 p.m. UTC | #3
Hello Daniel!

On Mon, Jun 12, 2023 at 1:21 PM Daniel Bristot de Oliveira
<bristot@kernel.org> wrote:
[...]
> > On Thu, Jun 8, 2023 at 11:58 AM Daniel Bristot de Oliveira
> > <bristot@kernel.org> wrote:
> >>
> >> From: Juri Lelli <juri.lelli@redhat.com>
> >>
> >> Starting deadline server for lower priority classes right away when
> >> first task is enqueued might break guarantees, as tasks belonging to
> >> intermediate priority classes could be uselessly preempted. E.g., a well
> >> behaving (non hog) FIFO task can be preempted by NORMAL tasks even if
> >> there are still CPU cycles available for NORMAL tasks to run, as they'll
> >> be running inside the fair deadline server for some period of time.
> >>
> >> To prevent this issue, implement a starvation monitor mechanism that
> >> starts the deadline server only if a (fair in this case) task hasn't
> >> been scheduled for some interval of time after it has been enqueued.
> >> Use pick/put functions to manage starvation monitor status.
> >
> > Me and Vineeth were discussing that another way of resolving this
> > issue is to use a DL-server for RT as well, and then using a smaller
> > deadline  for RT. That way the RT is more likely to be selected due to
> > its earlier deadline/period.
>
> It would not be that different from what we have now.
>
> One of the problems of throttling nowadays is that it accounts for a large window
> of time, and any "imprecision" can cause the mechanism not to work as expected.
>
> For example, we work on a fully-isolated CPU scenario, where some very sporadic
> workload can be placed on the isolated CPU because of per-cpu kernel activities,
> e.g., kworkers... We need to let them run, but for a minimal amount of time, for
> instance, 20 us, to bound the interference.
>
> The current mechanism does not give this precision because the IRQ accounting
> does not account for runtime for the rt throttling (which makes sense).

I lost you here, "Runtime for the rt throttling" does not make much
sense to me as a statement.

> So the
> RT throttling has the 20 us stolen from IRQs and keeps running. The same will
> happen if we swap the current mechanism with a DL server for the RT.

I read this about 10 times to learn that *maybe* you mean that IRQs
stole time from the "Well behaved running time" of the RT class. I am
not seeing how that is related to creation of a DL-server for the RT
class. Maybe describe your point a bit more clearly?

>
> Also, thinking about short deadlines to fake a fixed priority is... not starting
> well. A fixed-priority higher instance is not a property of a deadline-based
> scheduler, and Linux has a fixed-priority hierarchy: STOP -> DL -> RT -> CFS...
> It is simple, and that is good.
>
> That is why it is better to boost CFS instead of throttling RT. By boosting
> CFS, you do not need a server for RT, and we account for anything on top of CFS
> for free (IRQ/DL/FIFO...).

I did mention in my last email that it is not ideal. I just brought it
up as an option. It might reduce the problem being seen and is better
than not having it.

> > Another approach could be to implement the 0-laxity scheduling as a
> > general SCHED_DEADLINE feature, perhaps through a flag. And allow DL
> > tasks to opt-in to 0-laxity scheduling unless there are idle cycles.
> > And then opt-in the feature for the CFS deadline server task.
>
> A 0-laxity scheduler is not as simple as it sounds, as the priority also depends
> on the "C" (runtime, generally WCET), which is hard to find and embeds
> pessimism. Also, having such a feature would make other mechanisms harder, as
> well as debugging things. For example, proxy-execution or a more precise
> schedulability test...

I think you did not read my email properly, I was saying make the
0-laxity default-off and the opt-in for certain DL tasks. That may
work perfectly well for a system like ChromeOS where likely we will
use the DL server as the sole deadline task and opt-in for the
0-laxity. Then we don't need watchdog hacks at all and it all cleanly
works within the DL class itself. There are the drawbacks of the
pessimism/locking  etc (I already knew that by the way as the obvious
drawbacks of 0-laxity) but I am not immediately seeing how this
CFS-watchdog with 0-laxity is any different from the DL-server itself
having such a property. If you really have a concrete point on why
that won't work, and if you could clarify that more clearly why a
watchdog is better than it, that would be great.

> In a paper, the scheduler alone is the solution. In real life, the solution
> for problems like locking is as fundamental as the scheduler. We need to keep
> things simple to expand on these other topics as well.
>
> So, I do not think we need all the drawbacks of a mixed solution to just fix
> the throttling problem, and EDF is more capable and explored for the
> general case.

Again, I was saying making it opt-in seems like a reasonable approach
and just enabling such property for the DL server.

> With this patch's idea (and expansions), we can fix the throttling problem
> without breaking other behaviors like scheduling order...

I don't mind the watchdog patch as such, of course. I presented its
mechanics at OSSNA and I know how it works, but I feel the DL server
opting-in for 0-laxity would be cleaner while keeping such behavior as
default-off for regular DL uses, that's my opinion -- but what else am
I missing?  Either way, no harm in discussing alternate approaches as
well even if we are settling for the watchdog.

> > Lastly, if the goal is to remove RT throttling code eventually, are
> > you also planning to remove RT group scheduling as well? Are there
> > users of RT group scheduling that might be impacted? On the other
> > hand, RT throttling / group scheduling code can be left as it is
> > (perhaps documenting it as deprecated) and the server stuff can be
> > implemented via a CONFIG option.
>
> I think that the idea is to have the DL servers eventually replace the group
> schedule. But I also believe that it is better to start by solving the
> throttling and then moving to other constructions on top of the mechanism.

Hmm. For throttling at the root level yes, but  I am not seeing how
you can replace the group scheduling code for existing users of RT
Cgroups with this. The throttling in the RT group scheduling code is
not exactly only about "not starving CFS", it is more related to
letting RT groups run with certain bandwidth. So you cannot really
delete it if there are real users of that code -- you'll have to
migrate those users away first (to an alternate implementation like
DL).  If there are no users of RT group scheduling, that's lovely
though. We don't use it in ChromeOS fwiw.

- Joel
  
Daniel Bristot de Oliveira June 13, 2023, 1:41 p.m. UTC | #4
On 6/12/23 22:35, Joel Fernandes wrote:
> Hello Daniel!
> 
> On Mon, Jun 12, 2023 at 1:21 PM Daniel Bristot de Oliveira
> <bristot@kernel.org> wrote:
> [...]
>>> On Thu, Jun 8, 2023 at 11:58 AM Daniel Bristot de Oliveira
>>> <bristot@kernel.org> wrote:
>>>>
>>>> From: Juri Lelli <juri.lelli@redhat.com>
>>>>
>>>> Starting deadline server for lower priority classes right away when
>>>> first task is enqueued might break guarantees, as tasks belonging to
>>>> intermediate priority classes could be uselessly preempted. E.g., a well
>>>> behaving (non hog) FIFO task can be preempted by NORMAL tasks even if
>>>> there are still CPU cycles available for NORMAL tasks to run, as they'll
>>>> be running inside the fair deadline server for some period of time.
>>>>
>>>> To prevent this issue, implement a starvation monitor mechanism that
>>>> starts the deadline server only if a (fair in this case) task hasn't
>>>> been scheduled for some interval of time after it has been enqueued.
>>>> Use pick/put functions to manage starvation monitor status.
>>>
>>> Me and Vineeth were discussing that another way of resolving this
>>> issue is to use a DL-server for RT as well, and then using a smaller
>>> deadline  for RT. That way the RT is more likely to be selected due to
>>> its earlier deadline/period.
>>
>> It would not be that different from what we have now.
>>
>> One of the problems of throttling nowadays is that it accounts for a large window
>> of time, and any "imprecision" can cause the mechanism not to work as expected.
>>
>> For example, we work on a fully-isolated CPU scenario, where some very sporadic
>> workload can be placed on the isolated CPU because of per-cpu kernel activities,
>> e.g., kworkers... We need to let them run, but for a minimal amount of time, for
>> instance, 20 us, to bound the interference.
>>
>> The current mechanism does not give this precision because the IRQ accounting
>> does not account for runtime for the rt throttling (which makes sense).
> 
> I lost you here, "Runtime for the rt throttling" does not make much
> sense to me as a statement.

Both update_curr_rt() and update_curr_dl() use rq_clock_task() as clock. update_rq_clock_task()
reduces the irq_delta from task's clock (inside #ifdef CONFIG_IRQ_TIME_ACCOUNTING), and this
clock is used to throttling.

>> So the
>> RT throttling has the 20 us stolen from IRQs and keeps running. The same will
>> happen if we swap the current mechanism with a DL server for the RT.
> 
> I read this about 10 times to learn that *maybe* you mean that IRQs
> stole time from the "Well behaved running time" of the RT class.

I also read emails many times :-)

I am
> not seeing how that is related to creation of a DL-server for the RT
> class. Maybe describe your point a bit more clearly?

This patch is targeting a better way to avoid SCHED_OTHER starvation.
Having a DL server for RT class does not help on that. We need to boost
SCHED_OTHER.

>>
>> Also, thinking about short deadlines to fake a fixed priority is... not starting
>> well. A fixed-priority higher instance is not a property of a deadline-based
>> scheduler, and Linux has a fixed-priority hierarchy: STOP -> DL -> RT -> CFS...
>> It is simple, and that is good.
>>
>> That is why it is better to boost CFS instead of throttling RT. By boosting
>> CFS, you do not need a server for RT, and we account for anything on top of CFS
>> for free (IRQ/DL/FIFO...).
> 
> I did mention in my last email that it is not ideal. I just brought it
> up as an option. It might reduce the problem being seen and is better
> than not having it.

We have thought about it, but boosting SCHED_OTHER is the way to go.

> 
>>> Another approach could be to implement the 0-laxity scheduling as a
>>> general SCHED_DEADLINE feature, perhaps through a flag. And allow DL
>>> tasks to opt-in to 0-laxity scheduling unless there are idle cycles.
>>> And then opt-in the feature for the CFS deadline server task.
>>
>> A 0-laxity scheduler is not as simple as it sounds, as the priority also depends
>> on the "C" (runtime, generally WCET), which is hard to find and embeds
>> pessimism. Also, having such a feature would make other mechanisms harder, as
>> well as debugging things. For example, proxy-execution or a more precise
>> schedulability test...
> 
> I think you did not read my email properly, I was saying make the
> 0-laxity default-off and the opt-in for certain DL tasks. That may
> work perfectly well for a system like ChromeOS where likely we will
> use the DL server as the sole deadline task and opt-in for the
> 0-laxity. Then we don't need watchdog hacks at all and it all cleanly
> works within the DL class itself. There are the drawbacks of the
> pessimism/locking  etc (I already knew that by the way as the obvious
> drawbacks of 0-laxity) but I am not immediately seeing how this
> CFS-watchdog with 0-laxity is any different from the DL-server itself
> having such a property. If you really have a concrete point on why
> that won't work, and if you could clarify that more clearly why a
> watchdog is better than it, that would be great.


I think you are overloading a term and a goal, and this makes your
thoughts ambiguous.

0-laxity is a point in time. What do you want to do at 0-laxity? Do you
want to run or start/replenish?

In the previous discussions, we mentioned using a scheduler that uses
it as a way to prioritize the task (to run). That is an overkill, as
it would be another scheduler. That is the first interpretation for
0-laxity in this thread, mainly associated with the word "scheduling"
(not only I read that way).

In this patch, Juri's PoC shows that if we defer the DL server start
(replenish) for a point in the future, we can keep the fixed-priority
order of the schedulers, boosting SCHED_OTHER if it starves,
without breaking EDF.

If you see the cover, I mentioned using the 0-laxity point in time to
activate the DL server under EDF. In that way, at the 0-laxity point,
the DL server is replenished with runtime and deadline as
"now" + period. With that implemented...

In the base case:
  it is never activated.

In the Busy-loop FIFO case:
 Busy-loop FIFO task run starving OTHER for (period - runtime):
   SCHED_OTHER server will be started at 0-laxity and get the
   processor for its runtime immediately because there are no DL
   tasks.

In the presence of DL & RT tasks:
 DL and RT Starving OTHER for (period - runtime):
   SCHED_OTHER server will be started & scheduled under EDF, before or
   after the other DL tasks, following EDF. Anyways, before
   returning to the SCHED_RT.

So, in this way, the OTHER will be boosted over SCHED_RT without breaking
SCHED_DEADLINE tasks.

In an 0-laxity scheduler, the server would run at 0-laxity, jumping in
front of DL tasks... that would break EDF. It would be mixing two
schedulers in one. It is not required and likely not a good idea either.

In the cover, I mentioned improving this patch, so maybe watchdog is not
the appropriate name. 0-laxity server is not a good name either because
it might induce people to think that the server will RUN at 0-laxity
while it will actually be replenished at 0-laxity. Maybe a deferred server
might be a better name.

>> In a paper, the scheduler alone is the solution. In real life, the solution
>> for problems like locking is as fundamental as the scheduler. We need to keep
>> things simple to expand on these other topics as well.
>>
>> So, I do not think we need all the drawbacks of a mixed solution to just fix
>> the throttling problem, and EDF is more capable and explored for the
>> general case.
> 
> Again, I was saying making it opt-in seems like a reasonable approach
> and just enabling such property for the DL server.

Can we have a "deferred DL server?" is that your question?

If so, I think so. But we have other points to look first. DL servers are per-cpu,
so they break global. We need to think about an interface... and there are
other points that we need to understand before trying some other more
optimized cases.

>> With this patch's idea (and expansions), we can fix the throttling problem
>> without breaking other behaviors like scheduling order...
> 
> I don't mind the watchdog patch as such, of course. I presented its
> mechanics at OSSNA and I know how it works, but I feel the DL server
> opting-in for 0-laxity would be cleaner while keeping such behavior as
> default-off for regular DL uses, that's my opinion -- but what else am
> I missing?  Either way, no harm in discussing alternate approaches as
> well even if we are settling for the watchdog.
> 
>>> Lastly, if the goal is to remove RT throttling code eventually, are
>>> you also planning to remove RT group scheduling as well? Are there
>>> users of RT group scheduling that might be impacted? On the other
>>> hand, RT throttling / group scheduling code can be left as it is
>>> (perhaps documenting it as deprecated) and the server stuff can be
>>> implemented via a CONFIG option.
>>
>> I think that the idea is to have the DL servers eventually replace the group
>> schedule. But I also believe that it is better to start by solving the
>> throttling and then moving to other constructions on top of the mechanism.
> 
> Hmm. For throttling at the root level yes, but  I am not seeing how
> you can replace the group scheduling code for existing users of RT
> Cgroups with this. The throttling in the RT group scheduling code is
> not exactly only about "not starving CFS", it is more related to
> letting RT groups run with certain bandwidth. So you cannot really
> delete it if there are real users of that code -- you'll have to
> migrate those users away first (to an alternate implementation like
> DL).  If there are no users of RT group scheduling, that's lovely
> though. We don't use it in ChromeOS fwiw.

The idea behind the base patchset from Peter is solid and is the best way we
can start, and starting with avoiding OTHER starvation is an easy starting point.
Many people will benefit from it - like all the people that ping me
because of the RT_RUNTIME_GREED (including Google in the past)... which is
the starting point of all this work.

Generalizing it requires time, but it will happen... and you know that Juri and I
care about Chromeos' use case, as I have been discussing this with you all and
even participating in Google/chrome focused meetings about sched...
at 6 pm our time ;-).

-- Daniel

> 
> - Joel
>
  
Joel Fernandes June 13, 2023, 3:32 p.m. UTC | #5
On Tue, Jun 13, 2023 at 9:43 AM Daniel Bristot de Oliveira
<bristot@kernel.org> wrote:
[...]
> > On Mon, Jun 12, 2023 at 1:21 PM Daniel Bristot de Oliveira
> > <bristot@kernel.org> wrote:
> > [...]
> >>> On Thu, Jun 8, 2023 at 11:58 AM Daniel Bristot de Oliveira
> >>> <bristot@kernel.org> wrote:
> >>>>
> >>>> From: Juri Lelli <juri.lelli@redhat.com>
> >>>>
> >>>> Starting deadline server for lower priority classes right away when
> >>>> first task is enqueued might break guarantees, as tasks belonging to
> >>>> intermediate priority classes could be uselessly preempted. E.g., a well
> >>>> behaving (non hog) FIFO task can be preempted by NORMAL tasks even if
> >>>> there are still CPU cycles available for NORMAL tasks to run, as they'll
> >>>> be running inside the fair deadline server for some period of time.
> >>>>
> >>>> To prevent this issue, implement a starvation monitor mechanism that
> >>>> starts the deadline server only if a (fair in this case) task hasn't
> >>>> been scheduled for some interval of time after it has been enqueued.
> >>>> Use pick/put functions to manage starvation monitor status.
> >>>
> >>> Me and Vineeth were discussing that another way of resolving this
> >>> issue is to use a DL-server for RT as well, and then using a smaller
> >>> deadline  for RT. That way the RT is more likely to be selected due to
> >>> its earlier deadline/period.
> >>
> >> It would not be that different from what we have now.
> >>
> >> One of the problems of throttling nowadays is that it accounts for a large window
> >> of time, and any "imprecision" can cause the mechanism not to work as expected.
> >>
> >> For example, we work on a fully-isolated CPU scenario, where some very sporadic
> >> workload can be placed on the isolated CPU because of per-cpu kernel activities,
> >> e.g., kworkers... We need to let them run, but for a minimal amount of time, for
> >> instance, 20 us, to bound the interference.
> >>
> >> The current mechanism does not give this precision because the IRQ accounting
> >> does not account for runtime for the rt throttling (which makes sense).
> >
> > I lost you here, "Runtime for the rt throttling" does not make much
> > sense to me as a statement.
>
> Both update_curr_rt() and update_curr_dl() use rq_clock_task() as clock. update_rq_clock_task()
> reduces the irq_delta from task's clock (inside #ifdef CONFIG_IRQ_TIME_ACCOUNTING), and this
> clock is used to throttling.

That was a much better description. You're basically saying that since
the running time of the RT class is not accounted for in the clock, it
affects the throttling and unthrottling times. I actually ran into a
similar issue on Android I recall, where the RT time was showing up as
CFS load if I recall.

For RT throttling though, in our testing the time scales are large
enough (for our usecase) that such time stealing wasn't an issue. I am
going for something that is practical and that works, does not have to
be perfect since it has been several years now with these problems and
leaving RT throttling broken is probably not a good thing.

> >> So the
> >> RT throttling has the 20 us stolen from IRQs and keeps running. The same will
> >> happen if we swap the current mechanism with a DL server for the RT.
> >
> > I read this about 10 times to learn that *maybe* you mean that IRQs
> > stole time from the "Well behaved running time" of the RT class.
>
> I also read emails many times :-)

Not a problem, ;-) Hopefully my emails are not too painful to read too
many times, but I appreciate that ;-). I was just mentioning that if
you're (or I am) not precise then it's hard to follow what you mean
that's all. Really the person writing the best emails I've seen is
Paul McKenney and that's kind of what I strive for. YMMV.

> > not seeing how that is related to creation of a DL-server for the RT
> > class. Maybe describe your point a bit more clearly?
>
> This patch is targeting a better way to avoid SCHED_OTHER starvation.
> Having a DL server for RT class does not help on that. We need to boost
> SCHED_OTHER.

Oh, actually the problem of boosting SCHED_OTHER is a bit orthogonal
to what I said. I was not saying not to boost SCHED_OTHER, I was
talking more about this particular patch and using an DL-based RT
server to mitigate that issue. The boosting is already handled in
previous patches with the DL-server.

> >> Also, thinking about short deadlines to fake a fixed priority is... not starting
> >> well. A fixed-priority higher instance is not a property of a deadline-based
> >> scheduler, and Linux has a fixed-priority hierarchy: STOP -> DL -> RT -> CFS...
> >> It is simple, and that is good.
> >>
> >> That is why it is better to boost CFS instead of throttling RT. By boosting
> >> CFS, you do not need a server for RT, and we account for anything on top of CFS
> >> for free (IRQ/DL/FIFO...).
> >
> > I did mention in my last email that it is not ideal. I just brought it
> > up as an option. It might reduce the problem being seen and is better
> > than not having it.
>
> We have thought about it, but boosting SCHED_OTHER is the way to go.

As mentioned above, there is no argument from my side on boosting CFS.
That I agree is the goal.

> >>> Another approach could be to implement the 0-laxity scheduling as a
> >>> general SCHED_DEADLINE feature, perhaps through a flag. And allow DL
> >>> tasks to opt-in to 0-laxity scheduling unless there are idle cycles.
> >>> And then opt-in the feature for the CFS deadline server task.
> >>
> >> A 0-laxity scheduler is not as simple as it sounds, as the priority also depends
> >> on the "C" (runtime, generally WCET), which is hard to find and embeds
> >> pessimism. Also, having such a feature would make other mechanisms harder, as
> >> well as debugging things. For example, proxy-execution or a more precise
> >> schedulability test...
> >
> > I think you did not read my email properly, I was saying make the
> > 0-laxity default-off and the opt-in for certain DL tasks. That may
> > work perfectly well for a system like ChromeOS where likely we will
> > use the DL server as the sole deadline task and opt-in for the
> > 0-laxity. Then we don't need watchdog hacks at all and it all cleanly
> > works within the DL class itself. There are the drawbacks of the
> > pessimism/locking  etc (I already knew that by the way as the obvious
> > drawbacks of 0-laxity) but I am not immediately seeing how this
> > CFS-watchdog with 0-laxity is any different from the DL-server itself
> > having such a property. If you really have a concrete point on why
> > that won't work, and if you could clarify that more clearly why a
> > watchdog is better than it, that would be great.
>
>
> I think you are overloading a term and a goal, and this makes your
> thoughts ambiguous.

Well the term was mentioned in this series cover letter as well. ;-)

> 0-laxity is a point in time. What do you want to do at 0-laxity? Do you
> want to run or start/replenish?

You could do either. It could be run a bit earlier than 0-laxity. Or
could you just push it out to run in the next period if it has the
flag.

Here is the definition of 0-laxity as I understand it. Please correct
me as I have not done a phD in these things like you ;-)

Laxity, also known as slack time, is the amount of time that a task
can be delayed without causing a missed deadline. A 0-laxity task is
one that has no more time to spare and must be executed immediately to
avoid missing its deadline.

And here's where I need your input: If we take all admitted DL tasks
and run it at their respective 0-laxity times or slightly earlier,
then in-theory, they should all meet their deadlines correctly?

Now, I don't really mean to run it exactly at 0-laxity. It could be
run a bit earlier to factor in sleep times, locking time, preemptions
etc. I mean with SCHED_DEADLINE, you really can't control those things
anyway -- so even if  you run the DL task as soon as possible, you
might still miss your deadline. Or at 0-laxity, push its deadline out
to the next period and consider it "activated". I am just thinking out
loud.

That could break EDF the way it is now. However, it could be an
interesting idea that could be developed into a better idea.  A DL
task does not have to run immediately to meet its deadline (it could
be run later as well) and that I know for a fact -- so why not add
this flexibility within SCHED_DEADLINE itself rather than inventing a
hack (and by hack I mean only this patch, not the other patches from
Peter or the idea of CFS boosting).

My impression is the other (DL tasks without flag) should still have
their promised bandwidth so it is not mixing 2 schedulers.  If this
series gets stalled for some reason, I would probably explore such an
idea in my own time later.

> In the previous discussions, we mentioned using a scheduler that uses
> it as a way to prioritize the task (to run). That is an overkill, as
> it would be another scheduler. That is the first interpretation for
> 0-laxity in this thread, mainly associated with the word "scheduling"
> (not only I read that way).
>
> In this patch, Juri's PoC shows that if we defer the DL server start
> (replenish) for a point in the future, we can keep the fixed-priority
> order of the schedulers, boosting SCHED_OTHER if it starves,
> without breaking EDF.
>
> If you see the cover, I mentioned using the 0-laxity point in time to
> activate the DL server under EDF. In that way, at the 0-laxity point,
> the DL server is replenished with runtime and deadline as
> "now" + period. With that implemented...
>
> In the base case:
>   it is never activated.
>
> In the Busy-loop FIFO case:
>  Busy-loop FIFO task run starving OTHER for (period - runtime):
>    SCHED_OTHER server will be started at 0-laxity and get the
>    processor for its runtime immediately because there are no DL
>    tasks.
>
> In the presence of DL & RT tasks:
>  DL and RT Starving OTHER for (period - runtime):
>    SCHED_OTHER server will be started & scheduled under EDF, before or
>    after the other DL tasks, following EDF. Anyways, before
>    returning to the SCHED_RT.
>
> So, in this way, the OTHER will be boosted over SCHED_RT without breaking
> SCHED_DEADLINE tasks.
>
> In an 0-laxity scheduler, the server would run at 0-laxity, jumping in
> front of DL tasks... that would break EDF. It would be mixing two
> schedulers in one. It is not required and likely not a good idea either.

I am still missing why some tasks cannot be run at close to 0-laxity
time, and as opt-in. And if the DL-server is the sole task running,
then there's nothing else to break.

In fact, I am not immediately seeing how this can break SCHED_DEADLINE
if you allow at most 1-task to run at close to 0-laxity. The others
should still have their promised bandwidth so it is not mixing 2
schedulers, you just delay the DL-server till it's close to the
0-laxity. What am I missing?

Perhaps you mean the algorithm needs to push the new period/deadline
to a later time at the 0-laxity. That's also fine with me. But some
variation of the idea is possible AFAICS (again could be missing
something that mathematically makes this impossible).

> In the cover, I mentioned improving this patch, so maybe watchdog is not
> the appropriate name. 0-laxity server is not a good name either because
> it might induce people to think that the server will RUN at 0-laxity
> while it will actually be replenished at 0-laxity. Maybe a deferred server
> might be a better name.

Yeah maybe a deferred server could be a better name.

> >> In a paper, the scheduler alone is the solution. In real life, the solution
> >> for problems like locking is as fundamental as the scheduler. We need to keep
> >> things simple to expand on these other topics as well.
> >>
> >> So, I do not think we need all the drawbacks of a mixed solution to just fix
> >> the throttling problem, and EDF is more capable and explored for the
> >> general case.
> >
> > Again, I was saying making it opt-in seems like a reasonable approach
> > and just enabling such property for the DL server.
>
> Can we have a "deferred DL server?" is that your question?
>
> If so, I think so. But we have other points to look first. DL servers are per-cpu,
> so they break global. We need to think about an interface... and there are
> other points that we need to understand before trying some other more
> optimized cases.

You mean an interface for the DL server params? Those can be similar
to other sched knobs. And then boot with a CONFIG option and boost CFS
things by default if RT is active. Would that work or did you mean
something else by interface?

> >>> Lastly, if the goal is to remove RT throttling code eventually, are
> >>> you also planning to remove RT group scheduling as well? Are there
> >>> users of RT group scheduling that might be impacted? On the other
> >>> hand, RT throttling / group scheduling code can be left as it is
> >>> (perhaps documenting it as deprecated) and the server stuff can be
> >>> implemented via a CONFIG option.
> >>
> >> I think that the idea is to have the DL servers eventually replace the group
> >> schedule. But I also believe that it is better to start by solving the
> >> throttling and then moving to other constructions on top of the mechanism.
> >
> > Hmm. For throttling at the root level yes, but  I am not seeing how
> > you can replace the group scheduling code for existing users of RT
> > Cgroups with this. The throttling in the RT group scheduling code is
> > not exactly only about "not starving CFS", it is more related to
> > letting RT groups run with certain bandwidth. So you cannot really
> > delete it if there are real users of that code -- you'll have to
> > migrate those users away first (to an alternate implementation like
> > DL).  If there are no users of RT group scheduling, that's lovely
> > though. We don't use it in ChromeOS fwiw.
>
> The idea behind the base patchset from Peter is solid and is the best way we
> can start, and starting with avoiding OTHER starvation is an easy starting point.
> Many people will benefit from it - like all the people that ping me
> because of the RT_RUNTIME_GREED (including Google in the past)... which is
> the starting point of all this work.

Right, you know I am on the same page about that. I presented exactly
the same stuff at 2 conferences in 2 countries this year.

>
> Generalizing it requires time, but it will happen... and you know that Juri and I
> care about Chromeos' use case, as I have been discussing this with you all and
> even participating in Google/chrome focused meetings about sched...
> at 6 pm our time ;-).

I totally appreciate that, please don't get offended, we go a long way
back as friends ;-) And I really want to help, I am not trying to
prove I am an expert compared to you. I just want to get it *done* and
not have to wait for more years. You can see my 2 presentations this
year on this topic alone -- I travelled to 2 countries leaving my
family behind to discuss these.

Many thanks,

 - Joel
  
Daniel Bristot de Oliveira June 14, 2023, 1:45 p.m. UTC | #6
On 6/13/23 17:32, Joel Fernandes wrote:
> On Tue, Jun 13, 2023 at 9:43 AM Daniel Bristot de Oliveira
> <bristot@kernel.org> wrote:
> [...]
>>> On Mon, Jun 12, 2023 at 1:21 PM Daniel Bristot de Oliveira
>>> <bristot@kernel.org> wrote:
>>> [...]
>>>>> On Thu, Jun 8, 2023 at 11:58 AM Daniel Bristot de Oliveira
>>>>> <bristot@kernel.org> wrote:
>>>>>>
>>>>>> From: Juri Lelli <juri.lelli@redhat.com>
>>>>>>
>>>>>> Starting deadline server for lower priority classes right away when
>>>>>> first task is enqueued might break guarantees, as tasks belonging to
>>>>>> intermediate priority classes could be uselessly preempted. E.g., a well
>>>>>> behaving (non hog) FIFO task can be preempted by NORMAL tasks even if
>>>>>> there are still CPU cycles available for NORMAL tasks to run, as they'll
>>>>>> be running inside the fair deadline server for some period of time.
>>>>>>
>>>>>> To prevent this issue, implement a starvation monitor mechanism that
>>>>>> starts the deadline server only if a (fair in this case) task hasn't
>>>>>> been scheduled for some interval of time after it has been enqueued.
>>>>>> Use pick/put functions to manage starvation monitor status.
>>>>>
>>>>> Me and Vineeth were discussing that another way of resolving this
>>>>> issue is to use a DL-server for RT as well, and then using a smaller
>>>>> deadline  for RT. That way the RT is more likely to be selected due to
>>>>> its earlier deadline/period.
>>>>
>>>> It would not be that different from what we have now.
>>>>
>>>> One of the problems of throttling nowadays is that it accounts for a large window
>>>> of time, and any "imprecision" can cause the mechanism not to work as expected.
>>>>
>>>> For example, we work on a fully-isolated CPU scenario, where some very sporadic
>>>> workload can be placed on the isolated CPU because of per-cpu kernel activities,
>>>> e.g., kworkers... We need to let them run, but for a minimal amount of time, for
>>>> instance, 20 us, to bound the interference.
>>>>
>>>> The current mechanism does not give this precision because the IRQ accounting
>>>> does not account for runtime for the rt throttling (which makes sense).
>>>
>>> I lost you here, "Runtime for the rt throttling" does not make much
>>> sense to me as a statement.
>>
>> Both update_curr_rt() and update_curr_dl() use rq_clock_task() as clock. update_rq_clock_task()
>> reduces the irq_delta from task's clock (inside #ifdef CONFIG_IRQ_TIME_ACCOUNTING), and this
>> clock is used to throttling.
> 
> That was a much better description. You're basically saying that since
> the running time of the RT class is not accounted for in the clock, it
> affects the throttling and unthrottling times. I actually ran into a
> similar issue on Android I recall, where the RT time was showing up as
> CFS load if I recall.
> 
> For RT throttling though, in our testing the time scales are large
> enough (for our usecase) that such time stealing wasn't an issue. I am
> going for something that is practical and that works, does not have to
> be perfect since it has been several years now with these problems and
> leaving RT throttling broken is probably not a good thing.

By postponing the enqueue/replanishment of the DL server here, we are fixing the
problem in a practical way, that works without breaking existing useful properties &
use-cases.

[...]
>>> not seeing how that is related to creation of a DL-server for the RT
>>> class. Maybe describe your point a bit more clearly?
>>
>> This patch is targeting a better way to avoid SCHED_OTHER starvation.
>> Having a DL server for RT class does not help on that. We need to boost
>> SCHED_OTHER.
> 
> Oh, actually the problem of boosting SCHED_OTHER is a bit orthogonal
> to what I said. I was not saying not to boost SCHED_OTHER, I was
> talking more about this particular patch and using an DL-based RT
> server to mitigate that issue. The boosting is already handled in
> previous patches with the DL-server.

The boosting of the previous patch is breaking FIFO priority. This patch fixes that
single point without touching and or breaking SCHED_DEADLINE/EDF properties. With
these things in place we do not mitigate, we fix the problem.

[...]
>>>>> Another approach could be to implement the 0-laxity scheduling as a
>>>>> general SCHED_DEADLINE feature, perhaps through a flag. And allow DL
>>>>> tasks to opt-in to 0-laxity scheduling unless there are idle cycles.
>>>>> And then opt-in the feature for the CFS deadline server task.
>>>>
>>>> A 0-laxity scheduler is not as simple as it sounds, as the priority also depends
>>>> on the "C" (runtime, generally WCET), which is hard to find and embeds
>>>> pessimism. Also, having such a feature would make other mechanisms harder, as
>>>> well as debugging things. For example, proxy-execution or a more precise
>>>> schedulability test...
>>>
>>> I think you did not read my email properly, I was saying make the
>>> 0-laxity default-off and the opt-in for certain DL tasks. That may
>>> work perfectly well for a system like ChromeOS where likely we will
>>> use the DL server as the sole deadline task and opt-in for the
>>> 0-laxity. Then we don't need watchdog hacks at all and it all cleanly
>>> works within the DL class itself. There are the drawbacks of the
>>> pessimism/locking  etc (I already knew that by the way as the obvious
>>> drawbacks of 0-laxity) but I am not immediately seeing how this
>>> CFS-watchdog with 0-laxity is any different from the DL-server itself
>>> having such a property. If you really have a concrete point on why
>>> that won't work, and if you could clarify that more clearly why a
>>> watchdog is better than it, that would be great.
>>
>>
>> I think you are overloading a term and a goal, and this makes your
>> thoughts ambiguous.
> 
> Well the term was mentioned in this series cover letter as well. ;-)

This term was mentioned also on my slides, and also on the other threads, but
not alone...

> 
>> 0-laxity is a point in time. What do you want to do at 0-laxity? Do you
>> want to run or start/replenish?
> 
> You could do either.

No, these are two different things depending on the current ready queue status.

It could be run a bit earlier than 0-laxity. Or
> could you just push it out to run in the next period if it has the
> flag.

The improvements on top of this patch is to postpone the enqueue/replenish to the 0-laxity
time. By doing so, the task receives a new period (and so deadline) a period ahead.

> 
> Here is the definition of 0-laxity as I understand it. Please correct
> me as I have not done a phD in these things like you ;-)
> 
> Laxity, also known as slack time,

laxity = absolute deadline - activation time - runtime.

is the amount of time that a task
> can be delayed without causing a missed deadline.

If you look at the task alone! e.g, if it does not face preemption!

A 0-laxity task is
> one that has no more time to spare and must be executed immediately

and not be preempted!

to
> avoid missing its deadline.

Again, you are looking at the task alone.

> And here's where I need your input: If we take all admitted DL tasks
> and run it at their respective 0-laxity times or slightly earlier,
> then in-theory, they should all meet their deadlines correctly?
For all tasksets, no!

There might be a taskset here o there that creates such timeline under EDF,
but it is not always true that a task under EDF will wait until the 0-laxity
time for them to run. EDF is working conserving.

For a working conserving scheduler to build such a timeline, it needs to
have no idle time. Then, lets get the classical single core assumptions
(these servers are per-cpu).

- Assuming single-core/partitioned scheduler.
- Assuming periodic tasks with deadline = period
- Assuming a task set with the sum of each task utilization = 1
- Assuming all tasks are dispatched at the same time (critical instant)
- Assuming all tasks will run for their entire runtime, without blocking

(so... the thing that EDF is optimum... fully loaded...)

Even so you will not have EDF always creating such timeline because the
task with the earliest deadline will run first, still deadlines will be met.

For example:

t1 = 5/10
t2 = 5/10

Each task you pick first will run 5 unities of time before the "0-laxity time".

If there is a scheduler that always build a timeline like you want, it will not
schedule the taskset I mentioned... thus.. it will schedule less than EDF.

> 
> Now, I don't really mean to run it exactly at 0-laxity. It could be
> run a bit earlier to factor in sleep times, locking time, preemptions
> etc.

For you to force the delay... you would need to do things like... injecting idle?

Measuring these values is worst than measuring the runtime... you are pilling
up complexity.

I mean with SCHED_DEADLINE, you really can't control those things
> anyway -- so even if  you run the DL task as soon as possible, you
> might still miss your deadline.

If the taskset is schedulable... EDF will. The overheads are part of the problem
for any real implementation, and moving to more complex scheduling design will
only make those things worse.

Or at 0-laxity, push its deadline out
> to the next period and consider it "activated". I am just thinking out
> loud.
> 
> That could break EDF the way it is now. However, it could be an
> interesting idea that could be developed into a better idea.  A DL
> task does not have to run immediately to meet its deadline (it could
> be run later as well)and that I know for a fact -- so why not add
> this flexibility within SCHED_DEADLINE itself rather than inventing a
> hack (and by hack I mean only this patch, not the other patches from
> Peter or the idea of CFS boosting).
This "hack" is not inside the deadline.c because it is a PoC... in the next version
it will not in fair.c anymore, and it will part of deadline. A deferred server start.

I think you are focusing here in the code, not in the idea. We said we will improve
the code.

> 
> My impression is the other (DL tasks without flag) should still have
> their promised bandwidth so it is not mixing 2 schedulers.If this
> series gets stalled for some reason, I would probably explore such an
> idea in my own time later.
> 
>> In the previous discussions, we mentioned using a scheduler that uses
>> it as a way to prioritize the task (to run). That is an overkill, as
>> it would be another scheduler. That is the first interpretation for
>> 0-laxity in this thread, mainly associated with the word "scheduling"
>> (not only I read that way).
>>
>> In this patch, Juri's PoC shows that if we defer the DL server start
>> (replenish) for a point in the future, we can keep the fixed-priority
>> order of the schedulers, boosting SCHED_OTHER if it starves,
>> without breaking EDF.
>>
>> If you see the cover, I mentioned using the 0-laxity point in time to
>> activate the DL server under EDF. In that way, at the 0-laxity point,
>> the DL server is replenished with runtime and deadline as
>> "now" + period. With that implemented...
>>
>> In the base case:
>>   it is never activated.
>>
>> In the Busy-loop FIFO case:
>>  Busy-loop FIFO task run starving OTHER for (period - runtime):
>>    SCHED_OTHER server will be started at 0-laxity and get the
>>    processor for its runtime immediately because there are no DL
>>    tasks.
>>
>> In the presence of DL & RT tasks:
>>  DL and RT Starving OTHER for (period - runtime):
>>    SCHED_OTHER server will be started & scheduled under EDF, before or
>>    after the other DL tasks, following EDF. Anyways, before
>>    returning to the SCHED_RT.
>>
>> So, in this way, the OTHER will be boosted over SCHED_RT without breaking
>> SCHED_DEADLINE tasks.
>>
>> In an 0-laxity scheduler, the server would run at 0-laxity, jumping in
>> front of DL tasks... that would break EDF. It would be mixing two
>> schedulers in one. It is not required and likely not a good idea either.
> 
> I am still missing why some tasks cannot be run at close to 0-laxity
> time, and as opt-in.And if the DL-server is the sole task running,
> then there's nothing else to break.

If it is the sole (DL!) task running, this patch is equivalent to place the task
to run at 0-laxity. I explained this the previous email.

> In fact, I am not immediately seeing how this can break SCHED_DEADLINE
> if you allow at most 1-task to run at close to 0-laxity. The others
> should still have their promised bandwidth so it is not mixing 2
> schedulers, you just delay the DL-server till it's close to the
> 0-laxity. What am I missing?
if you want to do a 0-laxity scheduler like decision, that is, the task will
start running at 0-laxity, and once the task starts to run it will not be
preempted so it can finish before its deadline, you will break other EDF
tasks deadline in a schedulable task set.

It is not hard to create a timeline that breaks it...

server 50/1000 postponed by 950.
task 1/10.

At time 950 the server starts not to be preempted for 50.
at 951 the 1/10 starts... BOOM.

Expanding the idea in this patch, the task will be enqueued at 950,
with a deadline at 1950... so it will not break the EDF scheduler,
while still getting the time on top of SCHED_RT.

The SCHED_RT is the problem we are addressing here, because of lack of
fairness w.r.t SCHED_OTHER.

Moreover, DL tasks run when RT rq is throttled. So, that 1/10 preemption
on top of the deferred server happens in the current behavior. Thinking twice,
with this patch in place, SCHED_OTHER will also recover that time, which makes
even more sense.

(noticing that here we are only scratching the consequences, as anything that
is utilization based on our design will be broken as the system starts because
this is always enabled).

> Perhaps you mean the algorithm needs to push the new period/deadline
> to a later time at the 0-laxity. 

This is the idea behind this patch ^^^^ This is the different between running
and replenishing I mention on previous emails.

That's also fine with me. But some
> variation of the idea is possible AFAICS (again could be missing
> something that mathematically makes this impossible).

you are looking for a fragment of the information... "0-laxity time," with
a single task in mind - not in the context of a scheduler.

> 
>> In the cover, I mentioned improving this patch, so maybe watchdog is not
>> the appropriate name. 0-laxity server is not a good name either because
>> it might induce people to think that the server will RUN at 0-laxity
>> while it will actually be replenished at 0-laxity. Maybe a deferred server
>> might be a better name.
> 
> Yeah maybe a deferred server could be a better name.
> 
>>>> In a paper, the scheduler alone is the solution. In real life, the solution
>>>> for problems like locking is as fundamental as the scheduler. We need to keep
>>>> things simple to expand on these other topics as well.
>>>>
>>>> So, I do not think we need all the drawbacks of a mixed solution to just fix
>>>> the throttling problem, and EDF is more capable and explored for the
>>>> general case.
>>>
>>> Again, I was saying making it opt-in seems like a reasonable approach
>>> and just enabling such property for the DL server.
>>
>> Can we have a "deferred DL server?" is that your question?
>>
>> If so, I think so. But we have other points to look first. DL servers are per-cpu,
>> so they break global. We need to think about an interface... and there are
>> other points that we need to understand before trying some other more
>> optimized cases.
> 
> You mean an interface for the DL server params? Those can be similar
> to other sched knobs. And then boot with a CONFIG option and boost CFS
> things by default if RT is active. Would that work or did you mean
> something else by interface?
> 
>>>>> Lastly, if the goal is to remove RT throttling code eventually, are
>>>>> you also planning to remove RT group scheduling as well? Are there
>>>>> users of RT group scheduling that might be impacted? On the other
>>>>> hand, RT throttling / group scheduling code can be left as it is
>>>>> (perhaps documenting it as deprecated) and the server stuff can be
>>>>> implemented via a CONFIG option.
>>>>
>>>> I think that the idea is to have the DL servers eventually replace the group
>>>> schedule. But I also believe that it is better to start by solving the
>>>> throttling and then moving to other constructions on top of the mechanism.
>>>
>>> Hmm. For throttling at the root level yes, but  I am not seeing how
>>> you can replace the group scheduling code for existing users of RT
>>> Cgroups with this. The throttling in the RT group scheduling code is
>>> not exactly only about "not starving CFS", it is more related to
>>> letting RT groups run with certain bandwidth. So you cannot really
>>> delete it if there are real users of that code -- you'll have to
>>> migrate those users away first (to an alternate implementation like
>>> DL).  If there are no users of RT group scheduling, that's lovely
>>> though. We don't use it in ChromeOS fwiw.
>>
>> The idea behind the base patchset from Peter is solid and is the best way we
>> can start, and starting with avoiding OTHER starvation is an easy starting point.
>> Many people will benefit from it - like all the people that ping me
>> because of the RT_RUNTIME_GREED (including Google in the past)... which is
>> the starting point of all this work.
> 
> Right, you know I am on the same page about that. I presented exactly
> the same stuff at 2 conferences in 2 countries this year.
> 
>>
>> Generalizing it requires time, but it will happen... and you know that Juri and I
>> care about Chromeos' use case, as I have been discussing this with you all and
>> even participating in Google/chrome focused meetings about sched...
>> at 6 pm our time ;-).
> 
> I totally appreciate that, please don't get offended, we go a long way
> back as friends ;-)

I did not get offended, and nothing changes on our friendship :-). I am just
clarifying you things we know - even before this rebase... We are aware of
Chrome needs, as well as general RT Linux needs.

The basic idea behind this patch works for all cases and is unlocking this
situation. The code will be improved in the next version.

Thanks
-- Daniel

And I really want to help, I am not trying to
> prove I am an expert compared to you. I just want to get it *done* and
> not have to wait for more years. You can see my 2 presentations this
> year on this topic alone -- I travelled to 2 countries leaving my
> family behind to discuss these.
> 
> Many thanks,
> 
>  - Joel
  
Juri Lelli June 14, 2023, 2:15 p.m. UTC | #7
Hey,

So Daniel provided the gory details :) .. but please let me highlight
one of his points below, which I think it might clarify why we might
want to start with a special case, patch 6 improved approach, before
possibly moving to more complex implementations.

On 14/06/23 15:45, Daniel Bristot de Oliveira wrote:

...

> By postponing the enqueue/replanishment of the DL server here, we are fixing the
> problem in a practical way, that works without breaking existing useful properties &
> use-cases.

In my understanding, if we simply postpone actual activation of the DL
server up to the point it really needs to boost/run for giving CFS tasks
some breath (the infamous 0-laxity :), we save RT tasks from useless
interruptions and still can keep EDF/CBS working w/o much changes.

It looks like a low hanging fruit, small improvement on what we have today
than doesn't prevent us for implementing more complex features (i.e., full
blown hierarchical scheduling, alternative schedulers) in the future if
the need arises.

Thanks!
Juri
  
Joel Fernandes June 14, 2023, 6:24 p.m. UTC | #8
Before I reply, I just want to mention: I am OK with this patch 6/6 as
such and the ideas I mentioned earlier are just alternatives just for
patch 6/6 -- just for discussion sake. The devil is in the details as
Daniel and Juri pointed out.

With that said...

On Wed, Jun 14, 2023 at 9:45 AM Daniel Bristot de Oliveira
<bristot@kernel.org> wrote:
>
> On 6/13/23 17:32, Joel Fernandes wrote:
> > On Tue, Jun 13, 2023 at 9:43 AM Daniel Bristot de Oliveira
> > <bristot@kernel.org> wrote:
> > [...]
> >>> On Mon, Jun 12, 2023 at 1:21 PM Daniel Bristot de Oliveira
> >>> <bristot@kernel.org> wrote:
> >>> [...]
> >>>>> On Thu, Jun 8, 2023 at 11:58 AM Daniel Bristot de Oliveira
> >>>>> <bristot@kernel.org> wrote:
> >>>>>>
> >>>>>> From: Juri Lelli <juri.lelli@redhat.com>
> >>>>>>
> >>>>>> Starting deadline server for lower priority classes right away when
> >>>>>> first task is enqueued might break guarantees, as tasks belonging to
> >>>>>> intermediate priority classes could be uselessly preempted. E.g., a well
> >>>>>> behaving (non hog) FIFO task can be preempted by NORMAL tasks even if
> >>>>>> there are still CPU cycles available for NORMAL tasks to run, as they'll
> >>>>>> be running inside the fair deadline server for some period of time.
> >>>>>>
> >>>>>> To prevent this issue, implement a starvation monitor mechanism that
> >>>>>> starts the deadline server only if a (fair in this case) task hasn't
> >>>>>> been scheduled for some interval of time after it has been enqueued.
> >>>>>> Use pick/put functions to manage starvation monitor status.
> >>>>>
> >>>>> Me and Vineeth were discussing that another way of resolving this
> >>>>> issue is to use a DL-server for RT as well, and then using a smaller
> >>>>> deadline  for RT. That way the RT is more likely to be selected due to
> >>>>> its earlier deadline/period.
> >>>>
> >>>> It would not be that different from what we have now.
> >>>>
> >>>> One of the problems of throttling nowadays is that it accounts for a large window
> >>>> of time, and any "imprecision" can cause the mechanism not to work as expected.
> >>>>
> >>>> For example, we work on a fully-isolated CPU scenario, where some very sporadic
> >>>> workload can be placed on the isolated CPU because of per-cpu kernel activities,
> >>>> e.g., kworkers... We need to let them run, but for a minimal amount of time, for
> >>>> instance, 20 us, to bound the interference.
> >>>>
> >>>> The current mechanism does not give this precision because the IRQ accounting
> >>>> does not account for runtime for the rt throttling (which makes sense).
> >>>
> >>> I lost you here, "Runtime for the rt throttling" does not make much
> >>> sense to me as a statement.
> >>
> >> Both update_curr_rt() and update_curr_dl() use rq_clock_task() as clock. update_rq_clock_task()
> >> reduces the irq_delta from task's clock (inside #ifdef CONFIG_IRQ_TIME_ACCOUNTING), and this
> >> clock is used to throttling.
> >
> > That was a much better description. You're basically saying that since
> > the running time of the RT class is not accounted for in the clock, it
> > affects the throttling and unthrottling times. I actually ran into a
> > similar issue on Android I recall, where the RT time was showing up as
> > CFS load if I recall.
> >
> > For RT throttling though, in our testing the time scales are large
> > enough (for our usecase) that such time stealing wasn't an issue. I am
> > going for something that is practical and that works, does not have to
> > be perfect since it has been several years now with these problems and
> > leaving RT throttling broken is probably not a good thing.
>
> By postponing the enqueue/replanishment of the DL server here, we are fixing the
> problem in a practical way, that works without breaking existing useful properties &
> use-cases.

Ok, that sounds good to me.

> [...]
> >>> not seeing how that is related to creation of a DL-server for the RT
> >>> class. Maybe describe your point a bit more clearly?
> >>
> >> This patch is targeting a better way to avoid SCHED_OTHER starvation.
> >> Having a DL server for RT class does not help on that. We need to boost
> >> SCHED_OTHER.
> >
> > Oh, actually the problem of boosting SCHED_OTHER is a bit orthogonal
> > to what I said. I was not saying not to boost SCHED_OTHER, I was
> > talking more about this particular patch and using an DL-based RT
> > server to mitigate that issue. The boosting is already handled in
> > previous patches with the DL-server.
>
> The boosting of the previous patch is breaking FIFO priority. This patch fixes that
> single point without touching and or breaking SCHED_DEADLINE/EDF properties. With
> these things in place we do not mitigate, we fix the problem.

Sure, that's fine with me.

[..]
> > could you just push it out to run in the next period if it has the
> > flag.
>
> The improvements on top of this patch is to postpone the enqueue/replenish to the 0-laxity
> time. By doing so, the task receives a new period (and so deadline) a period ahead.

Yes, I understand. I was hoping for us to do that from within the DL
class itself as a DL task property, but perhaps that's my wishful
thinking...

> >
> > Here is the definition of 0-laxity as I understand it. Please correct
> > me as I have not done a phD in these things like you ;-)
> >
> > Laxity, also known as slack time,
>
> laxity = absolute deadline - activation time - runtime.

Correct.

> is the amount of time that a task
> > can be delayed without causing a missed deadline.
>
> If you look at the task alone! e.g, if it does not face preemption!
> A 0-laxity task is
> > one that has no more time to spare and must be executed immediately
>
> and not be preempted!
>
> to
> > avoid missing its deadline.
>
> Again, you are looking at the task alone.

Sure, that's why I mentioned running it before 0-laxity itself to
account for that and not just preemption but also other issues as well
like I/O, locking, sleeping etc. I don't have a formula right now and
I need to think more about it, but that was the idea at a high-level.
Again it is all rainbows and ponies and the devil is in the details so
just consider it as an idea/suggestion and not something we must
urgently do. I may find time later to go over the papers such as those
related to the laxity-based scheduling.

> > And here's where I need your input: If we take all admitted DL tasks
> > and run it at their respective 0-laxity times or slightly earlier,
> > then in-theory, they should all meet their deadlines correctly?
> For all tasksets, no!
>
> There might be a taskset here o there that creates such timeline under EDF,
> but it is not always true that a task under EDF will wait until the 0-laxity
> time for them to run. EDF is working conserving.
>
> For a working conserving scheduler to build such a timeline, it needs to
> have no idle time. Then, lets get the classical single core assumptions
> (these servers are per-cpu).
>
> - Assuming single-core/partitioned scheduler.
> - Assuming periodic tasks with deadline = period
> - Assuming a task set with the sum of each task utilization = 1
> - Assuming all tasks are dispatched at the same time (critical instant)
> - Assuming all tasks will run for their entire runtime, without blocking
>
> (so... the thing that EDF is optimum... fully loaded...)
>
> Even so you will not have EDF always creating such timeline because the
> task with the earliest deadline will run first, still deadlines will be met.
>
> For example:
>
> t1 = 5/10
> t2 = 5/10
>
> Each task you pick first will run 5 unities of time before the "0-laxity time".
>
> If there is a scheduler that always build a timeline like you want, it will not
> schedule the taskset I mentioned... thus.. it will schedule less than EDF.

Yes, I think you are kind of saying the same thing that I mentioned,
which is to run it before the 0-laxity time (perhaps depending on the
runtime of the existing admitted tasks).

> > Perhaps you mean the algorithm needs to push the new period/deadline
> > to a later time at the 0-laxity.
>
> This is the idea behind this patch ^^^^ This is the different between running
> and replenishing I mention on previous emails.
>
> That's also fine with me. But some
> > variation of the idea is possible AFAICS (again could be missing
> > something that mathematically makes this impossible).
>
> you are looking for a fragment of the information... "0-laxity time," with
> a single task in mind - not in the context of a scheduler.

I should have probably not used the word 0-laxity, because I did
mention running *before* 0-laxity arrives to account for delays,
that's what I was saying. So like run it before you get to 0 laxity to
account for delays, like that. But hey it is just an idea and sorry if
it sounded like noise. It goes back to the premise I mentioned which
is, DL task do not need to run immediately and preempt RT, it can run
later and still meet the deadline. When to run it is a different
question but if there was a crystal ball, DL task can still meet its
deadline by running at a later time.

> >> In the cover, I mentioned improving this patch, so maybe watchdog is not
> >> the appropriate name. 0-laxity server is not a good name either because
> >> it might induce people to think that the server will RUN at 0-laxity
> >> while it will actually be replenished at 0-laxity. Maybe a deferred server
> >> might be a better name.
> >
> > Yeah maybe a deferred server could be a better name.
> >
> >>>> In a paper, the scheduler alone is the solution. In real life, the solution
> >>>> for problems like locking is as fundamental as the scheduler. We need to keep
> >>>> things simple to expand on these other topics as well.
> >>>>
> >>>> So, I do not think we need all the drawbacks of a mixed solution to just fix
> >>>> the throttling problem, and EDF is more capable and explored for the
> >>>> general case.
> >>>
> >>> Again, I was saying making it opt-in seems like a reasonable approach
> >>> and just enabling such property for the DL server.
> >>
> >> Can we have a "deferred DL server?" is that your question?
> >>
> >> If so, I think so. But we have other points to look first. DL servers are per-cpu,
> >> so they break global. We need to think about an interface... and there are
> >> other points that we need to understand before trying some other more
> >> optimized cases.
> >
> > You mean an interface for the DL server params? Those can be similar
> > to other sched knobs. And then boot with a CONFIG option and boost CFS
> > things by default if RT is active. Would that work or did you mean
> > something else by interface?

About the interface, perhaps you are referring to using this mechanism
to replace the stalld daemon? That's what I remember from our
conversations in OSPM. In general, I think it is a great idea to
automatically detect "important" starving CFS tasks and boost them
(whether they are starving because of RT, or some other reason).

> >> Generalizing it requires time, but it will happen... and you know that Juri and I
> >> care about Chromeos' use case, as I have been discussing this with you all and
> >> even participating in Google/chrome focused meetings about sched...
> >> at 6 pm our time ;-).
> >
> > I totally appreciate that, please don't get offended, we go a long way
> > back as friends ;-)
>
> I did not get offended, and nothing changes on our friendship :-). I am just
> clarifying you things we know - even before this rebase... We are aware of
> Chrome needs, as well as general RT Linux needs.
>
> The basic idea behind this patch works for all cases and is unlocking this
> situation. The code will be improved in the next version.

Thanks for the discussions! I am looking forward to helping in any way
I can on the series, I am going to be testing it for ChromeOS.

 - Joel
  
Joel Fernandes June 14, 2023, 6:27 p.m. UTC | #9
On Wed, Jun 14, 2023 at 10:15 AM Juri Lelli <juri.lelli@redhat.com> wrote:
>
> Hey,
[..]
> > By postponing the enqueue/replanishment of the DL server here, we are fixing the
> > problem in a practical way, that works without breaking existing useful properties &
> > use-cases.
>
> In my understanding, if we simply postpone actual activation of the DL
> server up to the point it really needs to boost/run for giving CFS tasks
> some breath (the infamous 0-laxity :), we save RT tasks from useless
> interruptions and still can keep EDF/CBS working w/o much changes.
>
> It looks like a low hanging fruit, small improvement on what we have today
> than doesn't prevent us for implementing more complex features (i.e., full
> blown hierarchical scheduling, alternative schedulers) in the future if
> the need arises.

Agreed, thanks!

 - Joel
  
Peter Zijlstra June 16, 2023, 11:51 a.m. UTC | #10
On Thu, Jun 08, 2023 at 05:58:18PM +0200, Daniel Bristot de Oliveira wrote:
> From: Juri Lelli <juri.lelli@redhat.com>
> 
> Starting deadline server for lower priority classes right away when
> first task is enqueued might break guarantees, as tasks belonging to
> intermediate priority classes could be uselessly preempted. E.g., a well
> behaving (non hog) FIFO task can be preempted by NORMAL tasks even if
> there are still CPU cycles available for NORMAL tasks to run, as they'll
> be running inside the fair deadline server for some period of time.
> 
> To prevent this issue, implement a starvation monitor mechanism that

Why should this be prevented?  FIFO can be preempted by a higher
priority FIFO/RR, or in this case by DEADLINE, and always by stop_task.

Just accept this.

Anything that's build around FIFO-99 not getting preempted is just plain
broken. Don't try and pander to broken.
  
Peter Zijlstra June 16, 2023, 11:56 a.m. UTC | #11
On Mon, Jun 12, 2023 at 04:45:54PM +0200, Daniel Bristot de Oliveira wrote:
> On 6/12/23 03:57, Joel Fernandes wrote:

> > Lastly, if the goal is to remove RT throttling code eventually, are
> > you also planning to remove RT group scheduling as well? Are there
> > users of RT group scheduling that might be impacted? On the other
> > hand, RT throttling / group scheduling code can be left as it is
> > (perhaps documenting it as deprecated) and the server stuff can be
> > implemented via a CONFIG option.
> 
> I think that the idea is to have the DL servers eventually replace the group
> schedule. But I also believe that it is better to start by solving the
> throttling and then moving to other constructions on top of the mechanism.

The big problem with the rt group scheduling mess is affinities. Other
than that, yes abosolutely, that crap needs to go.
  
Peter Zijlstra June 16, 2023, 12:05 p.m. UTC | #12
On Tue, Jun 13, 2023 at 03:41:30PM +0200, Daniel Bristot de Oliveira wrote:

> In an 0-laxity scheduler, the server would run at 0-laxity, jumping in
> front of DL tasks... that would break EDF. It would be mixing two
> schedulers in one. It is not required and likely not a good idea either.

I did consider a hybrid 0-laxity and EDF scheduler for mixed
criticality, as have others like Ted Baker IIRC. IIRC it can be done
using an augmented tree, but none of that solves the problems 0-laxity
has (like over preemption and the general problem of playing chicken by
doing things at the *VERY* last possible moment).

I think I did a talk at OSPERT on this at some point many years ago.
Luckily some bright fellow had this semi-partitioned stuff that would
make live much simpler :-)
  
Daniel Bristot de Oliveira June 16, 2023, 12:48 p.m. UTC | #13
On 6/16/23 14:05, Peter Zijlstra wrote:
> On Tue, Jun 13, 2023 at 03:41:30PM +0200, Daniel Bristot de Oliveira wrote:
> 
>> In an 0-laxity scheduler, the server would run at 0-laxity, jumping in
>> front of DL tasks... that would break EDF. It would be mixing two
>> schedulers in one. It is not required and likely not a good idea either.
> 
> I did consider a hybrid 0-laxity and EDF scheduler for mixed
> criticality, as have others like Ted Baker IIRC. IIRC it can be done
> using an augmented tree, but none of that solves the problems 0-laxity
> has (like over preemption and the general problem of playing chicken by
> doing things at the *VERY* last possible moment).

There are papers here or there about it, but it is far from being the most explored
way to do mixed criticality because of these side effects. It is more common to have
virtual deadlines for high and low criticalities, while using EDF.

Having EDF and being working conserving makes our life easier for other points
we are way behind, i.e., deadline inheritance.

> I think I did a talk at OSPERT on this at some point many years ago.
> Luckily some bright fellow had this semi-partitioned stuff that would
> make live much simpler :-)

It will, we can have two partitions, one for high and one for low. The general
case in the low power CPU, and if it does not make to finish on it, it continues
in the high power one. Still, always using EDF. Having virtual deadline is part
of semi-part. Anyways, EDF schedules more, and it is simpler... it is hard to beat.

This patch set is a warm up for that...

-- Daniel
  
Daniel Bristot de Oliveira June 16, 2023, 1:27 p.m. UTC | #14
On 6/16/23 13:51, Peter Zijlstra wrote:
> On Thu, Jun 08, 2023 at 05:58:18PM +0200, Daniel Bristot de Oliveira wrote:
>> From: Juri Lelli <juri.lelli@redhat.com>
>>
>> Starting deadline server for lower priority classes right away when
>> first task is enqueued might break guarantees, as tasks belonging to
>> intermediate priority classes could be uselessly preempted. E.g., a well
>> behaving (non hog) FIFO task can be preempted by NORMAL tasks even if
>> there are still CPU cycles available for NORMAL tasks to run, as they'll
>> be running inside the fair deadline server for some period of time.
>>
>> To prevent this issue, implement a starvation monitor mechanism that
> 
> Why should this be prevented?  FIFO can be preempted by a higher
> priority FIFO/RR, or in this case by DEADLINE, and always by stop_task.

Yeah, but in the context of "real-time throttling" the user is not asking to run things
as SCHED_DEADLINE, they are just running FIFO, and expecting it not to have SCHED_OTHER
jumping in front of them.

If we do not "deffer" the starting of the server for a point in which the starvation
is eminent, we can create some anomalies... for example:

A Interrupt wakes up a FIFO and a CFS task, which will will run first? The CFS!

A FIFO schedules a deferred work on a kworker... the kworker will preempt the
FIFO.

It is counter intuitive for the majority of people out there... It will
be counter-intuitive for most of PREEMPT_RT users :-/. From one day to the other
their tests using cyclictest will... break...

> Just accept this.
> 
> Anything that's build around FIFO-99 not getting preempted is just plain
> broken. Don't try and pander to broken
  
Peter Zijlstra June 19, 2023, 12:02 p.m. UTC | #15
On Fri, Jun 16, 2023 at 02:05:07PM +0200, Peter Zijlstra wrote:
> On Tue, Jun 13, 2023 at 03:41:30PM +0200, Daniel Bristot de Oliveira wrote:
> 
> > In an 0-laxity scheduler, the server would run at 0-laxity, jumping in
> > front of DL tasks... that would break EDF. It would be mixing two
> > schedulers in one. It is not required and likely not a good idea either.
> 
> I did consider a hybrid 0-laxity and EDF scheduler for mixed
> criticality, as have others like Ted Baker IIRC. IIRC it can be done
> using an augmented tree, but none of that solves the problems 0-laxity
> has (like over preemption and the general problem of playing chicken by
> doing things at the *VERY* last possible moment).
> 
> I think I did a talk at OSPERT on this at some point many years ago.
> Luckily some bright fellow had this semi-partitioned stuff that would
> make live much simpler :-)

I must clarify; I was thinking Least-Laxity-First, which is ofcourse not
the same as a 0-laxity scheduler.
  
Daniel Bristot de Oliveira June 19, 2023, 2:58 p.m. UTC | #16
On 6/19/23 14:02, Peter Zijlstra wrote:
> On Fri, Jun 16, 2023 at 02:05:07PM +0200, Peter Zijlstra wrote:
>> On Tue, Jun 13, 2023 at 03:41:30PM +0200, Daniel Bristot de Oliveira wrote:
>>
>>> In an 0-laxity scheduler, the server would run at 0-laxity, jumping in
>>> front of DL tasks... that would break EDF. It would be mixing two
>>> schedulers in one. It is not required and likely not a good idea either.
>>
>> I did consider a hybrid 0-laxity and EDF scheduler for mixed
>> criticality, as have others like Ted Baker IIRC. IIRC it can be done
>> using an augmented tree, but none of that solves the problems 0-laxity
>> has (like over preemption and the general problem of playing chicken by
>> doing things at the *VERY* last possible moment).
>>
>> I think I did a talk at OSPERT on this at some point many years ago.
>> Luckily some bright fellow had this semi-partitioned stuff that would
>> make live much simpler :-)
> 
> I must clarify; I was thinking Least-Laxity-First, which is ofcourse not
> the same as a 0-laxity scheduler.

ok, least-laxity-first is another thing... I think the 0-laxity came from the need
to wait until that point in time to deffer the dl server for the throttling case only...
not as a scheduler.

but still, the vast majority of research is concentrated on EDF. The laxity depends
on the runtime. As the task consumes runtime its laxity changes, so its priority.
With deadline only, the priority stays fixed during the job (Job level fixed priority)
It is easier to take decisions, less overheads & context switch and  we can explore
things with virtual deadlines.

IIUC, the EVVDF is also uses virtual deadline abstraction, right?

-- Daniel
  

Patch

diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index f493f05c1f84..75eadd85e2b3 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -6315,6 +6315,53 @@  static int sched_idle_cpu(int cpu)
 }
 #endif
 
+
+static void fair_server_watchdog(struct timer_list *list)
+{
+	struct rq *rq = container_of(list, struct rq, fair_server_wd);
+	struct rq_flags rf;
+
+	rq_lock_irqsave(rq, &rf);
+	rq->fair_server_wd_running = 0;
+
+	if (!rq->cfs.h_nr_running)
+		goto out;
+
+	update_rq_clock(rq);
+	dl_server_start(&rq->fair_server);
+	rq->fair_server_active = 1;
+	resched_curr(rq);
+
+out:
+	rq_unlock_irqrestore(rq, &rf);
+}
+
+static inline void fair_server_watchdog_start(struct rq *rq)
+{
+	if (rq->fair_server_wd_running || rq->fair_server_active)
+		return;
+
+	timer_setup(&rq->fair_server_wd, fair_server_watchdog, 0);
+	rq->fair_server_wd.expires = jiffies + FAIR_SERVER_WATCHDOG_INTERVAL;
+	add_timer_on(&rq->fair_server_wd, cpu_of(rq));
+	rq->fair_server_active = 0;
+	rq->fair_server_wd_running = 1;
+}
+
+static inline void fair_server_watchdog_stop(struct rq *rq, bool stop_server)
+{
+	if (!rq->fair_server_wd_running && !stop_server)
+		return;
+
+	del_timer(&rq->fair_server_wd);
+	rq->fair_server_wd_running = 0;
+
+	if (stop_server && rq->fair_server_active) {
+		dl_server_stop(&rq->fair_server);
+		rq->fair_server_active = 0;
+	}
+}
+
 /*
  * The enqueue_task method is called before nr_running is
  * increased. Here we update the fair scheduling stats and
@@ -6337,7 +6384,7 @@  enqueue_task_fair(struct rq *rq, struct task_struct *p, int flags)
 	util_est_enqueue(&rq->cfs, p);
 
 	if (!rq->cfs.h_nr_running)
-		dl_server_start(&rq->fair_server);
+		fair_server_watchdog_start(rq);
 
 	/*
 	 * If in_iowait is set, the code below may not trigger any cpufreq
@@ -6484,7 +6531,7 @@  static void dequeue_task_fair(struct rq *rq, struct task_struct *p, int flags)
 
 dequeue_throttle:
 	if (!rq->cfs.h_nr_running)
-		dl_server_stop(&rq->fair_server);
+		fair_server_watchdog_stop(rq, true);
 
 	util_est_update(&rq->cfs, p, task_sleep);
 	hrtick_update(rq);
@@ -8193,6 +8240,7 @@  done: __maybe_unused;
 		hrtick_start_fair(rq, p);
 
 	update_misfit_status(p, rq);
+	fair_server_watchdog_stop(rq, false);
 
 	return p;
 
@@ -8248,6 +8296,8 @@  void fair_server_init(struct rq *rq)
 	dl_se->dl_period = 20 * TICK_NSEC;
 
 	dl_server_init(dl_se, rq, fair_server_has_tasks, fair_server_pick);
+
+	rq->fair_server_wd_running = 0;
 }
 
 /*
@@ -8262,6 +8312,9 @@  static void put_prev_task_fair(struct rq *rq, struct task_struct *prev)
 		cfs_rq = cfs_rq_of(se);
 		put_prev_entity(cfs_rq, se);
 	}
+
+	if (rq->cfs.h_nr_running)
+		fair_server_watchdog_start(rq);
 }
 
 /*
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index d4a7c0823c53..cab5d2b1e71f 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -353,6 +353,7 @@  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);
 
+#define FAIR_SERVER_WATCHDOG_INTERVAL (HZ >> 1)
 extern void fair_server_init(struct rq *);
 
 #ifdef CONFIG_CGROUP_SCHED
@@ -1018,6 +1019,9 @@  struct rq {
 	struct dl_rq		dl;
 
 	struct sched_dl_entity	fair_server;
+	int			fair_server_active;
+	struct timer_list	fair_server_wd;
+	int			fair_server_wd_running;
 
 #ifdef CONFIG_FAIR_GROUP_SCHED
 	/* list of leaf cfs_rq on this CPU: */