diff mbox series

[v6] i2c: pnx: Fix potential deadlock warning from del_timer_sync() call in isr

Message ID 20240628152543.281105-1-piotr.wojtaszczyk@timesys.com (mailing list archive)
State New, archived
Headers show
Series [v6] i2c: pnx: Fix potential deadlock warning from del_timer_sync() call in isr | expand

Commit Message

Piotr Wojtaszczyk June 28, 2024, 3:25 p.m. UTC
When del_timer_sync() is called in an interrupt context it throws a warning
because of potential deadlock. The timer is used only to exit from
wait_for_completion() after a timeout so replacing the call with
wait_for_completion_timeout() allows to remove the problematic timer and
its related functions altogether.

Signed-off-by: Piotr Wojtaszczyk <piotr.wojtaszczyk@timesys.com>
---
Changes for v6:
- Fixed typo in the patch subject

Changes for v5:
- Replaced wait_for_completion() with wait_for_completion_timeout().
- Removed unneded "alg_data->mif.timer" and its functions
- Request irq with devm_request_irq() as before the patch
- Renamed the patch and reword description for the new way to fix
  the warning

Changes for v4:
- Request irq with devm_request_threaded_irq() to prevent the warning

 drivers/i2c/busses/i2c-pnx.c | 48 ++++++++----------------------------
 1 file changed, 10 insertions(+), 38 deletions(-)

Comments

Andi Shyti July 1, 2024, 11:01 p.m. UTC | #1
Hi Piotr,

On Fri, Jun 28, 2024 at 05:25:42PM GMT, Piotr Wojtaszczyk wrote:
> When del_timer_sync() is called in an interrupt context it throws a warning
> because of potential deadlock. The timer is used only to exit from
> wait_for_completion() after a timeout so replacing the call with
> wait_for_completion_timeout() allows to remove the problematic timer and
> its related functions altogether.

nice patch! I still would like an ack from Vladimir.

> Signed-off-by: Piotr Wojtaszczyk <piotr.wojtaszczyk@timesys.com>

Fixes: 41561f28e76a ("i2c: New Philips PNX bus driver")
Cc: Vitaly Wool <vitaly.wool@konsulko.com>
Cc: <stable@vger.kernel.org> # v2.6.20+

...

> @@ -653,7 +624,10 @@ static int i2c_pnx_probe(struct platform_device *pdev)
>  	alg_data->adapter.algo_data = alg_data;
>  	alg_data->adapter.nr = pdev->id;
>  
> -	alg_data->timeout = I2C_PNX_TIMEOUT_DEFAULT;
> +	alg_data->timeout = msecs_to_jiffies(I2C_PNX_TIMEOUT_DEFAULT);
> +	if (alg_data->timeout <= 1)
> +		alg_data->timeout = 2;

I don't see the need for this check. The default timeout is
defined as 10.

Thanks,
Andi
Piotr Wojtaszczyk July 2, 2024, 9:13 a.m. UTC | #2
On Tue, Jul 2, 2024 at 1:01 AM Andi Shyti <andi.shyti@kernel.org> wrote:
> > @@ -653,7 +624,10 @@ static int i2c_pnx_probe(struct platform_device *pdev)
> >       alg_data->adapter.algo_data = alg_data;
> >       alg_data->adapter.nr = pdev->id;
> >
> > -     alg_data->timeout = I2C_PNX_TIMEOUT_DEFAULT;
> > +     alg_data->timeout = msecs_to_jiffies(I2C_PNX_TIMEOUT_DEFAULT);
> > +     if (alg_data->timeout <= 1)
> > +             alg_data->timeout = 2;
>
> I don't see the need for this check. The default timeout is
> defined as 10.
>
> Thanks,
> Andi

That's the timeout value which was in the previous timer in i2c_pnx_arm_timer(),
without this I had time out events.
Andi Shyti July 3, 2024, 10:19 p.m. UTC | #3
Hi Piotr,

On Tue, Jul 02, 2024 at 11:13:06AM GMT, Piotr Wojtaszczyk wrote:
> On Tue, Jul 2, 2024 at 1:01 AM Andi Shyti <andi.shyti@kernel.org> wrote:
> > > @@ -653,7 +624,10 @@ static int i2c_pnx_probe(struct platform_device *pdev)
> > >       alg_data->adapter.algo_data = alg_data;
> > >       alg_data->adapter.nr = pdev->id;
> > >
> > > -     alg_data->timeout = I2C_PNX_TIMEOUT_DEFAULT;
> > > +     alg_data->timeout = msecs_to_jiffies(I2C_PNX_TIMEOUT_DEFAULT);
> > > +     if (alg_data->timeout <= 1)
> > > +             alg_data->timeout = 2;
> >
> > I don't see the need for this check. The default timeout is
> > defined as 10.
> >
> > Thanks,
> > Andi
> 
> That's the timeout value which was in the previous timer in i2c_pnx_arm_timer(),
> without this I had time out events.

