linux-pm.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/2] cpufreq: governor: Fixups on top of recent changes
@ 2016-02-21  2:10 Rafael J. Wysocki
  2016-02-21  2:14 ` [PATCH 1/2] cpufreq: governor: Fix race in dbs_update_util_handler() Rafael J. Wysocki
  2016-02-21  2:15 ` [PATCH 2/2] cpufreq: governor: Make gov_set_update_util() static Rafael J. Wysocki
  0 siblings, 2 replies; 10+ messages in thread
From: Rafael J. Wysocki @ 2016-02-21  2:10 UTC (permalink / raw)
  To: Linux PM list; +Cc: Linux Kernel Mailing List, Viresh Kumar

Hi,

Two fixups on top of recent cpufreq governor changes in the linux-next
branch of linux-pm.git.

[1/2] closes a tiny theoretical race in dbs_update_util_handler()
      that may lead to taking a sample prematurely in some weird
      cases.

[2/2] removes an unnecessary function export.

Thanks,
Rafael


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 1/2] cpufreq: governor: Fix race in dbs_update_util_handler()
  2016-02-21  2:10 [PATCH 0/2] cpufreq: governor: Fixups on top of recent changes Rafael J. Wysocki
@ 2016-02-21  2:14 ` Rafael J. Wysocki
  2016-02-22  5:23   ` Viresh Kumar
  2016-02-22 13:14   ` [PATCH v2 " Rafael J. Wysocki
  2016-02-21  2:15 ` [PATCH 2/2] cpufreq: governor: Make gov_set_update_util() static Rafael J. Wysocki
  1 sibling, 2 replies; 10+ messages in thread
From: Rafael J. Wysocki @ 2016-02-21  2:14 UTC (permalink / raw)
  To: Linux PM list; +Cc: Linux Kernel Mailing List, Viresh Kumar

From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

There is a scenarion that may lead to undesired results in
dbs_update_util_handler().  Namely, if two CPUs sharing a policy
enter the funtion at the same time, pass the sample delay check
and then one of them is stalled until dbs_work_handler() (queued
up by the other CPU) clears the work counter, it may update the
work counter and queue up another work item prematurely.

To prevent that from happening, use the observation that the CPU
queuing up a work item in dbs_update_util_handler() updates the
last sample time.  This means that if another CPU was stalling after
passing the sample delay check and now successfully updated the work
counter as a result of the race described above, it will see the new
value of the last sample time which is different from what it used in
the sample delay check before.  If that happens, the sample delay
check passed previously is not valid any more, so the CPU should not
continue, but leaving the funtion at that point might miss an
opportunity to take the next sample, so simply clear the work
counter and jump to the beginning of the function in that case.

Fixes: f17cbb53783c (cpufreq: governor: Avoid atomic operations in hot paths)
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
---
 drivers/cpufreq/cpufreq_governor.c |   22 +++++++++++++++++-----
 1 file changed, 17 insertions(+), 5 deletions(-)

