From patchwork Tue Sep 18 20:51:24 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tejun Heo X-Patchwork-Id: 1474671 Return-Path: X-Original-To: patchwork-linux-pm@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork2.kernel.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by patchwork2.kernel.org (Postfix) with ESMTP id 9F7A2DF24C for ; Tue, 18 Sep 2012 20:51:31 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1755247Ab2IRUva (ORCPT ); Tue, 18 Sep 2012 16:51:30 -0400 Received: from mail-pb0-f46.google.com ([209.85.160.46]:65462 "EHLO mail-pb0-f46.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1755236Ab2IRUv3 (ORCPT ); Tue, 18 Sep 2012 16:51:29 -0400 Received: by pbbrr13 with SMTP id rr13so790109pbb.19 for ; Tue, 18 Sep 2012 13:51:28 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=sender:date:from:to:cc:subject:message-id:references:mime-version :content-type:content-disposition:in-reply-to:user-agent; bh=+cb7adEhIeQoshBTUX09s5lvE2IssWx1S2HuNivXcGk=; b=DpPoXcr1slnvEdUMo2VbD1Mw/3j96IgQLAND0z8YgYO4/6yC41tWhjEXCD07RWwKSL HKCulyMTPOdEnajmmSCJR9eijgPnqYD0HA+NmhddTpOXwXi1mw451eQbMuJLa5NI75Al qgJmLnWEGAVmPkeEQRBTaYS3DxFLEkyXVcNQT21Bgijo0QH4pmOpL9QJgl0fl2uuL03g gxERtYeV+U+QqAGzg4lYTHETA6l3oN6wHP9FzQo2na3YScLdjnorMivz8Ky0WZZOy8/e GG7FICiQWMHtR9k4AgI9rVvw1bUuJIvwGteIY037Hj+5lwmo+F2czpu4lkRRhv9oGILK 2Hvg== Received: by 10.68.220.201 with SMTP id py9mr1679365pbc.137.1348001488764; Tue, 18 Sep 2012 13:51:28 -0700 (PDT) Received: from google.com (wtj.mtv.corp.google.com [172.18.110.84]) by mx.google.com with ESMTPS id c1sm110787pay.19.2012.09.18.13.51.26 (version=SSLv3 cipher=OTHER); Tue, 18 Sep 2012 13:51:27 -0700 (PDT) Date: Tue, 18 Sep 2012 13:51:24 -0700 From: Tejun Heo To: Linus Torvalds Cc: "Rafael J. Wysocki" , linux-kernel@vger.kernel.org, cpufreq@vger.kernel.org, linux-pm@vger.kernel.org, Duncan <1i5t5.duncan@cox.net>, Andreas Herrmann , Jiri Kosina , Bjorn Helgaas , Len Brown Subject: [PATCH 3.6-rc6 2/2] cpufreq/powernow-k8: workqueue user shouldn't migrate the kworker to another CPU Message-ID: <20120918205124.GH8474@google.com> References: <20120917201721.GJ18677@google.com> <20120918201231.GF8474@google.com> <20120918204934.GG8474@google.com> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: <20120918204934.GG8474@google.com> User-Agent: Mutt/1.5.20 (2009-06-14) Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org powernowk8_target() runs off a per-cpu work item and if the cpufreq_policy->cpu is different from the current one, it migrates the kworker to the target CPU by manipulating current->cpus_allowed. The function migrates the kworker back to the original CPU but this is still broken. Workqueue concurrency management requires the kworkers to stay on the same CPU and powernowk8_target() ends up triggerring BUG_ON(rq != this_rq()) in try_to_wake_up_local() if it contends on fidvid_mutex and sleeps. It is unclear why this bug is being reported now. Duncan says it appeared to be a regression of 3.6-rc1 and couldn't reproduce it on 3.5. Bisection seemed to point to 63d95a91 "workqueue: use @pool instead of @gcwq or @cpu where applicable" which is an non-functional change. Given that the reproduce case sometimes took upto days to trigger, it's easy to be misled while bisecting. Maybe something made contention on fidvid_mutex more likely? I don't know. This patch fixes the bug by using work_on_cpu() instead if @pol->cpu isn't the same as the current one. The code assumes that cpufreq_policy->cpu is kept online by the caller, which Rafael tells me is the case. stable: This needs the patch "workqueue: reimplement work_on_cpu() using system_wq"; otherwise, the behavior could be horrible. Signed-off-by: Tejun Heo Reported-by: Duncan <1i5t5.duncan@cox.net> Cc: Rafael J. Wysocki Cc: Andreas Herrmann Cc: stable@vger.kernel.org Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=47301 --- drivers/cpufreq/powernow-k8.c | 63 ++++++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 29 deletions(-) -- To unsubscribe from this list: send the line "unsubscribe linux-pm" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html --- a/drivers/cpufreq/powernow-k8.c +++ b/drivers/cpufreq/powernow-k8.c @@ -35,7 +35,6 @@ #include #include #include -#include /* for current / set_cpus_allowed() */ #include #include @@ -1139,16 +1138,23 @@ static int transition_frequency_pstate(s return res; } -/* Driver entry point to switch to the target frequency */ -static int powernowk8_target(struct cpufreq_policy *pol, - unsigned targfreq, unsigned relation) +struct powernowk8_target_arg { + struct cpufreq_policy *pol; + unsigned targfreq; + unsigned relation; +}; + +static long powernowk8_target_fn(void *arg) { - cpumask_var_t oldmask; + struct powernowk8_target_arg *pta = arg; + struct cpufreq_policy *pol = pta->pol; + unsigned targfreq = pta->targfreq; + unsigned relation = pta->relation; struct powernow_k8_data *data = per_cpu(powernow_data, pol->cpu); u32 checkfid; u32 checkvid; unsigned int newstate; - int ret = -EIO; + int ret; if (!data) return -EINVAL; @@ -1156,29 +1162,16 @@ static int powernowk8_target(struct cpuf checkfid = data->currfid; checkvid = data->currvid; - /* only run on specific CPU from here on. */ - /* This is poor form: use a workqueue or smp_call_function_single */ - if (!alloc_cpumask_var(&oldmask, GFP_KERNEL)) - return -ENOMEM; - - cpumask_copy(oldmask, tsk_cpus_allowed(current)); - set_cpus_allowed_ptr(current, cpumask_of(pol->cpu)); - - if (smp_processor_id() != pol->cpu) { - printk(KERN_ERR PFX "limiting to cpu %u failed\n", pol->cpu); - goto err_out; - } - if (pending_bit_stuck()) { printk(KERN_ERR PFX "failing targ, change pending bit set\n"); - goto err_out; + return -EIO; } pr_debug("targ: cpu %d, %d kHz, min %d, max %d, relation %d\n", pol->cpu, targfreq, pol->min, pol->max, relation); if (query_current_values_with_pending_wait(data)) - goto err_out; + return -EIO; if (cpu_family != CPU_HW_PSTATE) { pr_debug("targ: curr fid 0x%x, vid 0x%x\n", @@ -1196,7 +1189,7 @@ static int powernowk8_target(struct cpuf if (cpufreq_frequency_table_target(pol, data->powernow_table, targfreq, relation, &newstate)) - goto err_out; + return -EIO; mutex_lock(&fidvid_mutex); @@ -1209,9 +1202,8 @@ static int powernowk8_target(struct cpuf ret = transition_frequency_fidvid(data, newstate); if (ret) { printk(KERN_ERR PFX "transition frequency failed\n"); - ret = 1; mutex_unlock(&fidvid_mutex); - goto err_out; + return 1; } mutex_unlock(&fidvid_mutex); @@ -1220,12 +1212,25 @@ static int powernowk8_target(struct cpuf data->powernow_table[newstate].index); else pol->cur = find_khz_freq_from_fid(data->currfid); - ret = 0; -err_out: - set_cpus_allowed_ptr(current, oldmask); - free_cpumask_var(oldmask); - return ret; + return 0; +} + +/* Driver entry point to switch to the target frequency */ +static int powernowk8_target(struct cpufreq_policy *pol, + unsigned targfreq, unsigned relation) +{ + struct powernowk8_target_arg pta = { .pol = pol, .targfreq = targfreq, + .relation = relation }; + + /* + * Must run on @pol->cpu. cpufreq core is responsible for ensuring + * that we're bound to the current CPU and pol->cpu stays online. + */ + if (smp_processor_id() == pol->cpu) + return powernowk8_target_fn(&pta); + else + return work_on_cpu(pol->cpu, powernowk8_target_fn, &pta); } /* Driver entry point to verify the policy and range of frequencies */