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 */
memset(rdesc, 0, ETH_ALEN);
/* offset 4 comes from LAN destination field in LE control frames */
if (trh->rcf & htons((uint16_t) TR_RCF_DIR_BIT))
- memcpy(&rdesc[4], &trh->rseg[num_rdsc - 2], sizeof(uint16_t));
+ memcpy(&rdesc[4], &trh->rseg[num_rdsc - 2], sizeof(__be16));
else {
- memcpy(&rdesc[4], &trh->rseg[1], sizeof(uint16_t));
+ memcpy(&rdesc[4], &trh->rseg[1], sizeof(__be16));
rdesc[5] = ((ntohs(trh->rseg[0]) & 0x000f) | (rdesc[5] & 0xf0));
}
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);
netif_wake_queue(dev);
}
+out:
+ if (entry)
+ lec_arp_put(entry);
dev->trans_start = jiffies;
return 0;
}
unsigned char *src, *dst;
atm_return(vcc, skb->truesize);
- if (*(uint16_t *) skb->data == htons(priv->lecid) ||
+ if (*(__be16 *) skb->data == htons(priv->lecid) ||
!priv->lecd || !(dev->flags & IFF_UP)) {
/*
* Probably looping back, or if lecd is missing,
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);
if (table == NULL)
return -1;
- *tlvs = kmalloc(table->sizeoftlvs, GFP_ATOMIC);
+ *tlvs = kmemdup(table->tlvs, table->sizeoftlvs, GFP_ATOMIC);
if (*tlvs == NULL)
return -1;
- memcpy(*tlvs, table->tlvs, table->sizeoftlvs);
*sizeoftlvs = table->sizeoftlvs;
return 0;
kfree(priv->tlvs); /* NULL if there was no previous association */
- priv->tlvs = kmalloc(sizeoftlvs, GFP_KERNEL);
+ priv->tlvs = kmemdup(tlvs, sizeoftlvs, GFP_KERNEL);
if (priv->tlvs == NULL)
return (0);
priv->sizeoftlvs = sizeoftlvs;
- memcpy(priv->tlvs, tlvs, sizeoftlvs);
skb = alloc_skb(sizeoftlvs, GFP_ATOMIC);
if (skb == NULL)
kfree(entry->tlvs);
- entry->tlvs = kmalloc(sizeoftlvs, GFP_KERNEL);
+ entry->tlvs = kmemdup(tlvs, sizeoftlvs, GFP_KERNEL);
if (entry->tlvs == NULL)
return;
-
entry->sizeoftlvs = sizeoftlvs;
- memcpy(entry->tlvs, tlvs, sizeoftlvs);
#endif
#if 0
printk("lec.c: lane2_associate_ind()\n");
#define LEC_ARP_REFRESH_INTERVAL (3*HZ)
-static void lec_arp_check_expire(unsigned long data);
+static void lec_arp_check_expire(struct work_struct *work);
static void lec_arp_expire_arp(unsigned long data);
/*
INIT_HLIST_HEAD(&priv->lec_no_forward);
INIT_HLIST_HEAD(&priv->mcast_fwds);
spin_lock_init(&priv->lec_arp_lock);
- init_timer(&priv->lec_arp_timer);
- priv->lec_arp_timer.expires = jiffies + LEC_ARP_REFRESH_INTERVAL;
- priv->lec_arp_timer.data = (unsigned long)priv;
- priv->lec_arp_timer.function = lec_arp_check_expire;
- add_timer(&priv->lec_arp_timer);
+ INIT_DELAYED_WORK(&priv->lec_arp_work, lec_arp_check_expire);
+ schedule_delayed_work(&priv->lec_arp_work, LEC_ARP_REFRESH_INTERVAL);
}
static void lec_arp_clear_vccs(struct lec_arp_table *entry)
struct lec_arp_table *entry;
int i;
- del_timer_sync(&priv->lec_arp_timer);
+ cancel_rearming_delayed_work(&priv->lec_arp_work);
/*
* Remove all entries
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]);
}
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);
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);
/* 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;
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;
}
spin_unlock_irqrestore(&priv->lec_arp_lock, flags);
lec_arp_clear_vccs(to_remove);
- kfree(to_remove);
+ lec_arp_put(to_remove);
}
/*
* to ESI_FORWARD_DIRECT. This causes the flush period to end
* regardless of the progress of the flush protocol.
*/
-static void lec_arp_check_expire(unsigned long data)
+static void lec_arp_check_expire(struct work_struct *work)
{
unsigned long flags;
- struct lec_priv *priv = (struct lec_priv *)data;
+ struct lec_priv *priv =
+ container_of(work, struct lec_priv, lec_arp_work.work);
struct hlist_node *node, *next;
struct lec_arp_table *entry;
unsigned long now;
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) {
/* 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 ||
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;
}
}
}
}
spin_unlock_irqrestore(&priv->lec_arp_lock, flags);
- mod_timer(&priv->lec_arp_timer, jiffies + LEC_ARP_REFRESH_INTERVAL);
+ schedule_delayed_work(&priv->lec_arp_work, LEC_ARP_REFRESH_INTERVAL);
}
/*
if (entry->status == ESI_FORWARD_DIRECT) {
/* Connection Ok */
entry->last_used = jiffies;
+ lec_arp_hold(entry);
*ret_entry = entry;
found = entry->vcc;
goto out;
* 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);
&& (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;
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;
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;
}
}
}
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;
}
lec_arp_clear_vccs(entry);
del_timer(&entry->timer);
hlist_del(&entry->next);
- kfree(entry);
+ lec_arp_put(entry);
}
}
lec_arp_clear_vccs(entry);
del_timer(&entry->timer);
hlist_del(&entry->next);
- kfree(entry);
+ lec_arp_put(entry);
}
}
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);
}
}
/* 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);