I meant the if() statement. We are sure timeout is not <= 1 at
this point.

Anyway, it doesn't matter. I applied the patch in
i2c/i2c-host-next.

Thanks,
Andi
Andi Shyti July 3, 2024, 10:21 p.m. UTC | #4
Hi Piotr,

On Thu, Jul 04, 2024 at 12:19:38AM GMT, Andi Shyti wrote:
> Hi Piotr,
> 
> On Tue, Jul 02, 2024 at 11:13:06AM GMT, Piotr Wojtaszczyk wrote:
> > On Tue, Jul 2, 2024 at 1:01 AM Andi Shyti <andi.shyti@kernel.org> wrote:
> > > > @@ -653,7 +624,10 @@ static int i2c_pnx_probe(struct platform_device *pdev)
> > > >       alg_data->adapter.algo_data = alg_data;
> > > >       alg_data->adapter.nr = pdev->id;
> > > >
> > > > -     alg_data->timeout = I2C_PNX_TIMEOUT_DEFAULT;
> > > > +     alg_data->timeout = msecs_to_jiffies(I2C_PNX_TIMEOUT_DEFAULT);
> > > > +     if (alg_data->timeout <= 1)
> > > > +             alg_data->timeout = 2;
> > >
> > > I don't see the need for this check. The default timeout is
> > > defined as 10.
> > >
> > > Thanks,
> > > Andi
> > 
> > That's the timeout value which was in the previous timer in i2c_pnx_arm_timer(),
> > without this I had time out events.
> 
> I meant the if() statement. We are sure timeout is not <= 1 at
> this point.
> 
> Anyway, it doesn't matter. I applied the patch in
> i2c/i2c-host-next.

Sorry, applied to i2c/i2c-host-fixes on

git://git.kernel.org/pub/scm/linux/kernel/git/andi.shyti/linux.git

Thank you,
Andi

Patches applied
===============
[1/1] i2c: pnx: Fix potential deadlock warning from del_timer_sync() call in isr
      commit: f63b94be6942ba82c55343e196bd09b53227618e
diff mbox series

Patch

diff --git a/drivers/i2c/busses/i2c-pnx.c b/drivers/i2c/busses/i2c-pnx.c
index a12525b3186b..f448505d5468 100644
--- a/drivers/i2c/busses/i2c-pnx.c
+++ b/drivers/i2c/busses/i2c-pnx.c
@@ -15,7 +15,6 @@ 
 #include <linux/ioport.h>
 #include <linux/delay.h>
 #include <linux/i2c.h>
-#include <linux/timer.h>
 #include <linux/completion.h>
 #include <linux/platform_device.h>
 #include <linux/io.h>
@@ -32,7 +31,6 @@  struct i2c_pnx_mif {
 	int			ret;		/* Return value */
 	int			mode;		/* Interface mode */
 	struct completion	complete;	/* I/O completion */
-	struct timer_list	timer;		/* Timeout */
 	u8 *			buf;		/* Data buffer */
 	int			len;		/* Length of data buffer */
 	int			order;		/* RX Bytes to order via TX */
@@ -117,24 +115,6 @@  static inline int wait_reset(struct i2c_pnx_algo_data *data)
 	return (timeout <= 0);
 }
 
-static inline void i2c_pnx_arm_timer(struct i2c_pnx_algo_data *alg_data)
-{
-	struct timer_list *timer = &alg_data->mif.timer;
-	unsigned long expires = msecs_to_jiffies(alg_data->timeout);
-
-	if (expires <= 1)
-		expires = 2;
-
-	del_timer_sync(timer);
-
-	dev_dbg(&alg_data->adapter.dev, "Timer armed at %lu plus %lu jiffies.\n",
-		jiffies, expires);
-
-	timer->expires = jiffies + expires;
-
-	add_timer(timer);
-}
-
 /**
  * i2c_pnx_start - start a device
  * @slave_addr:		slave address
@@ -259,8 +239,6 @@  static int i2c_pnx_master_xmit(struct i2c_pnx_algo_data *alg_data)
 				~(mcntrl_afie | mcntrl_naie | mcntrl_drmie),
 				  I2C_REG_CTL(alg_data));
 
-			del_timer_sync(&alg_data->mif.timer);
-
 			dev_dbg(&alg_data->adapter.dev,
 				"%s(): Waking up xfer routine.\n",
 				__func__);
@@ -276,8 +254,6 @@  static int i2c_pnx_master_xmit(struct i2c_pnx_algo_data *alg_data)
 			~(mcntrl_afie | mcntrl_naie | mcntrl_drmie),
 			  I2C_REG_CTL(alg_data));
 
-		/* Stop timer. */
-		del_timer_sync(&alg_data->mif.timer);
 		dev_dbg(&alg_data->adapter.dev,
 			"%s(): Waking up xfer routine after zero-xfer.\n",
 			__func__);
