From: Tomeu Vizoso <tomeu.vizoso@collabora.com>
To: linux-kernel@vger.kernel.org
Cc: Stephen Warren <swarren@wwwdotorg.org>,
Javier Martinez Canillas <javier@osg.samsung.com>,
Mark Brown <broonie@kernel.org>,
Thierry Reding <thierry.reding@gmail.com>,
"Rafael J. Wysocki" <rjw@rjwysocki.net>,
linux-arm-kernel@lists.infradead.org,
Dmitry Torokhov <dmitry.torokhov@gmail.com>,
devicetree@vger.kernel.org,
Linus Walleij <linus.walleij@linaro.org>,
linux-acpi@vger.kernel.org, Arnd Bergmann <arnd@arndb.de>,
Tomeu Vizoso <tomeu.vizoso@collabora.com>,
Liam Girdwood <lgirdwood@gmail.com>
Subject: [PATCH v2 09/22] regulator: core: Reduce critical area in _regulator_get
Date: Tue, 28 Jul 2015 15:19:40 +0200 [thread overview]
Message-ID: <1438089593-7696-10-git-send-email-tomeu.vizoso@collabora.com> (raw)
In-Reply-To: <1438089593-7696-1-git-send-email-tomeu.vizoso@collabora.com>
...by moving the locking of regulator_list_mutex into
regulator_dev_lookup(), where it is iterated over.
The regulator device lock gets acquired before returning, and the caller
is responsible for releasing it after it's done with the regulator
device.
In _regulator_get() the regulator_list_mutex mutex is held for most of
the function, but is only strictly needed to protect the list lookups.
This change would be useful if for example regulator devices could be
registered on demand when a driver requests them. regulator_register()
could end up being called from within _regulator_get while the lock on
regulator_list_mutex is being held, causing a deadlock.
This backtrace illustrates the situation described above:
(regulator_register) from [<c05efe64>]
(devm_regulator_register+0x48/0x84)
(devm_regulator_register) from [<c05f0b20>]
(reg_fixed_voltage_probe+0x214/0x35c)
(reg_fixed_voltage_probe) from [<c06cc7fc>]
(platform_drv_probe+0x54/0xbc)
(platform_drv_probe) from [<c06caac8>] (driver_probe_device+0x184/0x2c4)
(driver_probe_device) from [<c06cac58>] (__device_attach+0x50/0x54)
(__device_attach) from [<c06c8eac>] (bus_for_each_drv+0x70/0xa4)
(bus_for_each_drv) from [<c06ca900>] (device_attach+0x90/0xa4)
(device_attach) from [<c06c9eb4>] (bus_probe_device+0x94/0xb8)
(bus_probe_device) from [<c06c7de8>] (device_add+0x384/0x580)
(device_add) from [<c095c104>] (of_device_add+0x44/0x4c)
(of_device_add) from [<c095c968>]
...
(regulator_dev_lookup) from [<c05ee7c0>] (_regulator_get+0x8c/0x26c)
(_regulator_get) from [<c05ee9c0>] (regulator_get+0x20/0x24)
(regulator_get) from [<c05efb1c>] (_devm_regulator_get+0xa4/0xc8)
(_devm_regulator_get) from [<c05efb5c>] (devm_regulator_get+0x1c/0x20)
(devm_regulator_get) from [<c06ba870>] (tegra_hdmi_probe+0xe0/0x278)
(tegra_hdmi_probe) from [<c06cc7fc>] (platform_drv_probe+0x54/0xbc)
Signed-off-by: Tomeu Vizoso <tomeu.vizoso@collabora.com>
---
Changes in v2:
- Acquire regulator device lock before returning from regulator_dev_lookup()
drivers/regulator/core.c | 98 +++++++++++++++++++++++++++---------------------
1 file changed, 55 insertions(+), 43 deletions(-)
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index 613034667b93..65a3f28d452c 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -110,6 +110,7 @@ static struct regulator *create_regulator(struct regulator_dev *rdev,
struct device *dev,
const char *supply_name);
static void _regulator_put(struct regulator *regulator);
+static int _regulator_enable(struct regulator *regulator, bool do_lock);
static const char *rdev_get_name(struct regulator_dev *rdev)
{
@@ -1212,6 +1213,7 @@ static void unset_regulator_supplies(struct regulator_dev *rdev)
#define REG_STR_SIZE 64
+/* rdev->mutex held by caller */
static struct regulator *create_regulator(struct regulator_dev *rdev,
struct device *dev,
const char *supply_name)
@@ -1224,7 +1226,7 @@ static struct regulator *create_regulator(struct regulator_dev *rdev,
if (regulator == NULL)
return NULL;
- mutex_lock(&rdev->mutex);
+ lockdep_assert_held_once(&rdev->mutex);
regulator->rdev = rdev;
list_add(®ulator->list, &rdev->consumer_list);
@@ -1276,12 +1278,10 @@ static struct regulator *create_regulator(struct regulator_dev *rdev,
_regulator_is_enabled(rdev))
regulator->always_on = true;
- mutex_unlock(&rdev->mutex);
return regulator;
overflow_err:
list_del(®ulator->list);
kfree(regulator);
- mutex_unlock(&rdev->mutex);
return NULL;
}
@@ -1320,6 +1320,7 @@ static void regulator_supply_alias(struct device **dev, const char **supply)
}
}
+/* Caller has to release r->mutex once done with r */
static struct regulator_dev *regulator_dev_lookup(struct device *dev,
const char *supply,
int *ret)
@@ -1335,10 +1336,15 @@ static struct regulator_dev *regulator_dev_lookup(struct device *dev,
if (dev && dev->of_node) {
node = of_get_regulator(dev, supply);
if (node) {
+ mutex_lock(®ulator_list_mutex);
list_for_each_entry(r, ®ulator_list, list)
if (r->dev.parent &&
- node == r->dev.of_node)
+ node == r->dev.of_node) {
+ mutex_lock(&r->mutex);
+ mutex_unlock(®ulator_list_mutex);
return r;
+ }
+ mutex_unlock(®ulator_list_mutex);
*ret = -EPROBE_DEFER;
return NULL;
} else {
@@ -1356,9 +1362,14 @@ static struct regulator_dev *regulator_dev_lookup(struct device *dev,
if (dev)
devname = dev_name(dev);
+ mutex_lock(®ulator_list_mutex);
list_for_each_entry(r, ®ulator_list, list)
- if (strcmp(rdev_get_name(r), supply) == 0)
+ if (strcmp(rdev_get_name(r), supply) == 0) {
+ mutex_lock(&r->mutex);
+ mutex_unlock(®ulator_list_mutex);
return r;
+ }
+ mutex_unlock(®ulator_list_mutex);
list_for_each_entry(map, ®ulator_map_list, list) {
/* If the mapping has a device set up it must match */
@@ -1400,6 +1411,7 @@ static int regulator_resolve_supply(struct regulator_dev *rdev)
if (!r) {
if (have_full_constraints()) {
r = dummy_regulator_rdev;
+ mutex_lock(&r->mutex);
} else {
dev_err(dev, "Failed to resolve %s-supply for %s\n",
rdev->supply_name, rdev->desc->name);
@@ -1410,23 +1422,25 @@ static int regulator_resolve_supply(struct regulator_dev *rdev)
/* Recursively resolve the supply of the supply */
ret = regulator_resolve_supply(r);
if (ret < 0)
- return ret;
+ goto out;
ret = set_supply(rdev, r);
if (ret < 0)
- return ret;
+ goto out;
/* Cascade always-on state to supply */
if (_regulator_is_enabled(rdev)) {
- ret = regulator_enable(rdev->supply);
+ ret = _regulator_enable(rdev->supply, false);
if (ret < 0) {
if (rdev->supply)
_regulator_put(rdev->supply);
- return ret;
+ goto out;
}
}
- return 0;
+out:
+ mutex_unlock(&r->mutex);
+ return ret;
}
/* Internal regulator request function */
@@ -1451,8 +1465,6 @@ static struct regulator *_regulator_get(struct device *dev, const char *id,
else
ret = -EPROBE_DEFER;
- mutex_lock(®ulator_list_mutex);
-
rdev = regulator_dev_lookup(dev, id, &ret);
if (rdev)
goto found;
@@ -1478,13 +1490,13 @@ static struct regulator *_regulator_get(struct device *dev, const char *id,
devname, id);
rdev = dummy_regulator_rdev;
+ mutex_lock(&rdev->mutex);
goto found;
/* Don't log an error when called from regulator_get_optional() */
} else if (!have_full_constraints() || exclusive) {
dev_warn(dev, "dummy supplies not allowed\n");
}
- mutex_unlock(®ulator_list_mutex);
return regulator;
found:
@@ -1526,8 +1538,7 @@ found:
}
out:
- mutex_unlock(®ulator_list_mutex);
-
+ mutex_unlock(&rdev->mutex);
return regulator;
}
@@ -1987,11 +1998,22 @@ static int _regulator_do_enable(struct regulator_dev *rdev)
}
/* locks held by regulator_enable() */
-static int _regulator_enable(struct regulator_dev *rdev)
+static int _regulator_enable(struct regulator *regulator, bool do_lock)
{
- int ret;
+ struct regulator_dev *rdev = regulator->rdev;
+ int ret = 0;
- lockdep_assert_held_once(&rdev->mutex);
+ if (regulator->always_on)
+ return 0;
+
+ if (rdev->supply) {
+ ret = regulator_enable(rdev->supply);
+ if (ret != 0)
+ return ret;
+ }
+
+ if (do_lock)
+ mutex_lock(&rdev->mutex);
/* check voltage and requested load before enabling */
if (rdev->constraints &&
@@ -2002,23 +2024,32 @@ static int _regulator_enable(struct regulator_dev *rdev)
/* The regulator may on if it's not switchable or left on */
ret = _regulator_is_enabled(rdev);
if (ret == -EINVAL || ret == 0) {
- if (!_regulator_can_change_status(rdev))
- return -EPERM;
+ if (!_regulator_can_change_status(rdev)) {
+ ret = -EPERM;
+ goto out;
+ }
ret = _regulator_do_enable(rdev);
if (ret < 0)
- return ret;
+ goto out;
} else if (ret < 0) {
rdev_err(rdev, "is_enabled() failed: %d\n", ret);
- return ret;
+ goto out;
}
/* Fallthrough on positive return values - already enabled */
}
rdev->use_count++;
- return 0;
+out:
+ if (do_lock)
+ mutex_unlock(&rdev->mutex);
+
+ if (ret != 0 && rdev->supply)
+ regulator_disable(rdev->supply);
+
+ return ret;
}
/**
@@ -2034,26 +2065,7 @@ static int _regulator_enable(struct regulator_dev *rdev)
*/
int regulator_enable(struct regulator *regulator)
{
- struct regulator_dev *rdev = regulator->rdev;
- int ret = 0;
-
- if (regulator->always_on)
- return 0;
-
- if (rdev->supply) {
- ret = regulator_enable(rdev->supply);
- if (ret != 0)
- return ret;
- }
-
- mutex_lock(&rdev->mutex);
- ret = _regulator_enable(rdev);
- mutex_unlock(&rdev->mutex);
-
- if (ret != 0 && rdev->supply)
- regulator_disable(rdev->supply);
-
- return ret;
+ return _regulator_enable(regulator, true);
}
EXPORT_SYMBOL_GPL(regulator_enable);
--
2.4.3
next prev parent reply other threads:[~2015-07-28 13:25 UTC|newest]
Thread overview: 37+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-07-28 13:19 [PATCH v2 0/22] On-demand device probing Tomeu Vizoso
2015-07-28 13:19 ` [PATCH v2 01/22] platform: delay device-driver matches until late_initcall Tomeu Vizoso
2015-07-30 3:20 ` Rob Herring
2015-07-31 10:06 ` Tomeu Vizoso
2015-07-28 13:19 ` [PATCH v2 02/22] of/platform: Set fwnode field for new devices Tomeu Vizoso
2015-07-28 13:19 ` [PATCH v2 03/22] device property: add fwnode_get_name() Tomeu Vizoso
2015-07-28 13:19 ` [PATCH v2 04/22] of/platform: add of_platform_device_find() Tomeu Vizoso
2015-07-28 13:39 ` Rob Herring
2015-07-28 13:54 ` Tomeu Vizoso
2015-07-28 15:31 ` Rob Herring
2015-07-29 6:14 ` Tomeu Vizoso
2015-07-29 11:20 ` Tomeu Vizoso
2015-07-29 12:15 ` Tomeu Vizoso
2015-07-29 15:27 ` Rob Herring
2015-07-31 10:32 ` Tomeu Vizoso
2015-07-28 13:19 ` [PATCH v2 05/22] ACPI: add acpi_dev_get_device() Tomeu Vizoso
2015-07-30 3:08 ` Rob Herring
2015-07-28 13:19 ` [PATCH v2 06/22] device property: add fwnode_ensure_device() Tomeu Vizoso
2015-07-28 13:19 ` [PATCH v2 07/22] gpio: Probe GPIO drivers on demand Tomeu Vizoso
2015-07-28 13:19 ` [PATCH v2 08/22] gpio: Probe pinctrl devices " Tomeu Vizoso
2015-07-28 13:19 ` Tomeu Vizoso [this message]
2015-07-28 13:19 ` [PATCH v2 10/22] regulator: core: Probe regulators " Tomeu Vizoso
2015-07-28 13:19 ` [PATCH v2 11/22] drm: Probe panels " Tomeu Vizoso
2015-07-28 13:19 ` [PATCH v2 12/22] drm/tegra: Probe dpaux devices " Tomeu Vizoso
2015-07-28 13:19 ` [PATCH v2 13/22] i2c: core: Probe i2c master " Tomeu Vizoso
2015-08-09 12:34 ` Wolfram Sang
2015-08-09 13:37 ` Tomeu Vizoso
2015-07-28 13:19 ` [PATCH v2 14/22] pwm: Probe PWM chip " Tomeu Vizoso
2015-07-28 13:19 ` [PATCH v2 15/22] backlight: Probe backlight " Tomeu Vizoso
2015-07-28 13:19 ` [PATCH v2 16/22] usb: phy: Probe phy " Tomeu Vizoso
2015-07-28 13:19 ` [PATCH v2 17/22] clk: Probe clk providers " Tomeu Vizoso
2015-07-28 13:19 ` [PATCH v2 18/22] pinctrl: Probe pinctrl devices " Tomeu Vizoso
2015-07-28 13:19 ` [PATCH v2 19/22] phy: core: Probe phy providers " Tomeu Vizoso
2015-07-28 13:19 ` [PATCH v2 20/22] dma: of: Probe DMA controllers " Tomeu Vizoso
2015-07-28 13:19 ` [PATCH v2 21/22] power-supply: Probe power supplies " Tomeu Vizoso
2015-07-28 13:19 ` [PATCH v2 22/22] ASoC: core: Probe components " Tomeu Vizoso
2015-07-29 0:36 ` [PATCH v2 0/22] On-demand device probing Rafael J. Wysocki
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=1438089593-7696-10-git-send-email-tomeu.vizoso@collabora.com \
--to=tomeu.vizoso@collabora.com \
--cc=arnd@arndb.de \
--cc=broonie@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=dmitry.torokhov@gmail.com \
--cc=javier@osg.samsung.com \
--cc=lgirdwood@gmail.com \
--cc=linus.walleij@linaro.org \
--cc=linux-acpi@vger.kernel.org \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=rjw@rjwysocki.net \
--cc=swarren@wwwdotorg.org \
--cc=thierry.reding@gmail.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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®