From patchwork Mon Aug 17 18:21:42 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Javi Merino X-Patchwork-Id: 7026431 X-Patchwork-Delegate: eduardo.valentin@ti.com Return-Path: X-Original-To: patchwork-linux-pm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id ECE0D9F373 for ; Mon, 17 Aug 2015 18:22:51 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 0F66120711 for ; Mon, 17 Aug 2015 18:22:51 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 2A6812073B for ; Mon, 17 Aug 2015 18:22:49 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751585AbbHQSWY (ORCPT ); Mon, 17 Aug 2015 14:22:24 -0400 Received: from foss.arm.com ([217.140.101.70]:46396 "EHLO foss.arm.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751181AbbHQSWW (ORCPT ); Mon, 17 Aug 2015 14:22:22 -0400 Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.72.51.249]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 7FDB975; Mon, 17 Aug 2015 11:22:14 -0700 (PDT) Received: from e104805.cambridge.arm.com (e104805.cambridge.arm.com [10.2.131.190]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPA id AAD7B3F59B; Mon, 17 Aug 2015 11:22:20 -0700 (PDT) From: Javi Merino To: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org, Javi Merino , Zhang Rui , Eduardo Valentin Subject: [PATCH 1/2] thermal: cpu_cooling: don't call kcalloc() under rcu_read_lock Date: Mon, 17 Aug 2015 19:21:42 +0100 Message-Id: <1439835703-16470-2-git-send-email-javi.merino@arm.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1439835703-16470-1-git-send-email-javi.merino@arm.com> References: <1439835703-16470-1-git-send-email-javi.merino@arm.com> Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org X-Spam-Status: No, score=-7.5 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP build_dyn_power_table() allocates the power table while holding rcu_read_lock. kcalloc using GFP_KERNEL may sleep, so it can't be called in an RCU read-side path. Move the rcu protection to the part of the function that really needs it: the part that handles the dev_pm_opp pointer received from dev_pm_opp_find_freq_ceil(). In the unlikely case that there is an OPP added to the cpu while this function is running, return -EAGAIN. Fixes: c36cf0717631 ("thermal: cpu_cooling: implement the power cooling device API") Cc: Zhang Rui Cc: Eduardo Valentin Signed-off-by: Javi Merino --- drivers/thermal/cpu_cooling.c | 47 +++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index 6509c61b9648..b6c0f93ea5c2 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c @@ -253,7 +253,9 @@ static int cpufreq_thermal_notifier(struct notifier_block *nb, * efficiently. Power is stored in mW, frequency in KHz. The * resulting table is in ascending order. * - * Return: 0 on success, -E* on error. + * Return: 0 on success, -EINVAL if there are no OPPs for any CPUs, + * -ENOMEM if we run out of memory or -EAGAIN if an OPP was + * added/enabled while the function was executing. */ static int build_dyn_power_table(struct cpufreq_cooling_device *cpufreq_device, u32 capacitance) @@ -261,11 +263,9 @@ static int build_dyn_power_table(struct cpufreq_cooling_device *cpufreq_device, struct power_table *power_table; struct dev_pm_opp *opp; struct device *dev = NULL; - int num_opps = 0, cpu, i, ret = 0; + int num_opps = 0, cpu, i; unsigned long freq; - rcu_read_lock(); - for_each_cpu(cpu, &cpufreq_device->allowed_cpus) { dev = get_cpu_device(cpu); if (!dev) { @@ -275,24 +275,20 @@ static int build_dyn_power_table(struct cpufreq_cooling_device *cpufreq_device, } num_opps = dev_pm_opp_get_opp_count(dev); - if (num_opps > 0) { + if (num_opps > 0) break; - } else if (num_opps < 0) { - ret = num_opps; - goto unlock; - } + else if (num_opps < 0) + return num_opps; } - if (num_opps == 0) { - ret = -EINVAL; - goto unlock; - } + if (num_opps == 0) + return -EINVAL; power_table = kcalloc(num_opps, sizeof(*power_table), GFP_KERNEL); - if (!power_table) { - ret = -ENOMEM; - goto unlock; - } + if (!power_table) + return -ENOMEM; + + rcu_read_lock(); for (freq = 0, i = 0; opp = dev_pm_opp_find_freq_ceil(dev, &freq), !IS_ERR(opp); @@ -300,6 +296,11 @@ static int build_dyn_power_table(struct cpufreq_cooling_device *cpufreq_device, u32 freq_mhz, voltage_mv; u64 power; + if (i >= num_opps) { + rcu_read_unlock(); + return -EAGAIN; + } + freq_mhz = freq / 1000000; voltage_mv = dev_pm_opp_get_voltage(opp) / 1000; @@ -317,18 +318,16 @@ static int build_dyn_power_table(struct cpufreq_cooling_device *cpufreq_device, power_table[i].power = power; } - if (i == 0) { - ret = PTR_ERR(opp); - goto unlock; - } + rcu_read_unlock(); + + if (i != num_opps) + return PTR_ERR(opp); cpufreq_device->cpu_dev = dev; cpufreq_device->dyn_power_table = power_table; cpufreq_device->dyn_power_table_entries = i; -unlock: - rcu_read_unlock(); - return ret; + return 0; } static u32 cpu_freq_to_power(struct cpufreq_cooling_device *cpufreq_device,