@@ -364,8 +340,6 @@  static int i2c_pnx_master_rcv(struct i2c_pnx_algo_data *alg_data)
 				 mcntrl_drmie | mcntrl_daie);
 			iowrite32(ctl, I2C_REG_CTL(alg_data));
 
-			/* Kill timer. */
-			del_timer_sync(&alg_data->mif.timer);
 			complete(&alg_data->mif.complete);
 		}
 	}
@@ -400,8 +374,6 @@  static irqreturn_t i2c_pnx_interrupt(int irq, void *dev_id)
 			 mcntrl_drmie);
 		iowrite32(ctl, I2C_REG_CTL(alg_data));
 
-		/* Stop timer, to prevent timeout. */
-		del_timer_sync(&alg_data->mif.timer);
 		complete(&alg_data->mif.complete);
 	} else if (stat & mstatus_nai) {
 		/* Slave did not acknowledge, generate a STOP */
@@ -419,8 +391,6 @@  static irqreturn_t i2c_pnx_interrupt(int irq, void *dev_id)
 		/* Our return value. */
 		alg_data->mif.ret = -EIO;
 
-		/* Stop timer, to prevent timeout. */
-		del_timer_sync(&alg_data->mif.timer);
 		complete(&alg_data->mif.complete);
 	} else {
 		/*
@@ -453,9 +423,8 @@  static irqreturn_t i2c_pnx_interrupt(int irq, void *dev_id)
 	return IRQ_HANDLED;
 }
 
-static void i2c_pnx_timeout(struct timer_list *t)
+static void i2c_pnx_timeout(struct i2c_pnx_algo_data *alg_data)
 {
-	struct i2c_pnx_algo_data *alg_data = from_timer(alg_data, t, mif.timer);
 	u32 ctl;
 
 	dev_err(&alg_data->adapter.dev,
@@ -472,7 +441,6 @@  static void i2c_pnx_timeout(struct timer_list *t)
 	iowrite32(ctl, I2C_REG_CTL(alg_data));
 	wait_reset(alg_data);
 	alg_data->mif.ret = -EIO;
-	complete(&alg_data->mif.complete);
 }
 
 static inline void bus_reset_if_active(struct i2c_pnx_algo_data *alg_data)
@@ -514,6 +482,7 @@  i2c_pnx_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
 	struct i2c_msg *pmsg;
 	int rc = 0, completed = 0, i;
 	struct i2c_pnx_algo_data *alg_data = adap->algo_data;
+	unsigned long time_left;
 	u32 stat;
 
 	dev_dbg(&alg_data->adapter.dev,
@@ -548,7 +517,6 @@  i2c_pnx_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
 		dev_dbg(&alg_data->adapter.dev, "%s(): mode %d, %d bytes\n",
 			__func__, alg_data->mif.mode, alg_data->mif.len);
 
-		i2c_pnx_arm_timer(alg_data);
 
 		/* initialize the completion var */
 		init_completion(&alg_data->mif.complete);
@@ -564,7 +532,10 @@  i2c_pnx_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
 			break;
 
 		/* Wait for completion */
-		wait_for_completion(&alg_data->mif.complete);
+		time_left = wait_for_completion_timeout(&alg_data->mif.complete,
+							alg_data->timeout);
+		if (time_left == 0)
+			i2c_pnx_timeout(alg_data);
 
 		if (!(rc = alg_data->mif.ret))
 			completed++;
@@ -653,7 +624,10 @@  static int i2c_pnx_probe(struct platform_device *pdev)
 	alg_data->adapter.algo_data = alg_data;
 	alg_data->adapter.nr = pdev->id;
 
-	alg_data->timeout = I2C_PNX_TIMEOUT_DEFAULT;
+	alg_data->timeout = msecs_to_jiffies(I2C_PNX_TIMEOUT_DEFAULT);
+	if (alg_data->timeout <= 1)
+		alg_data->timeout = 2;
+
 #ifdef CONFIG_OF
 	alg_data->adapter.dev.of_node = of_node_get(pdev->dev.of_node);
 	if (pdev->dev.of_node) {
@@ -673,8 +647,6 @@  static int i2c_pnx_probe(struct platform_device *pdev)
 	if (IS_ERR(alg_data->clk))
 		return PTR_ERR(alg_data->clk);
 
-	timer_setup(&alg_data->mif.timer, i2c_pnx_timeout, 0);
-
 	snprintf(alg_data->adapter.name, sizeof(alg_data->adapter.name),
 		 "%s", pdev->name);