Index: linux-pm/drivers/cpufreq/cpufreq_governor.c
===================================================================
--- linux-pm.orig/drivers/cpufreq/cpufreq_governor.c
+++ linux-pm/drivers/cpufreq/cpufreq_governor.c
@@ -341,8 +341,9 @@ static void dbs_update_util_handler(stru
 {
 	struct cpu_dbs_info *cdbs = container_of(data, struct cpu_dbs_info, update_util);
 	struct policy_dbs_info *policy_dbs = cdbs->policy_dbs;
-	u64 delta_ns;
+	u64 delta_ns, lst;
 
+ start:
 	/*
 	 * The work may not be allowed to be queued up right now.
 	 * Possible reasons:
@@ -357,7 +358,8 @@ static void dbs_update_util_handler(stru
 	 * of sample_delay_ns used in the computation may be stale.
 	 */
 	smp_rmb();
-	delta_ns = time - policy_dbs->last_sample_time;
+	lst = ACCESS_ONCE(policy_dbs->last_sample_time);
+	delta_ns = time - lst;
 	if ((s64)delta_ns < policy_dbs->sample_delay_ns)
 		return;
 
@@ -366,9 +368,19 @@ static void dbs_update_util_handler(stru
 	 * at this point.  Otherwise, we need to ensure that only one of the
 	 * CPUs sharing the policy will do that.
 	 */
-	if (policy_dbs->is_shared &&
-	    !atomic_add_unless(&policy_dbs->work_count, 1, 1))
-		return;
+	if (policy_dbs->is_shared) {
+		if (!atomic_add_unless(&policy_dbs->work_count, 1, 1))
+			return;
+
+		/*
+		 * If another CPU updated last_sample_time in the meantime, we
+		 * shouldn't be here, so clear the work counter and try again.
+		 */
+		if (unlikely(lst != ACCESS_ONCE(policy_dbs->last_sample_time))) {
+			atomic_set(&policy_dbs->work_count, 0);
+			goto start;
+		}
+	}
 
 	policy_dbs->last_sample_time = time;
 	policy_dbs->work_in_progress = true;


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 2/2] cpufreq: governor: Make gov_set_update_util() static
  2016-02-21  2:10 [PATCH 0/2] cpufreq: governor: Fixups on top of recent changes Rafael J. Wysocki
  2016-02-21  2:14 ` [PATCH 1/2] cpufreq: governor: Fix race in dbs_update_util_handler() Rafael J. Wysocki
@ 2016-02-21  2:15 ` Rafael J. Wysocki
  2016-02-22  5:24   ` Viresh Kumar
  1 sibling, 1 reply; 10+ messages in thread
From: Rafael J. Wysocki @ 2016-02-21  2:15 UTC (permalink / raw)
  To: Linux PM list; +Cc: Linux Kernel Mailing List, Viresh Kumar

From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

The gov_set_update_util() routine is only used internally by the
common governor code and it doesn't need to be exported, so make
it static.

No functional changes.

Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
---
 drivers/cpufreq/cpufreq_governor.c |    5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

Index: linux-pm/drivers/cpufreq/cpufreq_governor.c
===================================================================
--- linux-pm.orig/drivers/cpufreq/cpufreq_governor.c
+++ linux-pm/drivers/cpufreq/cpufreq_governor.c
@@ -261,8 +261,8 @@ unsigned int dbs_update(struct cpufreq_p
 }
 EXPORT_SYMBOL_GPL(dbs_update);
 
-void gov_set_update_util(struct policy_dbs_info *policy_dbs,
-			 unsigned int delay_us)
+static void gov_set_update_util(struct policy_dbs_info *policy_dbs,
+				unsigned int delay_us)
 {
 	struct cpufreq_policy *policy = policy_dbs->policy;
 	int cpu;
@@ -276,7 +276,6 @@ void gov_set_update_util(struct policy_d
 		cpufreq_set_update_util_data(cpu, &cdbs->update_util);
 	}
 }
-EXPORT_SYMBOL_GPL(gov_set_update_util);
 
 static inline void gov_clear_update_util(struct cpufreq_policy *policy)
 {


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/2] cpufreq: governor: Fix race in dbs_update_util_handler()
  2016-02-21  2:14 ` [PATCH 1/2] cpufreq: governor: Fix race in dbs_update_util_handler() Rafael J. Wysocki
@ 2016-02-22  5:23   ` Viresh Kumar
  2016-02-22 12:26     ` Rafael J. Wysocki
  2016-02-22 13:14   ` [PATCH v2 " Rafael J. Wysocki
  1 sibling, 1 reply; 10+ messages in thread
From: Viresh Kumar @ 2016-02-22  5:23 UTC (permalink / raw)
  To: Rafael J. Wysocki; +Cc: Linux PM list, Linux Kernel Mailing List

On 21-02-16, 03:14, Rafael J. Wysocki wrote:
> From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
> 
> There is a scenarion that may lead to undesired results in

             scenario

> dbs_update_util_handler().  Namely, if two CPUs sharing a policy
> enter the funtion at the same time, pass the sample delay check
> and then one of them is stalled until dbs_work_handler() (queued
> up by the other CPU) clears the work counter, it may update the
> work counter and queue up another work item prematurely.
> 
> To prevent that from happening, use the observation that the CPU
> queuing up a work item in dbs_update_util_handler() updates the
> last sample time.  This means that if another CPU was stalling after
> passing the sample delay check and now successfully updated the work
> counter as a result of the race described above, it will see the new
> value of the last sample time which is different from what it used in
> the sample delay check before.  If that happens, the sample delay
> check passed previously is not valid any more, so the CPU should not
> continue, but leaving the funtion at that point might miss an
> opportunity to take the next sample, so simply clear the work
> counter and jump to the beginning of the function in that case.
> 
> Fixes: f17cbb53783c (cpufreq: governor: Avoid atomic operations in hot paths)
> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
> ---
>  drivers/cpufreq/cpufreq_governor.c |   22 +++++++++++++++++-----
>  1 file changed, 17 insertions(+), 5 deletions(-)
> 
> Index: linux-pm/drivers/cpufreq/cpufreq_governor.c
> ===================================================================
> --- linux-pm.orig/drivers/cpufreq/cpufreq_governor.c
> +++ linux-pm/drivers/cpufreq/cpufreq_governor.c
> @@ -341,8 +341,9 @@ static void dbs_update_util_handler(stru
>  {
>  	struct cpu_dbs_info *cdbs = container_of(data, struct cpu_dbs_info, update_util);
>  	struct policy_dbs_info *policy_dbs = cdbs->policy_dbs;
> -	u64 delta_ns;
> +	u64 delta_ns, lst;
>  
> + start:
>  	/*
>  	 * The work may not be allowed to be queued up right now.
>  	 * Possible reasons:
> @@ -357,7 +358,8 @@ static void dbs_update_util_handler(stru
>  	 * of sample_delay_ns used in the computation may be stale.
>  	 */
>  	smp_rmb();
> -	delta_ns = time - policy_dbs->last_sample_time;
> +	lst = ACCESS_ONCE(policy_dbs->last_sample_time);

The comment on the top of ACCESS_ONCE() asks us to use READ_ONCE() if possible.

> +	delta_ns = time - lst;
>  	if ((s64)delta_ns < policy_dbs->sample_delay_ns)
>  		return;
>  
> @@ -366,9 +368,19 @@ static void dbs_update_util_handler(stru
>  	 * at this point.  Otherwise, we need to ensure that only one of the
>  	 * CPUs sharing the policy will do that.
>  	 */
> -	if (policy_dbs->is_shared &&
> -	    !atomic_add_unless(&policy_dbs->work_count, 1, 1))
> -		return;
> +	if (policy_dbs->is_shared) {
> +		if (!atomic_add_unless(&policy_dbs->work_count, 1, 1))
> +			return;
> +
> +		/*
> +		 * If another CPU updated last_sample_time in the meantime, we
> +		 * shouldn't be here, so clear the work counter and try again.
> +		 */
> +		if (unlikely(lst != ACCESS_ONCE(policy_dbs->last_sample_time))) {
> +			atomic_set(&policy_dbs->work_count, 0);
> +			goto start;
> +		}

I think we should be doing this here:

	delta_ns = time - ACCESS_ONCE(policy_dbs->last_sample_time);
 	if ((s64)delta_ns < policy_dbs->sample_delay_ns) {
                atomic_set(&policy_dbs->work_count, 0);
 		return;
        }

There is no point running the routine again, we already have ->work_count
incremented for us, lets do the check right now.

Over that, in theory, with your code, it is possible that one of the CPU can get
stuck in the goto->start loop indefinitely :)

> +	}
>  
>  	policy_dbs->last_sample_time = time;
>  	policy_dbs->work_in_progress = true;

-- 
viresh

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 2/2] cpufreq: governor: Make gov_set_update_util() static
  2016-02-21  2:15 ` [PATCH 2/2] cpufreq: governor: Make gov_set_update_util() static Rafael J. Wysocki
@ 2016-02-22  5:24   ` Viresh Kumar
  0 siblings, 0 replies; 10+ messages in thread
From: Viresh Kumar @ 2016-02-22  5:24 UTC (permalink / raw)
  To: Rafael J. Wysocki; +Cc: Linux PM list, Linux Kernel Mailing List

On 21-02-16, 03:15, Rafael J. Wysocki wrote:
> From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
> 
> The gov_set_update_util() routine is only used internally by the
> common governor code and it doesn't need to be exported, so make
> it static.
> 
> No functional changes.
> 
> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
> ---
>  drivers/cpufreq/cpufreq_governor.c |    5 ++---
>  1 file changed, 2 insertions(+), 3 deletions(-)
> 
> Index: linux-pm/drivers/cpufreq/cpufreq_governor.c
> ===================================================================
> --- linux-pm.orig/drivers/cpufreq/cpufreq_governor.c
> +++ linux-pm/drivers/cpufreq/cpufreq_governor.c
> @@ -261,8 +261,8 @@ unsigned int dbs_update(struct cpufreq_p
>  }
>  EXPORT_SYMBOL_GPL(dbs_update);
>  
> -void gov_set_update_util(struct policy_dbs_info *policy_dbs,
> -			 unsigned int delay_us)
> +static void gov_set_update_util(struct policy_dbs_info *policy_dbs,
> +				unsigned int delay_us)
>  {
>  	struct cpufreq_policy *policy = policy_dbs->policy;
>  	int cpu;
> @@ -276,7 +276,6 @@ void gov_set_update_util(struct policy_d
>  		cpufreq_set_update_util_data(cpu, &cdbs->update_util);
>  	}
>  }
> -EXPORT_SYMBOL_GPL(gov_set_update_util);
>  
>  static inline void gov_clear_update_util(struct cpufreq_policy *policy)
>  {

Acked-by: Viresh Kumar <viresh.kumar@linaro.org>

-- 
viresh

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/2] cpufreq: governor: Fix race in dbs_update_util_handler()
  2016-02-22  5:23   ` Viresh Kumar
@ 2016-02-22 12:26     ` Rafael J. Wysocki
  2016-02-22 13:04       ` Viresh Kumar
  0 siblings, 1 reply; 10+ messages in thread
From: Rafael J. Wysocki @ 2016-02-22 12:26 UTC (permalink / raw)
  To: Viresh Kumar; +Cc: Rafael J. Wysocki, Linux PM list, Linux Kernel Mailing List

On Mon, Feb 22, 2016 at 6:23 AM, Viresh Kumar <viresh.kumar@linaro.org> wrote:
> On 21-02-16, 03:14, Rafael J. Wysocki wrote:
>> From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
>>
>> There is a scenarion that may lead to undesired results in
>
>              scenario
>
>> dbs_update_util_handler().  Namely, if two CPUs sharing a policy
>> enter the funtion at the same time, pass the sample delay check
>> and then one of them is stalled until dbs_work_handler() (queued
>> up by the other CPU) clears the work counter, it may update the
>> work counter and queue up another work item prematurely.
>>
>> To prevent that from happening, use the observation that the CPU
>> queuing up a work item in dbs_update_util_handler() updates the
>> last sample time.  This means that if another CPU was stalling after
>> passing the sample delay check and now successfully updated the work
>> counter as a result of the race described above, it will see the new
>> value of the last sample time which is different from what it used in
>> the sample delay check before.  If that happens, the sample delay
>> check passed previously is not valid any more, so the CPU should not
>> continue, but leaving the funtion at that point might miss an
>> opportunity to take the next sample, so simply clear the work
>> counter and jump to the beginning of the function in that case.
>>
>> Fixes: f17cbb53783c (cpufreq: governor: Avoid atomic operations in hot paths)
>> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
>> ---
>>  drivers/cpufreq/cpufreq_governor.c |   22 +++++++++++++++++-----
>>  1 file changed, 17 insertions(+), 5 deletions(-)
>>
>> Index: linux-pm/drivers/cpufreq/cpufreq_governor.c
>> ===================================================================
>> --- linux-pm.orig/drivers/cpufreq/cpufreq_governor.c
>> +++ linux-pm/drivers/cpufreq/cpufreq_governor.c
>> @@ -341,8 +341,9 @@ static void dbs_update_util_handler(stru
>>  {
>>       struct cpu_dbs_info *cdbs = container_of(data, struct cpu_dbs_info, update_util);
>>       struct policy_dbs_info *policy_dbs = cdbs->policy_dbs;
>> -     u64 delta_ns;
>> +     u64 delta_ns, lst;
>>
>> + start:
>>       /*
>>        * The work may not be allowed to be queued up right now.
>>        * Possible reasons:
>> @@ -357,7 +358,8 @@ static void dbs_update_util_handler(stru
>>        * of sample_delay_ns used in the computation may be stale.
>>        */
>>       smp_rmb();
>> -     delta_ns = time - policy_dbs->last_sample_time;
>> +     lst = ACCESS_ONCE(policy_dbs->last_sample_time);
>
> The comment on the top of ACCESS_ONCE() asks us to use READ_ONCE() if possible.

I forgot about this one, thanks!

>> +     delta_ns = time - lst;
>>       if ((s64)delta_ns < policy_dbs->sample_delay_ns)
>>               return;
>>
>> @@ -366,9 +368,19 @@ static void dbs_update_util_handler(stru
>>        * at this point.  Otherwise, we need to ensure that only one of the
>>        * CPUs sharing the policy will do that.
>>        */
>> -     if (policy_dbs->is_shared &&
>> -         !atomic_add_unless(&policy_dbs->work_count, 1, 1))
>> -             return;
>> +     if (policy_dbs->is_shared) {
>> +             if (!atomic_add_unless(&policy_dbs->work_count, 1, 1))
>> +                     return;
>> +
>> +             /*
>> +              * If another CPU updated last_sample_time in the meantime, we
>> +              * shouldn't be here, so clear the work counter and try again.
>> +              */
>> +             if (unlikely(lst != ACCESS_ONCE(policy_dbs->last_sample_time))) {
>> +                     atomic_set(&policy_dbs->work_count, 0);
>> +                     goto start;
>> +             }
>
> I think we should be doing this here:
>
>         delta_ns = time - ACCESS_ONCE(policy_dbs->last_sample_time);
>         if ((s64)delta_ns < policy_dbs->sample_delay_ns) {
>                 atomic_set(&policy_dbs->work_count, 0);
>                 return;
>         }
>
> There is no point running the routine again, we already have ->work_count
> incremented for us, lets do the check right now.

No, we need to check work_in_progress too.

> Over that, in theory, with your code, it is possible that one of the CPU can get
> stuck in the goto->start loop indefinitely :)

Good point (although that's not very likely to happen in practice).

There's one more problem, however.  The value of time is already stale
at this point, so going to start and keeping the time value unmodified
is a mistake.

Let me respin the patch.

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/2] cpufreq: governor: Fix race in dbs_update_util_handler()
  2016-02-22 12:26     ` Rafael J. Wysocki
@ 2016-02-22 13:04       ` Viresh Kumar
  2016-02-22 13:30         ` Rafael J. Wysocki
  0 siblings, 1 reply; 10+ messages in thread
From: Viresh Kumar @ 2016-02-22 13:04 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Rafael J. Wysocki, Linux PM list, Linux Kernel Mailing List

On 22-02-16, 13:26, Rafael J. Wysocki wrote:
> On Mon, Feb 22, 2016 at 6:23 AM, Viresh Kumar <viresh.kumar@linaro.org> wrote:
> > On 21-02-16, 03:14, Rafael J. Wysocki wrote:
> >> From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
> >>
> >> There is a scenarion that may lead to undesired results in
> >
> >              scenario
> >
> >> dbs_update_util_handler().  Namely, if two CPUs sharing a policy
> >> enter the funtion at the same time, pass the sample delay check
> >> and then one of them is stalled until dbs_work_handler() (queued
> >> up by the other CPU) clears the work counter, it may update the
> >> work counter and queue up another work item prematurely.
> >>
> >> To prevent that from happening, use the observation that the CPU
> >> queuing up a work item in dbs_update_util_handler() updates the
> >> last sample time.  This means that if another CPU was stalling after
> >> passing the sample delay check and now successfully updated the work
> >> counter as a result of the race described above, it will see the new
> >> value of the last sample time which is different from what it used in
> >> the sample delay check before.  If that happens, the sample delay
> >> check passed previously is not valid any more, so the CPU should not
> >> continue, but leaving the funtion at that point might miss an
> >> opportunity to take the next sample, so simply clear the work
> >> counter and jump to the beginning of the function in that case.
> >>
> >> Fixes: f17cbb53783c (cpufreq: governor: Avoid atomic operations in hot paths)
> >> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
> >> ---
> >>  drivers/cpufreq/cpufreq_governor.c |   22 +++++++++++++++++-----
> >>  1 file changed, 17 insertions(+), 5 deletions(-)
> >>
> >> Index: linux-pm/drivers/cpufreq/cpufreq_governor.c
> >> ===================================================================
> >> --- linux-pm.orig/drivers/cpufreq/cpufreq_governor.c
> >> +++ linux-pm/drivers/cpufreq/cpufreq_governor.c
> >> @@ -341,8 +341,9 @@ static void dbs_update_util_handler(stru
> >>  {
> >>       struct cpu_dbs_info *cdbs = container_of(data, struct cpu_dbs_info, update_util);
> >>       struct policy_dbs_info *policy_dbs = cdbs->policy_dbs;
> >> -     u64 delta_ns;
> >> +     u64 delta_ns, lst;
> >>
> >> + start:
> >>       /*
> >>        * The work may not be allowed to be queued up right now.
> >>        * Possible reasons:
> >> @@ -357,7 +358,8 @@ static void dbs_update_util_handler(stru
> >>        * of sample_delay_ns used in the computation may be stale.
> >>        */
> >>       smp_rmb();
> >> -     delta_ns = time - policy_dbs->last_sample_time;
> >> +     lst = ACCESS_ONCE(policy_dbs->last_sample_time);
> >
> > The comment on the top of ACCESS_ONCE() asks us to use READ_ONCE() if possible.
> 
> I forgot about this one, thanks!
> 
> >> +     delta_ns = time - lst;
> >>       if ((s64)delta_ns < policy_dbs->sample_delay_ns)
> >>               return;
> >>
> >> @@ -366,9 +368,19 @@ static void dbs_update_util_handler(stru
> >>        * at this point.  Otherwise, we need to ensure that only one of the
> >>        * CPUs sharing the policy will do that.
> >>        */
> >> -     if (policy_dbs->is_shared &&
> >> -         !atomic_add_unless(&policy_dbs->work_count, 1, 1))
> >> -             return;
> >> +     if (policy_dbs->is_shared) {
> >> +             if (!atomic_add_unless(&policy_dbs->work_count, 1, 1))
> >> +                     return;
> >> +
> >> +             /*
> >> +              * If another CPU updated last_sample_time in the meantime, we
> >> +              * shouldn't be here, so clear the work counter and try again.
> >> +              */
> >> +             if (unlikely(lst != ACCESS_ONCE(policy_dbs->last_sample_time))) {
> >> +                     atomic_set(&policy_dbs->work_count, 0);
> >> +                     goto start;
> >> +             }
> >
> > I think we should be doing this here:
> >
> >         delta_ns = time - ACCESS_ONCE(policy_dbs->last_sample_time);
> >         if ((s64)delta_ns < policy_dbs->sample_delay_ns) {
> >                 atomic_set(&policy_dbs->work_count, 0);
> >                 return;
> >         }
> >
> > There is no point running the routine again, we already have ->work_count
> > incremented for us, lets do the check right now.
> 
> No, we need to check work_in_progress too.

Then maybe move first half of this routine into a separate function
and call it from the beginning and here ?

-- 
viresh

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH v2 1/2] cpufreq: governor: Fix race in dbs_update_util_handler()
  2016-02-21  2:14 ` [PATCH 1/2] cpufreq: governor: Fix race in dbs_update_util_handler() Rafael J. Wysocki
  2016-02-22  5:23   ` Viresh Kumar
@ 2016-02-22 13:14   ` Rafael J. Wysocki
  2016-02-22 13:45     ` Viresh Kumar
  1 sibling, 1 reply; 10+ messages in thread
From: Rafael J. Wysocki @ 2016-02-22 13:14 UTC (permalink / raw)
  To: Linux PM list, Viresh Kumar; +Cc: Linux Kernel Mailing List

From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

There is a scenario that may lead to undesired results in
dbs_update_util_handler().  Namely, if two CPUs sharing a policy
enter the funtion at the same time, pass the sample delay check
and then one of them is stalled until dbs_work_handler() (queued
up by the other CPU) clears the work counter, it may update the
work counter and queue up another work item prematurely.

To prevent that from happening, use the observation that the CPU
queuing up a work item in dbs_update_util_handler() updates the
last sample time.  This means that if another CPU was stalling after
passing the sample delay check and now successfully updated the work
counter as a result of the race described above, it will see the new
value of the last sample time which is different from what it used in
the sample delay check before.  If that happens, the sample delay
check passed previously is not valid any more, so the CPU should not
continue.

Fixes: f17cbb53783c (cpufreq: governor: Avoid atomic operations in hot paths)
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
---

Changes from v1:
- Typo in the changelog fixed.
- READ_ONCE() used instead of ACCESS_ONCE().
- If the race is detected, return instead of looping.

---
 drivers/cpufreq/cpufreq_governor.c |   21 ++++++++++++++++-----
 1 file changed, 16 insertions(+), 5 deletions(-)

Index: linux-pm/drivers/cpufreq/cpufreq_governor.c
===================================================================
--- linux-pm.orig/drivers/cpufreq/cpufreq_governor.c
+++ linux-pm/drivers/cpufreq/cpufreq_governor.c
@@ -341,7 +341,7 @@ static void dbs_update_util_handler(stru
 {
 	struct cpu_dbs_info *cdbs = container_of(data, struct cpu_dbs_info, update_util);
 	struct policy_dbs_info *policy_dbs = cdbs->policy_dbs;
-	u64 delta_ns;
+	u64 delta_ns, lst;
 
 	/*
 	 * The work may not be allowed to be queued up right now.
@@ -357,7 +357,8 @@ static void dbs_update_util_handler(stru
 	 * of sample_delay_ns used in the computation may be stale.
 	 */
 	smp_rmb();
-	delta_ns = time - policy_dbs->last_sample_time;
+	lst = READ_ONCE(policy_dbs->last_sample_time);
+	delta_ns = time - lst;
 	if ((s64)delta_ns < policy_dbs->sample_delay_ns)
 		return;
 
@@ -366,9 +367,19 @@ static void dbs_update_util_handler(stru
 	 * at this point.  Otherwise, we need to ensure that only one of the
 	 * CPUs sharing the policy will do that.
 	 */
-	if (policy_dbs->is_shared &&
-	    !atomic_add_unless(&policy_dbs->work_count, 1, 1))
-		return;
+	if (policy_dbs->is_shared) {
+		if (!atomic_add_unless(&policy_dbs->work_count, 1, 1))
+			return;
+
+		/*
+		 * If another CPU updated last_sample_time in the meantime, we
+		 * shouldn't be here, so clear the work counter and bail out.
+		 */
+		if (unlikely(lst != READ_ONCE(policy_dbs->last_sample_time))) {
+			atomic_set(&policy_dbs->work_count, 0);
+			return;
+		}
+	}
 
 	policy_dbs->last_sample_time = time;
 	policy_dbs->work_in_progress = true;


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/2] cpufreq: governor: Fix race in dbs_update_util_handler()
  2016-02-22 13:04       ` Viresh Kumar
@ 2016-02-22 13:30         ` Rafael J. Wysocki
  0 siblings, 0 replies; 10+ messages in thread
From: Rafael J. Wysocki @ 2016-02-22 13:30 UTC (permalink / raw)
  To: Viresh Kumar
  Cc: Rafael J. Wysocki, Rafael J. Wysocki, Linux PM list,
	Linux Kernel Mailing List

On Mon, Feb 22, 2016 at 2:04 PM, Viresh Kumar <viresh.kumar@linaro.org> wrote:
> On 22-02-16, 13:26, Rafael J. Wysocki wrote:
>> On Mon, Feb 22, 2016 at 6:23 AM, Viresh Kumar <viresh.kumar@linaro.org> wrote:
>> > On 21-02-16, 03:14, Rafael J. Wysocki wrote:
>> >> From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

[cut]

>> > I think we should be doing this here:
>> >
>> >         delta_ns = time - ACCESS_ONCE(policy_dbs->last_sample_time);
>> >         if ((s64)delta_ns < policy_dbs->sample_delay_ns) {
>> >                 atomic_set(&policy_dbs->work_count, 0);
>> >                 return;
>> >         }
>> >
>> > There is no point running the routine again, we already have ->work_count
>> > incremented for us, lets do the check right now.
>>
>> No, we need to check work_in_progress too.
>
> Then maybe move first half of this routine into a separate function
> and call it from the beginning and here ?

That won't help.   The time value is still going to be stale.

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v2 1/2] cpufreq: governor: Fix race in dbs_update_util_handler()
  2016-02-22 13:14   ` [PATCH v2 " Rafael J. Wysocki
@ 2016-02-22 13:45     ` Viresh Kumar
  0 siblings, 0 replies; 10+ messages in thread
From: Viresh Kumar @ 2016-02-22 13:45 UTC (permalink / raw)
  To: Rafael J. Wysocki; +Cc: Linux PM list, Linux Kernel Mailing List

On 22-02-16, 14:14, Rafael J. Wysocki wrote:
> From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
> 
> There is a scenario that may lead to undesired results in
> dbs_update_util_handler().  Namely, if two CPUs sharing a policy
> enter the funtion at the same time, pass the sample delay check
> and then one of them is stalled until dbs_work_handler() (queued
> up by the other CPU) clears the work counter, it may update the
> work counter and queue up another work item prematurely.
> 
> To prevent that from happening, use the observation that the CPU
> queuing up a work item in dbs_update_util_handler() updates the
> last sample time.  This means that if another CPU was stalling after
> passing the sample delay check and now successfully updated the work
> counter as a result of the race described above, it will see the new
> value of the last sample time which is different from what it used in
> the sample delay check before.  If that happens, the sample delay
> check passed previously is not valid any more, so the CPU should not
> continue.
> 
> Fixes: f17cbb53783c (cpufreq: governor: Avoid atomic operations in hot paths)
> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
> ---
> 
> Changes from v1:
> - Typo in the changelog fixed.
> - READ_ONCE() used instead of ACCESS_ONCE().
> - If the race is detected, return instead of looping.

Acked-by: Viresh Kumar <viresh.kumar@linaro.org>

-- 
viresh

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2016-02-22 13:45 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2016-02-21  2:10 [PATCH 0/2] cpufreq: governor: Fixups on top of recent changes Rafael J. Wysocki
2016-02-21  2:14 ` [PATCH 1/2] cpufreq: governor: Fix race in dbs_update_util_handler() Rafael J. Wysocki
2016-02-22  5:23   ` Viresh Kumar
2016-02-22 12:26     ` Rafael J. Wysocki
2016-02-22 13:04       ` Viresh Kumar
2016-02-22 13:30         ` Rafael J. Wysocki
2016-02-22 13:14   ` [PATCH v2 " Rafael J. Wysocki
2016-02-22 13:45     ` Viresh Kumar
2016-02-21  2:15 ` [PATCH 2/2] cpufreq: governor: Make gov_set_update_util() static Rafael J. Wysocki
2016-02-22  5:24   ` Viresh Kumar

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).