[linux-yocto] [PATCH 07/10] drivers/net: Avoid Possible Deadlock in the Axxia FEMAC Driver

Daniel Dragomir daniel.dragomir at windriver.com
Tue May 17 15:50:43 PDT 2016


From: John Jacques <john.jacques at intel.com>

This is to fix mount.nfs over TCP generating the  following circular
dependency and possible DEADLOCK  message. FEMAC driver has been
converted to LLTX to  avoid this issue. Tx lock is introduced in the
FEMAC driver.

 CPU0                    CPU1
 ----                    ----
lock(clock-AF_INET);
                        lock(_xmit_ETHER#2);
                        lock(clock-AF_INET);
lock(_xmit_ETHER#2);
*** DEADLOCK ***

Signed-off-by: Sreedevi Joshi <sreedevi.joshi at intel.com>
Signed-off-by: Bruce Ashfield <bruce.ashfield at windriver.com>
Signed-off-by: John Jacques <john.jacques at intel.com>
---
 drivers/net/ethernet/lsi/lsi_acp_mdio.c |  2 ++
 drivers/net/ethernet/lsi/lsi_acp_net.c  | 23 +++++++++++++++--------
 drivers/net/ethernet/lsi/lsi_acp_net.h  | 22 ++++++++++++++++------
 3 files changed, 33 insertions(+), 14 deletions(-)

diff --git a/drivers/net/ethernet/lsi/lsi_acp_mdio.c b/drivers/net/ethernet/lsi/lsi_acp_mdio.c
index ca4bc67..7bde921 100644
--- a/drivers/net/ethernet/lsi/lsi_acp_mdio.c
+++ b/drivers/net/ethernet/lsi/lsi_acp_mdio.c
@@ -251,6 +251,8 @@ acp_mdio_initialize(int offset, int period)
 {
 	write_reg(MDIO_CLK_OFFSET, offset);
 	write_reg(MDIO_CLK_PERIOD, period);
+
+	return;
 }
 
 /* acp_wrappers_init */
diff --git a/drivers/net/ethernet/lsi/lsi_acp_net.c b/drivers/net/ethernet/lsi/lsi_acp_net.c
index 4aae097..610ec8a 100644
--- a/drivers/net/ethernet/lsi/lsi_acp_net.c
+++ b/drivers/net/ethernet/lsi/lsi_acp_net.c
@@ -877,10 +877,11 @@ static irqreturn_t appnic_isr(int irq, void *device_id)
 	struct net_device *dev = (struct net_device *)device_id;
 	struct appnic_device *pdata = netdev_priv(dev);
 	unsigned long dma_interrupt_status;
-	unsigned long flags;
+	unsigned long dev_flags;
+	unsigned long tx_flags;
 
 	/* Acquire the lock. */
-	spin_lock_irqsave(&pdata->dev_lock, flags);
+	spin_lock_irqsave(&pdata->dev_lock, dev_flags);
 
 	/* Get the status. */
 	dma_interrupt_status = read_mac(APPNIC_DMA_INTERRUPT_STATUS);
@@ -893,7 +894,9 @@ static irqreturn_t appnic_isr(int irq, void *device_id)
 	if (TX_INTERRUPT(dma_interrupt_status)) {
 		/* transmition complete */
 		pdata->transmit_interrupts++;
+		spin_lock_irqsave(&pdata->tx_lock, tx_flags);
 		handle_transmit_interrupt(dev);
+		spin_unlock_irqrestore(&pdata->tx_lock, tx_flags);
 	}
 
 	if (RX_INTERRUPT(dma_interrupt_status)) {
@@ -912,7 +915,7 @@ static irqreturn_t appnic_isr(int irq, void *device_id)
 	}
 
 	/* Release the lock */
-	spin_unlock_irqrestore(&pdata->dev_lock, flags);
+	spin_unlock_irqrestore(&pdata->dev_lock, dev_flags);
 
 	return IRQ_HANDLED;
 }
@@ -988,12 +991,8 @@ static int appnic_stop(struct net_device *dev)
 
 	pr_info("%s: Stopping the interface.\n", LSI_DRV_NAME);
 
-	/* Disable interrupts. Note that disable_irq() will wait for
-	 * any interrupt handlers that are currently executing to
-	 * complete.
-	 */
+	/* Disable all device interrupts. */
 	write_mac(0, APPNIC_DMA_INTERRUPT_ENABLE);
-	disable_irq(dev->irq);
 	free_irq(dev->irq, dev);
 
 	/* Indicate to the OS that no more packets should be sent.  */
@@ -1029,6 +1028,10 @@ static int appnic_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
 	int length;
 	int buf_per_desc;
 	union appnic_queue_pointer queue;
+	unsigned long flags;
+
+	if (!spin_trylock_irqsave(&pdata->tx_lock, flags))
+		return NETDEV_TX_LOCKED;
 
 	length = skb->len < ETH_ZLEN ? ETH_ZLEN : skb->len;
 	buf_per_desc = pdata->tx_buf_sz / pdata->tx_num_desc;
@@ -1107,9 +1110,11 @@ static int appnic_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
 		pdata->out_of_tx_descriptors++;
 		pr_err("%s: No transmit descriptors available!\n",
 		       LSI_DRV_NAME);
+		spin_unlock_irqrestore(&pdata->tx_lock, flags);
 		return NETDEV_TX_BUSY;
 	}
 
