drivers/net: fix tasklet misuse issue
In commit 175c0dff, drivers uses tasklet_kill to avoid put disabled tasklet
on the tasklet vec. But some of the drivers uses tasklet_init & tasklet_disable
in the driver init code, then tasklet_enable when it is opened. This makes
tasklet_enable on a killed tasklet and make ksoftirqd crazy then. Normally,
drivers should use tasklet_init/tasklet_kill on device open/remove, and use
tasklet_disable/tasklet_enable on device suspend/resume.
Reported-by: Peter Wu <lekensteyn@gmail.com>
Tested-by: Peter Wu <lekensteyn@gmail.com>
Signed-off-by: Xiaotian Feng <dannyfeng@tencent.com>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: netdev@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Signed-off-by: David S. Miller <davem@davemloft.net>
	
	
This commit is contained in:
		
					parent
					
						
							
								b2942004fb
							
						
					
				
			
			
				commit
				
					
						71c6c837a0
					
				
			
		
					 3 changed files with 18 additions and 38 deletions
				
			
		|  | @ -1860,10 +1860,14 @@ jme_open(struct net_device *netdev) | ||||||
| 	jme_clear_pm(jme); | 	jme_clear_pm(jme); | ||||||
| 	JME_NAPI_ENABLE(jme); | 	JME_NAPI_ENABLE(jme); | ||||||
| 
 | 
 | ||||||
| 	tasklet_enable(&jme->linkch_task); | 	tasklet_init(&jme->linkch_task, jme_link_change_tasklet, | ||||||
| 	tasklet_enable(&jme->txclean_task); | 		     (unsigned long) jme); | ||||||
| 	tasklet_hi_enable(&jme->rxclean_task); | 	tasklet_init(&jme->txclean_task, jme_tx_clean_tasklet, | ||||||
| 	tasklet_hi_enable(&jme->rxempty_task); | 		     (unsigned long) jme); | ||||||
|  | 	tasklet_init(&jme->rxclean_task, jme_rx_clean_tasklet, | ||||||
|  | 		     (unsigned long) jme); | ||||||
|  | 	tasklet_init(&jme->rxempty_task, jme_rx_empty_tasklet, | ||||||
|  | 		     (unsigned long) jme); | ||||||
| 
 | 
 | ||||||
