diff mbox

[11/32] dmaengine: imx-dma: explicitly freeup irq

Message ID 1467730478-9696-12-git-send-email-vinod.koul@intel.com (mailing list archive)
State Accepted
Headers show

Commit Message

Vinod Koul July 5, 2016, 2:54 p.m. UTC
dmaengine device should explicitly call devm_free_irq() when using
devm_reqister_irq().

The irq is still ON when devices remove is executed and irq should be
quiesced before remove is completed.

Signed-off-by: Vinod Koul <vinod.koul@intel.com>
Cc: Sascha Hauer <s.hauer@pengutronix.de>
Cc: Linus Walleij <linus.walleij@linaro.org>
---
 drivers/dma/imx-dma.c | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)

Comments

Sascha Hauer July 6, 2016, 9:02 a.m. UTC | #1
On Tue, Jul 05, 2016 at 08:24:17PM +0530, Vinod Koul wrote:
> dmaengine device should explicitly call devm_free_irq() when using
> devm_reqister_irq().
> 
> The irq is still ON when devices remove is executed and irq should be
> quiesced before remove is completed.
> 
> Signed-off-by: Vinod Koul <vinod.koul@intel.com>
> Cc: Sascha Hauer <s.hauer@pengutronix.de>
> Cc: Linus Walleij <linus.walleij@linaro.org>
> ---
> +static void imxdma_free_irq(struct platform_device *pdev, struct imxdma_engine *imxdma)
> +{
> +	int i;
> +
> +	if (is_imx1_dma(imxdma)) {
> +		devm_free_irq(&pdev->dev, imxdma->irq, imxdma);
> +		devm_free_irq(&pdev->dev, imxdma->irq_err, imxdma);
> +	}
> +
> +	for (i = 0; i < IMX_DMA_CHANNELS; i++) {
> +		struct imxdma_channel *imxdmac = &imxdma->channel[i];
> +
> +		if (!is_imx1_dma(imxdma))
> +			devm_free_irq(&pdev->dev, imxdmac->irq, imxdma);
> +	}
> +}
> +
>  static int imxdma_remove(struct platform_device *pdev)
>  {
>  	struct imxdma_engine *imxdma = platform_get_drvdata(pdev);
>  
> +	imxdma_free_irq(pdev, imxdma);
> +

I don't think this is a good idea for various reasons.

When it's not safe to rely on the interrupt being released by devm later
then shouldn't we explicitly release the interrupt in the probe error
path aswell? Then if we don't let devm release the irq what's the point
in using devm_request_irq at all?

Also what's the case in which we have to explicitly release the irq? The
imx-dma driver explicitly disables the interrupts for deactivated
channels and I assume that when the drivers remove() function is called
every channel *is* deactivated. If it's not then we are in big trouble
since then we must assume that also a DMA transfer might be ongoing which
will surely lead to trouble when the driver is removed.

I think that when this patch solves an issue we really have a problem
elsewhere, including that most users of devm_request_irq are wrong and
this function maybe shouldn't even exist.

Sascha
Vinod Koul July 6, 2016, 5:07 p.m. UTC | #2
On Wed, Jul 06, 2016 at 11:02:06AM +0200, Sascha Hauer wrote:
> >  static int imxdma_remove(struct platform_device *pdev)
> >  {
> >  	struct imxdma_engine *imxdma = platform_get_drvdata(pdev);
> >  
> > +	imxdma_free_irq(pdev, imxdma);
> > +
> 
> I don't think this is a good idea for various reasons.
> 
> When it's not safe to rely on the interrupt being released by devm later
> then shouldn't we explicitly release the interrupt in the probe error
> path aswell? Then if we don't let devm release the irq what's the point
> in using devm_request_irq at all?

IMHO devm_request_irq was not really a good idea, it helps, but is very
easy to get it wrong, which seems to be the usual case.

> Also what's the case in which we have to explicitly release the irq? The
> imx-dma driver explicitly disables the interrupts for deactivated
> channels and I assume that when the drivers remove() function is called
> every channel *is* deactivated. If it's not then we are in big trouble
> since then we must assume that also a DMA transfer might be ongoing which
> will surely lead to trouble when the driver is removed.

DMA transfer cannot be running at that time. That is not the point.

Sure you may have set the controller to not fire again, but does it
really protect you against spurious interrupts.

The point is that, there is no guarantee, so we are better off freeing
up explicitly so that it doesn't fire when remove is completed. Same is
true for tasklets.

Btw I would have been okay if the driver disabled irq in remove, few of
them do that and I haven't tinkered those..

Thanks
Sascha Hauer July 8, 2016, 5:34 a.m. UTC | #3
On Wed, Jul 06, 2016 at 10:37:22PM +0530, Vinod Koul wrote:
> On Wed, Jul 06, 2016 at 11:02:06AM +0200, Sascha Hauer wrote:
> > >  static int imxdma_remove(struct platform_device *pdev)
> > >  {
> > >  	struct imxdma_engine *imxdma = platform_get_drvdata(pdev);
> > >  
> > > +	imxdma_free_irq(pdev, imxdma);
> > > +
> > 
> > I don't think this is a good idea for various reasons.
> > 
> > When it's not safe to rely on the interrupt being released by devm later
> > then shouldn't we explicitly release the interrupt in the probe error
> > path aswell? Then if we don't let devm release the irq what's the point
> > in using devm_request_irq at all?
> 
> IMHO devm_request_irq was not really a good idea, it helps, but is very
> easy to get it wrong, which seems to be the usual case.
> 
> > Also what's the case in which we have to explicitly release the irq? The
> > imx-dma driver explicitly disables the interrupts for deactivated
> > channels and I assume that when the drivers remove() function is called
> > every channel *is* deactivated. If it's not then we are in big trouble
> > since then we must assume that also a DMA transfer might be ongoing which
> > will surely lead to trouble when the driver is removed.
> 
> DMA transfer cannot be running at that time. That is not the point.
> 
> Sure you may have set the controller to not fire again, but does it
> really protect you against spurious interrupts.
> 
> The point is that, there is no guarantee, so we are better off freeing
> up explicitly so that it doesn't fire when remove is completed. Same is
> true for tasklets.
> 
> Btw I would have been okay if the driver disabled irq in remove, few of
> them do that and I haven't tinkered those..

Wouldn't it then be better to use disable_irq() instead of releasing it?
This would make the intention clearer.

Sascha
Vinod Koul July 8, 2016, 7:43 a.m. UTC | #4
On Fri, Jul 08, 2016 at 07:34:23AM +0200, Sascha Hauer wrote:
> On Wed, Jul 06, 2016 at 10:37:22PM +0530, Vinod Koul wrote:
> > On Wed, Jul 06, 2016 at 11:02:06AM +0200, Sascha Hauer wrote:
> > > >  static int imxdma_remove(struct platform_device *pdev)
> > > >  {
> > > >  	struct imxdma_engine *imxdma = platform_get_drvdata(pdev);
> > > >  
> > > > +	imxdma_free_irq(pdev, imxdma);
> > > > +
> > > 
> > > I don't think this is a good idea for various reasons.
> > > 
> > > When it's not safe to rely on the interrupt being released by devm later
> > > then shouldn't we explicitly release the interrupt in the probe error
> > > path aswell? Then if we don't let devm release the irq what's the point
> > > in using devm_request_irq at all?
> > 
> > IMHO devm_request_irq was not really a good idea, it helps, but is very
> > easy to get it wrong, which seems to be the usual case.
> > 
> > > Also what's the case in which we have to explicitly release the irq? The
> > > imx-dma driver explicitly disables the interrupts for deactivated
> > > channels and I assume that when the drivers remove() function is called
> > > every channel *is* deactivated. If it's not then we are in big trouble
> > > since then we must assume that also a DMA transfer might be ongoing which
> > > will surely lead to trouble when the driver is removed.
> > 
> > DMA transfer cannot be running at that time. That is not the point.
> > 
> > Sure you may have set the controller to not fire again, but does it
> > really protect you against spurious interrupts.
> > 
> > The point is that, there is no guarantee, so we are better off freeing
> > up explicitly so that it doesn't fire when remove is completed. Same is
> > true for tasklets.
> > 
> > Btw I would have been okay if the driver disabled irq in remove, few of
> > them do that and I haven't tinkered those..
> 
> Wouldn't it then be better to use disable_irq() instead of releasing it?
> This would make the intention clearer.

That would solve the problem as well. Either way it doesn't matter to me.

Let me know your preference and I will change this accordingly

Thanks
Sascha Hauer July 8, 2016, 12:55 p.m. UTC | #5
On Fri, Jul 08, 2016 at 01:13:05PM +0530, Vinod Koul wrote:
> On Fri, Jul 08, 2016 at 07:34:23AM +0200, Sascha Hauer wrote:
> > On Wed, Jul 06, 2016 at 10:37:22PM +0530, Vinod Koul wrote:
> > > On Wed, Jul 06, 2016 at 11:02:06AM +0200, Sascha Hauer wrote:
> > > > >  static int imxdma_remove(struct platform_device *pdev)
> > > > >  {
> > > > >  	struct imxdma_engine *imxdma = platform_get_drvdata(pdev);
> > > > >  
> > > > > +	imxdma_free_irq(pdev, imxdma);
> > > > > +
> > > > 
> > > > I don't think this is a good idea for various reasons.
> > > > 
> > > > When it's not safe to rely on the interrupt being released by devm later
> > > > then shouldn't we explicitly release the interrupt in the probe error
> > > > path aswell? Then if we don't let devm release the irq what's the point
> > > > in using devm_request_irq at all?
> > > 
> > > IMHO devm_request_irq was not really a good idea, it helps, but is very
> > > easy to get it wrong, which seems to be the usual case.
> > > 
> > > > Also what's the case in which we have to explicitly release the irq? The
> > > > imx-dma driver explicitly disables the interrupts for deactivated
> > > > channels and I assume that when the drivers remove() function is called
> > > > every channel *is* deactivated. If it's not then we are in big trouble
> > > > since then we must assume that also a DMA transfer might be ongoing which
> > > > will surely lead to trouble when the driver is removed.
> > > 
> > > DMA transfer cannot be running at that time. That is not the point.
> > > 
> > > Sure you may have set the controller to not fire again, but does it
> > > really protect you against spurious interrupts.
> > > 
> > > The point is that, there is no guarantee, so we are better off freeing
> > > up explicitly so that it doesn't fire when remove is completed. Same is
> > > true for tasklets.
> > > 
> > > Btw I would have been okay if the driver disabled irq in remove, few of
> > > them do that and I haven't tinkered those..
> > 
> > Wouldn't it then be better to use disable_irq() instead of releasing it?
> > This would make the intention clearer.
> 
> That would solve the problem as well. Either way it doesn't matter to me.
> 
> Let me know your preference and I will change this accordingly

I prefer disable_irq().

Regards
 Sascha
diff mbox

Patch

diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c
index 48d85f8b95fe..aed0b77285da 100644
--- a/drivers/dma/imx-dma.c
+++ b/drivers/dma/imx-dma.c
@@ -167,6 +167,7 @@  struct imxdma_channel {
 	u32				ccr_to_device;
 	bool				enabled_2d;
 	int				slot_2d;
+	unsigned int			irq;
 };
 
 enum imx_dma_type {
@@ -186,6 +187,9 @@  struct imxdma_engine {
 	struct imx_dma_2d_config	slots_2d[IMX_DMA_2D_SLOTS];
 	struct imxdma_channel		channel[IMX_DMA_CHANNELS];
 	enum imx_dma_type		devtype;
+	unsigned int			irq;
+	unsigned int			irq_err;
+
 };
 
 struct imxdma_filter_data {
@@ -1100,6 +1104,7 @@  static int __init imxdma_probe(struct platform_device *pdev)
 			dev_warn(imxdma->dev, "Can't register IRQ for DMA\n");
 			goto disable_dma_ahb_clk;
 		}
+		imxdma->irq = irq;
 
 		irq_err = platform_get_irq(pdev, 1);
 		if (irq_err < 0) {
@@ -1113,6 +1118,7 @@  static int __init imxdma_probe(struct platform_device *pdev)
 			dev_warn(imxdma->dev, "Can't register ERRIRQ for DMA\n");
 			goto disable_dma_ahb_clk;
 		}
+		imxdma->irq_err = irq_err;
 	}
 
 	/* enable DMA module */
@@ -1150,6 +1156,8 @@  static int __init imxdma_probe(struct platform_device *pdev)
 					 irq + i, i);
 				goto disable_dma_ahb_clk;
 			}
+
+			imxdmac->irq = irq + i;
 			init_timer(&imxdmac->watchdog);
 			imxdmac->watchdog.function = &imxdma_watchdog;
 			imxdmac->watchdog.data = (unsigned long)imxdmac;
@@ -1217,10 +1225,29 @@  disable_dma_ipg_clk:
 	return ret;
 }
 
+static void imxdma_free_irq(struct platform_device *pdev, struct imxdma_engine *imxdma)
+{
+	int i;
+
+	if (is_imx1_dma(imxdma)) {
+		devm_free_irq(&pdev->dev, imxdma->irq, imxdma);
+		devm_free_irq(&pdev->dev, imxdma->irq_err, imxdma);
+	}
+
+	for (i = 0; i < IMX_DMA_CHANNELS; i++) {
+		struct imxdma_channel *imxdmac = &imxdma->channel[i];
+
+		if (!is_imx1_dma(imxdma))
+			devm_free_irq(&pdev->dev, imxdmac->irq, imxdma);
+	}
+}
+
 static int imxdma_remove(struct platform_device *pdev)
 {
 	struct imxdma_engine *imxdma = platform_get_drvdata(pdev);
 
+	imxdma_free_irq(pdev, imxdma);
+
         dma_async_device_unregister(&imxdma->dma_device);
 
 	if (pdev->dev.of_node)