Commit 2787710f authored by Daniel Thompson's avatar Daniel Thompson Committed by Jiri Kosina

HID: i2c-hid: goodix: Fix a lockdep splat

I'm was on the receiving end of a lockdep splat from this driver and after
scratching my head I couldn't be entirely sure it was a false positive
given we would also have to think about whether the regulator locking is
safe (since the notifier is called whilst holding regulator locks which
are also needed for regulator_is_enabled() ).

Regardless of whether it is a real bug or not, the mutex isn't needed.
We can use reference counting tricks instead to avoid races with the
notifier calls.

The observed splat follows:

------------------------------------------------------
kworker/u16:3/127 is trying to acquire lock:
ffff00008021fb20 (&ihid_goodix->regulator_mutex){+.+.}-{4:4}, at: ihid_goodix_vdd_notify+0x30/0x94

but task is already holding lock:
ffff0000835c60c0 (&(&rdev->notifier)->rwsem){++++}-{4:4}, at: blocking_notifier_call_chain+0x30/0x70

which lock already depends on the new lock.

the existing dependency chain (in reverse order) is:

-> #1 (&(&rdev->notifier)->rwsem){++++}-{4:4}:
       down_write+0x68/0x8c
       blocking_notifier_chain_register+0x54/0x70
       regulator_register_notifier+0x1c/0x24
       devm_regulator_register_notifier+0x58/0x98
       i2c_hid_of_goodix_probe+0xdc/0x158
       i2c_device_probe+0x25d/0x270
       really_probe+0x174/0x2cc
       __driver_probe_device+0xc0/0xd8
       driver_probe_device+0x50/0xe4
       __device_attach_driver+0xa8/0xc0
       bus_for_each_drv+0x9c/0xc0
       __device_attach_async_helper+0x6c/0xbc
       async_run_entry_fn+0x38/0x100
       process_one_work+0x294/0x438
       worker_thread+0x180/0x258
       kthread+0x120/0x130
       ret_from_fork+0x10/0x20

-> #0 (&ihid_goodix->regulator_mutex){+.+.}-{4:4}:
       __lock_acquire+0xd24/0xfe8
       lock_acquire+0x288/0x2f4
       __mutex_lock+0xa0/0x338
       mutex_lock_nested+0x3c/0x5c
       ihid_goodix_vdd_notify+0x30/0x94
       notifier_call_chain+0x6c/0x8c
       blocking_notifier_call_chain+0x48/0x70
       _notifier_call_chain.isra.0+0x18/0x20
       _regulator_enable+0xc0/0x178
       regulator_enable+0x40/0x7c
       goodix_i2c_hid_power_up+0x18/0x20
       i2c_hid_core_power_up.isra.0+0x1c/0x2c
       i2c_hid_core_probe+0xd8/0x3d4
       i2c_hid_of_goodix_probe+0x14c/0x158
       i2c_device_probe+0x25c/0x270
       really_probe+0x174/0x2cc
       __driver_probe_device+0xc0/0xd8
       driver_probe_device+0x50/0xe4
       __device_attach_driver+0xa8/0xc0
       bus_for_each_drv+0x9c/0xc0
       __device_attach_async_helper+0x6c/0xbc
       async_run_entry_fn+0x38/0x100
       process_one_work+0x294/0x438
       worker_thread+0x180/0x258
       kthread+0x120/0x130
       ret_from_fork+0x10/0x20

other info that might help us debug this:

 Possible unsafe locking scenario:

       CPU0                    CPU1
       ----                    ----
  lock(&(&rdev->notifier)->rwsem);
                               lock(&ihid_goodix->regulator_mutex);
                               lock(&(&rdev->notifier)->rwsem);
  lock(&ihid_goodix->regulator_mutex);

 *** DEADLOCK ***
