2021-07-19 18:29:48

by Quentin Perret

[permalink] [raw]
Subject: [PATCH v4 0/2] A couple of uclamp fixes

Hi all,

This is v4 of small series previously posted here:

https://lore.kernel.org/lkml/[email protected]/

I dropped the last patch of the series which requires a bit more
discussion, and kept the first two here as they're standalone fixes.

Thanks,
Quentin

Quentin Perret (2):
sched: Fix UCLAMP_FLAG_IDLE setting
sched: Skip priority checks with SCHED_FLAG_KEEP_PARAMS

kernel/sched/core.c | 44 ++++++++++++++++++++++++++++++++------------
1 file changed, 32 insertions(+), 12 deletions(-)

--
2.32.0.402.g57bb445576-goog


2021-07-20 02:03:59

by Quentin Perret

[permalink] [raw]
Subject: [PATCH v4 2/2] sched: Skip priority checks with SCHED_FLAG_KEEP_PARAMS

SCHED_FLAG_KEEP_PARAMS can be passed to sched_setattr to specify that
the call must not touch scheduling parameters (nice or priority). This
is particularly handy for uclamp when used in conjunction with
SCHED_FLAG_KEEP_POLICY as that allows to issue a syscall that only
impacts uclamp values.

However, sched_setattr always checks whether the priorities and nice
values passed in sched_attr are valid first, even if those never get
used down the line. This is useless at best since userspace can
trivially bypass this check to set the uclamp values by specifying low
priorities. However, it is cumbersome to do so as there is no single
expression of this that skips both RT and CFS checks at once. As such,
userspace needs to query the task policy first with e.g. sched_getattr
and then set sched_attr.sched_priority accordingly. This is racy and
slower than a single call.

As the priority and nice checks are useless when SCHED_FLAG_KEEP_PARAMS
is specified, simply inherit them in this case to match the policy
inheritance of SCHED_FLAG_KEEP_POLICY.

Reported-by: Wei Wang <[email protected]>
Reviewed-by: Qais Yousef <[email protected]>
Signed-off-by: Quentin Perret <[email protected]>
---
kernel/sched/core.c | 19 +++++++++++++------
1 file changed, 13 insertions(+), 6 deletions(-)

diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index e801d2c3077b..914076eab242 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -7332,6 +7332,16 @@ static int sched_copy_attr(struct sched_attr __user *uattr, struct sched_attr *a
return -E2BIG;
}

