Commit 87fe29b6 authored by Michał Mirosław's avatar Michał Mirosław Committed by Mark Brown

regulator: push allocations in create_regulator() outside of lock

Move all allocations outside of the regulator_lock()ed section.

======================================================
WARNING: possible circular locking dependency detected
5.7.13+ #535 Not tainted
------------------------------------------------------
f2fs_discard-179:7/702 is trying to acquire lock:
c0e5d920 (regulator_list_mutex){+.+.}-{3:3}, at: regulator_lock_dependent+0x54/0x2c0

but task is already holding lock:
cb95b080 (&dcc->cmd_lock){+.+.}-{3:3}, at: __issue_discard_cmd+0xec/0x5f8

which lock already depends on the new lock.

the existing dependency chain (in reverse order) is:

[...]

-> #3 (fs_reclaim){+.+.}-{0:0}:
       fs_reclaim_acquire.part.11+0x40/0x50
       fs_reclaim_acquire+0x24/0x28
       __kmalloc_track_caller+0x54/0x218
       kstrdup+0x40/0x5c
       create_regulator+0xf4/0x368
       regulator_resolve_supply+0x1a0/0x200
       regulator_register+0x9c8/0x163c

[...]

other info that might help us debug this:

Chain exists of:
  regulator_list_mutex --> &sit_i->sentry_lock --> &dcc->cmd_lock

[...]

Fixes: f8702f9e ("regulator: core: Use ww_mutex for regulators locking")
Signed-off-by: default avatarMichał Mirosław <mirq-linux@rere.qmqm.pl>
Cc: stable@vger.kernel.org
Link: https://lore.kernel.org/r/6eebc99b2474f4ffaa0405b15178ece0e7e4f608.1597195321.git.mirq-linux@rere.qmqm.plSigned-off-by: default avatarMark Brown <broonie@kernel.org>
parent 467bf301
...@@ -1580,44 +1580,53 @@ static struct regulator *create_regulator(struct regulator_dev *rdev, ...@@ -1580,44 +1580,53 @@ static struct regulator *create_regulator(struct regulator_dev *rdev,
const char *supply_name) const char *supply_name)
{ {
struct regulator *regulator; struct regulator *regulator;
int err;
if (dev) {
char buf[REG_STR_SIZE]; char buf[REG_STR_SIZE];
int err, size; int size;
size = snprintf(buf, REG_STR_SIZE, "%s-%s",
dev->kobj.name, supply_name);
if (size >= REG_STR_SIZE)
return NULL;
supply_name = kstrdup(buf, GFP_KERNEL);
if (supply_name == NULL)
return NULL;
} else {
supply_name = kstrdup_const(supply_name, GFP_KERNEL);
if (supply_name == NULL)
return NULL;
}
regulator = kzalloc(sizeof(*regulator), GFP_KERNEL); regulator = kzalloc(sizeof(*regulator), GFP_KERNEL);
if (regulator == NULL) if (regulator == NULL) {
kfree(supply_name);
return NULL; return NULL;
}
regulator_lock(rdev);
regulator->rdev = rdev; regulator->rdev = rdev;
regulator->supply_name = supply_name;
regulator_lock(rdev);
list_add(&regulator->list, &rdev->consumer_list); list_add(&regulator->list, &rdev->consumer_list);
regulator_unlock(rdev);
if (dev) { if (dev) {
regulator->dev = dev; regulator->dev = dev;
/* Add a link to the device sysfs entry */ /* Add a link to the device sysfs entry */
size = snprintf(buf, REG_STR_SIZE, "%s-%s",
dev->kobj.name, supply_name);
if (size >= REG_STR_SIZE)
goto overflow_err;
regulator->supply_name = kstrdup(buf, GFP_KERNEL);
if (regulator->supply_name == NULL)
goto overflow_err;
err = sysfs_create_link_nowarn(&rdev->dev.kobj, &dev->kobj, err = sysfs_create_link_nowarn(&rdev->dev.kobj, &dev->kobj,
buf); supply_name);
if (err) { if (err) {
rdev_dbg(rdev, "could not add device link %s err %d\n", rdev_dbg(rdev, "could not add device link %s err %d\n",
dev->kobj.name, err); dev->kobj.name, err);
/* non-fatal */ /* non-fatal */
} }
} else {
regulator->supply_name = kstrdup_const(supply_name, GFP_KERNEL);
if (regulator->supply_name == NULL)
goto overflow_err;
} }
regulator->debugfs = debugfs_create_dir(regulator->supply_name, regulator->debugfs = debugfs_create_dir(supply_name,
rdev->debugfs); rdev->debugfs);
if (!regulator->debugfs) { if (!regulator->debugfs) {
rdev_dbg(rdev, "Failed to create debugfs directory\n"); rdev_dbg(rdev, "Failed to create debugfs directory\n");
...@@ -1642,13 +1651,7 @@ static struct regulator *create_regulator(struct regulator_dev *rdev, ...@@ -1642,13 +1651,7 @@ 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;
regulator_unlock(rdev);
return regulator; return regulator;
overflow_err:
list_del(&regulator->list);
kfree(regulator);
regulator_unlock(rdev);
return NULL;
} }
static int _regulator_get_enable_time(struct regulator_dev *rdev) static int _regulator_get_enable_time(struct regulator_dev *rdev)
......
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