Signed-off-by: default avatarDaniel Thompson <daniel.thompson@linaro.org>
Fixes: 18eeef46 ("HID: i2c-hid: goodix: Tie the reset line to true state of the regulator")
Reviewed-by: default avatarDouglas Anderson <dianders@chromium.org>
Signed-off-by: default avatarJiri Kosina <jkosina@suse.cz>
parent 817b8b9c
...@@ -27,7 +27,6 @@ struct i2c_hid_of_goodix { ...@@ -27,7 +27,6 @@ struct i2c_hid_of_goodix {
struct regulator *vdd; struct regulator *vdd;
struct notifier_block nb; struct notifier_block nb;
struct mutex regulator_mutex;
struct gpio_desc *reset_gpio; struct gpio_desc *reset_gpio;
const struct goodix_i2c_hid_timing_data *timings; const struct goodix_i2c_hid_timing_data *timings;
}; };
...@@ -67,8 +66,6 @@ static int ihid_goodix_vdd_notify(struct notifier_block *nb, ...@@ -67,8 +66,6 @@ static int ihid_goodix_vdd_notify(struct notifier_block *nb,
container_of(nb, struct i2c_hid_of_goodix, nb); container_of(nb, struct i2c_hid_of_goodix, nb);
int ret = NOTIFY_OK; int ret = NOTIFY_OK;
mutex_lock(&ihid_goodix->regulator_mutex);
switch (event) { switch (event) {
case REGULATOR_EVENT_PRE_DISABLE: case REGULATOR_EVENT_PRE_DISABLE:
gpiod_set_value_cansleep(ihid_goodix->reset_gpio, 1); gpiod_set_value_cansleep(ihid_goodix->reset_gpio, 1);
...@@ -87,8 +84,6 @@ static int ihid_goodix_vdd_notify(struct notifier_block *nb, ...@@ -87,8 +84,6 @@ static int ihid_goodix_vdd_notify(struct notifier_block *nb,
break; break;
} }
mutex_unlock(&ihid_goodix->regulator_mutex);
return ret; return ret;
} }
...@@ -102,8 +97,6 @@ static int i2c_hid_of_goodix_probe(struct i2c_client *client, ...@@ -102,8 +97,6 @@ static int i2c_hid_of_goodix_probe(struct i2c_client *client,
if (!ihid_goodix) if (!ihid_goodix)
return -ENOMEM; return -ENOMEM;
mutex_init(&ihid_goodix->regulator_mutex);
ihid_goodix->ops.power_up = goodix_i2c_hid_power_up; ihid_goodix->ops.power_up = goodix_i2c_hid_power_up;
ihid_goodix->ops.power_down = goodix_i2c_hid_power_down; ihid_goodix->ops.power_down = goodix_i2c_hid_power_down;
...@@ -130,25 +123,28 @@ static int i2c_hid_of_goodix_probe(struct i2c_client *client, ...@@ -130,25 +123,28 @@ static int i2c_hid_of_goodix_probe(struct i2c_client *client,
* long. Holding the controller in reset apparently draws extra * long. Holding the controller in reset apparently draws extra
* power. * power.
*/ */
mutex_lock(&ihid_goodix->regulator_mutex);
ihid_goodix->nb.notifier_call = ihid_goodix_vdd_notify; ihid_goodix->nb.notifier_call = ihid_goodix_vdd_notify;
ret = devm_regulator_register_notifier(ihid_goodix->vdd, &ihid_goodix->nb); ret = devm_regulator_register_notifier(ihid_goodix->vdd, &ihid_goodix->nb);
if (ret) { if (ret)
mutex_unlock(&ihid_goodix->regulator_mutex);
return dev_err_probe(&client->dev, ret, return dev_err_probe(&client->dev, ret,
"regulator notifier request failed\n"); "regulator notifier request failed\n");
}
/* /*
* If someone else is holding the regulator on (or the regulator is * If someone else is holding the regulator on (or the regulator is
* an always-on one) we might never be told to deassert reset. Do it * an always-on one) we might never be told to deassert reset. Do it
* now. Here we'll assume that someone else might have _just * now... and temporarily bump the regulator reference count just to
* barely_ turned the regulator on so we'll do the full * make sure it is impossible for this to race with our own notifier!
* "post_power_delay" just in case. * We also assume that someone else might have _just barely_ turned
* the regulator on so we'll do the full "post_power_delay" just in
* case.
*/ */
if (ihid_goodix->reset_gpio && regulator_is_enabled(ihid_goodix->vdd)) if (ihid_goodix->reset_gpio && regulator_is_enabled(ihid_goodix->vdd)) {
ret = regulator_enable(ihid_goodix->vdd);
if (ret)
return ret;
goodix_i2c_hid_deassert_reset(ihid_goodix, true); goodix_i2c_hid_deassert_reset(ihid_goodix, true);
mutex_unlock(&ihid_goodix->regulator_mutex); regulator_disable(ihid_goodix->vdd);
}
return i2c_hid_core_probe(client, &ihid_goodix->ops, 0x0001, 0); return i2c_hid_core_probe(client, &ihid_goodix->ops, 0x0001, 0);
} }
......
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