+static void get_params(struct task_struct *p, struct sched_attr *attr)
+{
+ if (task_has_dl_policy(p))
+ __getparam_dl(p, attr);
+ else if (task_has_rt_policy(p))
+ attr->sched_priority = p->rt_priority;
+ else
+ attr->sched_nice = task_nice(p);
+}
+
/**
* sys_sched_setscheduler - set/change the scheduler policy and RT priority
* @pid: the pid in question.
@@ -7393,6 +7403,8 @@ SYSCALL_DEFINE3(sched_setattr, pid_t, pid, struct sched_attr __user *, uattr,
rcu_read_unlock();

if (likely(p)) {
+ if (attr.sched_flags & SCHED_FLAG_KEEP_PARAMS)
+ get_params(p, &attr);
retval = sched_setattr(p, &attr);
put_task_struct(p);
}
@@ -7541,12 +7553,7 @@ SYSCALL_DEFINE4(sched_getattr, pid_t, pid, struct sched_attr __user *, uattr,
kattr.sched_policy = p->policy;
if (p->sched_reset_on_fork)
kattr.sched_flags |= SCHED_FLAG_RESET_ON_FORK;
- if (task_has_dl_policy(p))
- __getparam_dl(p, &kattr);
- else if (task_has_rt_policy(p))
- kattr.sched_priority = p->rt_priority;
- else
- kattr.sched_nice = task_nice(p);
+ get_params(p, &kattr);

#ifdef CONFIG_UCLAMP_TASK
/*
--
2.32.0.402.g57bb445576-goog

2021-07-22 08:50:30

by Dietmar Eggemann

[permalink] [raw]
Subject: Re: [PATCH v4 2/2] sched: Skip priority checks with SCHED_FLAG_KEEP_PARAMS

On 19/07/2021 18:16, Quentin Perret wrote:
> SCHED_FLAG_KEEP_PARAMS can be passed to sched_setattr to specify that
> the call must not touch scheduling parameters (nice or priority). This

What about DL params (runtime, deadline, period)?

Uclamp is not for DL but we could (*) still set uclamp values on a DL
task. Obviously they would only be used when the task switches policy.

On tip/sched/core:

root@juno:~# chrt -d -P 1000000000 -T 100000000 -p 0 1671

root@juno:~# uclampset -m200 -M400 -p 1671

root@juno:~# cat /proc/1671/sched | grep uclamp
uclamp.min : 200
uclamp.max : 400
effective uclamp.min : 200
effective uclamp.max : 400

root@juno:~# chrt -o -p 0
pid 1702's current scheduling policy: SCHED_OTHER
pid 1702's current scheduling priority: 0

root@juno:~# cat /proc/1671/sched | grep uclamp
uclamp.min : 200
uclamp.max : 400
effective uclamp.min : 200
effective uclamp.max : 400


> is particularly handy for uclamp when used in conjunction with
> SCHED_FLAG_KEEP_POLICY as that allows to issue a syscall that only
> impacts uclamp values.
>
> However, sched_setattr always checks whether the priorities and nice
> values passed in sched_attr are valid first, even if those never get

+ DL params (__checkparam_dl())

> used down the line. This is useless at best since userspace can
> trivially bypass this check to set the uclamp values by specifying low
> priorities. However, it is cumbersome to do so as there is no single
> expression of this that skips both RT and CFS checks at once. As such,
> userspace needs to query the task policy first with e.g. sched_getattr
> and then set sched_attr.sched_priority accordingly. This is racy and
> slower than a single call.
>
> As the priority and nice checks are useless when SCHED_FLAG_KEEP_PARAMS
> is specified, simply inherit them in this case to match the policy
> inheritance of SCHED_FLAG_KEEP_POLICY.
>
> Reported-by: Wei Wang <[email protected]>
> Reviewed-by: Qais Yousef <[email protected]>
> Signed-off-by: Quentin Perret <[email protected]>
> ---
> kernel/sched/core.c | 19 +++++++++++++------
> 1 file changed, 13 insertions(+), 6 deletions(-)
>
> diff --git a/kernel/sched/core.c b/kernel/sched/core.c
> index e801d2c3077b..914076eab242 100644
> --- a/kernel/sched/core.c
> +++ b/kernel/sched/core.c
> @@ -7332,6 +7332,16 @@ static int sched_copy_attr(struct sched_attr __user *uattr, struct sched_attr *a
> return -E2BIG;
> }
>
> +static void get_params(struct task_struct *p, struct sched_attr *attr)
> +{
> + if (task_has_dl_policy(p))
> + __getparam_dl(p, attr);

(*) This changes the behaviour when setting uclamp values on a DL task.

Before uclamp values could be set but now, because of

void __getparam_dl(struct task_struct *p, struct sched_attr *attr)
..
attr->sched_flags = dl_se->flags

SCHED_FLAG_UTIL_CLAMP gets overwritten and __sched_setscheduler() bails in:

if (unlikely(policy == p->policy)) {
...
retval = 0;
goto unlock;
}
change:

I.e. the:

if (attr->sched_flags & SCHED_FLAG_UTIL_CLAMP)
goto change;

can't trigger anymore.


> + else if (task_has_rt_policy(p))
> + attr->sched_priority = p->rt_priority;
> + else
> + attr->sched_nice = task_nice(p);
> +}
> +
> /**
> * sys_sched_setscheduler - set/change the scheduler policy and RT priority
> * @pid: the pid in question.
> @@ -7393,6 +7403,8 @@ SYSCALL_DEFINE3(sched_setattr, pid_t, pid, struct sched_attr __user *, uattr,
> rcu_read_unlock();
>
> if (likely(p)) {
> + if (attr.sched_flags & SCHED_FLAG_KEEP_PARAMS)
> + get_params(p, &attr);

SCHED_FLAG_KEEP_PARAMS is handled here but SCHED_FLAG_KEEP_POLICY
outside (before) the `if (likely(p))`?

> retval = sched_setattr(p, &attr);
> put_task_struct(p);
> }

[...]

2021-07-26 13:57:44

by Quentin Perret

[permalink] [raw]
Subject: Re: [PATCH v4 2/2] sched: Skip priority checks with SCHED_FLAG_KEEP_PARAMS

On Thursday 22 Jul 2021 at 10:47:33 (+0200), Dietmar Eggemann wrote:
> (*) This changes the behaviour when setting uclamp values on a DL task.
>
> Before uclamp values could be set but now, because of
>
> void __getparam_dl(struct task_struct *p, struct sched_attr *attr)
> ..
> attr->sched_flags = dl_se->flags
>
> SCHED_FLAG_UTIL_CLAMP gets overwritten and __sched_setscheduler() bails in:
>
> if (unlikely(policy == p->policy)) {
> ...
> retval = 0;
> goto unlock;
> }
> change:
>
> I.e. the:
>
> if (attr->sched_flags & SCHED_FLAG_UTIL_CLAMP)
> goto change;
>
> can't trigger anymore.

Bah, as you said it doesn't seem to be a big deal, but clearly that was
unintentional. Let me try and fix this.

> > + else if (task_has_rt_policy(p))
> > + attr->sched_priority = p->rt_priority;
> > + else
> > + attr->sched_nice = task_nice(p);
> > +}
> > +
> > /**
> > * sys_sched_setscheduler - set/change the scheduler policy and RT priority
> > * @pid: the pid in question.
> > @@ -7393,6 +7403,8 @@ SYSCALL_DEFINE3(sched_setattr, pid_t, pid, struct sched_attr __user *, uattr,
> > rcu_read_unlock();
> >
> > if (likely(p)) {
> > + if (attr.sched_flags & SCHED_FLAG_KEEP_PARAMS)
> > + get_params(p, &attr);
>
> SCHED_FLAG_KEEP_PARAMS is handled here but SCHED_FLAG_KEEP_POLICY
> outside (before) the `if (likely(p))`?

Because I need to dereference p while SCHED_FLAG_KEEP_POLICY doesn't :)

> > retval = sched_setattr(p, &attr);
> > put_task_struct(p);
> > }
>
> [...]

2021-07-27 10:18:30

by Quentin Perret

[permalink] [raw]
Subject: Re: [PATCH v4 2/2] sched: Skip priority checks with SCHED_FLAG_KEEP_PARAMS

On Monday 26 Jul 2021 at 14:56:10 (+0100), Quentin Perret wrote:
> On Thursday 22 Jul 2021 at 10:47:33 (+0200), Dietmar Eggemann wrote:
> > (*) This changes the behaviour when setting uclamp values on a DL task.
> >
> > Before uclamp values could be set but now, because of
> >
> > void __getparam_dl(struct task_struct *p, struct sched_attr *attr)
> > ..
> > attr->sched_flags = dl_se->flags
> >
> > SCHED_FLAG_UTIL_CLAMP gets overwritten and __sched_setscheduler() bails in:
> >
> > if (unlikely(policy == p->policy)) {
> > ...
> > retval = 0;
> > goto unlock;
> > }
> > change:
> >
> > I.e. the:
> >
> > if (attr->sched_flags & SCHED_FLAG_UTIL_CLAMP)
> > goto change;
> >
> > can't trigger anymore.
>
> Bah, as you said it doesn't seem to be a big deal, but clearly that was
> unintentional. Let me try and fix this.

While looking at this I found existing bugs in the area. Fixes are here:

https://lore.kernel.org/lkml/[email protected]/

And with the above series applied this patch should behave correctly
now.

Thanks,
Quentin

2021-07-29 17:32:24

by Dietmar Eggemann

[permalink] [raw]
Subject: Re: [PATCH v4 2/2] sched: Skip priority checks with SCHED_FLAG_KEEP_PARAMS

On 26/07/2021 15:56, Quentin Perret wrote:
> On Thursday 22 Jul 2021 at 10:47:33 (+0200), Dietmar Eggemann wrote:

[...]

>>> @@ -7393,6 +7403,8 @@ SYSCALL_DEFINE3(sched_setattr, pid_t, pid, struct sched_attr __user *, uattr,
>>> rcu_read_unlock();
>>>
>>> if (likely(p)) {
>>> + if (attr.sched_flags & SCHED_FLAG_KEEP_PARAMS)
>>> + get_params(p, &attr);
>>
>> SCHED_FLAG_KEEP_PARAMS is handled here but SCHED_FLAG_KEEP_POLICY
>> outside (before) the `if (likely(p))`?
>
> Because I need to dereference p while SCHED_FLAG_KEEP_POLICY doesn't :)

Ah, true. Looked weird though.
But then the SCHED_FLAG_KEEP_POLICY condition can be placed closer to
the SCHED_FLAG_KEEP_PARAMS condition. We don't have to set
SETPARAM_POLICY if p == NULL.

>>> retval = sched_setattr(p, &attr);
>>> put_task_struct(p);
>>> }

[...]

2021-07-29 17:38:29

by Dietmar Eggemann

[permalink] [raw]
Subject: Re: [PATCH v4 2/2] sched: Skip priority checks with SCHED_FLAG_KEEP_PARAMS

On 27/07/2021 12:16, Quentin Perret wrote:
> On Monday 26 Jul 2021 at 14:56:10 (+0100), Quentin Perret wrote:
>> On Thursday 22 Jul 2021 at 10:47:33 (+0200), Dietmar Eggemann wrote:
>>> (*) This changes the behaviour when setting uclamp values on a DL task.
>>>
>>> Before uclamp values could be set but now, because of
>>>
>>> void __getparam_dl(struct task_struct *p, struct sched_attr *attr)
>>> ..
>>> attr->sched_flags = dl_se->flags
>>>
>>> SCHED_FLAG_UTIL_CLAMP gets overwritten and __sched_setscheduler() bails in:
>>>
>>> if (unlikely(policy == p->policy)) {
>>> ...
>>> retval = 0;
>>> goto unlock;
>>> }
>>> change:
>>>
>>> I.e. the:
>>>
>>> if (attr->sched_flags & SCHED_FLAG_UTIL_CLAMP)
>>> goto change;
>>>
>>> can't trigger anymore.
>>
>> Bah, as you said it doesn't seem to be a big deal, but clearly that was
>> unintentional. Let me try and fix this.
>
> While looking at this I found existing bugs in the area. Fixes are here:
>
> https://lore.kernel.org/lkml/[email protected]/
>
> And with the above series applied this patch should behave correctly
> now.

It does. Like depicted in

https://lkml.kernel.org/r/[email protected]

Reviewed-by: Dietmar Eggemann <[email protected]>