Message ID | 20230516133248.712242-1-zengheng4@huawei.com |
---|---|
State | New |
Headers |
Return-Path: <linux-kernel-owner@vger.kernel.org> Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:b0ea:0:b0:3b6:4342:cba0 with SMTP id b10csp447803vqo; Tue, 16 May 2023 07:00:28 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ7y+XADNf6LCGPMZwq6t6owK52zBp7QYy/EUI2pD2JO11xZUUnaXuTUP2F4sWQAkX0NMQDP X-Received: by 2002:a17:902:cec9:b0:19a:a9d8:e47f with SMTP id d9-20020a170902cec900b0019aa9d8e47fmr53206053plg.36.1684245628314; Tue, 16 May 2023 07:00:28 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1684245628; cv=none; d=google.com; s=arc-20160816; b=hanOkiksBDm5L0P1hIPcAO1XEXOf18aTXf+/FTtGioEhGR7ISp8oWr4d01W3f5BHd2 SLiHObHARaVelNWLq4Nbq12K0+fjELu9PLtGiW0N/iIkQRH3XJDnKADaLF1ylH565zJz aPqqhzQyMq0bNz/l1QHGULokFTfQX6n8JAds0RH1O5SVu8EqHXKU8etGcBVNSgI38mpI wtID36rs6CdQflhVu0/Wsa399wUFy6oq6QGYaeatqMSbCIOGKyHqDmjzBi8lDryM28R7 bYD96psrbop5rjjlr8R8w8n7ABwFE//Q7ZzeutT1/4BDRwLPOEJcxWjFwFEn7Au0tb9J Ck1g== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :message-id:date:subject:cc:to:from; bh=PzmuAghbzhLXpxPFHB7IH3hgsBrsQGuYP5Hk5hw9Exk=; b=qXFUszigvXoW28x+5XrulkbhefVDh+6z7iL7Q808Wu+SdfXJDSQj/R2oSemr8cLIGG Q/OIhWMk+LxDAN3zGc7M48Iht35vbk++Qjekqsbjmfc+jk8BqE7UWab1qBPdtGk3ugCR Z72Sa+UNpG8zMgqh/ybDQrJPP/7mhxDRs0eVoh0IaForCBXZrdPnjMYPdStnPiYWil4d q2T7Ye8Ko8dwTiayPJSKVd9dE23vV9HEWDlRiHboZtU9Tw5HI1XH1ldEy/LS5pq7YD+2 odAzAz3vUNmQCp7uOXmqvb/TOBk6s3UcWyvMHKlvFLYXzOOL01+OMvb+N+eux9SZD7uJ OvSA== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=huawei.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id q12-20020a17090311cc00b001ae16575087si5903625plh.595.2023.05.16.07.00.14; Tue, 16 May 2023 07:00:28 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=huawei.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233538AbjEPNdL (ORCPT <rfc822;peekingduck44@gmail.com> + 99 others); Tue, 16 May 2023 09:33:11 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:45576 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232641AbjEPNdH (ORCPT <rfc822;linux-kernel@vger.kernel.org>); Tue, 16 May 2023 09:33:07 -0400 Received: from szxga02-in.huawei.com (szxga02-in.huawei.com [45.249.212.188]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 4CB3D10DA; Tue, 16 May 2023 06:33:06 -0700 (PDT) Received: from kwepemi500024.china.huawei.com (unknown [172.30.72.56]) by szxga02-in.huawei.com (SkyGuard) with ESMTP id 4QLH9r5MzwzTkg4; Tue, 16 May 2023 21:28:16 +0800 (CST) Received: from huawei.com (10.175.103.91) by kwepemi500024.china.huawei.com (7.221.188.100) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.23; Tue, 16 May 2023 21:33:03 +0800 From: Zeng Heng <zengheng4@huawei.com> To: <lenb@kernel.org>, <viresh.kumar@linaro.org>, <rafael@kernel.org> CC: <linux-kernel@vger.kernel.org>, <linux-pm@vger.kernel.org>, <wangxiongfeng2@huawei.com>, <xiexiuqi@huawei.com>, <liwei391@huawei.com>, <linux-acpi@vger.kernel.org>, <weiyongjun1@huawei.com> Subject: [PATCH v2 1/2] cpufreq: CPPC: keep target core awake when reading its cpufreq rate Date: Tue, 16 May 2023 21:32:46 +0800 Message-ID: <20230516133248.712242-1-zengheng4@huawei.com> X-Mailer: git-send-email 2.25.1 MIME-Version: 1.0 Content-Transfer-Encoding: 7BIT Content-Type: text/plain; charset=US-ASCII X-Originating-IP: [10.175.103.91] X-ClientProxiedBy: dggems701-chm.china.huawei.com (10.3.19.178) To kwepemi500024.china.huawei.com (7.221.188.100) X-CFilter-Loop: Reflected X-Spam-Status: No, score=-4.2 required=5.0 tests=BAYES_00,RCVD_IN_DNSWL_MED, SPF_HELO_NONE,SPF_PASS,T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: <linux-kernel.vger.kernel.org> X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1766059543535857504?= X-GMAIL-MSGID: =?utf-8?q?1766059543535857504?= |
Series |
[v2,1/2] cpufreq: CPPC: keep target core awake when reading its cpufreq rate
|
|
Commit Message
Zeng Heng
May 16, 2023, 1:32 p.m. UTC
As ARM AMU's document says, all counters are subject to any changes
in clock frequency, including clock stopping caused by the WFI and WFE
instructions.
Therefore, using smp_call_on_cpu() to trigger target CPU to
read self's AMU counters, which ensures the counters are working
properly during calculation.
Signed-off-by: Zeng Heng <zengheng4@huawei.com>
---
drivers/cpufreq/cppc_cpufreq.c | 30 +++++++++++++++++++-----------
1 file changed, 19 insertions(+), 11 deletions(-)
Comments
+Ionela, Sumit, Yang, Hello Zeng, I think solutions around related issues were suggested at: [1] https://lore.kernel.org/all/20230418113459.12860-7-sumitg@nvidia.com/ [2] https://lore.kernel.org/all/20230328193846.8757-1-yang@os.amperecomputing.com/ [3] https://lore.kernel.org/all/ZEl1Fms%2FJmdEZsVn@arm.com/ About this patch, it seems to mean that CPPC counters of CPUx are always accessed from CPUx, even when they are not AMUs. For instance CPPC counters could be memory mapped and accessible from any CPU. cpu_has_amu_feat() should allow to probe if a CPU uses AMUs or not, and [2] had an implementation using it. Another comment about PATCH 2/2 is that if the counters are accessed through FFH, arm64 version of cpc_read_ffh() is calling counters_read_on_cpu(), and a comment in counters_read_on_cpu() seems to specify the function must be called with interrupt enabled. I think the best solution so far was the one at [3], suggested by Ionela, but it doesn't seem to solve your issue. Indeed, it is not checked whether the counters are AMU counters and that they must be remotely read (to have the CPU awake), Regards, Pierre On 5/16/23 15:32, Zeng Heng wrote: > As ARM AMU's document says, all counters are subject to any changes > in clock frequency, including clock stopping caused by the WFI and WFE > instructions. > > Therefore, using smp_call_on_cpu() to trigger target CPU to > read self's AMU counters, which ensures the counters are working > properly during calculation. > > Signed-off-by: Zeng Heng <zengheng4@huawei.com> > --- > drivers/cpufreq/cppc_cpufreq.c | 30 +++++++++++++++++++----------- > 1 file changed, 19 insertions(+), 11 deletions(-) > > diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c > index 022e3555407c..910167f58bb3 100644 > --- a/drivers/cpufreq/cppc_cpufreq.c > +++ b/drivers/cpufreq/cppc_cpufreq.c > @@ -837,9 +837,24 @@ static int cppc_perf_from_fbctrs(struct cppc_cpudata *cpu_data, > return (reference_perf * delta_delivered) / delta_reference; > } > > +static int cppc_get_perf_ctrs_smp(void *val) > +{ > + int cpu = smp_processor_id(); > + struct cppc_perf_fb_ctrs *fb_ctrs = val; > + int ret; > + > + ret = cppc_get_perf_ctrs(cpu, fb_ctrs); > + if (ret) > + return ret; > + > + udelay(2); /* 2usec delay between sampling */ > + > + return cppc_get_perf_ctrs(cpu, fb_ctrs + 1); > +} > + > static unsigned int cppc_cpufreq_get_rate(unsigned int cpu) > { > - struct cppc_perf_fb_ctrs fb_ctrs_t0 = {0}, fb_ctrs_t1 = {0}; > + struct cppc_perf_fb_ctrs fb_ctrs[2] = {0}; > struct cpufreq_policy *policy = cpufreq_cpu_get(cpu); > struct cppc_cpudata *cpu_data = policy->driver_data; > u64 delivered_perf; > @@ -847,19 +862,12 @@ static unsigned int cppc_cpufreq_get_rate(unsigned int cpu) > > cpufreq_cpu_put(policy); > > - ret = cppc_get_perf_ctrs(cpu, &fb_ctrs_t0); > - if (ret) > - return ret; > - > - udelay(2); /* 2usec delay between sampling */ > - > - ret = cppc_get_perf_ctrs(cpu, &fb_ctrs_t1); > + ret = smp_call_on_cpu(cpu, cppc_get_perf_ctrs_smp, fb_ctrs, 1); > if (ret) > return ret; > > - delivered_perf = cppc_perf_from_fbctrs(cpu_data, &fb_ctrs_t0, > - &fb_ctrs_t1); > - > + delivered_perf = cppc_perf_from_fbctrs(cpu_data, fb_ctrs, > + fb_ctrs + 1); > return cppc_cpufreq_perf_to_khz(cpu_data, delivered_perf); > } >
On 17/05/23 13:47, Pierre Gondois wrote: > External email: Use caution opening links or attachments > > > +Ionela, Sumit, Yang, > > Hello Zeng, > > I think solutions around related issues were suggested at: > > [1] https://lore.kernel.org/all/20230418113459.12860-7-sumitg@nvidia.com/ > [2] > https://lore.kernel.org/all/20230328193846.8757-1-yang@os.amperecomputing.com/ > [3] https://lore.kernel.org/all/ZEl1Fms%2FJmdEZsVn@arm.com/ > > About this patch, it seems to mean that CPPC counters of CPUx are always > accessed from CPUx, even when they are not AMUs. For instance CPPC > counters could be memory mapped and accessible from any CPU. > cpu_has_amu_feat() should allow to probe if a CPU uses AMUs or not, > and [2] had an implementation using it. > > Another comment about PATCH 2/2 is that if the counters are accessed > through FFH, arm64 version of cpc_read_ffh() is calling > counters_read_on_cpu(), and a comment in counters_read_on_cpu() seems > to specify the function must be called with interrupt enabled. > > I think the best solution so far was the one at [3], suggested by Ionela, > but it doesn't seem to solve your issue. Indeed, it is not checked whether > the counters are AMU counters and that they must be remotely read (to > have the CPU awake), > > Regards, > Pierre > I think the solution in [1] is simple and solves all the three cases. Also, it provides better accuracy between the set and get frequency as compared to [3]. This can be merged and can later still be improved in Upstream. If OK, I can send new version by changing the patch to apply for all ARM SoC's with AMU and not specific to Tegra. Thank you, Sumit Gupta > > On 5/16/23 15:32, Zeng Heng wrote: >> As ARM AMU's document says, all counters are subject to any changes >> in clock frequency, including clock stopping caused by the WFI and WFE >> instructions. >> >> Therefore, using smp_call_on_cpu() to trigger target CPU to >> read self's AMU counters, which ensures the counters are working >> properly during calculation. >> >> Signed-off-by: Zeng Heng <zengheng4@huawei.com> >> --- >> drivers/cpufreq/cppc_cpufreq.c | 30 +++++++++++++++++++----------- >> 1 file changed, 19 insertions(+), 11 deletions(-) >> >> diff --git a/drivers/cpufreq/cppc_cpufreq.c >> b/drivers/cpufreq/cppc_cpufreq.c >> index 022e3555407c..910167f58bb3 100644 >> --- a/drivers/cpufreq/cppc_cpufreq.c >> +++ b/drivers/cpufreq/cppc_cpufreq.c >> @@ -837,9 +837,24 @@ static int cppc_perf_from_fbctrs(struct >> cppc_cpudata *cpu_data, >> return (reference_perf * delta_delivered) / delta_reference; >> } >> >> +static int cppc_get_perf_ctrs_smp(void *val) >> +{ >> + int cpu = smp_processor_id(); >> + struct cppc_perf_fb_ctrs *fb_ctrs = val; >> + int ret; >> + >> + ret = cppc_get_perf_ctrs(cpu, fb_ctrs); >> + if (ret) >> + return ret; >> + >> + udelay(2); /* 2usec delay between sampling */ >> + >> + return cppc_get_perf_ctrs(cpu, fb_ctrs + 1); >> +} >> + >> static unsigned int cppc_cpufreq_get_rate(unsigned int cpu) >> { >> - struct cppc_perf_fb_ctrs fb_ctrs_t0 = {0}, fb_ctrs_t1 = {0}; >> + struct cppc_perf_fb_ctrs fb_ctrs[2] = {0}; >> struct cpufreq_policy *policy = cpufreq_cpu_get(cpu); >> struct cppc_cpudata *cpu_data = policy->driver_data; >> u64 delivered_perf; >> @@ -847,19 +862,12 @@ static unsigned int >> cppc_cpufreq_get_rate(unsigned int cpu) >> >> cpufreq_cpu_put(policy); >> >> - ret = cppc_get_perf_ctrs(cpu, &fb_ctrs_t0); >> - if (ret) >> - return ret; >> - >> - udelay(2); /* 2usec delay between sampling */ >> - >> - ret = cppc_get_perf_ctrs(cpu, &fb_ctrs_t1); >> + ret = smp_call_on_cpu(cpu, cppc_get_perf_ctrs_smp, fb_ctrs, 1); >> if (ret) >> return ret; >> >> - delivered_perf = cppc_perf_from_fbctrs(cpu_data, &fb_ctrs_t0, >> - &fb_ctrs_t1); >> - >> + delivered_perf = cppc_perf_from_fbctrs(cpu_data, fb_ctrs, >> + fb_ctrs + 1); >> return cppc_cpufreq_perf_to_khz(cpu_data, delivered_perf); >> } >>
On 5/17/23 8:03 AM, Sumit Gupta wrote: > > > On 17/05/23 13:47, Pierre Gondois wrote: >> External email: Use caution opening links or attachments >> >> >> +Ionela, Sumit, Yang, >> >> Hello Zeng, >> >> I think solutions around related issues were suggested at: >> >> [1] >> https://lore.kernel.org/all/20230418113459.12860-7-sumitg@nvidia.com/ >> [2] >> https://lore.kernel.org/all/20230328193846.8757-1-yang@os.amperecomputing.com/ >> [3] https://lore.kernel.org/all/ZEl1Fms%2FJmdEZsVn@arm.com/ >> >> About this patch, it seems to mean that CPPC counters of CPUx are always >> accessed from CPUx, even when they are not AMUs. For instance CPPC >> counters could be memory mapped and accessible from any CPU. >> cpu_has_amu_feat() should allow to probe if a CPU uses AMUs or not, >> and [2] had an implementation using it. >> >> Another comment about PATCH 2/2 is that if the counters are accessed >> through FFH, arm64 version of cpc_read_ffh() is calling >> counters_read_on_cpu(), and a comment in counters_read_on_cpu() seems >> to specify the function must be called with interrupt enabled. >> >> I think the best solution so far was the one at [3], suggested by >> Ionela, >> but it doesn't seem to solve your issue. Indeed, it is not checked >> whether >> the counters are AMU counters and that they must be remotely read (to >> have the CPU awake), >> >> Regards, >> Pierre >> > > I think the solution in [1] is simple and solves all the three cases. > Also, it provides better accuracy between the set and get frequency as > compared to [3]. I don't think [1] patches work for our case. We use mmio instead of AMU. Increasing delay could help to mitigate it somehow, buyt 25us is not good enough for our case. IIRC the fix proposed by Ionela works for both yours and mine. > > This can be merged and can later still be improved in Upstream. > > If OK, I can send new version by changing the patch to apply for all > ARM SoC's with AMU and not specific to Tegra. > > Thank you, > Sumit Gupta > >> >> On 5/16/23 15:32, Zeng Heng wrote: >>> As ARM AMU's document says, all counters are subject to any changes >>> in clock frequency, including clock stopping caused by the WFI and WFE >>> instructions. >>> >>> Therefore, using smp_call_on_cpu() to trigger target CPU to >>> read self's AMU counters, which ensures the counters are working >>> properly during calculation. >>> >>> Signed-off-by: Zeng Heng <zengheng4@huawei.com> >>> --- >>> drivers/cpufreq/cppc_cpufreq.c | 30 +++++++++++++++++++----------- >>> 1 file changed, 19 insertions(+), 11 deletions(-) >>> >>> diff --git a/drivers/cpufreq/cppc_cpufreq.c >>> b/drivers/cpufreq/cppc_cpufreq.c >>> index 022e3555407c..910167f58bb3 100644 >>> --- a/drivers/cpufreq/cppc_cpufreq.c >>> +++ b/drivers/cpufreq/cppc_cpufreq.c >>> @@ -837,9 +837,24 @@ static int cppc_perf_from_fbctrs(struct >>> cppc_cpudata *cpu_data, >>> return (reference_perf * delta_delivered) / delta_reference; >>> } >>> >>> +static int cppc_get_perf_ctrs_smp(void *val) >>> +{ >>> + int cpu = smp_processor_id(); >>> + struct cppc_perf_fb_ctrs *fb_ctrs = val; >>> + int ret; >>> + >>> + ret = cppc_get_perf_ctrs(cpu, fb_ctrs); >>> + if (ret) >>> + return ret; >>> + >>> + udelay(2); /* 2usec delay between sampling */ >>> + >>> + return cppc_get_perf_ctrs(cpu, fb_ctrs + 1); >>> +} >>> + >>> static unsigned int cppc_cpufreq_get_rate(unsigned int cpu) >>> { >>> - struct cppc_perf_fb_ctrs fb_ctrs_t0 = {0}, fb_ctrs_t1 = {0}; >>> + struct cppc_perf_fb_ctrs fb_ctrs[2] = {0}; >>> struct cpufreq_policy *policy = cpufreq_cpu_get(cpu); >>> struct cppc_cpudata *cpu_data = policy->driver_data; >>> u64 delivered_perf; >>> @@ -847,19 +862,12 @@ static unsigned int >>> cppc_cpufreq_get_rate(unsigned int cpu) >>> >>> cpufreq_cpu_put(policy); >>> >>> - ret = cppc_get_perf_ctrs(cpu, &fb_ctrs_t0); >>> - if (ret) >>> - return ret; >>> - >>> - udelay(2); /* 2usec delay between sampling */ >>> - >>> - ret = cppc_get_perf_ctrs(cpu, &fb_ctrs_t1); >>> + ret = smp_call_on_cpu(cpu, cppc_get_perf_ctrs_smp, fb_ctrs, 1); >>> if (ret) >>> return ret; >>> >>> - delivered_perf = cppc_perf_from_fbctrs(cpu_data, &fb_ctrs_t0, >>> - &fb_ctrs_t1); >>> - >>> + delivered_perf = cppc_perf_from_fbctrs(cpu_data, fb_ctrs, >>> + fb_ctrs + 1); >>> return cppc_cpufreq_perf_to_khz(cpu_data, delivered_perf); >>> } >>>
在 2023/5/17 23:03, Sumit Gupta 写道: > > > On 17/05/23 13:47, Pierre Gondois wrote: >> External email: Use caution opening links or attachments >> >> >> +Ionela, Sumit, Yang, >> >> Hello Zeng, >> >> I think solutions around related issues were suggested at: >> >> [1] >> https://lore.kernel.org/all/20230418113459.12860-7-sumitg@nvidia.com/ >> [2] >> https://lore.kernel.org/all/20230328193846.8757-1-yang@os.amperecomputing.com/ >> [3] https://lore.kernel.org/all/ZEl1Fms%2FJmdEZsVn@arm.com/ >> >> About this patch, it seems to mean that CPPC counters of CPUx are always >> accessed from CPUx, even when they are not AMUs. For instance CPPC >> counters could be memory mapped and accessible from any CPU. >> cpu_has_amu_feat() should allow to probe if a CPU uses AMUs or not, >> and [2] had an implementation using it. >> >> Another comment about PATCH 2/2 is that if the counters are accessed >> through FFH, arm64 version of cpc_read_ffh() is calling >> counters_read_on_cpu(), and a comment in counters_read_on_cpu() seems >> to specify the function must be called with interrupt enabled. >> >> I think the best solution so far was the one at [3], suggested by >> Ionela, >> but it doesn't seem to solve your issue. Indeed, it is not checked >> whether >> the counters are AMU counters and that they must be remotely read (to >> have the CPU awake), >> >> Regards, >> Pierre >> > > I think the solution in [1] is simple and solves all the three cases. > Also, it provides better accuracy between the set and get frequency as > compared to [3]. > > This can be merged and can later still be improved in Upstream. > > If OK, I can send new version by changing the patch to apply for all > ARM SoC's with AMU and not specific to Tegra. > > Thank you, > Sumit Gupta > I vote solution [1] and it should be applied to all ARM SoCs with AMU. Zeng Heng >> >> On 5/16/23 15:32, Zeng Heng wrote: >>> As ARM AMU's document says, all counters are subject to any changes >>> in clock frequency, including clock stopping caused by the WFI and WFE >>> instructions. >>> >>> Therefore, using smp_call_on_cpu() to trigger target CPU to >>> read self's AMU counters, which ensures the counters are working >>> properly during calculation. >>> >>> Signed-off-by: Zeng Heng <zengheng4@huawei.com> >>> --- >>> drivers/cpufreq/cppc_cpufreq.c | 30 +++++++++++++++++++----------- >>> 1 file changed, 19 insertions(+), 11 deletions(-) >>> >>> diff --git a/drivers/cpufreq/cppc_cpufreq.c >>> b/drivers/cpufreq/cppc_cpufreq.c >>> index 022e3555407c..910167f58bb3 100644 >>> --- a/drivers/cpufreq/cppc_cpufreq.c >>> +++ b/drivers/cpufreq/cppc_cpufreq.c >>> @@ -837,9 +837,24 @@ static int cppc_perf_from_fbctrs(struct >>> cppc_cpudata *cpu_data, >>> return (reference_perf * delta_delivered) / delta_reference; >>> } >>> >>> +static int cppc_get_perf_ctrs_smp(void *val) >>> +{ >>> + int cpu = smp_processor_id(); >>> + struct cppc_perf_fb_ctrs *fb_ctrs = val; >>> + int ret; >>> + >>> + ret = cppc_get_perf_ctrs(cpu, fb_ctrs); >>> + if (ret) >>> + return ret; >>> + >>> + udelay(2); /* 2usec delay between sampling */ >>> + >>> + return cppc_get_perf_ctrs(cpu, fb_ctrs + 1); >>> +} >>> + >>> static unsigned int cppc_cpufreq_get_rate(unsigned int cpu) >>> { >>> - struct cppc_perf_fb_ctrs fb_ctrs_t0 = {0}, fb_ctrs_t1 = {0}; >>> + struct cppc_perf_fb_ctrs fb_ctrs[2] = {0}; >>> struct cpufreq_policy *policy = cpufreq_cpu_get(cpu); >>> struct cppc_cpudata *cpu_data = policy->driver_data; >>> u64 delivered_perf; >>> @@ -847,19 +862,12 @@ static unsigned int >>> cppc_cpufreq_get_rate(unsigned int cpu) >>> >>> cpufreq_cpu_put(policy); >>> >>> - ret = cppc_get_perf_ctrs(cpu, &fb_ctrs_t0); >>> - if (ret) >>> - return ret; >>> - >>> - udelay(2); /* 2usec delay between sampling */ >>> - >>> - ret = cppc_get_perf_ctrs(cpu, &fb_ctrs_t1); >>> + ret = smp_call_on_cpu(cpu, cppc_get_perf_ctrs_smp, fb_ctrs, 1); >>> if (ret) >>> return ret; >>> >>> - delivered_perf = cppc_perf_from_fbctrs(cpu_data, &fb_ctrs_t0, >>> - &fb_ctrs_t1); >>> - >>> + delivered_perf = cppc_perf_from_fbctrs(cpu_data, fb_ctrs, >>> + fb_ctrs + 1); >>> return cppc_cpufreq_perf_to_khz(cpu_data, delivered_perf); >>> } >>>
>>> >>> +Ionela, Sumit, Yang, >>> >>> Hello Zeng, >>> >>> I think solutions around related issues were suggested at: >>> >>> [1] >>> https://lore.kernel.org/all/20230418113459.12860-7-sumitg@nvidia.com/ >>> [2] >>> https://lore.kernel.org/all/20230328193846.8757-1-yang@os.amperecomputing.com/ >>> [3] https://lore.kernel.org/all/ZEl1Fms%2FJmdEZsVn@arm.com/ >>> >>> About this patch, it seems to mean that CPPC counters of CPUx are always >>> accessed from CPUx, even when they are not AMUs. For instance CPPC >>> counters could be memory mapped and accessible from any CPU. >>> cpu_has_amu_feat() should allow to probe if a CPU uses AMUs or not, >>> and [2] had an implementation using it. >>> >>> Another comment about PATCH 2/2 is that if the counters are accessed >>> through FFH, arm64 version of cpc_read_ffh() is calling >>> counters_read_on_cpu(), and a comment in counters_read_on_cpu() seems >>> to specify the function must be called with interrupt enabled. >>> >>> I think the best solution so far was the one at [3], suggested by >>> Ionela, >>> but it doesn't seem to solve your issue. Indeed, it is not checked >>> whether >>> the counters are AMU counters and that they must be remotely read (to >>> have the CPU awake), >>> >>> Regards, >>> Pierre >>> >> >> I think the solution in [1] is simple and solves all the three cases. >> Also, it provides better accuracy between the set and get frequency as >> compared to [3]. > > I don't think [1] patches work for our case. We use mmio instead of AMU. > Increasing delay could help to mitigate it somehow, buyt 25us is not > good enough for our case. IIRC the fix proposed by Ionela works for both > yours and mine. > I have added the CPC_IN_SYSTEM_MEMORY check from [2] in [1]. Could you please test if the below change works for you. ----------------------------------------- diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index 7ff269a78c20..67aa09b5f15c 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -1315,6 +1315,7 @@ int cppc_get_perf_ctrs(int cpunum, struct cppc_perf_fb_ctrs *perf_fb_ctrs) struct cppc_pcc_data *pcc_ss_data = NULL; u64 delivered, reference, ref_perf, ctr_wrap_time; int ret = 0, regs_in_pcc = 0; + unsigned long flags; if (!cpc_desc) { pr_debug("No CPC descriptor for CPU:%d\n", cpunum); @@ -1350,8 +1351,17 @@ int cppc_get_perf_ctrs(int cpunum, struct cppc_perf_fb_ctrs *perf_fb_ctrs) } } + if (CPC_IN_SYSTEM_MEMORY(delivered_reg) && + CPC_IN_SYSTEM_MEMORY(reference_reg)) + local_irq_save(flags); + cpc_read(cpunum, delivered_reg, &delivered); cpc_read(cpunum, reference_reg, &reference); + + if (CPC_IN_SYSTEM_MEMORY(delivered_reg) && + CPC_IN_SYSTEM_MEMORY(reference_reg)) + local_irq_restore(flags); + cpc_read(cpunum, ref_perf_reg, &ref_perf); /* diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c index 5e6a132a525e..23e690854459 100644 --- a/drivers/cpufreq/cppc_cpufreq.c +++ b/drivers/cpufreq/cppc_cpufreq.c @@ -46,6 +46,8 @@ static bool boost_supported; /* default 2usec delay between sampling */ static unsigned int sampling_delay_us = 2; +static bool get_rate_use_wq; + static void cppc_check_hisi_workaround(void); static void cppc_nvidia_workaround(void); @@ -99,6 +101,12 @@ struct cppc_freq_invariance { static DEFINE_PER_CPU(struct cppc_freq_invariance, cppc_freq_inv); static struct kthread_worker *kworker_fie; +struct feedback_ctrs { + u32 cpu; + struct cppc_perf_fb_ctrs fb_ctrs_t0; + struct cppc_perf_fb_ctrs fb_ctrs_t1; +}; + static unsigned int hisi_cppc_cpufreq_get_rate(unsigned int cpu); static int cppc_perf_from_fbctrs(struct cppc_cpudata *cpu_data, struct cppc_perf_fb_ctrs *fb_ctrs_t0, @@ -851,28 +859,44 @@ static int cppc_perf_from_fbctrs(struct cppc_cpudata *cpu_data, return (reference_perf * delta_delivered) / delta_reference; } +static int cppc_get_perf_ctrs_sync(void *fb_ctrs) +{ + struct feedback_ctrs *ctrs = fb_ctrs; + int ret; + + ret = cppc_get_perf_ctrs(ctrs->cpu, &(ctrs->fb_ctrs_t0)); + if (ret) + return ret; + + udelay(sampling_delay_us); + + ret = cppc_get_perf_ctrs(ctrs->cpu, &(ctrs->fb_ctrs_t1)); + if (ret) + return ret; + + return ret; +} + static unsigned int cppc_cpufreq_get_rate(unsigned int cpu) { - struct cppc_perf_fb_ctrs fb_ctrs_t0 = {0}, fb_ctrs_t1 = {0}; struct cpufreq_policy *policy = cpufreq_cpu_get(cpu); struct cppc_cpudata *cpu_data = policy->driver_data; + struct feedback_ctrs fb_ctrs = {0}; u64 delivered_perf; int ret; cpufreq_cpu_put(policy); + fb_ctrs.cpu = cpu; - ret = cppc_get_perf_ctrs(cpu, &fb_ctrs_t0); - if (ret) - return ret; - - udelay(sampling_delay_us); - - ret = cppc_get_perf_ctrs(cpu, &fb_ctrs_t1); + if (get_rate_use_wq) + ret = smp_call_on_cpu(cpu, cppc_get_perf_ctrs_sync, &fb_ctrs, false); + else + ret = cppc_get_perf_ctrs_sync(&fb_ctrs); if (ret) return ret; - delivered_perf = cppc_perf_from_fbctrs(cpu_data, &fb_ctrs_t0, - &fb_ctrs_t1); + delivered_perf = cppc_perf_from_fbctrs(cpu_data, &(fb_ctrs.fb_ctrs_t0), + &(fb_ctrs.fb_ctrs_t1)); return cppc_cpufreq_perf_to_khz(cpu_data, delivered_perf); } @@ -1002,10 +1026,18 @@ static void cppc_apply_workarounds(void) static int __init cppc_cpufreq_init(void) { int ret; + int cpu; if (!acpi_cpc_valid()) return -ENODEV; +#ifdef CONFIG_ARM64_AMU_EXTN + cpu = get_cpu_with_amu_feat(); + + if (cpu < nr_cpu_ids) + get_rate_use_wq = true; +#endif -------------------------------------------- We can add additional check to call smp_call_on_cpu() only when CPC_IN_FFH if we want to reduce the scope of calling smp_call_on_cpu. diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index 67aa09b5f15c..3d8348911403 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -110,6 +110,11 @@ static DEFINE_PER_CPU(struct cpc_desc *, cpc_desc_ptr); (cpc)->cpc_entry.reg.space_id == \ ACPI_ADR_SPACE_SYSTEM_IO) +/* Check if a CPC register is in FFH */ +#define CPC_IN_FFH(cpc) ((cpc)->type == ACPI_TYPE_BUFFER && \ + (cpc)->cpc_entry.reg.space_id == \ + ACPI_ADR_SPACE_FIXED_HARDWARE) + /* Evaluates to True if reg is a NULL register descriptor */ #define IS_NULL_REG(reg) ((reg)->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY && \ (reg)->address == 0 && \ @@ -437,6 +442,29 @@ bool acpi_cpc_valid(void) } EXPORT_SYMBOL_GPL(acpi_cpc_valid); +bool acpi_cpc_in_ffh(void) +{ + struct cpc_register_resource *delivered_reg, *reference_reg; + struct cpc_desc *cpc_ptr; + int cpu; + + if (acpi_disabled) + return false; + + for_each_possible_cpu(cpu) { + cpc_ptr = per_cpu(cpc_desc_ptr, cpu); + delivered_reg = &cpc_ptr->cpc_regs[DELIVERED_CTR]; + reference_reg = &cpc_ptr->cpc_regs[REFERENCE_CTR]; + + if (!CPC_IN_FFH(delivered_reg) || + !CPC_IN_FFH(reference_reg)) + return false; + } + + return true; +} +EXPORT_SYMBOL_GPL(acpi_cpc_in_ffh); + bool cppc_allow_fast_switch(void) { struct cpc_register_resource *desired_reg; diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c index 23e690854459..4109e00b957e 100644 --- a/drivers/cpufreq/cppc_cpufreq.c +++ b/drivers/cpufreq/cppc_cpufreq.c @@ -1034,7 +1034,7 @@ static int __init cppc_cpufreq_init(void) #ifdef CONFIG_ARM64_AMU_EXTN cpu = get_cpu_with_amu_feat(); - if (cpu < nr_cpu_ids) + if ((cpu < nr_cpu_ids) && acpi_cpc_in_ffh()) get_rate_use_wq = true; #endif >> >> This can be merged and can later still be improved in Upstream. >> >> If OK, I can send new version by changing the patch to apply for all >> ARM SoC's with AMU and not specific to Tegra. >> >> Thank you, >> Sumit Gupta
On Thu, May 18, 2023 at 08:10:36PM +0530, Sumit Gupta wrote: > > > > > > > > +Ionela, Sumit, Yang, > > > > > > > > Hello Zeng, > > > > > > > > I think solutions around related issues were suggested at: > > > > > > > > [1] > > > > https://lore.kernel.org/all/20230418113459.12860-7-sumitg@nvidia.com/ > > > > [2] > > > > https://lore.kernel.org/all/20230328193846.8757-1-yang@os.amperecomputing.com/ > > > > [3] https://lore.kernel.org/all/ZEl1Fms%2FJmdEZsVn@arm.com/ > > > > > > > > About this patch, it seems to mean that CPPC counters of CPUx are always > > > > accessed from CPUx, even when they are not AMUs. For instance CPPC > > > > counters could be memory mapped and accessible from any CPU. > > > > cpu_has_amu_feat() should allow to probe if a CPU uses AMUs or not, > > > > and [2] had an implementation using it. > > > > > > > > Another comment about PATCH 2/2 is that if the counters are accessed > > > > through FFH, arm64 version of cpc_read_ffh() is calling > > > > counters_read_on_cpu(), and a comment in counters_read_on_cpu() seems > > > > to specify the function must be called with interrupt enabled. > > > > > > > > I think the best solution so far was the one at [3], suggested by > > > > Ionela, > > > > but it doesn't seem to solve your issue. Indeed, it is not checked > > > > whether > > > > the counters are AMU counters and that they must be remotely read (to > > > > have the CPU awake), > > > > > > > > Regards, > > > > Pierre > > > > > > > > > > I think the solution in [1] is simple and solves all the three cases. > > > Also, it provides better accuracy between the set and get frequency as > > > compared to [3]. > > > > I don't think [1] patches work for our case. We use mmio instead of AMU. > > Increasing delay could help to mitigate it somehow, buyt 25us is not > > good enough for our case. IIRC the fix proposed by Ionela works for both > > yours and mine. > > FWIW, we are preparing a change providing an arch specific implementation of _arch_freq_get_on_cpu_ that will leverage existing support for FIE with AMU counters. This will give a reliable way of querying current CPU frequency reflecting the last sched tick period. The only case when we would fall back to cpufreq_gov's get functionality would be for CPUs with dynamic ticks when those are the only ones withing given policy or there is no available housekeeping CPU to query the frequency from. This should solve most of the experienced issues. To be posted soon. --- BR B. > > I have added the CPC_IN_SYSTEM_MEMORY check from [2] in [1]. > Could you please test if the below change works for you. > > ----------------------------------------- > diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c > index 7ff269a78c20..67aa09b5f15c 100644 > --- a/drivers/acpi/cppc_acpi.c > +++ b/drivers/acpi/cppc_acpi.c > @@ -1315,6 +1315,7 @@ int cppc_get_perf_ctrs(int cpunum, struct > cppc_perf_fb_ctrs *perf_fb_ctrs) > struct cppc_pcc_data *pcc_ss_data = NULL; > u64 delivered, reference, ref_perf, ctr_wrap_time; > int ret = 0, regs_in_pcc = 0; > + unsigned long flags; > > if (!cpc_desc) { > pr_debug("No CPC descriptor for CPU:%d\n", cpunum); > @@ -1350,8 +1351,17 @@ int cppc_get_perf_ctrs(int cpunum, struct > cppc_perf_fb_ctrs *perf_fb_ctrs) > } > } > > + if (CPC_IN_SYSTEM_MEMORY(delivered_reg) && > + CPC_IN_SYSTEM_MEMORY(reference_reg)) > + local_irq_save(flags); > + > cpc_read(cpunum, delivered_reg, &delivered); > cpc_read(cpunum, reference_reg, &reference); > + > + if (CPC_IN_SYSTEM_MEMORY(delivered_reg) && > + CPC_IN_SYSTEM_MEMORY(reference_reg)) > + local_irq_restore(flags); > + > cpc_read(cpunum, ref_perf_reg, &ref_perf); > > /* > diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c > index 5e6a132a525e..23e690854459 100644 > --- a/drivers/cpufreq/cppc_cpufreq.c > +++ b/drivers/cpufreq/cppc_cpufreq.c > @@ -46,6 +46,8 @@ static bool boost_supported; > /* default 2usec delay between sampling */ > static unsigned int sampling_delay_us = 2; > > +static bool get_rate_use_wq; > + > static void cppc_check_hisi_workaround(void); > static void cppc_nvidia_workaround(void); > > @@ -99,6 +101,12 @@ struct cppc_freq_invariance { > static DEFINE_PER_CPU(struct cppc_freq_invariance, cppc_freq_inv); > static struct kthread_worker *kworker_fie; > > +struct feedback_ctrs { > + u32 cpu; > + struct cppc_perf_fb_ctrs fb_ctrs_t0; > + struct cppc_perf_fb_ctrs fb_ctrs_t1; > +}; > + > static unsigned int hisi_cppc_cpufreq_get_rate(unsigned int cpu); > static int cppc_perf_from_fbctrs(struct cppc_cpudata *cpu_data, > struct cppc_perf_fb_ctrs *fb_ctrs_t0, > @@ -851,28 +859,44 @@ static int cppc_perf_from_fbctrs(struct cppc_cpudata > *cpu_data, > return (reference_perf * delta_delivered) / delta_reference; > } > > +static int cppc_get_perf_ctrs_sync(void *fb_ctrs) > +{ > + struct feedback_ctrs *ctrs = fb_ctrs; > + int ret; > + > + ret = cppc_get_perf_ctrs(ctrs->cpu, &(ctrs->fb_ctrs_t0)); > + if (ret) > + return ret; > + > + udelay(sampling_delay_us); > + > + ret = cppc_get_perf_ctrs(ctrs->cpu, &(ctrs->fb_ctrs_t1)); > + if (ret) > + return ret; > + > + return ret; > +} > + > static unsigned int cppc_cpufreq_get_rate(unsigned int cpu) > { > - struct cppc_perf_fb_ctrs fb_ctrs_t0 = {0}, fb_ctrs_t1 = {0}; > struct cpufreq_policy *policy = cpufreq_cpu_get(cpu); > struct cppc_cpudata *cpu_data = policy->driver_data; > + struct feedback_ctrs fb_ctrs = {0}; > u64 delivered_perf; > int ret; > > cpufreq_cpu_put(policy); > + fb_ctrs.cpu = cpu; > > - ret = cppc_get_perf_ctrs(cpu, &fb_ctrs_t0); > - if (ret) > - return ret; > - > - udelay(sampling_delay_us); > - > - ret = cppc_get_perf_ctrs(cpu, &fb_ctrs_t1); > + if (get_rate_use_wq) > + ret = smp_call_on_cpu(cpu, cppc_get_perf_ctrs_sync, &fb_ctrs, false); > + else > + ret = cppc_get_perf_ctrs_sync(&fb_ctrs); > if (ret) > return ret; > > - delivered_perf = cppc_perf_from_fbctrs(cpu_data, &fb_ctrs_t0, > - &fb_ctrs_t1); > + delivered_perf = cppc_perf_from_fbctrs(cpu_data, &(fb_ctrs.fb_ctrs_t0), > + &(fb_ctrs.fb_ctrs_t1)); > > return cppc_cpufreq_perf_to_khz(cpu_data, delivered_perf); > } > @@ -1002,10 +1026,18 @@ static void cppc_apply_workarounds(void) > static int __init cppc_cpufreq_init(void) > { > int ret; > + int cpu; > > if (!acpi_cpc_valid()) > return -ENODEV; > > +#ifdef CONFIG_ARM64_AMU_EXTN > + cpu = get_cpu_with_amu_feat(); > + > + if (cpu < nr_cpu_ids) > + get_rate_use_wq = true; > +#endif > > -------------------------------------------- > > We can add additional check to call smp_call_on_cpu() only when CPC_IN_FFH > if we want to reduce the scope of calling smp_call_on_cpu. > > diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c > index 67aa09b5f15c..3d8348911403 100644 > --- a/drivers/acpi/cppc_acpi.c > +++ b/drivers/acpi/cppc_acpi.c > @@ -110,6 +110,11 @@ static DEFINE_PER_CPU(struct cpc_desc *, cpc_desc_ptr); > (cpc)->cpc_entry.reg.space_id == \ > ACPI_ADR_SPACE_SYSTEM_IO) > > +/* Check if a CPC register is in FFH */ > +#define CPC_IN_FFH(cpc) ((cpc)->type == ACPI_TYPE_BUFFER && \ > + (cpc)->cpc_entry.reg.space_id == \ > + ACPI_ADR_SPACE_FIXED_HARDWARE) > + > /* Evaluates to True if reg is a NULL register descriptor */ > #define IS_NULL_REG(reg) ((reg)->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY > && \ > (reg)->address == 0 && \ > @@ -437,6 +442,29 @@ bool acpi_cpc_valid(void) > } > EXPORT_SYMBOL_GPL(acpi_cpc_valid); > > +bool acpi_cpc_in_ffh(void) > +{ > + struct cpc_register_resource *delivered_reg, *reference_reg; > + struct cpc_desc *cpc_ptr; > + int cpu; > + > + if (acpi_disabled) > + return false; > + > + for_each_possible_cpu(cpu) { > + cpc_ptr = per_cpu(cpc_desc_ptr, cpu); > + delivered_reg = &cpc_ptr->cpc_regs[DELIVERED_CTR]; > + reference_reg = &cpc_ptr->cpc_regs[REFERENCE_CTR]; > + > + if (!CPC_IN_FFH(delivered_reg) || > + !CPC_IN_FFH(reference_reg)) > + return false; > + } > + > + return true; > +} > +EXPORT_SYMBOL_GPL(acpi_cpc_in_ffh); > + > bool cppc_allow_fast_switch(void) > { > struct cpc_register_resource *desired_reg; > diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c > index 23e690854459..4109e00b957e 100644 > --- a/drivers/cpufreq/cppc_cpufreq.c > +++ b/drivers/cpufreq/cppc_cpufreq.c > @@ -1034,7 +1034,7 @@ static int __init cppc_cpufreq_init(void) > #ifdef CONFIG_ARM64_AMU_EXTN > cpu = get_cpu_with_amu_feat(); > > - if (cpu < nr_cpu_ids) > + if ((cpu < nr_cpu_ids) && acpi_cpc_in_ffh()) > get_rate_use_wq = true; > #endif > > > > > > > This can be merged and can later still be improved in Upstream. > > > > > > If OK, I can send new version by changing the patch to apply for all > > > ARM SoC's with AMU and not specific to Tegra. > > > > > > Thank you, > > > Sumit Gupta
On 5/18/23 7:40 AM, Sumit Gupta wrote: >>>> >>>> +Ionela, Sumit, Yang, >>>> >>>> Hello Zeng, >>>> >>>> I think solutions around related issues were suggested at: >>>> >>>> [1] >>>> https://lore.kernel.org/all/20230418113459.12860-7-sumitg@nvidia.com/ >>>> [2] >>>> https://lore.kernel.org/all/20230328193846.8757-1-yang@os.amperecomputing.com/ >>>> >>>> [3] https://lore.kernel.org/all/ZEl1Fms%2FJmdEZsVn@arm.com/ >>>> >>>> About this patch, it seems to mean that CPPC counters of CPUx are >>>> always >>>> accessed from CPUx, even when they are not AMUs. For instance CPPC >>>> counters could be memory mapped and accessible from any CPU. >>>> cpu_has_amu_feat() should allow to probe if a CPU uses AMUs or not, >>>> and [2] had an implementation using it. >>>> >>>> Another comment about PATCH 2/2 is that if the counters are accessed >>>> through FFH, arm64 version of cpc_read_ffh() is calling >>>> counters_read_on_cpu(), and a comment in counters_read_on_cpu() seems >>>> to specify the function must be called with interrupt enabled. >>>> >>>> I think the best solution so far was the one at [3], suggested by >>>> Ionela, >>>> but it doesn't seem to solve your issue. Indeed, it is not checked >>>> whether >>>> the counters are AMU counters and that they must be remotely read (to >>>> have the CPU awake), >>>> >>>> Regards, >>>> Pierre >>>> >>> >>> I think the solution in [1] is simple and solves all the three cases. >>> Also, it provides better accuracy between the set and get frequency as >>> compared to [3]. >> >> I don't think [1] patches work for our case. We use mmio instead of AMU. >> Increasing delay could help to mitigate it somehow, buyt 25us is not >> good enough for our case. IIRC the fix proposed by Ionela works for both >> yours and mine. >> > > I have added the CPC_IN_SYSTEM_MEMORY check from [2] in [1]. > Could you please test if the below change works for you. Thanks, Sumit. Sorry for the late reply. I didn't find time to test your patch, but the visual inspection shows it should work for my case. The deviation in our case is mainly caused by: - interrupt - congestion on interconnect So disabling interrupt when reading the counters + longer delay (e.g. 25 us) could mitigate it significantly. So as long as the two are covered by the patch, it should work for us and your patch does. > > ----------------------------------------- > diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c > index 7ff269a78c20..67aa09b5f15c 100644 > --- a/drivers/acpi/cppc_acpi.c > +++ b/drivers/acpi/cppc_acpi.c > @@ -1315,6 +1315,7 @@ int cppc_get_perf_ctrs(int cpunum, struct > cppc_perf_fb_ctrs *perf_fb_ctrs) > struct cppc_pcc_data *pcc_ss_data = NULL; > u64 delivered, reference, ref_perf, ctr_wrap_time; > int ret = 0, regs_in_pcc = 0; > + unsigned long flags; > > if (!cpc_desc) { > pr_debug("No CPC descriptor for CPU:%d\n", cpunum); > @@ -1350,8 +1351,17 @@ int cppc_get_perf_ctrs(int cpunum, struct > cppc_perf_fb_ctrs *perf_fb_ctrs) > } > } > > + if (CPC_IN_SYSTEM_MEMORY(delivered_reg) && > + CPC_IN_SYSTEM_MEMORY(reference_reg)) > + local_irq_save(flags); > + > cpc_read(cpunum, delivered_reg, &delivered); > cpc_read(cpunum, reference_reg, &reference); > + > + if (CPC_IN_SYSTEM_MEMORY(delivered_reg) && > + CPC_IN_SYSTEM_MEMORY(reference_reg)) > + local_irq_restore(flags); > + > cpc_read(cpunum, ref_perf_reg, &ref_perf); > > /* > diff --git a/drivers/cpufreq/cppc_cpufreq.c > b/drivers/cpufreq/cppc_cpufreq.c > index 5e6a132a525e..23e690854459 100644 > --- a/drivers/cpufreq/cppc_cpufreq.c > +++ b/drivers/cpufreq/cppc_cpufreq.c > @@ -46,6 +46,8 @@ static bool boost_supported; > /* default 2usec delay between sampling */ > static unsigned int sampling_delay_us = 2; > > +static bool get_rate_use_wq; > + > static void cppc_check_hisi_workaround(void); > static void cppc_nvidia_workaround(void); > > @@ -99,6 +101,12 @@ struct cppc_freq_invariance { > static DEFINE_PER_CPU(struct cppc_freq_invariance, cppc_freq_inv); > static struct kthread_worker *kworker_fie; > > +struct feedback_ctrs { > + u32 cpu; > + struct cppc_perf_fb_ctrs fb_ctrs_t0; > + struct cppc_perf_fb_ctrs fb_ctrs_t1; > +}; > + > static unsigned int hisi_cppc_cpufreq_get_rate(unsigned int cpu); > static int cppc_perf_from_fbctrs(struct cppc_cpudata *cpu_data, > struct cppc_perf_fb_ctrs *fb_ctrs_t0, > @@ -851,28 +859,44 @@ static int cppc_perf_from_fbctrs(struct > cppc_cpudata *cpu_data, > return (reference_perf * delta_delivered) / delta_reference; > } > > +static int cppc_get_perf_ctrs_sync(void *fb_ctrs) > +{ > + struct feedback_ctrs *ctrs = fb_ctrs; > + int ret; > + > + ret = cppc_get_perf_ctrs(ctrs->cpu, &(ctrs->fb_ctrs_t0)); > + if (ret) > + return ret; > + > + udelay(sampling_delay_us); > + > + ret = cppc_get_perf_ctrs(ctrs->cpu, &(ctrs->fb_ctrs_t1)); > + if (ret) > + return ret; > + > + return ret; > +} > + > static unsigned int cppc_cpufreq_get_rate(unsigned int cpu) > { > - struct cppc_perf_fb_ctrs fb_ctrs_t0 = {0}, fb_ctrs_t1 = {0}; > struct cpufreq_policy *policy = cpufreq_cpu_get(cpu); > struct cppc_cpudata *cpu_data = policy->driver_data; > + struct feedback_ctrs fb_ctrs = {0}; > u64 delivered_perf; > int ret; > > cpufreq_cpu_put(policy); > + fb_ctrs.cpu = cpu; > > - ret = cppc_get_perf_ctrs(cpu, &fb_ctrs_t0); > - if (ret) > - return ret; > - > - udelay(sampling_delay_us); > - > - ret = cppc_get_perf_ctrs(cpu, &fb_ctrs_t1); > + if (get_rate_use_wq) > + ret = smp_call_on_cpu(cpu, cppc_get_perf_ctrs_sync, &fb_ctrs, > false); > + else > + ret = cppc_get_perf_ctrs_sync(&fb_ctrs); > if (ret) > return ret; > > - delivered_perf = cppc_perf_from_fbctrs(cpu_data, &fb_ctrs_t0, > - &fb_ctrs_t1); > + delivered_perf = cppc_perf_from_fbctrs(cpu_data, > &(fb_ctrs.fb_ctrs_t0), > + &(fb_ctrs.fb_ctrs_t1)); > > return cppc_cpufreq_perf_to_khz(cpu_data, delivered_perf); > } > @@ -1002,10 +1026,18 @@ static void cppc_apply_workarounds(void) > static int __init cppc_cpufreq_init(void) > { > int ret; > + int cpu; > > if (!acpi_cpc_valid()) > return -ENODEV; > > +#ifdef CONFIG_ARM64_AMU_EXTN > + cpu = get_cpu_with_amu_feat(); > + > + if (cpu < nr_cpu_ids) > + get_rate_use_wq = true; > +#endif > > -------------------------------------------- > > We can add additional check to call smp_call_on_cpu() only when > CPC_IN_FFH if we want to reduce the scope of calling smp_call_on_cpu. > > diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c > index 67aa09b5f15c..3d8348911403 100644 > --- a/drivers/acpi/cppc_acpi.c > +++ b/drivers/acpi/cppc_acpi.c > @@ -110,6 +110,11 @@ static DEFINE_PER_CPU(struct cpc_desc *, > cpc_desc_ptr); > (cpc)->cpc_entry.reg.space_id == \ > ACPI_ADR_SPACE_SYSTEM_IO) > > +/* Check if a CPC register is in FFH */ > +#define CPC_IN_FFH(cpc) ((cpc)->type == ACPI_TYPE_BUFFER && \ > + (cpc)->cpc_entry.reg.space_id == \ > + ACPI_ADR_SPACE_FIXED_HARDWARE) > + > /* Evaluates to True if reg is a NULL register descriptor */ > #define IS_NULL_REG(reg) ((reg)->space_id == > ACPI_ADR_SPACE_SYSTEM_MEMORY && \ > (reg)->address == 0 && \ > @@ -437,6 +442,29 @@ bool acpi_cpc_valid(void) > } > EXPORT_SYMBOL_GPL(acpi_cpc_valid); > > +bool acpi_cpc_in_ffh(void) > +{ > + struct cpc_register_resource *delivered_reg, *reference_reg; > + struct cpc_desc *cpc_ptr; > + int cpu; > + > + if (acpi_disabled) > + return false; > + > + for_each_possible_cpu(cpu) { > + cpc_ptr = per_cpu(cpc_desc_ptr, cpu); > + delivered_reg = &cpc_ptr->cpc_regs[DELIVERED_CTR]; > + reference_reg = &cpc_ptr->cpc_regs[REFERENCE_CTR]; > + > + if (!CPC_IN_FFH(delivered_reg) || > + !CPC_IN_FFH(reference_reg)) > + return false; > + } > + > + return true; > +} > +EXPORT_SYMBOL_GPL(acpi_cpc_in_ffh); > + > bool cppc_allow_fast_switch(void) > { > struct cpc_register_resource *desired_reg; > diff --git a/drivers/cpufreq/cppc_cpufreq.c > b/drivers/cpufreq/cppc_cpufreq.c > index 23e690854459..4109e00b957e 100644 > --- a/drivers/cpufreq/cppc_cpufreq.c > +++ b/drivers/cpufreq/cppc_cpufreq.c > @@ -1034,7 +1034,7 @@ static int __init cppc_cpufreq_init(void) > #ifdef CONFIG_ARM64_AMU_EXTN > cpu = get_cpu_with_amu_feat(); > > - if (cpu < nr_cpu_ids) > + if ((cpu < nr_cpu_ids) && acpi_cpc_in_ffh()) > get_rate_use_wq = true; > #endif > >>> >>> This can be merged and can later still be improved in Upstream. >>> >>> If OK, I can send new version by changing the patch to apply for all >>> ARM SoC's with AMU and not specific to Tegra. >>> >>> Thank you, >>> Sumit Gupta
在 2023/5/17 16:17, Pierre Gondois 写道: > +Ionela, Sumit, Yang, > > Hello Zeng, > > I think solutions around related issues were suggested at: > > [1] https://lore.kernel.org/all/20230418113459.12860-7-sumitg@nvidia.com/ > [2] > https://lore.kernel.org/all/20230328193846.8757-1-yang@os.amperecomputing.com/ > [3] https://lore.kernel.org/all/ZEl1Fms%2FJmdEZsVn@arm.com/ > > About this patch, it seems to mean that CPPC counters of CPUx are always > accessed from CPUx, even when they are not AMUs. For instance CPPC > counters could be memory mapped and accessible from any CPU. > cpu_has_amu_feat() should allow to probe if a CPU uses AMUs or not, > and [2] had an implementation using it. > > Another comment about PATCH 2/2 is that if the counters are accessed > through FFH, arm64 version of cpc_read_ffh() is calling > counters_read_on_cpu(), and a comment in counters_read_on_cpu() seems > to specify the function must be called with interrupt enabled. > > I think the best solution so far was the one at [3], suggested by Ionela, > but it doesn't seem to solve your issue. Indeed, it is not checked > whether > the counters are AMU counters and that they must be remotely read (to > have the CPU awake), > > Regards, > Pierre Here is segment I picked from patch[3], and there is a modification is deserved to be discussed: --------------------------------------------------------------------------- @@ -1336,8 +1361,25 @@ int cppc_get_perf_ctrs(int cpunum, struct cppc_perf_fb_ctrs *perf_fb_ctrs) } } - cpc_read(cpunum, delivered_reg, &delivered); - cpc_read(cpunum, reference_reg, &reference); + ctrs.cpunum = cpunum; + ctrs.delivered_reg = delivered_reg; + ctrs.reference_reg = reference_reg; + ctrs.delivered = &delivered; + ctrs.reference = &reference; + + if (CPC_IN_FFH(delivered_reg) && CPC_IN_FFH(reference_reg)) { Here we call cpu_has_amu_feat() as precondition (like Sumit's mail mentions), which could be compatible with any SoCs with AMU that could be accessible via sys-register and system memory both. + ret = smp_call_on_cpu(cpunum, cppc_get_cycle_ctrs, &ctrs, false); + } else { + if (CPC_IN_SYSTEM_MEMORY(delivered_reg) && + CPC_IN_SYSTEM_MEMORY(reference_reg)) { + local_irq_save(flags); + cppc_get_cycle_ctrs(&ctrs); + local_irq_restore(flags); + } else { + cppc_get_cycle_ctrs(&ctrs); + } + } + cpc_read(cpunum, ref_perf_reg, &ref_perf); ----------------------------------------------------------------------------------- If there were a new version patch released, please loop me at that time. Thanks a lot in advance. B.R., Zeng Heng
diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c index 022e3555407c..910167f58bb3 100644 --- a/drivers/cpufreq/cppc_cpufreq.c +++ b/drivers/cpufreq/cppc_cpufreq.c @@ -837,9 +837,24 @@ static int cppc_perf_from_fbctrs(struct cppc_cpudata *cpu_data, return (reference_perf * delta_delivered) / delta_reference; } +static int cppc_get_perf_ctrs_smp(void *val) +{ + int cpu = smp_processor_id(); + struct cppc_perf_fb_ctrs *fb_ctrs = val; + int ret; + + ret = cppc_get_perf_ctrs(cpu, fb_ctrs); + if (ret) + return ret; + + udelay(2); /* 2usec delay between sampling */ + + return cppc_get_perf_ctrs(cpu, fb_ctrs + 1); +} + static unsigned int cppc_cpufreq_get_rate(unsigned int cpu) { - struct cppc_perf_fb_ctrs fb_ctrs_t0 = {0}, fb_ctrs_t1 = {0}; + struct cppc_perf_fb_ctrs fb_ctrs[2] = {0}; struct cpufreq_policy *policy = cpufreq_cpu_get(cpu); struct cppc_cpudata *cpu_data = policy->driver_data; u64 delivered_perf; @@ -847,19 +862,12 @@ static unsigned int cppc_cpufreq_get_rate(unsigned int cpu) cpufreq_cpu_put(policy); - ret = cppc_get_perf_ctrs(cpu, &fb_ctrs_t0); - if (ret) - return ret; - - udelay(2); /* 2usec delay between sampling */ - - ret = cppc_get_perf_ctrs(cpu, &fb_ctrs_t1); + ret = smp_call_on_cpu(cpu, cppc_get_perf_ctrs_smp, fb_ctrs, 1); if (ret) return ret; - delivered_perf = cppc_perf_from_fbctrs(cpu_data, &fb_ctrs_t0, - &fb_ctrs_t1); - + delivered_perf = cppc_perf_from_fbctrs(cpu_data, fb_ctrs, + fb_ctrs + 1); return cppc_cpufreq_perf_to_khz(cpu_data, delivered_perf); }