From: Mark Rutland <mark.rutland@arm.com>
To: Dawei Li <dawei.li@shingroup.cn>
Cc: will@kernel.org, xueshuai@linux.alibaba.com,
renyu.zj@linux.alibaba.com, yangyicong@hisilicon.com,
jonathan.cameron@huawei.com, andersson@kernel.org,
konrad.dybcio@linaro.org, linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org
Subject: Re: [PATCH 0/9] perf: Avoid explicit cpumask var allocation from stack
Date: Tue, 2 Apr 2024 15:41:51 +0100 [thread overview]
Message-ID: <ZgwZL679Tc1S3AxH@FVFF77S0Q05N> (raw)
In-Reply-To: <190FE91C35AB9AE8+ZgwKuORh3VzTkfeJ@centos8>
On Tue, Apr 02, 2024 at 09:40:08PM +0800, Dawei Li wrote:
> Hi Mark,
>
> Thanks for the quick review.
>
> On Tue, Apr 02, 2024 at 12:12:50PM +0100, Mark Rutland wrote:
> > On Tue, Apr 02, 2024 at 06:56:01PM +0800, Dawei Li wrote:
> > > Hi,
> > >
> > > This series try to eliminate direct cpumask var allocation from stack
> > > for perf subsystem.
> > >
> > > Direct/explicit allocation of cpumask on stack could be dangerous since
> > > it can lead to stack overflow for systems with big NR_CPUS or
> > > CONFIG_CPUMASK_OFFSTACK=y.
> > >
> > > For arm64, it's more urgent since commit 3fbd56f0e7c1 ("ARM64: Dynamically
> > > allocate cpumasks and increase supported CPUs to 512").
> > >
> > > It's sort of a pattern that almost every cpumask var in perf subystem
> > > occurs in teardown callback of cpuhp. In which case, if dynamic
> > > allocation failed(which is unlikely), we choose return 0 rather than
> > > -ENOMEM to caller cuz:
> > > @teardown is not supposed to fail and if it does, system crashes:
> >
> > .. but we've left the system in an incorrect state, so that makes no sense.
> >
> > As I commented on the first patch, NAK to dynamically allocating cpumasks in
> > the CPUHP callbacks. Please allocate the necessry cpumasks up-front when we
> > probe the PMU. At that time we can handle an allocation failure by cleaning up
> > and failing to probe the PMU, and then the CPUHP callbacks don't need to
> > allocate memory to offline a CPU...
>
> Agreed that dynamically allocation in callbacks lead to inconsistency
> to system.
>
> My (original)alternative plan is simple but ugly, just make cpumask var
> _static_ and add extra static lock to protect it.
>
> The only difference between solution above and your proposal is static/
> dynamic alloction. CPUHP's teardown cb is supposed to run in targetted
> cpuhp thread for most cases, and it's racy. Even the cpumask var is
> wrapped in dynamically allocated struct xxx_pmu, it's still shareable
> between different threads/contexts and needs proper protection.
I was under the impression that the cpuhp callbacks were *strictly* serialised.
If that's not the case, the existing offlining callbacks are horrendously
broken.
Are you *certain* these can race?
Regardless, adding additional locking here is not ok.
> Simple as this(_untested_):
>
> diff --git a/drivers/perf/arm-cmn.c b/drivers/perf/arm-cmn.c
> index 7ef9c7e4836b..fa89c3db4d7d 100644
> --- a/drivers/perf/arm-cmn.c
> +++ b/drivers/perf/arm-cmn.c
> @@ -1950,18 +1950,24 @@ static int arm_cmn_pmu_offline_cpu(unsigned int cpu, struct hlist_node *cpuhp_no
> struct arm_cmn *cmn;
> unsigned int target;
> int node;
> - cpumask_t mask;
> + static cpumask_t mask;
> + static DEFINE_SPINLOCK(cpumask_lock);
>
> cmn = hlist_entry_safe(cpuhp_node, struct arm_cmn, cpuhp_node);
> if (cpu != cmn->cpu)
> return 0;
>
> + spin_lock(&cpumask_lock);
> +
> node = dev_to_node(cmn->dev);
> if (cpumask_and(&mask, cpumask_of_node(node), cpu_online_mask) &&
> cpumask_andnot(&mask, &mask, cpumask_of(cpu)))
> target = cpumask_any(&mask);
> else
> target = cpumask_any_but(cpu_online_mask, cpu);
> +
> + spin_unlock(&cpumask_lock);
> +
> if (target < nr_cpu_ids)
> arm_cmn_migrate(cmn, target);
> return 0;
Looking at this case, the only reason we need the mask is because it made the
logic a little easier to write. All we really want is to choose some CPU in the
intersection of two masks ignoring a specific CPU, and there was no helper
function to do that.
We can add a new helper to do that for us, which would avoid redundant work to
manipulate the entire mask, and it would make the existing code simpler. I had
a series a few years back to add cpumask_any_and_but():
https://lore.kernel.org/lkml/1486381132-5610-1-git-send-email-mark.rutland@arm.com/
... and that's easy to use here, e.g.
| diff --git a/drivers/perf/arm-cmn.c b/drivers/perf/arm-cmn.c
| index 7ef9c7e4836b7..c6bbd387ccf8b 100644
| --- a/drivers/perf/arm-cmn.c
| +++ b/drivers/perf/arm-cmn.c
| @@ -1950,17 +1950,15 @@ static int arm_cmn_pmu_offline_cpu(unsigned int cpu, struct hlist_node *cpuhp_no
| struct arm_cmn *cmn;
| unsigned int target;
| int node;
| - cpumask_t mask;
|
| cmn = hlist_entry_safe(cpuhp_node, struct arm_cmn, cpuhp_node);
| if (cpu != cmn->cpu)
| return 0;
|
| node = dev_to_node(cmn->dev);
| - if (cpumask_and(&mask, cpumask_of_node(node), cpu_online_mask) &&
| - cpumask_andnot(&mask, &mask, cpumask_of(cpu)))
| - target = cpumask_any(&mask);
| - else
| + target = cpumask_any_and_but(cpu_online_mask, cpumask_of_node(node),
| + cpu);
| + if (target >= nr_cpu_ids)
| target = cpumask_any_but(cpu_online_mask, cpu);
| if (target < nr_cpu_ids)
| arm_cmn_migrate(cmn, target);
It doesn't trivially rebase since the cpumask code has changed a fair amount,
but I've managed to do that locally, and I can send that out as a
seven-years-late v2 if it's useful.
From a quick scan, it looks like that'd handle all cases in this series. Are
there any patterns in this series for which that would not be sufficient?
Mark.
>
> And yes, static allocation is evil :)
>
>
> Thanks,
>
> Dawei
>
> >
> > Also, for the titles it'd be better to say something like "avoid placing
> > cpumasks on the stack", because "explicit cpumask var allocation" sounds like
> > the use of alloc_cpumask_var().
>
> Sound great! I will update it.
>
> >
> > Mark.
> >
> > >
> > > static int cpuhp_issue_call(int cpu, enum cpuhp_state state, bool bringup,
> > > struct hlist_node *node)
> > > {
> > > struct cpuhp_step *sp = cpuhp_get_step(state);
> > > int ret;
> > >
> > > /*
> > > * If there's nothing to do, we done.
> > > * Relies on the union for multi_instance.
> > > */
> > > if (cpuhp_step_empty(bringup, sp))
> > > return 0;
> > > /*
> > > * The non AP bound callbacks can fail on bringup. On teardown
> > > * e.g. module removal we crash for now.
> > > */
> > > #ifdef CONFIG_SMP
> > > if (cpuhp_is_ap_state(state))
> > > ret = cpuhp_invoke_ap_callback(cpu, state, bringup, node);
> > > else
> > > ret = cpuhp_invoke_callback(cpu, state, bringup, node,
> > > NULL);
> > > #else
> > > ret = cpuhp_invoke_callback(cpu, state, bringup, node, NULL);
> > > #endif
> > > BUG_ON(ret && !bringup);
> > > return ret;
> > > }
> > >
> > > Dawei Li (9):
> > > perf/alibaba_uncore_drw: Avoid explicit cpumask var allocation from
> > > stack
> > > perf/arm-cmn: Avoid explicit cpumask var allocation from stack
> > > perf/arm_cspmu: Avoid explicit cpumask var allocation from stack
> > > perf/arm_dsu: Avoid explicit cpumask var allocation from stack
> > > perf/dwc_pcie: Avoid explicit cpumask var allocation from stack
> > > perf/hisi_pcie: Avoid explicit cpumask var allocation from stack
> > > perf/hisi_uncore: Avoid explicit cpumask var allocation from stack
> > > perf/qcom_l2: Avoid explicit cpumask var allocation from stack
> > > perf/thunder_x2: Avoid explicit cpumask var allocation from stack
> > >
> > > drivers/perf/alibaba_uncore_drw_pmu.c | 13 +++++++++----
> > > drivers/perf/arm-cmn.c | 13 +++++++++----
> > > drivers/perf/arm_cspmu/arm_cspmu.c | 13 +++++++++----
> > > drivers/perf/arm_dsu_pmu.c | 18 +++++++++++++-----
> > > drivers/perf/dwc_pcie_pmu.c | 17 +++++++++++------
> > > drivers/perf/hisilicon/hisi_pcie_pmu.c | 15 ++++++++++-----
> > > drivers/perf/hisilicon/hisi_uncore_pmu.c | 13 +++++++++----
> > > drivers/perf/qcom_l2_pmu.c | 15 ++++++++++-----
> > > drivers/perf/thunderx2_pmu.c | 20 ++++++++++++--------
> > > 9 files changed, 92 insertions(+), 45 deletions(-)
> > >
> > >
> > > Thanks,
> > >
> > > Dawei
> > >
> > > --
> > > 2.27.0
> > >
> >
WARNING: multiple messages have this Message-ID (diff)
From: Mark Rutland <mark.rutland@arm.com>
To: Dawei Li <dawei.li@shingroup.cn>
Cc: will@kernel.org, xueshuai@linux.alibaba.com,
renyu.zj@linux.alibaba.com, yangyicong@hisilicon.com,
jonathan.cameron@huawei.com, andersson@kernel.org,
konrad.dybcio@linaro.org, linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org
Subject: Re: [PATCH 0/9] perf: Avoid explicit cpumask var allocation from stack
Date: Tue, 2 Apr 2024 15:41:51 +0100 [thread overview]
Message-ID: <ZgwZL679Tc1S3AxH@FVFF77S0Q05N> (raw)
In-Reply-To: <190FE91C35AB9AE8+ZgwKuORh3VzTkfeJ@centos8>
On Tue, Apr 02, 2024 at 09:40:08PM +0800, Dawei Li wrote:
> Hi Mark,
>
> Thanks for the quick review.
>
> On Tue, Apr 02, 2024 at 12:12:50PM +0100, Mark Rutland wrote:
> > On Tue, Apr 02, 2024 at 06:56:01PM +0800, Dawei Li wrote:
> > > Hi,
> > >
> > > This series try to eliminate direct cpumask var allocation from stack
> > > for perf subsystem.
> > >
> > > Direct/explicit allocation of cpumask on stack could be dangerous since
> > > it can lead to stack overflow for systems with big NR_CPUS or
> > > CONFIG_CPUMASK_OFFSTACK=y.
> > >
> > > For arm64, it's more urgent since commit 3fbd56f0e7c1 ("ARM64: Dynamically
> > > allocate cpumasks and increase supported CPUs to 512").
> > >
> > > It's sort of a pattern that almost every cpumask var in perf subystem
> > > occurs in teardown callback of cpuhp. In which case, if dynamic
> > > allocation failed(which is unlikely), we choose return 0 rather than
> > > -ENOMEM to caller cuz:
> > > @teardown is not supposed to fail and if it does, system crashes:
> >
> > .. but we've left the system in an incorrect state, so that makes no sense.
> >
> > As I commented on the first patch, NAK to dynamically allocating cpumasks in
> > the CPUHP callbacks. Please allocate the necessry cpumasks up-front when we
> > probe the PMU. At that time we can handle an allocation failure by cleaning up
> > and failing to probe the PMU, and then the CPUHP callbacks don't need to
> > allocate memory to offline a CPU...
>
> Agreed that dynamically allocation in callbacks lead to inconsistency
> to system.
>
> My (original)alternative plan is simple but ugly, just make cpumask var
> _static_ and add extra static lock to protect it.
>
> The only difference between solution above and your proposal is static/
> dynamic alloction. CPUHP's teardown cb is supposed to run in targetted
> cpuhp thread for most cases, and it's racy. Even the cpumask var is
> wrapped in dynamically allocated struct xxx_pmu, it's still shareable
> between different threads/contexts and needs proper protection.
I was under the impression that the cpuhp callbacks were *strictly* serialised.
If that's not the case, the existing offlining callbacks are horrendously
broken.
Are you *certain* these can race?
Regardless, adding additional locking here is not ok.
> Simple as this(_untested_):
>
> diff --git a/drivers/perf/arm-cmn.c b/drivers/perf/arm-cmn.c
> index 7ef9c7e4836b..fa89c3db4d7d 100644
> --- a/drivers/perf/arm-cmn.c
> +++ b/drivers/perf/arm-cmn.c
> @@ -1950,18 +1950,24 @@ static int arm_cmn_pmu_offline_cpu(unsigned int cpu, struct hlist_node *cpuhp_no
> struct arm_cmn *cmn;
> unsigned int target;
> int node;
> - cpumask_t mask;
> + static cpumask_t mask;
> + static DEFINE_SPINLOCK(cpumask_lock);
>
> cmn = hlist_entry_safe(cpuhp_node, struct arm_cmn, cpuhp_node);
> if (cpu != cmn->cpu)
> return 0;
>
> + spin_lock(&cpumask_lock);
> +
> node = dev_to_node(cmn->dev);
> if (cpumask_and(&mask, cpumask_of_node(node), cpu_online_mask) &&
> cpumask_andnot(&mask, &mask, cpumask_of(cpu)))
> target = cpumask_any(&mask);
> else
> target = cpumask_any_but(cpu_online_mask, cpu);
> +
> + spin_unlock(&cpumask_lock);
> +
> if (target < nr_cpu_ids)
> arm_cmn_migrate(cmn, target);
> return 0;
Looking at this case, the only reason we need the mask is because it made the
logic a little easier to write. All we really want is to choose some CPU in the
intersection of two masks ignoring a specific CPU, and there was no helper
function to do that.
We can add a new helper to do that for us, which would avoid redundant work to
manipulate the entire mask, and it would make the existing code simpler. I had
a series a few years back to add cpumask_any_and_but():
https://lore.kernel.org/lkml/1486381132-5610-1-git-send-email-mark.rutland@arm.com/
... and that's easy to use here, e.g.
| diff --git a/drivers/perf/arm-cmn.c b/drivers/perf/arm-cmn.c
| index 7ef9c7e4836b7..c6bbd387ccf8b 100644
| --- a/drivers/perf/arm-cmn.c
| +++ b/drivers/perf/arm-cmn.c
| @@ -1950,17 +1950,15 @@ static int arm_cmn_pmu_offline_cpu(unsigned int cpu, struct hlist_node *cpuhp_no
| struct arm_cmn *cmn;
| unsigned int target;
| int node;
| - cpumask_t mask;
|
| cmn = hlist_entry_safe(cpuhp_node, struct arm_cmn, cpuhp_node);
| if (cpu != cmn->cpu)
| return 0;
|
| node = dev_to_node(cmn->dev);
| - if (cpumask_and(&mask, cpumask_of_node(node), cpu_online_mask) &&
| - cpumask_andnot(&mask, &mask, cpumask_of(cpu)))
| - target = cpumask_any(&mask);
| - else
| + target = cpumask_any_and_but(cpu_online_mask, cpumask_of_node(node),
| + cpu);
| + if (target >= nr_cpu_ids)
| target = cpumask_any_but(cpu_online_mask, cpu);
| if (target < nr_cpu_ids)
| arm_cmn_migrate(cmn, target);
It doesn't trivially rebase since the cpumask code has changed a fair amount,
but I've managed to do that locally, and I can send that out as a
seven-years-late v2 if it's useful.
From a quick scan, it looks like that'd handle all cases in this series. Are
there any patterns in this series for which that would not be sufficient?
Mark.
>
> And yes, static allocation is evil :)
>
>
> Thanks,
>
> Dawei
>
> >
> > Also, for the titles it'd be better to say something like "avoid placing
> > cpumasks on the stack", because "explicit cpumask var allocation" sounds like
> > the use of alloc_cpumask_var().
>
> Sound great! I will update it.
>
> >
> > Mark.
> >
> > >
> > > static int cpuhp_issue_call(int cpu, enum cpuhp_state state, bool bringup,
> > > struct hlist_node *node)
> > > {
> > > struct cpuhp_step *sp = cpuhp_get_step(state);
> > > int ret;
> > >
> > > /*
> > > * If there's nothing to do, we done.
> > > * Relies on the union for multi_instance.
> > > */
> > > if (cpuhp_step_empty(bringup, sp))
> > > return 0;
> > > /*
> > > * The non AP bound callbacks can fail on bringup. On teardown
> > > * e.g. module removal we crash for now.
> > > */
> > > #ifdef CONFIG_SMP
> > > if (cpuhp_is_ap_state(state))
> > > ret = cpuhp_invoke_ap_callback(cpu, state, bringup, node);
> > > else
> > > ret = cpuhp_invoke_callback(cpu, state, bringup, node,
> > > NULL);
> > > #else
> > > ret = cpuhp_invoke_callback(cpu, state, bringup, node, NULL);
> > > #endif
> > > BUG_ON(ret && !bringup);
> > > return ret;
> > > }
> > >
> > > Dawei Li (9):
> > > perf/alibaba_uncore_drw: Avoid explicit cpumask var allocation from
> > > stack
> > > perf/arm-cmn: Avoid explicit cpumask var allocation from stack
> > > perf/arm_cspmu: Avoid explicit cpumask var allocation from stack
> > > perf/arm_dsu: Avoid explicit cpumask var allocation from stack
> > > perf/dwc_pcie: Avoid explicit cpumask var allocation from stack
> > > perf/hisi_pcie: Avoid explicit cpumask var allocation from stack
> > > perf/hisi_uncore: Avoid explicit cpumask var allocation from stack
> > > perf/qcom_l2: Avoid explicit cpumask var allocation from stack
> > > perf/thunder_x2: Avoid explicit cpumask var allocation from stack
> > >
> > > drivers/perf/alibaba_uncore_drw_pmu.c | 13 +++++++++----
> > > drivers/perf/arm-cmn.c | 13 +++++++++----
> > > drivers/perf/arm_cspmu/arm_cspmu.c | 13 +++++++++----
> > > drivers/perf/arm_dsu_pmu.c | 18 +++++++++++++-----
> > > drivers/perf/dwc_pcie_pmu.c | 17 +++++++++++------
> > > drivers/perf/hisilicon/hisi_pcie_pmu.c | 15 ++++++++++-----
> > > drivers/perf/hisilicon/hisi_uncore_pmu.c | 13 +++++++++----
> > > drivers/perf/qcom_l2_pmu.c | 15 ++++++++++-----
> > > drivers/perf/thunderx2_pmu.c | 20 ++++++++++++--------
> > > 9 files changed, 92 insertions(+), 45 deletions(-)
> > >
> > >
> > > Thanks,
> > >
> > > Dawei
> > >
> > > --
> > > 2.27.0
> > >
> >
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
next prev parent reply other threads:[~2024-04-02 14:42 UTC|newest]
Thread overview: 38+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-04-02 10:56 [PATCH 0/9] perf: Avoid explicit cpumask var allocation from stack Dawei Li
2024-04-02 10:56 ` Dawei Li
2024-04-02 10:56 ` [PATCH 1/9] perf/alibaba_uncore_drw: " Dawei Li
2024-04-02 10:56 ` Dawei Li
2024-04-02 11:06 ` Mark Rutland
2024-04-02 11:06 ` Mark Rutland
2024-04-02 10:56 ` [PATCH 2/9] perf/arm-cmn: " Dawei Li
2024-04-02 10:56 ` Dawei Li
2024-04-05 14:30 ` Robin Murphy
2024-04-05 14:30 ` Robin Murphy
2024-04-02 10:56 ` [PATCH 3/9] perf/arm_cspmu: " Dawei Li
2024-04-02 10:56 ` Dawei Li
2024-04-02 10:56 ` [PATCH 4/9] perf/arm_dsu: " Dawei Li
2024-04-02 10:56 ` Dawei Li
2024-04-02 23:58 ` kernel test robot
2024-04-02 23:58 ` kernel test robot
2024-04-03 1:43 ` kernel test robot
2024-04-03 1:43 ` kernel test robot
2024-04-02 10:56 ` [PATCH 5/9] perf/dwc_pcie: " Dawei Li
2024-04-02 10:56 ` Dawei Li
2024-04-02 10:56 ` [PATCH 6/9] perf/hisi_pcie: " Dawei Li
2024-04-02 10:56 ` Dawei Li
2024-04-02 10:56 ` [PATCH 7/9] perf/hisi_uncore: " Dawei Li
2024-04-02 10:56 ` Dawei Li
2024-04-02 10:56 ` [PATCH 8/9] perf/qcom_l2: " Dawei Li
2024-04-02 10:56 ` Dawei Li
2024-04-02 10:56 ` [PATCH 9/9] perf/thunder_x2: " Dawei Li
2024-04-02 10:56 ` Dawei Li
2024-04-02 11:12 ` [PATCH 0/9] perf: " Mark Rutland
2024-04-02 11:12 ` Mark Rutland
2024-04-02 13:40 ` Dawei Li
2024-04-02 13:40 ` Dawei Li
2024-04-02 14:41 ` Mark Rutland [this message]
2024-04-02 14:41 ` Mark Rutland
2024-04-03 10:41 ` Dawei Li
2024-04-03 10:41 ` Dawei Li
2024-04-03 11:10 ` Mark Rutland
2024-04-03 11:10 ` Mark Rutland
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=ZgwZL679Tc1S3AxH@FVFF77S0Q05N \
--to=mark.rutland@arm.com \
--cc=andersson@kernel.org \
--cc=dawei.li@shingroup.cn \
--cc=jonathan.cameron@huawei.com \
--cc=konrad.dybcio@linaro.org \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-arm-msm@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=renyu.zj@linux.alibaba.com \
--cc=will@kernel.org \
--cc=xueshuai@linux.alibaba.com \
--cc=yangyicong@hisilicon.com \
/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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.