regulator: qcom_pm8008-regulator: Avoid deadlock in OCP handling
For handling OCP events in PM8008, the PM8008 driver will acquire the chip-level regulator's mutex and then the affected LDO's regulator mutex, as part of the call flow, needed for calling regulator_notifier_call_chain twice. This could lead to a deadlock if any PM8008 LDO client also tries to enable the affected LDO at the same time. Thread 1: Camera trying to enable a PM8008 LDO [<ffffff9bb5ecebac>] mutex_lock+0x54 (en_supply regulator mutex, taken in below thread) [<ffffff9bb50912b8>] regulator_disable+0x30 [<ffffff9bb50a0ad4>] pm8008_regulator_enable+0x26c [<ffffff9bb5096cfc>] _regulator_do_enable+0x25c [<ffffff9bb5091228>] regulator_enable+0x168 (PM8008 regulator mutex acquired) Thread 2: OCP IRQ trigger and handling [<ffffff9bb5ecebac>] mutex_lock+0x54 (PM8008 regulator mutex, taken in above thread) [<ffffff9bb50a0450>] pm8008_ldo_cb+0x170 [<ffffff9bb4af7270>] blocking_notifier_call_chain+0x90 [<ffffff9bb5094448>] regulator_notifier_call_chain+0x30 [<ffffff9bb50a15e0>] pm8008_ocp_irq+0x30 (en_supply regulator mutex acquired) To avoid this race condition, move the second call to regulator_notifier_call_chain along with the acquisition of the LDO regulator mutex into a queued work task, to avoid the OCP handling thread holding both mutexes simultaneously. Change-Id: I649bfdc5cfd1d7bf84e6538cf91df6898adb90a0 Signed-off-by: Jishnu Prakash <quic_jprakash@quicinc.com>
This commit is contained in:
parent
2753cc581b
commit
5a48708e96
@ -1,5 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/* Copyright (c) 2019-2021, The Linux Foundation. All rights reserved. */
|
||||
/* Copyright (c) 2021, Qualcomm Innovation Center, Inc. All rights reserved. */
|
||||
|
||||
#define pr_fmt(fmt) "PM8008: %s: " fmt, __func__
|
||||
|
||||
@ -113,6 +114,7 @@ struct pm8008_regulator {
|
||||
int step_rate;
|
||||
bool enable_ocp_broadcast;
|
||||
enum pmic_subtype pmic_subtype;
|
||||
struct work_struct notify_clients_work;
|
||||
};
|
||||
|
||||
static const struct regulator_data pm8008_reg_data[PM8008_MAX_LDO] = {
|
||||
@ -601,18 +603,26 @@ static int pm8008_ldo_cb(struct notifier_block *nb, ulong event, void *data)
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Notify the consumers about the OCP event */
|
||||
mutex_lock(&pm8008_reg->rdev->mutex);
|
||||
regulator_notifier_call_chain(pm8008_reg->rdev,
|
||||
REGULATOR_EVENT_OVER_CURRENT, NULL);
|
||||
mutex_unlock(&pm8008_reg->rdev->mutex);
|
||||
schedule_work(&pm8008_reg->notify_clients_work);
|
||||
|
||||
error:
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static void notify_clients_work(struct work_struct *work)
|
||||
{
|
||||
struct pm8008_regulator *pm8008_reg = container_of(work,
|
||||
struct pm8008_regulator, notify_clients_work);
|
||||
|
||||
/* Notify the consumers about the OCP event */
|
||||
mutex_lock(&pm8008_reg->rdev->mutex);
|
||||
regulator_notifier_call_chain(pm8008_reg->rdev,
|
||||
REGULATOR_EVENT_OVER_CURRENT, NULL);
|
||||
mutex_unlock(&pm8008_reg->rdev->mutex);
|
||||
}
|
||||
|
||||
static int pm8008_regulator_register_init(struct pm8008_regulator *pm8008_reg,
|
||||
const struct regulator_data *reg_data)
|
||||
const struct regulator_data *reg_data)
|
||||
{
|
||||
int i, rc;
|
||||
|
||||
@ -789,6 +799,8 @@ static int pm8008_register_ldo(struct pm8008_regulator *pm8008_reg,
|
||||
rc);
|
||||
return rc;
|
||||
}
|
||||
INIT_WORK(&pm8008_reg->notify_clients_work,
|
||||
notify_clients_work);
|
||||
}
|
||||
|
||||
pr_debug("%s regulator registered\n", name);
|
||||
|
Loading…
Reference in New Issue
Block a user