public inbox for linux-arm-kernel@lists.infradead.org
 help / color / mirror / Atom feed
From: Robin Murphy <robin.murphy@arm.com>
To: Waiman Long <longman@redhat.com>, Will Deacon <will@kernel.org>,
	Mark Rutland <mark.rutland@arm.com>
Cc: linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH v4] perf/arm-dmc620: Fix dmc620_pmu_irqs_lock/cpu_hotplug_lock circular lock dependency
Date: Tue, 8 Aug 2023 13:29:31 +0100	[thread overview]
Message-ID: <0d32adf1-43fd-2762-d5ab-707d5969dcb0@arm.com> (raw)
In-Reply-To: <20230807154446.208572-1-longman@redhat.com>

On 2023-08-07 16:44, Waiman Long wrote:
> The following circular locking dependency was reported when running
> cpus online/offline test on an arm64 system.
> 
> [   84.195923] Chain exists of:
>                   dmc620_pmu_irqs_lock --> cpu_hotplug_lock --> cpuhp_state-down
> 
> [   84.207305]  Possible unsafe locking scenario:
> 
> [   84.213212]        CPU0                    CPU1
> [   84.217729]        ----                    ----
> [   84.222247]   lock(cpuhp_state-down);
> [   84.225899]                                lock(cpu_hotplug_lock);
> [   84.232068]                                lock(cpuhp_state-down);
> [   84.238237]   lock(dmc620_pmu_irqs_lock);
> [   84.242236]
>                  *** DEADLOCK ***
> 
> The problematic locking order seems to be
> 
> 	lock(dmc620_pmu_irqs_lock) --> lock(cpu_hotplug_lock)
> 
> This locking order happens when dmc620_pmu_get_irq() calls
> cpuhp_state_add_instance_nocalls(). Since dmc620_pmu_irqs_lock is used
> for protecting the dmc620_pmu_irqs structure only, we don't actually need
> to hold the lock when adding a new instance to the CPU hotplug subsystem.
> 
> Fix this possible deadlock scenario by adding a new
> dmc620_pmu_get_irq_lock for protecting the call to __dmc620_pmu_get_irq()
> and taking dmc620_pmu_irqs_lock inside __dmc620_pmu_get_irq()
> only when dmc620_pmu_irqs is being searched or modified. As a
> result, cpuhp_state_add_instance_nocalls() won't be called with
> dmc620_pmu_irqs_lock held and cpu_hotplug_lock won't be acquired after
> dmc620_pmu_irqs_lock.
> 
> Suggested-by: Robin Murphy <robin.murphy@arm.com>
> Signed-off-by: Waiman Long <longman@redhat.com>
> ---
>   drivers/perf/arm_dmc620_pmu.c | 18 ++++++++++++++----
>   1 file changed, 14 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/perf/arm_dmc620_pmu.c b/drivers/perf/arm_dmc620_pmu.c
> index 9d0f01c4455a..895971915f2d 100644
> --- a/drivers/perf/arm_dmc620_pmu.c
> +++ b/drivers/perf/arm_dmc620_pmu.c
> @@ -68,6 +68,7 @@
>   
>   static LIST_HEAD(dmc620_pmu_irqs);
>   static DEFINE_MUTEX(dmc620_pmu_irqs_lock);
> +static DEFINE_MUTEX(dmc620_pmu_get_irq_lock);
>   
>   struct dmc620_pmu_irq {
>   	struct hlist_node node;
> @@ -421,11 +422,18 @@ static irqreturn_t dmc620_pmu_handle_irq(int irq_num, void *data)
>   static struct dmc620_pmu_irq *__dmc620_pmu_get_irq(int irq_num)
>   {
>   	struct dmc620_pmu_irq *irq;
> +	bool found = false;
>   	int ret;
>   
> +	mutex_lock(&dmc620_pmu_irqs_lock);

Do we strictly need this? I'd hope that the outer release/acquire of 
dmc620_get_pmu_irqs_lock already means we can't observe an invalid value 
of irq->irq_num, and the refcount op should be atomic in itself, no? 
Fair enough if there's some other subtlety I'm missing - I do trust that 
you're more experienced in locking and barrier semantics than I am! - 
and if it comes to it I'd agree that simple extra locking is preferable 
to getting into explicit memory barriers here.

One other nit either way, could we clarify the names to be something 
like irqs_list_lock and irqs_users_lock? The split locking scheme 
doesn't exactly lend itself to being super-obvious, especially if we do 
end up nesting both locks, so I think naming them after what they 
semantically protect seems the most readable option. Otherwise, this 
does pretty much look like what I originally had in mind.

Cheers,
Robin.

>   	list_for_each_entry(irq, &dmc620_pmu_irqs, irqs_node)
> -		if (irq->irq_num == irq_num && refcount_inc_not_zero(&irq->refcount))
> -			return irq;
> +		if (irq->irq_num == irq_num && refcount_inc_not_zero(&irq->refcount)) {
> +			found = true;
> +			break;
> +		}
> +	mutex_unlock(&dmc620_pmu_irqs_lock);
> +	if (found)
> +		return irq;
>   
>   	irq = kzalloc(sizeof(*irq), GFP_KERNEL);
>   	if (!irq)
> @@ -452,7 +460,9 @@ static struct dmc620_pmu_irq *__dmc620_pmu_get_irq(int irq_num)
>   		goto out_free_irq;
>   
>   	irq->irq_num = irq_num;
> +	mutex_lock(&dmc620_pmu_irqs_lock);
>   	list_add(&irq->irqs_node, &dmc620_pmu_irqs);
> +	mutex_unlock(&dmc620_pmu_irqs_lock);
>   
>   	return irq;
>   
> @@ -467,9 +477,9 @@ static int dmc620_pmu_get_irq(struct dmc620_pmu *dmc620_pmu, int irq_num)
>   {
>   	struct dmc620_pmu_irq *irq;
>   
> -	mutex_lock(&dmc620_pmu_irqs_lock);
> +	mutex_lock(&dmc620_pmu_get_irq_lock);
>   	irq = __dmc620_pmu_get_irq(irq_num);
> -	mutex_unlock(&dmc620_pmu_irqs_lock);
> +	mutex_unlock(&dmc620_pmu_get_irq_lock);
>   
>   	if (IS_ERR(irq))
>   		return PTR_ERR(irq);

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

  reply	other threads:[~2023-08-08 12:30 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-08-07 15:44 [PATCH v4] perf/arm-dmc620: Fix dmc620_pmu_irqs_lock/cpu_hotplug_lock circular lock dependency Waiman Long
2023-08-08 12:29 ` Robin Murphy [this message]
2023-08-08 19:10   ` Waiman Long
2023-08-09 11:58     ` Will Deacon
2023-08-10 15:27       ` Waiman Long

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=0d32adf1-43fd-2762-d5ab-707d5969dcb0@arm.com \
    --to=robin.murphy@arm.com \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=longman@redhat.com \
    --cc=mark.rutland@arm.com \
    --cc=will@kernel.org \
    /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