aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/rapidio
diff options
context:
space:
mode:
authorAlexandre Bounine <alexandre.bounine@idt.com>2014-03-03 18:38:36 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2014-03-04 10:55:49 -0500
commit04379dffdd4da820d51a1566ad2e86f3b1ad97ed (patch)
tree8a010fae92af371d454169c070a8c05ae714f2b0 /drivers/rapidio
parentbd2c00353286d63542a8a0896a8c747f7c880edd (diff)
rapidio/tsi721: fix tasklet termination in dma channel release
This patch is a modification of the patch originally proposed by Xiaotian Feng <xtfeng@gmail.com>: https://lkml.org/lkml/2012/11/5/413 This new version disables DMA channel interrupts and ensures that the tasklet wil not be scheduled again before calling tasklet_kill(). Unfortunately the updated patch was not released at that time due to planned rework of Tsi721 mport driver to use threaded interrupts (which has yet to happen). Recently the issue was reported again: https://lkml.org/lkml/2014/2/19/762. Description from the original Xiaotian's patch: "Some drivers use tasklet_disable in device remove/release process, tasklet_disable will inc tasklet->count and return. If the tasklet is not handled yet under some softirq pressure, the tasklet will be placed on the tasklet_vec, never have a chance to be excuted. This might lead to a heavy loaded ksoftirqd, wakeup with pending_softirq, but tasklet is disabled. tasklet_kill should be used in this case." This patch is applicable to kernel versions starting from v3.5. Signed-off-by: Alexandre Bounine <alexandre.bounine@idt.com> Cc: Matt Porter <mporter@kernel.crashing.org> Cc: Xiaotian Feng <xtfeng@gmail.com> Reviewed-by: Thomas Gleixner <tglx@linutronix.de> Cc: Mike Galbraith <bitbucket@online.de> Cc: <stable@vger.kernel.org> [3.5+] Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/rapidio')
-rw-r--r--drivers/rapidio/devices/tsi721.h1
-rw-r--r--drivers/rapidio/devices/tsi721_dma.c27
2 files changed, 19 insertions, 9 deletions
diff --git a/drivers/rapidio/devices/tsi721.h b/drivers/rapidio/devices/tsi721.h
index b4b0d83f9ef6..7061ac0ad428 100644
--- a/drivers/rapidio/devices/tsi721.h
+++ b/drivers/rapidio/devices/tsi721.h
@@ -678,6 +678,7 @@ struct tsi721_bdma_chan {
678 struct list_head free_list; 678 struct list_head free_list;
679 dma_cookie_t completed_cookie; 679 dma_cookie_t completed_cookie;
680 struct tasklet_struct tasklet; 680 struct tasklet_struct tasklet;
681 bool active;
681}; 682};
682 683
683#endif /* CONFIG_RAPIDIO_DMA_ENGINE */ 684#endif /* CONFIG_RAPIDIO_DMA_ENGINE */
diff --git a/drivers/rapidio/devices/tsi721_dma.c b/drivers/rapidio/devices/tsi721_dma.c
index 502663f5f7c6..91245f5dbe81 100644
--- a/drivers/rapidio/devices/tsi721_dma.c
+++ b/drivers/rapidio/devices/tsi721_dma.c
@@ -206,8 +206,8 @@ void tsi721_bdma_handler(struct tsi721_bdma_chan *bdma_chan)
206{ 206{
207 /* Disable BDMA channel interrupts */ 207 /* Disable BDMA channel interrupts */
208 iowrite32(0, bdma_chan->regs + TSI721_DMAC_INTE); 208 iowrite32(0, bdma_chan->regs + TSI721_DMAC_INTE);
209 209 if (bdma_chan->active)
210 tasklet_schedule(&bdma_chan->tasklet); 210 tasklet_schedule(&bdma_chan->tasklet);
211} 211}
212 212
213#ifdef CONFIG_PCI_MSI 213#ifdef CONFIG_PCI_MSI
@@ -562,7 +562,7 @@ static int tsi721_alloc_chan_resources(struct dma_chan *dchan)
562 } 562 }
563#endif /* CONFIG_PCI_MSI */ 563#endif /* CONFIG_PCI_MSI */
564 564
565 tasklet_enable(&bdma_chan->tasklet); 565 bdma_chan->active = true;
566 tsi721_bdma_interrupt_enable(bdma_chan, 1); 566 tsi721_bdma_interrupt_enable(bdma_chan, 1);
567 567
568 return bdma_chan->bd_num - 1; 568 return bdma_chan->bd_num - 1;
@@ -576,9 +576,7 @@ err_out:
576static void tsi721_free_chan_resources(struct dma_chan *dchan) 576static void tsi721_free_chan_resources(struct dma_chan *dchan)
577{ 577{
578 struct tsi721_bdma_chan *bdma_chan = to_tsi721_chan(dchan); 578 struct tsi721_bdma_chan *bdma_chan = to_tsi721_chan(dchan);
579#ifdef CONFIG_PCI_MSI
580 struct tsi721_device *priv = to_tsi721(dchan->device); 579 struct tsi721_device *priv = to_tsi721(dchan->device);
581#endif
582 LIST_HEAD(list); 580 LIST_HEAD(list);
583 581
584 dev_dbg(dchan->device->dev, "%s: Entry\n", __func__); 582 dev_dbg(dchan->device->dev, "%s: Entry\n", __func__);
@@ -589,14 +587,25 @@ static void tsi721_free_chan_resources(struct dma_chan *dchan)
589 BUG_ON(!list_empty(&bdma_chan->active_list)); 587 BUG_ON(!list_empty(&bdma_chan->active_list));
590 BUG_ON(!list_empty(&bdma_chan->queue)); 588 BUG_ON(!list_empty(&bdma_chan->queue));
591 589
592 tasklet_disable(&bdma_chan->tasklet); 590 tsi721_bdma_interrupt_enable(bdma_chan, 0);
591 bdma_chan->active = false;
592
593#ifdef CONFIG_PCI_MSI
594 if (priv->flags & TSI721_USING_MSIX) {
595 synchronize_irq(priv->msix[TSI721_VECT_DMA0_DONE +
596 bdma_chan->id].vector);
597 synchronize_irq(priv->msix[TSI721_VECT_DMA0_INT +
598 bdma_chan->id].vector);
599 } else
600#endif
601 synchronize_irq(priv->pdev->irq);
602
603 tasklet_kill(&bdma_chan->tasklet);
593 604
594 spin_lock_bh(&bdma_chan->lock); 605 spin_lock_bh(&bdma_chan->lock);
595 list_splice_init(&bdma_chan->free_list, &list); 606 list_splice_init(&bdma_chan->free_list, &list);
596 spin_unlock_bh(&bdma_chan->lock); 607 spin_unlock_bh(&bdma_chan->lock);
597 608
598 tsi721_bdma_interrupt_enable(bdma_chan, 0);
599
600#ifdef CONFIG_PCI_MSI 609#ifdef CONFIG_PCI_MSI
601 if (priv->flags & TSI721_USING_MSIX) { 610 if (priv->flags & TSI721_USING_MSIX) {
602 free_irq(priv->msix[TSI721_VECT_DMA0_DONE + 611 free_irq(priv->msix[TSI721_VECT_DMA0_DONE +
@@ -790,6 +799,7 @@ int tsi721_register_dma(struct tsi721_device *priv)
790 bdma_chan->dchan.cookie = 1; 799 bdma_chan->dchan.cookie = 1;
791 bdma_chan->dchan.chan_id = i; 800 bdma_chan->dchan.chan_id = i;
792 bdma_chan->id = i; 801 bdma_chan->id = i;
802 bdma_chan->active = false;
793 803
794 spin_lock_init(&bdma_chan->lock); 804 spin_lock_init(&bdma_chan->lock);
795 805
@@ -799,7 +809,6 @@ int tsi721_register_dma(struct tsi721_device *priv)
799 809
800 tasklet_init(&bdma_chan->tasklet, tsi721_dma_tasklet, 810 tasklet_init(&bdma_chan->tasklet, tsi721_dma_tasklet,
801 (unsigned long)bdma_chan); 811 (unsigned long)bdma_chan);
802 tasklet_disable(&bdma_chan->tasklet);
803 list_add_tail(&bdma_chan->dchan.device_node, 812 list_add_tail(&bdma_chan->dchan.device_node,
804 &mport->dma.channels); 813 &mport->dma.channels);
805 } 814 }