| 	rc = jme_request_irq(jme); | 	rc = jme_request_irq(jme); | ||||||
| 	if (rc) | 	if (rc) | ||||||
|  | @ -3079,22 +3083,6 @@ jme_init_one(struct pci_dev *pdev, | ||||||
| 	tasklet_init(&jme->pcc_task, | 	tasklet_init(&jme->pcc_task, | ||||||
| 		     jme_pcc_tasklet, | 		     jme_pcc_tasklet, | ||||||
| 		     (unsigned long) jme); | 		     (unsigned long) jme); | ||||||
| 	tasklet_init(&jme->linkch_task, |  | ||||||
| 		     jme_link_change_tasklet, |  | ||||||
| 		     (unsigned long) jme); |  | ||||||
| 	tasklet_init(&jme->txclean_task, |  | ||||||
| 		     jme_tx_clean_tasklet, |  | ||||||
| 		     (unsigned long) jme); |  | ||||||
| 	tasklet_init(&jme->rxclean_task, |  | ||||||
| 		     jme_rx_clean_tasklet, |  | ||||||
| 		     (unsigned long) jme); |  | ||||||
| 	tasklet_init(&jme->rxempty_task, |  | ||||||
| 		     jme_rx_empty_tasklet, |  | ||||||
| 		     (unsigned long) jme); |  | ||||||
| 	tasklet_disable_nosync(&jme->linkch_task); |  | ||||||
| 	tasklet_disable_nosync(&jme->txclean_task); |  | ||||||
| 	tasklet_disable_nosync(&jme->rxclean_task); |  | ||||||
| 	tasklet_disable_nosync(&jme->rxempty_task); |  | ||||||
| 	jme->dpi.cur = PCC_P1; | 	jme->dpi.cur = PCC_P1; | ||||||
| 
 | 
 | ||||||
| 	jme->reg_ghc = 0; | 	jme->reg_ghc = 0; | ||||||
|  |  | ||||||
|  | @ -5459,8 +5459,10 @@ static int prepare_hardware(struct net_device *dev) | ||||||
| 	rc = request_irq(dev->irq, netdev_intr, IRQF_SHARED, dev->name, dev); | 	rc = request_irq(dev->irq, netdev_intr, IRQF_SHARED, dev->name, dev); | ||||||
| 	if (rc) | 	if (rc) | ||||||
| 		return rc; | 		return rc; | ||||||
| 	tasklet_enable(&hw_priv->rx_tasklet); | 	tasklet_init(&hw_priv->rx_tasklet, rx_proc_task, | ||||||
| 	tasklet_enable(&hw_priv->tx_tasklet); | 		     (unsigned long) hw_priv); | ||||||
|  | 	tasklet_init(&hw_priv->tx_tasklet, tx_proc_task, | ||||||
|  | 		     (unsigned long) hw_priv); | ||||||
| 
 | 
 | ||||||
| 	hw->promiscuous = 0; | 	hw->promiscuous = 0; | ||||||
| 	hw->all_multi = 0; | 	hw->all_multi = 0; | ||||||
|  | @ -7033,16 +7035,6 @@ static int __devinit pcidev_init(struct pci_dev *pdev, | ||||||
| 	spin_lock_init(&hw_priv->hwlock); | 	spin_lock_init(&hw_priv->hwlock); | ||||||
| 	mutex_init(&hw_priv->lock); | 	mutex_init(&hw_priv->lock); | ||||||
| 
 | 
 | ||||||
| 	/* tasklet is enabled. */ |  | ||||||
| 	tasklet_init(&hw_priv->rx_tasklet, rx_proc_task, |  | ||||||
| 		(unsigned long) hw_priv); |  | ||||||
| 	tasklet_init(&hw_priv->tx_tasklet, tx_proc_task, |  | ||||||
| 		(unsigned long) hw_priv); |  | ||||||
| 
 |  | ||||||
| 	/* tasklet_enable will decrement the atomic counter. */ |  | ||||||
| 	tasklet_disable(&hw_priv->rx_tasklet); |  | ||||||
| 	tasklet_disable(&hw_priv->tx_tasklet); |  | ||||||
| 
 |  | ||||||
| 	for (i = 0; i < TOTAL_PORT_NUM; i++) | 	for (i = 0; i < TOTAL_PORT_NUM; i++) | ||||||
| 		init_waitqueue_head(&hw_priv->counter[i].counter); | 		init_waitqueue_head(&hw_priv->counter[i].counter); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -942,6 +942,10 @@ static int axienet_open(struct net_device *ndev) | ||||||
| 		phy_start(lp->phy_dev); | 		phy_start(lp->phy_dev); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	/* Enable tasklets for Axi DMA error handling */ | ||||||
|  | 	tasklet_init(&lp->dma_err_tasklet, axienet_dma_err_handler, | ||||||
|  | 		     (unsigned long) lp); | ||||||
|  | 
 | ||||||
| 	/* Enable interrupts for Axi DMA Tx */ | 	/* Enable interrupts for Axi DMA Tx */ | ||||||
| 	ret = request_irq(lp->tx_irq, axienet_tx_irq, 0, ndev->name, ndev); | 	ret = request_irq(lp->tx_irq, axienet_tx_irq, 0, ndev->name, ndev); | ||||||
| 	if (ret) | 	if (ret) | ||||||
|  | @ -950,8 +954,7 @@ static int axienet_open(struct net_device *ndev) | ||||||
| 	ret = request_irq(lp->rx_irq, axienet_rx_irq, 0, ndev->name, ndev); | 	ret = request_irq(lp->rx_irq, axienet_rx_irq, 0, ndev->name, ndev); | ||||||
| 	if (ret) | 	if (ret) | ||||||
| 		goto err_rx_irq; | 		goto err_rx_irq; | ||||||
| 	/* Enable tasklets for Axi DMA error handling */ | 
 | ||||||
| 	tasklet_enable(&lp->dma_err_tasklet); |  | ||||||
| 	return 0; | 	return 0; | ||||||
| 
 | 
 | ||||||
| err_rx_irq: | err_rx_irq: | ||||||
|  | @ -960,6 +963,7 @@ err_tx_irq: | ||||||
| 	if (lp->phy_dev) | 	if (lp->phy_dev) | ||||||
| 		phy_disconnect(lp->phy_dev); | 		phy_disconnect(lp->phy_dev); | ||||||
| 	lp->phy_dev = NULL; | 	lp->phy_dev = NULL; | ||||||
|  | 	tasklet_kill(&lp->dma_err_tasklet); | ||||||
| 	dev_err(lp->dev, "request_irq() failed\n"); | 	dev_err(lp->dev, "request_irq() failed\n"); | ||||||
| 	return ret; | 	return ret; | ||||||
| } | } | ||||||
|  | @ -1613,10 +1617,6 @@ static int __devinit axienet_of_probe(struct platform_device *op) | ||||||
| 		goto err_iounmap_2; | 		goto err_iounmap_2; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	tasklet_init(&lp->dma_err_tasklet, axienet_dma_err_handler, |  | ||||||
| 		     (unsigned long) lp); |  | ||||||
| 	tasklet_disable(&lp->dma_err_tasklet); |  | ||||||
| 
 |  | ||||||
| 	return 0; | 	return 0; | ||||||
| 
 | 
 | ||||||
| err_iounmap_2: | err_iounmap_2: | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Xiaotian Feng
				Xiaotian Feng