Linux Power Management development
 help / color / mirror / Atom feed
From: Viresh Kumar <viresh.kumar@linaro.org>
To: "Rafael J. Wysocki" <rjw@rjwysocki.net>
Cc: Linux PM list <linux-pm@vger.kernel.org>,
	Linux Kernel Mailing List <linux-kernel@vger.kernel.org>
Subject: Re: [PATCH 1/2] cpufreq: governor: Fix race in dbs_update_util_handler()
Date: Mon, 22 Feb 2016 10:53:36 +0530	[thread overview]
Message-ID: <20160222052336.GB28226@vireshk-i7> (raw)
In-Reply-To: <2648721.JQxjFin8bu@vostro.rjw.lan>

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

  reply	other threads:[~2016-02-22  5:23 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
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 [this message]
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

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20160222052336.GB28226@vireshk-i7 \
    --to=viresh.kumar@linaro.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pm@vger.kernel.org \
    --cc=rjw@rjwysocki.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox