[ATM]: [lec] use refcnt to protect lec_arp_entries outside lock
[deliverable/linux.git] / net / atm / lec.c
index 29acfb0e6d04c9cf1adb57f339617dd0365a15d6..66c57c1091a8e3ff2020bd75135f7ac65e971544 100644 (file)
@@ -107,6 +107,19 @@ static void lec_vcc_added(struct lec_priv *priv, struct atmlec_ioc *ioc_data,
                                            struct sk_buff *skb));
 static void lec_vcc_close(struct lec_priv *priv, struct atm_vcc *vcc);
 
+/* must be done under lec_arp_lock */
+static inline void lec_arp_hold(struct lec_arp_table *entry)
+{
+       atomic_inc(&entry->usage);
+}
+
+static inline void lec_arp_put(struct lec_arp_table *entry)
+{
+       if (atomic_dec_and_test(&entry->usage))
+               kfree(entry);
+}
+
+
 static struct lane2_ops lane2_ops = {
        lane2_resolve,          /* resolve,             spec 3.1.3 */
        lane2_associate_req,    /* associate_req,       spec 3.1.4 */
@@ -383,7 +396,7 @@ static int lec_start_xmit(struct sk_buff *skb, struct net_device *dev)
                        priv->stats.tx_dropped++;
                        dev_kfree_skb(skb);
                }
-               return 0;
+               goto out;
        }
 #if DUMP_PACKETS > 0
        printk("%s:sending to vpi:%d vci:%d\n", dev->name, vcc->vpi, vcc->vci);
@@ -415,6 +428,9 @@ static int lec_start_xmit(struct sk_buff *skb, struct net_device *dev)
                        netif_wake_queue(dev);
        }
 
+out:
+       if (entry)
+               lec_arp_put(entry);
        dev->trans_start = jiffies;
        return 0;
 }
@@ -795,7 +811,7 @@ static void lec_push(struct atm_vcc *vcc, struct sk_buff *skb)
                        entry = lec_arp_find(priv, src);
                        if (entry && entry->vcc != vcc) {
                                lec_arp_remove(priv, entry);
-                               kfree(entry);
+                               lec_arp_put(entry);
                        }
                }
                spin_unlock_irqrestore(&priv->lec_arp_lock, flags);
@@ -1726,7 +1742,7 @@ static void lec_arp_destroy(struct lec_priv *priv)
        for (i = 0; i < LEC_ARP_TABLE_SIZE; i++) {
                hlist_for_each_entry_safe(entry, node, next, &priv->lec_arp_tables[i], next) {
                        lec_arp_remove(priv, entry);
-                       kfree(entry);
+                       lec_arp_put(entry);
                }
                INIT_HLIST_HEAD(&priv->lec_arp_tables[i]);
        }
@@ -1735,7 +1751,7 @@ static void lec_arp_destroy(struct lec_priv *priv)
                del_timer_sync(&entry->timer);
                lec_arp_clear_vccs(entry);
                hlist_del(&entry->next);
-               kfree(entry);
+               lec_arp_put(entry);
        }
        INIT_HLIST_HEAD(&priv->lec_arp_empty_ones);
 
@@ -1743,7 +1759,7 @@ static void lec_arp_destroy(struct lec_priv *priv)
                del_timer_sync(&entry->timer);
                lec_arp_clear_vccs(entry);
                hlist_del(&entry->next);
-               kfree(entry);
+               lec_arp_put(entry);
        }
        INIT_HLIST_HEAD(&priv->lec_no_forward);
 
@@ -1751,7 +1767,7 @@ static void lec_arp_destroy(struct lec_priv *priv)
                /* No timer, LANEv2 7.1.20 and 2.3.5.3 */
                lec_arp_clear_vccs(entry);
                hlist_del(&entry->next);
-               kfree(entry);
+               lec_arp_put(entry);
        }
        INIT_HLIST_HEAD(&priv->mcast_fwds);
        priv->mcast_vcc = NULL;
@@ -1799,6 +1815,7 @@ static struct lec_arp_table *make_entry(struct lec_priv *priv,
        to_return->last_used = jiffies;
        to_return->priv = priv;
        skb_queue_head_init(&to_return->tx_wait);
+       atomic_set(&to_return->usage, 1);
        return to_return;
 }
 
@@ -1843,7 +1860,7 @@ static void lec_arp_expire_vcc(unsigned long data)
        spin_unlock_irqrestore(&priv->lec_arp_lock, flags);
 
        lec_arp_clear_vccs(to_remove);
-       kfree(to_remove);
+       lec_arp_put(to_remove);
 }
 
 /*
@@ -1874,6 +1891,7 @@ static void lec_arp_check_expire(void *data)
 
        DPRINTK("lec_arp_check_expire %p\n", priv);
        now = jiffies;
+restart:
        spin_lock_irqsave(&priv->lec_arp_lock, flags);
        for (i = 0; i < LEC_ARP_TABLE_SIZE; i++) {
                hlist_for_each_entry_safe(entry, node, next, &priv->lec_arp_tables[i], next) {
@@ -1891,7 +1909,7 @@ static void lec_arp_check_expire(void *data)
                                /* Remove entry */
                                DPRINTK("LEC:Entry timed out\n");
                                lec_arp_remove(priv, entry);
-                               kfree(entry);
+                               lec_arp_put(entry);
                        } else {
                                /* Something else */
                                if ((entry->status == ESI_VC_PENDING ||
@@ -1913,14 +1931,16 @@ static void lec_arp_check_expire(void *data)
                                    time_after_eq(now, entry->timestamp +
                                                  priv->path_switching_delay)) {
                                        struct sk_buff *skb;
+                                       struct atm_vcc *vcc = entry->vcc;
 
-                                       while ((skb =
-                                               skb_dequeue(&entry->tx_wait)) !=
-                                              NULL)
-                                               lec_send(entry->vcc, skb,
-                                                        entry->priv);
+                                       lec_arp_hold(entry);
+                                       spin_unlock_irqrestore(&priv->lec_arp_lock, flags);
+                                       while ((skb = skb_dequeue(&entry->tx_wait)) != NULL)
+                                               lec_send(vcc, skb, entry->priv);
                                        entry->last_used = jiffies;
                                        entry->status = ESI_FORWARD_DIRECT;
+                                       lec_arp_put(entry);
+                                       goto restart;
                                }
                        }
                }
@@ -1963,6 +1983,7 @@ static struct atm_vcc *lec_arp_resolve(struct lec_priv *priv,
                if (entry->status == ESI_FORWARD_DIRECT) {
                        /* Connection Ok */
                        entry->last_used = jiffies;
+                       lec_arp_hold(entry);
                        *ret_entry = entry;
                        found = entry->vcc;
                        goto out;
@@ -1993,6 +2014,7 @@ static struct atm_vcc *lec_arp_resolve(struct lec_priv *priv,
                 * or BUS flood limit was reached for an entry which is
                 * in ESI_ARP_PENDING or ESI_VC_PENDING state.
                 */
+               lec_arp_hold(entry);
                *ret_entry = entry;
                DPRINTK("lec: entry->status %d entry->vcc %p\n", entry->status,
                        entry->vcc);
@@ -2045,7 +2067,7 @@ lec_addr_delete(struct lec_priv *priv, unsigned char *atm_addr,
                            && (permanent ||
                                !(entry->flags & LEC_PERMANENT_FLAG))) {
                                lec_arp_remove(priv, entry);
-                               kfree(entry);
+                               lec_arp_put(entry);
                        }
                        spin_unlock_irqrestore(&priv->lec_arp_lock, flags);
                        return 0;
@@ -2094,7 +2116,7 @@ lec_arp_update(struct lec_priv *priv, unsigned char *mac_addr,
                                        tmp->old_push = entry->old_push;
                                        tmp->last_used = jiffies;
                                        del_timer(&entry->timer);
-                                       kfree(entry);
+                                       lec_arp_put(entry);
                                        entry = tmp;
                                } else {
                                        entry->status = ESI_FORWARD_DIRECT;
@@ -2321,18 +2343,24 @@ static void lec_flush_complete(struct lec_priv *priv, unsigned long tran_id)
        int i;
 
        DPRINTK("LEC:lec_flush_complete %lx\n", tran_id);
+restart:
        spin_lock_irqsave(&priv->lec_arp_lock, flags);
        for (i = 0; i < LEC_ARP_TABLE_SIZE; i++) {
                hlist_for_each_entry(entry, node, &priv->lec_arp_tables[i], next) {
                        if (entry->flush_tran_id == tran_id
                            && entry->status == ESI_FLUSH_PENDING) {
                                struct sk_buff *skb;
+                               struct atm_vcc *vcc = entry->vcc;
 
-                               while ((skb =
-                                       skb_dequeue(&entry->tx_wait)) != NULL)
-                                       lec_send(entry->vcc, skb, entry->priv);
+                               lec_arp_hold(entry);
+                               spin_unlock_irqrestore(&priv->lec_arp_lock, flags);
+                               while ((skb = skb_dequeue(&entry->tx_wait)) != NULL)
+                                       lec_send(vcc, skb, entry->priv);
+                               entry->last_used = jiffies;
                                entry->status = ESI_FORWARD_DIRECT;
+                               lec_arp_put(entry);
                                DPRINTK("LEC_ARP: Flushed\n");
+                               goto restart;
                        }
                }
        }
@@ -2414,7 +2442,7 @@ static void lec_vcc_close(struct lec_priv *priv, struct atm_vcc *vcc)
                hlist_for_each_entry_safe(entry, node, next, &priv->lec_arp_tables[i], next) {
                        if (vcc == entry->vcc) {
                                lec_arp_remove(priv, entry);
-                               kfree(entry);
+                               lec_arp_put(entry);
                                if (priv->mcast_vcc == vcc) {
                                        priv->mcast_vcc = NULL;
                                }
@@ -2427,7 +2455,7 @@ static void lec_vcc_close(struct lec_priv *priv, struct atm_vcc *vcc)
                        lec_arp_clear_vccs(entry);
                        del_timer(&entry->timer);
                        hlist_del(&entry->next);
-                       kfree(entry);
+                       lec_arp_put(entry);
                }
        }
 
@@ -2436,7 +2464,7 @@ static void lec_vcc_close(struct lec_priv *priv, struct atm_vcc *vcc)
                        lec_arp_clear_vccs(entry);
                        del_timer(&entry->timer);
                        hlist_del(&entry->next);
-                       kfree(entry);
+                       lec_arp_put(entry);
                }
        }
 
@@ -2445,7 +2473,7 @@ static void lec_vcc_close(struct lec_priv *priv, struct atm_vcc *vcc)
                        lec_arp_clear_vccs(entry);
                        /* No timer, LANEv2 7.1.20 and 2.3.5.3 */
                        hlist_del(&entry->next);
-                       kfree(entry);
+                       lec_arp_put(entry);
                }
        }
 
@@ -2481,7 +2509,7 @@ lec_arp_check_empties(struct lec_priv *priv,
                        /* We might have got an entry */
                        if ((tmp = lec_arp_find(priv, src))) {
                                lec_arp_remove(priv, tmp);
-                               kfree(tmp);
+                               lec_arp_put(tmp);
                        }
                        hlist_del(&entry->next);
                        lec_arp_add(priv, entry);
This page took 0.028018 seconds and 5 git commands to generate.