Commit 66cf9a7e authored by Maciej Purski's avatar Maciej Purski Committed by Mark Brown

regulator: core: Make locks re-entrant

Setting voltage, enabling/disabling regulators requires operations on
all regulators related with the regulator being changed. Therefore,
all of them should be locked for the whole operation. With the current
locking implementation, adding additional dependency (regulators
coupling) causes deadlocks in some cases.

Introduce a possibility to attempt to lock a mutex multiple times
by the same task without waiting on a mutex. This should handle all
reasonable coupling-supplying combinations, especially when two coupled
regulators share common supplies. The only situation that should be
forbidden is simultaneous coupling and supplying between a pair of
regulators.

The idea is based on clk core.
Signed-off-by: default avatarMaciej Purski <m.purski@samsung.com>
Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent 0369e02b
...@@ -146,6 +146,56 @@ static inline struct regulator_dev *rdev_get_supply(struct regulator_dev *rdev) ...@@ -146,6 +146,56 @@ static inline struct regulator_dev *rdev_get_supply(struct regulator_dev *rdev)
return NULL; return NULL;
} }
/**
* regulator_lock_nested - lock a single regulator
* @rdev: regulator source
* @subclass: mutex subclass used for lockdep
*
* This function can be called many times by one task on
* a single regulator and its mutex will be locked only
* once. If a task, which is calling this function is other
* than the one, which initially locked the mutex, it will
* wait on mutex.
*/
static void regulator_lock_nested(struct regulator_dev *rdev,
unsigned int subclass)
{
if (!mutex_trylock(&rdev->mutex)) {
if (rdev->mutex_owner == current) {
rdev->ref_cnt++;
return;
}
mutex_lock_nested(&rdev->mutex, subclass);
}
rdev->ref_cnt = 1;
rdev->mutex_owner = current;
}
static inline void regulator_lock(struct regulator_dev *rdev)
{
regulator_lock_nested(rdev, 0);
}
/**
* regulator_unlock - unlock a single regulator
* @rdev: regulator_source
*
* This function unlocks the mutex when the
* reference counter reaches 0.
*/
static void regulator_unlock(struct regulator_dev *rdev)
{
if (rdev->ref_cnt != 0) {
rdev->ref_cnt--;
if (!rdev->ref_cnt) {
rdev->mutex_owner = NULL;
mutex_unlock(&rdev->mutex);
}
}
}
/** /**
* regulator_lock_supply - lock a regulator and its supplies * regulator_lock_supply - lock a regulator and its supplies
* @rdev: regulator source * @rdev: regulator source
...@@ -155,7 +205,7 @@ static void regulator_lock_supply(struct regulator_dev *rdev) ...@@ -155,7 +205,7 @@ static void regulator_lock_supply(struct regulator_dev *rdev)
int i; int i;
for (i = 0; rdev; rdev = rdev_get_supply(rdev), i++) for (i = 0; rdev; rdev = rdev_get_supply(rdev), i++)
mutex_lock_nested(&rdev->mutex, i); regulator_lock_nested(rdev, i);
} }
/** /**
...@@ -167,7 +217,7 @@ static void regulator_unlock_supply(struct regulator_dev *rdev) ...@@ -167,7 +217,7 @@ static void regulator_unlock_supply(struct regulator_dev *rdev)
struct regulator *supply; struct regulator *supply;
while (1) { while (1) {
mutex_unlock(&rdev->mutex); regulator_unlock(rdev);
supply = rdev->supply; supply = rdev->supply;
if (!rdev->supply) if (!rdev->supply)
...@@ -350,9 +400,9 @@ static ssize_t regulator_uV_show(struct device *dev, ...@@ -350,9 +400,9 @@ static ssize_t regulator_uV_show(struct device *dev,
struct regulator_dev *rdev = dev_get_drvdata(dev); struct regulator_dev *rdev = dev_get_drvdata(dev);
ssize_t ret; ssize_t ret;
mutex_lock(&rdev->mutex); regulator_lock(rdev);
ret = sprintf(buf, "%d\n", _regulator_get_voltage(rdev)); ret = sprintf(buf, "%d\n", _regulator_get_voltage(rdev));
mutex_unlock(&rdev->mutex); regulator_unlock(rdev);
return ret; return ret;
} }
...@@ -416,9 +466,9 @@ static ssize_t regulator_state_show(struct device *dev, ...@@ -416,9 +466,9 @@ static ssize_t regulator_state_show(struct device *dev,
struct regulator_dev *rdev = dev_get_drvdata(dev); struct regulator_dev *rdev = dev_get_drvdata(dev);
ssize_t ret; ssize_t ret;
mutex_lock(&rdev->mutex); regulator_lock(rdev);
ret = regulator_print_state(buf, _regulator_is_enabled(rdev)); ret = regulator_print_state(buf, _regulator_is_enabled(rdev));
mutex_unlock(&rdev->mutex); regulator_unlock(rdev);
return ret; return ret;
} }
...@@ -526,10 +576,10 @@ static ssize_t regulator_total_uA_show(struct device *dev, ...@@ -526,10 +576,10 @@ static ssize_t regulator_total_uA_show(struct device *dev,
struct regulator *regulator; struct regulator *regulator;
int uA = 0; int uA = 0;
mutex_lock(&rdev->mutex); regulator_lock(rdev);
list_for_each_entry(regulator, &rdev->consumer_list, list) list_for_each_entry(regulator, &rdev->consumer_list, list)
uA += regulator->uA_load; uA += regulator->uA_load;
mutex_unlock(&rdev->mutex); regulator_unlock(rdev);
return sprintf(buf, "%d\n", uA); return sprintf(buf, "%d\n", uA);
} }
static DEVICE_ATTR(requested_microamps, 0444, regulator_total_uA_show, NULL); static DEVICE_ATTR(requested_microamps, 0444, regulator_total_uA_show, NULL);
...@@ -1333,7 +1383,7 @@ static struct regulator *create_regulator(struct regulator_dev *rdev, ...@@ -1333,7 +1383,7 @@ static struct regulator *create_regulator(struct regulator_dev *rdev,
if (regulator == NULL) if (regulator == NULL)
return NULL; return NULL;
mutex_lock(&rdev->mutex); regulator_lock(rdev);
regulator->rdev = rdev; regulator->rdev = rdev;
list_add(&regulator->list, &rdev->consumer_list); list_add(&regulator->list, &rdev->consumer_list);
...@@ -1388,12 +1438,12 @@ static struct regulator *create_regulator(struct regulator_dev *rdev, ...@@ -1388,12 +1438,12 @@ static struct regulator *create_regulator(struct regulator_dev *rdev,
_regulator_is_enabled(rdev)) _regulator_is_enabled(rdev))
regulator->always_on = true; regulator->always_on = true;
mutex_unlock(&rdev->mutex); regulator_unlock(rdev);
return regulator; return regulator;
overflow_err: overflow_err:
list_del(&regulator->list); list_del(&regulator->list);
kfree(regulator); kfree(regulator);
mutex_unlock(&rdev->mutex); regulator_unlock(rdev);
return NULL; return NULL;
} }
...@@ -1782,13 +1832,13 @@ static void _regulator_put(struct regulator *regulator) ...@@ -1782,13 +1832,13 @@ static void _regulator_put(struct regulator *regulator)
/* remove any sysfs entries */ /* remove any sysfs entries */
if (regulator->dev) if (regulator->dev)
sysfs_remove_link(&rdev->dev.kobj, regulator->supply_name); sysfs_remove_link(&rdev->dev.kobj, regulator->supply_name);
mutex_lock(&rdev->mutex); regulator_lock(rdev);
list_del(&regulator->list); list_del(&regulator->list);
rdev->open_count--; rdev->open_count--;
rdev->exclusive = 0; rdev->exclusive = 0;
put_device(&rdev->dev); put_device(&rdev->dev);
mutex_unlock(&rdev->mutex); regulator_unlock(rdev);
kfree_const(regulator->supply_name); kfree_const(regulator->supply_name);
kfree(regulator); kfree(regulator);
...@@ -2396,7 +2446,7 @@ static void regulator_disable_work(struct work_struct *work) ...@@ -2396,7 +2446,7 @@ static void regulator_disable_work(struct work_struct *work)
disable_work.work); disable_work.work);
int count, i, ret; int count, i, ret;
mutex_lock(&rdev->mutex); regulator_lock(rdev);
BUG_ON(!rdev->deferred_disables); BUG_ON(!rdev->deferred_disables);
...@@ -2417,7 +2467,7 @@ static void regulator_disable_work(struct work_struct *work) ...@@ -2417,7 +2467,7 @@ static void regulator_disable_work(struct work_struct *work)
rdev_err(rdev, "Deferred disable failed: %d\n", ret); rdev_err(rdev, "Deferred disable failed: %d\n", ret);
} }
mutex_unlock(&rdev->mutex); regulator_unlock(rdev);
if (rdev->supply) { if (rdev->supply) {
for (i = 0; i < count; i++) { for (i = 0; i < count; i++) {
...@@ -2452,11 +2502,11 @@ int regulator_disable_deferred(struct regulator *regulator, int ms) ...@@ -2452,11 +2502,11 @@ int regulator_disable_deferred(struct regulator *regulator, int ms)
if (!ms) if (!ms)
return regulator_disable(regulator); return regulator_disable(regulator);
mutex_lock(&rdev->mutex); regulator_lock(rdev);
rdev->deferred_disables++; rdev->deferred_disables++;
mod_delayed_work(system_power_efficient_wq, &rdev->disable_work, mod_delayed_work(system_power_efficient_wq, &rdev->disable_work,
msecs_to_jiffies(ms)); msecs_to_jiffies(ms));
mutex_unlock(&rdev->mutex); regulator_unlock(rdev);
return 0; return 0;
} }
...@@ -2488,10 +2538,10 @@ static int _regulator_list_voltage(struct regulator_dev *rdev, ...@@ -2488,10 +2538,10 @@ static int _regulator_list_voltage(struct regulator_dev *rdev,
if (selector >= rdev->desc->n_voltages) if (selector >= rdev->desc->n_voltages)
return -EINVAL; return -EINVAL;
if (lock) if (lock)
mutex_lock(&rdev->mutex); regulator_lock(rdev);
ret = ops->list_voltage(rdev, selector); ret = ops->list_voltage(rdev, selector);
if (lock) if (lock)
mutex_unlock(&rdev->mutex); regulator_unlock(rdev);
} else if (rdev->is_switch && rdev->supply) { } else if (rdev->is_switch && rdev->supply) {
ret = _regulator_list_voltage(rdev->supply->rdev, ret = _regulator_list_voltage(rdev->supply->rdev,
selector, lock); selector, lock);
...@@ -3264,7 +3314,7 @@ int regulator_sync_voltage(struct regulator *regulator) ...@@ -3264,7 +3314,7 @@ int regulator_sync_voltage(struct regulator *regulator)
struct regulator_voltage *voltage = &regulator->voltage[PM_SUSPEND_ON]; struct regulator_voltage *voltage = &regulator->voltage[PM_SUSPEND_ON];
int ret, min_uV, max_uV; int ret, min_uV, max_uV;
mutex_lock(&rdev->mutex); regulator_lock(rdev);
if (!rdev->desc->ops->set_voltage && if (!rdev->desc->ops->set_voltage &&
!rdev->desc->ops->set_voltage_sel) { !rdev->desc->ops->set_voltage_sel) {
...@@ -3293,7 +3343,7 @@ int regulator_sync_voltage(struct regulator *regulator) ...@@ -3293,7 +3343,7 @@ int regulator_sync_voltage(struct regulator *regulator)
ret = _regulator_do_set_voltage(rdev, min_uV, max_uV); ret = _regulator_do_set_voltage(rdev, min_uV, max_uV);
out: out:
mutex_unlock(&rdev->mutex); regulator_unlock(rdev);
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(regulator_sync_voltage); EXPORT_SYMBOL_GPL(regulator_sync_voltage);
...@@ -3386,7 +3436,7 @@ int regulator_set_current_limit(struct regulator *regulator, ...@@ -3386,7 +3436,7 @@ int regulator_set_current_limit(struct regulator *regulator,
struct regulator_dev *rdev = regulator->rdev; struct regulator_dev *rdev = regulator->rdev;
int ret; int ret;
mutex_lock(&rdev->mutex); regulator_lock(rdev);
/* sanity check */ /* sanity check */
if (!rdev->desc->ops->set_current_limit) { if (!rdev->desc->ops->set_current_limit) {
...@@ -3401,7 +3451,7 @@ int regulator_set_current_limit(struct regulator *regulator, ...@@ -3401,7 +3451,7 @@ int regulator_set_current_limit(struct regulator *regulator,
ret = rdev->desc->ops->set_current_limit(rdev, min_uA, max_uA); ret = rdev->desc->ops->set_current_limit(rdev, min_uA, max_uA);
out: out:
mutex_unlock(&rdev->mutex); regulator_unlock(rdev);
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(regulator_set_current_limit); EXPORT_SYMBOL_GPL(regulator_set_current_limit);
...@@ -3410,7 +3460,7 @@ static int _regulator_get_current_limit(struct regulator_dev *rdev) ...@@ -3410,7 +3460,7 @@ static int _regulator_get_current_limit(struct regulator_dev *rdev)
{ {
int ret; int ret;
mutex_lock(&rdev->mutex); regulator_lock(rdev);
/* sanity check */ /* sanity check */
if (!rdev->desc->ops->get_current_limit) { if (!rdev->desc->ops->get_current_limit) {
...@@ -3420,7 +3470,7 @@ static int _regulator_get_current_limit(struct regulator_dev *rdev) ...@@ -3420,7 +3470,7 @@ static int _regulator_get_current_limit(struct regulator_dev *rdev)
ret = rdev->desc->ops->get_current_limit(rdev); ret = rdev->desc->ops->get_current_limit(rdev);
out: out:
mutex_unlock(&rdev->mutex); regulator_unlock(rdev);
return ret; return ret;
} }
...@@ -3456,7 +3506,7 @@ int regulator_set_mode(struct regulator *regulator, unsigned int mode) ...@@ -3456,7 +3506,7 @@ int regulator_set_mode(struct regulator *regulator, unsigned int mode)
int ret; int ret;
int regulator_curr_mode; int regulator_curr_mode;
mutex_lock(&rdev->mutex); regulator_lock(rdev);
/* sanity check */ /* sanity check */
if (!rdev->desc->ops->set_mode) { if (!rdev->desc->ops->set_mode) {
...@@ -3480,7 +3530,7 @@ int regulator_set_mode(struct regulator *regulator, unsigned int mode) ...@@ -3480,7 +3530,7 @@ int regulator_set_mode(struct regulator *regulator, unsigned int mode)
ret = rdev->desc->ops->set_mode(rdev, mode); ret = rdev->desc->ops->set_mode(rdev, mode);
out: out:
mutex_unlock(&rdev->mutex); regulator_unlock(rdev);
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(regulator_set_mode); EXPORT_SYMBOL_GPL(regulator_set_mode);
...@@ -3489,7 +3539,7 @@ static unsigned int _regulator_get_mode(struct regulator_dev *rdev) ...@@ -3489,7 +3539,7 @@ static unsigned int _regulator_get_mode(struct regulator_dev *rdev)
{ {
int ret; int ret;
mutex_lock(&rdev->mutex); regulator_lock(rdev);
/* sanity check */ /* sanity check */
if (!rdev->desc->ops->get_mode) { if (!rdev->desc->ops->get_mode) {
...@@ -3499,7 +3549,7 @@ static unsigned int _regulator_get_mode(struct regulator_dev *rdev) ...@@ -3499,7 +3549,7 @@ static unsigned int _regulator_get_mode(struct regulator_dev *rdev)
ret = rdev->desc->ops->get_mode(rdev); ret = rdev->desc->ops->get_mode(rdev);
out: out:
mutex_unlock(&rdev->mutex); regulator_unlock(rdev);
return ret; return ret;
} }
...@@ -3520,7 +3570,7 @@ static int _regulator_get_error_flags(struct regulator_dev *rdev, ...@@ -3520,7 +3570,7 @@ static int _regulator_get_error_flags(struct regulator_dev *rdev,
{ {
int ret; int ret;
mutex_lock(&rdev->mutex); regulator_lock(rdev);
/* sanity check */ /* sanity check */
if (!rdev->desc->ops->get_error_flags) { if (!rdev->desc->ops->get_error_flags) {
...@@ -3530,7 +3580,7 @@ static int _regulator_get_error_flags(struct regulator_dev *rdev, ...@@ -3530,7 +3580,7 @@ static int _regulator_get_error_flags(struct regulator_dev *rdev,
ret = rdev->desc->ops->get_error_flags(rdev, flags); ret = rdev->desc->ops->get_error_flags(rdev, flags);
out: out:
mutex_unlock(&rdev->mutex); regulator_unlock(rdev);
return ret; return ret;
} }
...@@ -3579,10 +3629,10 @@ int regulator_set_load(struct regulator *regulator, int uA_load) ...@@ -3579,10 +3629,10 @@ int regulator_set_load(struct regulator *regulator, int uA_load)
struct regulator_dev *rdev = regulator->rdev; struct regulator_dev *rdev = regulator->rdev;
int ret; int ret;
mutex_lock(&rdev->mutex); regulator_lock(rdev);
regulator->uA_load = uA_load; regulator->uA_load = uA_load;
ret = drms_uA_update(rdev); ret = drms_uA_update(rdev);
mutex_unlock(&rdev->mutex); regulator_unlock(rdev);
return ret; return ret;
} }
...@@ -3610,7 +3660,7 @@ int regulator_allow_bypass(struct regulator *regulator, bool enable) ...@@ -3610,7 +3660,7 @@ int regulator_allow_bypass(struct regulator *regulator, bool enable)
if (!regulator_ops_is_valid(rdev, REGULATOR_CHANGE_BYPASS)) if (!regulator_ops_is_valid(rdev, REGULATOR_CHANGE_BYPASS))
return 0; return 0;
mutex_lock(&rdev->mutex); regulator_lock(rdev);
if (enable && !regulator->bypass) { if (enable && !regulator->bypass) {
rdev->bypass_count++; rdev->bypass_count++;
...@@ -3634,7 +3684,7 @@ int regulator_allow_bypass(struct regulator *regulator, bool enable) ...@@ -3634,7 +3684,7 @@ int regulator_allow_bypass(struct regulator *regulator, bool enable)
if (ret == 0) if (ret == 0)
regulator->bypass = enable; regulator->bypass = enable;
mutex_unlock(&rdev->mutex); regulator_unlock(rdev);
return ret; return ret;
} }
...@@ -4300,9 +4350,9 @@ static int _regulator_suspend_late(struct device *dev, void *data) ...@@ -4300,9 +4350,9 @@ static int _regulator_suspend_late(struct device *dev, void *data)
suspend_state_t *state = data; suspend_state_t *state = data;
int ret; int ret;
mutex_lock(&rdev->mutex); regulator_lock(rdev);
ret = suspend_set_state(rdev, *state); ret = suspend_set_state(rdev, *state);
mutex_unlock(&rdev->mutex); regulator_unlock(rdev);
return ret; return ret;
} }
...@@ -4332,14 +4382,14 @@ static int _regulator_resume_early(struct device *dev, void *data) ...@@ -4332,14 +4382,14 @@ static int _regulator_resume_early(struct device *dev, void *data)
if (rstate == NULL) if (rstate == NULL)
return 0; return 0;
mutex_lock(&rdev->mutex); regulator_lock(rdev);
if (rdev->desc->ops->resume_early && if (rdev->desc->ops->resume_early &&
(rstate->enabled == ENABLE_IN_SUSPEND || (rstate->enabled == ENABLE_IN_SUSPEND ||
rstate->enabled == DISABLE_IN_SUSPEND)) rstate->enabled == DISABLE_IN_SUSPEND))
ret = rdev->desc->ops->resume_early(rdev); ret = rdev->desc->ops->resume_early(rdev);
mutex_unlock(&rdev->mutex); regulator_unlock(rdev);
return ret; return ret;
} }
...@@ -4641,7 +4691,7 @@ static int __init regulator_late_cleanup(struct device *dev, void *data) ...@@ -4641,7 +4691,7 @@ static int __init regulator_late_cleanup(struct device *dev, void *data)
if (!regulator_ops_is_valid(rdev, REGULATOR_CHANGE_STATUS)) if (!regulator_ops_is_valid(rdev, REGULATOR_CHANGE_STATUS))
return 0; return 0;
mutex_lock(&rdev->mutex); regulator_lock(rdev);
if (rdev->use_count) if (rdev->use_count)
goto unlock; goto unlock;
...@@ -4672,7 +4722,7 @@ static int __init regulator_late_cleanup(struct device *dev, void *data) ...@@ -4672,7 +4722,7 @@ static int __init regulator_late_cleanup(struct device *dev, void *data)
} }
unlock: unlock:
mutex_unlock(&rdev->mutex); regulator_unlock(rdev);
return 0; return 0;
} }
......
...@@ -434,6 +434,8 @@ struct regulator_dev { ...@@ -434,6 +434,8 @@ struct regulator_dev {
struct blocking_notifier_head notifier; struct blocking_notifier_head notifier;
struct mutex mutex; /* consumer lock */ struct mutex mutex; /* consumer lock */
struct task_struct *mutex_owner;
int ref_cnt;
struct module *owner; struct module *owner;
struct device dev; struct device dev;
struct regulation_constraints *constraints; struct regulation_constraints *constraints;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment