]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
dm9000: fix "BUG: spinlock recursion"
authorBaruch Siach <baruch@tkos.co.il>
Tue, 18 May 2010 00:45:48 +0000 (17:45 -0700)
committerGreg Kroah-Hartman <gregkh@suse.de>
Mon, 2 Aug 2010 17:26:45 +0000 (10:26 -0700)
commit 380fefb2ddabd4cd5f14dbe090481f0544e65078 upstream.

dm9000_set_rx_csum and dm9000_hash_table are called from atomic context (in
dm9000_init_dm9000), and from non-atomic context (via ethtool_ops and
net_device_ops respectively). This causes a spinlock recursion BUG. Fix this by
renaming these functions to *_unlocked for the atomic context, and make the
original functions locking wrappers for use in the non-atomic context.

Signed-off-by: Baruch Siach <baruch@tkos.co.il>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/net/dm9000.c

index b377300656889cd9db9a2242bcc213611798cd91..55cab1dc17322f0c1d960d9e13c12ae593e930c1 100644 (file)
@@ -475,17 +475,13 @@ static uint32_t dm9000_get_rx_csum(struct net_device *dev)
        return dm->rx_csum;
 }
 
-static int dm9000_set_rx_csum(struct net_device *dev, uint32_t data)
+static int dm9000_set_rx_csum_unlocked(struct net_device *dev, uint32_t data)
 {
        board_info_t *dm = to_dm9000_board(dev);
-       unsigned long flags;
 
        if (dm->can_csum) {
                dm->rx_csum = data;
-
-               spin_lock_irqsave(&dm->lock, flags);
                iow(dm, DM9000_RCSR, dm->rx_csum ? RCSR_CSUM : 0);
-               spin_unlock_irqrestore(&dm->lock, flags);
 
                return 0;
        }
@@ -493,6 +489,19 @@ static int dm9000_set_rx_csum(struct net_device *dev, uint32_t data)
        return -EOPNOTSUPP;
 }
 
+static int dm9000_set_rx_csum(struct net_device *dev, uint32_t data)
+{
+       board_info_t *dm = to_dm9000_board(dev);
+       unsigned long flags;
+       int ret;
+
+       spin_lock_irqsave(&dm->lock, flags);
+       ret = dm9000_set_rx_csum_unlocked(dev, data);
+       spin_unlock_irqrestore(&dm->lock, flags);
+
+       return ret;
+}
+
 static int dm9000_set_tx_csum(struct net_device *dev, uint32_t data)
 {
        board_info_t *dm = to_dm9000_board(dev);
@@ -721,7 +730,7 @@ static unsigned char dm9000_type_to_char(enum dm9000_type type)
  *  Set DM9000 multicast address
  */
 static void
-dm9000_hash_table(struct net_device *dev)
+dm9000_hash_table_unlocked(struct net_device *dev)
 {
        board_info_t *db = netdev_priv(dev);
        struct dev_mc_list *mcptr = dev->mc_list;
@@ -730,12 +739,9 @@ dm9000_hash_table(struct net_device *dev)
        u32 hash_val;
        u16 hash_table[4];
        u8 rcr = RCR_DIS_LONG | RCR_DIS_CRC | RCR_RXEN;
-       unsigned long flags;
 
        dm9000_dbg(db, 1, "entering %s\n", __func__);
 
-       spin_lock_irqsave(&db->lock, flags);
-
        for (i = 0, oft = DM9000_PAR; i < 6; i++, oft++)
                iow(db, oft, dev->dev_addr[i]);
 
@@ -765,6 +771,16 @@ dm9000_hash_table(struct net_device *dev)
        }
 
        iow(db, DM9000_RCR, rcr);
+}
+
+static void
+dm9000_hash_table(struct net_device *dev)
+{
+       board_info_t *db = netdev_priv(dev);
+       unsigned long flags;
+
+       spin_lock_irqsave(&db->lock, flags);
+       dm9000_hash_table_unlocked(dev);
        spin_unlock_irqrestore(&db->lock, flags);
 }
 
@@ -784,7 +800,7 @@ dm9000_init_dm9000(struct net_device *dev)
        db->io_mode = ior(db, DM9000_ISR) >> 6; /* ISR bit7:6 keeps I/O mode */
 
        /* Checksum mode */
-       dm9000_set_rx_csum(dev, db->rx_csum);
+       dm9000_set_rx_csum_unlocked(dev, db->rx_csum);
 
        /* GPIO0 on pre-activate PHY */
        iow(db, DM9000_GPR, 0); /* REG_1F bit0 activate phyxcer */
@@ -811,7 +827,7 @@ dm9000_init_dm9000(struct net_device *dev)
        iow(db, DM9000_ISR, ISR_CLR_STATUS); /* Clear interrupt status */
 
        /* Set address filter table */
-       dm9000_hash_table(dev);
+       dm9000_hash_table_unlocked(dev);
 
        imr = IMR_PAR | IMR_PTM | IMR_PRM;
        if (db->type != TYPE_DM9000E)