* [PATCH 0/3] imx i2c vs clk framework deadlock fix
@ 2017-11-17 18:05 Lucas Stach
2017-11-17 18:05 ` [PATCH 1/3] i2c: imx: use clk notifier for rate changes Lucas Stach
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: Lucas Stach @ 2017-11-17 18:05 UTC (permalink / raw)
To: Wolfram Sang; +Cc: linux-i2c, kernel, patchwork-lst, Lukas Rusak, Chris Healy
Hi all,
this series fixes a deadlock between the i2c and the clk framework as seen
on some i.MX machines. The basic issue is that both the i2c and clk framework
employ some big hammer locks (clk_prepare mutex and i2c bus mutex), which
potentially span across several devices.
The imx i2c driver needs to take the clk prepare mutex in its runtime PM
handlers, as it is switching the device clock on and off depending on the
RPM state. With the i2c bus mutex being taken before calling into the driver
xfer function and the xfer function handling the runtime PM state we get the
usual lock ordering of bus_lock->clk_prepare.
Now with an i2c attached clock source, clk framwork operations on this clock
which require the clk_prepare mutex to be locked get a lock ordering of
clk_prepare -> [i2c-transfer function] -> i2c bus_lock ->
[driver xfer function being called invoking RPM] -> clk_prepare.
The only reason that this isn't constantly deadlocking with i2c attached clk
sources is that the imx-i2c driver uses RPM autosuspend, so the device clk
is still running in a lot of cases rendering the RPM resume a no-op. Note
that the cyclic lock dependency above is only the simple case, we can also
run into a ABBA deadlock involving multiple device on the same i2c bus, so
a simple nested mutex lock of the clk_prepare lock won't do.
The simplest solution I came up with is to move runtime PM for i2c
controllers into the i2c framework itself, allowing the RPM handling to be
done outside of the i2c bus lock. It has the nice side-effect of simplifying
the actual hardware driver, but as it involves changes to the framework I
would really like to hear from i2c mainainers if this is looking fishy in
any way.
Regards,
Lucas
Lucas Stach (3):
i2c: imx: use clk notifier for rate changes
i2c: add runtime PM handling to core
i2c: imx: use runtime PM provided by core
drivers/i2c/busses/i2c-imx.c | 43 ++++++++++++++++++++++++++++---------------
drivers/i2c/i2c-core-base.c | 18 ++++++++++++++++--
include/linux/i2c.h | 1 +
3 files changed, 45 insertions(+), 17 deletions(-)
--
2.11.0
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH 1/3] i2c: imx: use clk notifier for rate changes
2017-11-17 18:05 [PATCH 0/3] imx i2c vs clk framework deadlock fix Lucas Stach
@ 2017-11-17 18:05 ` Lucas Stach
2017-11-17 18:05 ` [PATCH 2/3] i2c: add runtime PM handling to core Lucas Stach
2017-11-17 18:05 ` [PATCH 3/3] i2c: imx: use runtime PM provided by core Lucas Stach
2 siblings, 0 replies; 4+ messages in thread
From: Lucas Stach @ 2017-11-17 18:05 UTC (permalink / raw)
To: Wolfram Sang; +Cc: linux-i2c, kernel, patchwork-lst, Lukas Rusak, Chris Healy
Instead of repeatedly calling clk_get_rate for each transfer, register
a clock notifier to update the cached divider value each time the clock
rate actually changes.
Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
---
drivers/i2c/busses/i2c-imx.c | 34 +++++++++++++++++++++++++++-------
1 file changed, 27 insertions(+), 7 deletions(-)
diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c
index 54a47b40546f..800c1f390308 100644
--- a/drivers/i2c/busses/i2c-imx.c
+++ b/drivers/i2c/busses/i2c-imx.c
@@ -467,15 +467,14 @@ static int i2c_imx_acked(struct imx_i2c_struct *i2c_imx)
return 0;
}
-static void i2c_imx_set_clk(struct imx_i2c_struct *i2c_imx)
+static void i2c_imx_set_clk(struct imx_i2c_struct *i2c_imx,
+ unsigned int i2c_clk_rate)
{
struct imx_i2c_clk_pair *i2c_clk_div = i2c_imx->hwdata->clk_div;
- unsigned int i2c_clk_rate;
unsigned int div;
int i;
/* Divider value calculation */
- i2c_clk_rate = clk_get_rate(i2c_imx->clk);
if (i2c_imx->cur_clk == i2c_clk_rate)
return;
@@ -510,6 +509,24 @@ static void i2c_imx_set_clk(struct imx_i2c_struct *i2c_imx)
#endif
}
+static int i2c_imx_clk_notifier_call(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct clk_notifier_data *ndata = data;
+ struct imx_i2c_struct *i2c_imx = container_of(&ndata->clk,
+ struct imx_i2c_struct,
+ clk);
+
+ if (action & POST_RATE_CHANGE)
+ i2c_imx_set_clk(i2c_imx, ndata->new_rate);
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block i2c_imx_clk_notifier = {
+ .notifier_call = i2c_imx_clk_notifier_call,
+};
+
static int i2c_imx_start(struct imx_i2c_struct *i2c_imx)
{
unsigned int temp = 0;
@@ -517,8 +534,6 @@ static int i2c_imx_start(struct imx_i2c_struct *i2c_imx)
dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);
- i2c_imx_set_clk(i2c_imx);
-
imx_i2c_write_reg(i2c_imx->ifdr, i2c_imx, IMX_I2C_IFDR);
/* Enable I2C controller */
imx_i2c_write_reg(i2c_imx->hwdata->i2sr_clr_opcode, i2c_imx, IMX_I2C_I2SR);
@@ -1131,6 +1146,8 @@ static int i2c_imx_probe(struct platform_device *pdev)
"clock-frequency", &i2c_imx->bitrate);
if (ret < 0 && pdata && pdata->bitrate)
i2c_imx->bitrate = pdata->bitrate;
+ clk_notifier_register(i2c_imx->clk, &i2c_imx_clk_notifier);
+ i2c_imx_set_clk(i2c_imx, clk_get_rate(i2c_imx->clk));
/* Set up chip registers to defaults */
imx_i2c_write_reg(i2c_imx->hwdata->i2cr_ien_opcode ^ I2CR_IEN,
@@ -1141,12 +1158,12 @@ static int i2c_imx_probe(struct platform_device *pdev)
ret = i2c_imx_init_recovery_info(i2c_imx, pdev);
/* Give it another chance if pinctrl used is not ready yet */
if (ret == -EPROBE_DEFER)
- goto rpm_disable;
+ goto clk_notifier_unregister;
/* Add I2C adapter */
ret = i2c_add_numbered_adapter(&i2c_imx->adapter);
if (ret < 0)
- goto rpm_disable;
+ goto clk_notifier_unregister;
pm_runtime_mark_last_busy(&pdev->dev);
pm_runtime_put_autosuspend(&pdev->dev);
@@ -1162,6 +1179,8 @@ static int i2c_imx_probe(struct platform_device *pdev)
return 0; /* Return OK */
+clk_notifier_unregister:
+ clk_notifier_unregister(i2c_imx->clk, &i2c_imx_clk_notifier);
rpm_disable:
pm_runtime_put_noidle(&pdev->dev);
pm_runtime_disable(&pdev->dev);
@@ -1195,6 +1214,7 @@ static int i2c_imx_remove(struct platform_device *pdev)
imx_i2c_write_reg(0, i2c_imx, IMX_I2C_I2CR);
imx_i2c_write_reg(0, i2c_imx, IMX_I2C_I2SR);
+ clk_notifier_unregister(i2c_imx->clk, &i2c_imx_clk_notifier);
clk_disable_unprepare(i2c_imx->clk);
pm_runtime_put_noidle(&pdev->dev);
--
2.11.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH 2/3] i2c: add runtime PM handling to core
2017-11-17 18:05 [PATCH 0/3] imx i2c vs clk framework deadlock fix Lucas Stach
2017-11-17 18:05 ` [PATCH 1/3] i2c: imx: use clk notifier for rate changes Lucas Stach
@ 2017-11-17 18:05 ` Lucas Stach
2017-11-17 18:05 ` [PATCH 3/3] i2c: imx: use runtime PM provided by core Lucas Stach
2 siblings, 0 replies; 4+ messages in thread
From: Lucas Stach @ 2017-11-17 18:05 UTC (permalink / raw)
To: Wolfram Sang; +Cc: linux-i2c, kernel, patchwork-lst, Lukas Rusak, Chris Healy
This allows to simplify the drivers if they opt to use the runtime PM
provided by the core. Even more importantly it allows to move the
runtime PM handling out of the i2c bus lock. As runtime PM may need to
take other locks itself (i.e. when manipulating clocks), holding the
bus lock may lead to deadlocks, due to different lock ordering.
Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
---
drivers/i2c/i2c-core-base.c | 18 ++++++++++++++++--
include/linux/i2c.h | 1 +
2 files changed, 17 insertions(+), 2 deletions(-)
diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c
index 56e46581b84b..3c971711171c 100644
--- a/drivers/i2c/i2c-core-base.c
+++ b/drivers/i2c/i2c-core-base.c
@@ -1931,11 +1931,19 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
}
#endif
+ if (adap->auto_rpm) {
+ ret = pm_runtime_get_sync(adap->dev.parent);
+ if (ret < 0)
+ return ret;
+ }
+
if (in_atomic() || irqs_disabled()) {
ret = i2c_trylock_bus(adap, I2C_LOCK_SEGMENT);
- if (!ret)
+ if (!ret) {
/* I2C activity is ongoing. */
- return -EAGAIN;
+ ret = -EAGAIN;
+ goto out_rpm;
+ }
} else {
i2c_lock_bus(adap, I2C_LOCK_SEGMENT);
}
@@ -1943,6 +1951,12 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
ret = __i2c_transfer(adap, msgs, num);
i2c_unlock_bus(adap, I2C_LOCK_SEGMENT);
+out_rpm:
+ if (adap->auto_rpm) {
+ pm_runtime_mark_last_busy(adap->dev.parent);
+ pm_runtime_put_autosuspend(adap->dev.parent);
+ }
+
return ret;
} else {
dev_dbg(&adap->dev, "I2C level transfers not supported\n");
diff --git a/include/linux/i2c.h b/include/linux/i2c.h
index d501d3956f13..6bc3d1ed554e 100644
--- a/include/linux/i2c.h
+++ b/include/linux/i2c.h
@@ -585,6 +585,7 @@ struct i2c_adapter {
int nr;
char name[48];
struct completion dev_released;
+ bool auto_rpm; /* runtime PM managed by core */
struct mutex userspace_clients_lock;
struct list_head userspace_clients;
--
2.11.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH 3/3] i2c: imx: use runtime PM provided by core
2017-11-17 18:05 [PATCH 0/3] imx i2c vs clk framework deadlock fix Lucas Stach
2017-11-17 18:05 ` [PATCH 1/3] i2c: imx: use clk notifier for rate changes Lucas Stach
2017-11-17 18:05 ` [PATCH 2/3] i2c: add runtime PM handling to core Lucas Stach
@ 2017-11-17 18:05 ` Lucas Stach
2 siblings, 0 replies; 4+ messages in thread
From: Lucas Stach @ 2017-11-17 18:05 UTC (permalink / raw)
To: Wolfram Sang; +Cc: linux-i2c, kernel, patchwork-lst, Lukas Rusak, Chris Healy
Simplifies the driver and fixes a deadlock, which has been observed
in the wild, due to the i2c bus lock and the clk framework clk_prepare
lock being taken in different order by multiple i2c devices.
Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
---
drivers/i2c/busses/i2c-imx.c | 9 +--------
1 file changed, 1 insertion(+), 8 deletions(-)
diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c
index 800c1f390308..95da40e74847 100644
--- a/drivers/i2c/busses/i2c-imx.c
+++ b/drivers/i2c/busses/i2c-imx.c
@@ -904,10 +904,6 @@ static int i2c_imx_xfer(struct i2c_adapter *adapter,
dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);
- result = pm_runtime_get_sync(i2c_imx->adapter.dev.parent);
- if (result < 0)
- goto out;
-
/* Start I2C transfer */
result = i2c_imx_start(i2c_imx);
if (result) {
@@ -971,10 +967,6 @@ static int i2c_imx_xfer(struct i2c_adapter *adapter,
/* Stop I2C transfer */
i2c_imx_stop(i2c_imx);
- pm_runtime_mark_last_busy(i2c_imx->adapter.dev.parent);
- pm_runtime_put_autosuspend(i2c_imx->adapter.dev.parent);
-
-out:
dev_dbg(&i2c_imx->adapter.dev, "<%s> exit with: %s: %d\n", __func__,
(result < 0) ? "error" : "success msg",
(result < 0) ? result : num);
@@ -1099,6 +1091,7 @@ static int i2c_imx_probe(struct platform_device *pdev)
i2c_imx->adapter.dev.parent = &pdev->dev;
i2c_imx->adapter.nr = pdev->id;
i2c_imx->adapter.dev.of_node = pdev->dev.of_node;
+ i2c_imx->adapter.auto_rpm = true;
i2c_imx->base = base;
/* Get I2C clock */
--
2.11.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
end of thread, other threads:[~2017-11-17 18:05 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2017-11-17 18:05 [PATCH 0/3] imx i2c vs clk framework deadlock fix Lucas Stach
2017-11-17 18:05 ` [PATCH 1/3] i2c: imx: use clk notifier for rate changes Lucas Stach
2017-11-17 18:05 ` [PATCH 2/3] i2c: add runtime PM handling to core Lucas Stach
2017-11-17 18:05 ` [PATCH 3/3] i2c: imx: use runtime PM provided by core Lucas Stach
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).