Skip to content

Commit 7993d3a

Browse files
Rui Zhangbroonie
authored andcommitted
regulator: core: Only increment use_count when enable_count changes
The use_count of a regulator should only be incremented when the enable_count changes from 0 to 1. Similarly, the use_count should only be decremented when the enable_count changes from 1 to 0. In the previous implementation, use_count was sometimes decremented to 0 when some consumer called unbalanced disable, leading to unexpected disable even the regulator is enabled by other consumers. With this change, the use_count accurately reflects the number of users which the regulator is enabled. This should make things more robust in the case where a consumer does leak references. Signed-off-by: Rui Zhang <zr.zhang@vivo.com> Link: https://lore.kernel.org/r/20231103074231.8031-1-zr.zhang@vivo.com Signed-off-by: Mark Brown <broonie@kernel.org>
1 parent c986968 commit 7993d3a

File tree

1 file changed

+30
-26
lines changed

1 file changed

+30
-26
lines changed

drivers/regulator/core.c

Lines changed: 30 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2918,7 +2918,8 @@ static int _regulator_enable(struct regulator *regulator)
29182918
/* Fallthrough on positive return values - already enabled */
29192919
}
29202920

2921-
rdev->use_count++;
2921+
if (regulator->enable_count == 1)
2922+
rdev->use_count++;
29222923

29232924
return 0;
29242925

@@ -2993,37 +2994,40 @@ static int _regulator_disable(struct regulator *regulator)
29932994

29942995
lockdep_assert_held_once(&rdev->mutex.base);
29952996

2996-
if (WARN(rdev->use_count <= 0,
2997+
if (WARN(regulator->enable_count == 0,
29972998
"unbalanced disables for %s\n", rdev_get_name(rdev)))
29982999
return -EIO;
29993000

3000-
/* are we the last user and permitted to disable ? */
3001-
if (rdev->use_count == 1 &&
3002-
(rdev->constraints && !rdev->constraints->always_on)) {
3003-
3004-
/* we are last user */
3005-
if (regulator_ops_is_valid(rdev, REGULATOR_CHANGE_STATUS)) {
3006-
ret = _notifier_call_chain(rdev,
3007-
REGULATOR_EVENT_PRE_DISABLE,
3008-
NULL);
3009-
if (ret & NOTIFY_STOP_MASK)
3010-
return -EINVAL;
3011-
3012-
ret = _regulator_do_disable(rdev);
3013-
if (ret < 0) {
3014-
rdev_err(rdev, "failed to disable: %pe\n", ERR_PTR(ret));
3015-
_notifier_call_chain(rdev,
3016-
REGULATOR_EVENT_ABORT_DISABLE,
3001+
if (regulator->enable_count == 1) {
3002+
/* disabling last enable_count from this regulator */
3003+
/* are we the last user and permitted to disable ? */
3004+
if (rdev->use_count == 1 &&
3005+
(rdev->constraints && !rdev->constraints->always_on)) {
3006+
3007+
/* we are last user */
3008+
if (regulator_ops_is_valid(rdev, REGULATOR_CHANGE_STATUS)) {
3009+
ret = _notifier_call_chain(rdev,
3010+
REGULATOR_EVENT_PRE_DISABLE,
3011+
NULL);
3012+
if (ret & NOTIFY_STOP_MASK)
3013+
return -EINVAL;
3014+
3015+
ret = _regulator_do_disable(rdev);
3016+
if (ret < 0) {
3017+
rdev_err(rdev, "failed to disable: %pe\n", ERR_PTR(ret));
3018+
_notifier_call_chain(rdev,
3019+
REGULATOR_EVENT_ABORT_DISABLE,
3020+
NULL);
3021+
return ret;
3022+
}
3023+
_notifier_call_chain(rdev, REGULATOR_EVENT_DISABLE,
30173024
NULL);
3018-
return ret;
30193025
}
3020-
_notifier_call_chain(rdev, REGULATOR_EVENT_DISABLE,
3021-
NULL);
3022-
}
30233026

3024-
rdev->use_count = 0;
3025-
} else if (rdev->use_count > 1) {
3026-
rdev->use_count--;
3027+
rdev->use_count = 0;
3028+
} else if (rdev->use_count > 1) {
3029+
rdev->use_count--;
3030+
}
30273031
}
30283032

30293033
if (ret == 0)

0 commit comments

Comments
 (0)