+	spin_unlock_irqrestore(&pdata->tx_lock, flags);
 	/* Free the socket buffer. */
 	dev_kfree_skb(skb);
 
@@ -1507,6 +1512,7 @@ int appnic_init(struct net_device *dev)
 	/* Initialize the spinlocks. */
 
 	spin_lock_init(&pdata->dev_lock);
+	spin_lock_init(&pdata->tx_lock);
 
 	/* Take MAC out of reset. */
 
@@ -1632,6 +1638,7 @@ int appnic_init(struct net_device *dev)
 
 	dev->netdev_ops = &appnic_netdev_ops;
 	dev->ethtool_ops = &appnic_ethtool_ops;
+	dev->features |= NETIF_F_LLTX;
 
 	memset((void *) &pdata->napi, 0, sizeof(struct napi_struct));
 	netif_napi_add(dev, &pdata->napi,
diff --git a/drivers/net/ethernet/lsi/lsi_acp_net.h b/drivers/net/ethernet/lsi/lsi_acp_net.h
index 7dc08fc..9f5ae1d 100644
--- a/drivers/net/ethernet/lsi/lsi_acp_net.h
+++ b/drivers/net/ethernet/lsi/lsi_acp_net.h
@@ -13,6 +13,8 @@
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.
  */
 
 #ifndef _LSI_ACP_NET_H
@@ -38,6 +40,7 @@ extern int acp_mdio_write(unsigned long, unsigned long, unsigned short, int);
 /* Device Data Structures */
 
 struct appnic_dma_descriptor {
+
 #ifdef __LITTLE_ENDIAN
 	/* Word 0 */
 	/* 00=Fill|01=Block|10=Scatter */
@@ -87,6 +90,7 @@ struct appnic_dma_descriptor {
 } __packed;
 
 union appnic_queue_pointer {
+
 	unsigned long raw;
 
 	struct {
@@ -106,6 +110,7 @@ union appnic_queue_pointer {
 /* The appnic Device Structure */
 
 struct appnic_device {
+
 	/* net_device */
 	struct net_device *device;
 
@@ -183,6 +188,7 @@ struct appnic_device {
 
 	/* Spin Lock */
 	spinlock_t dev_lock;
+	spinlock_t tx_lock;
 
 	/* PHY */
 	struct mii_bus *mii_bus;
@@ -494,13 +500,16 @@ readdescriptor(unsigned long address, struct appnic_dma_descriptor *descriptor)
 {
 	memcpy(descriptor, (void *)address,
 	       sizeof(struct appnic_dma_descriptor));
+	return;
 }
 
 static inline void
 writedescriptor(unsigned long address,
 		const struct appnic_dma_descriptor *descriptor)
 {
-	memcpy((void *)address, descriptor, sizeof(struct appnic_dma_descriptor));
+	memcpy((void *)address, descriptor,
+		   sizeof(struct appnic_dma_descriptor));
+	return;
 }
 
 static inline union appnic_queue_pointer
@@ -521,33 +530,34 @@ femac_uncache(struct appnic_device *pdata)
 static inline void
 readdescriptor(unsigned long address, struct appnic_dma_descriptor *descriptor)
 {
-	unsigned long *from = (unsigned long *)address;
-	unsigned long *to = (unsigned long *)descriptor;
+	unsigned long *from = (unsigned long *) address;
+	unsigned long *to = (unsigned long *) descriptor;
 
 	*to++ = swab32(*from++);
 	*to++ = swab32(*from++);
 	*to++ = swab32(*from++);
 	*to++ = swab32(*from++);
+	return;
 }
 
 static inline void
 writedescriptor(unsigned long address,
 		const struct appnic_dma_descriptor *descriptor)
 {
-	unsigned long *to = (unsigned long *)address;
-	unsigned long *from = (unsigned long *)descriptor;
+	unsigned long *to = (unsigned long *) address;
+	unsigned long *from = (unsigned long *) descriptor;
 
 	*to++ = swab32(*from++);
 	*to++ = swab32(*from++);
 	*to++ = swab32(*from++);
 	*to++ = swab32(*from++);
+	return;
 }
 
 static inline union appnic_queue_pointer
 _swab_queue_pointer(const union appnic_queue_pointer *old_queue)
 {
 	union appnic_queue_pointer new_queue;
-
 	new_queue.raw = swab32(old_queue->raw);
 	return new_queue;
 }
-- 
1.9.1



More information about the linux-yocto mailing list