--- /dev/null
+diff -uN linux-2.4.20/include/linux/atmdev.h linux-2.5.63/include/linux/atmdev.h
+--- linux-2.4.20/include/linux/atmdev.h Sat Aug 3 02:39:45 2002
++++ linux-2.5.63/include/linux/atmdev.h Mon Feb 24 20:05:40 2003
+@@ -33,7 +33,8 @@
+ #define ATM_PDU_OVHD 0 /* number of bytes to charge against buffer
+ quota per PDU */
+
+-#define ATM_SD(s) ((s)->sk->protinfo.af_atm)
++#define atm_sk(__sk) ((struct atm_vcc *)(__sk)->protinfo.af_atm)
++#define ATM_SD(s) (atm_sk((s)->sk))
+
+
+ #define __AAL_STAT_ITEMS \
+@@ -206,7 +205,7 @@
+ #undef __AAL_STAT_ITEMS
+ #else
+
+-#include <linux/sched.h> /* wait_queue_head_t */
++#include <linux/wait.h> /* wait_queue_head_t */
+ #include <linux/time.h> /* struct timeval */
+ #include <linux/net.h>
+ #include <linux/skbuff.h> /* struct sk_buff */
+@@ -276,12 +275,8 @@
+
+ #define ATM_ATMOPT_CLP 1 /* set CLP bit */
+
+-
+-typedef struct { unsigned long bits; } atm_vcc_flags_t;
+-
+-
+ struct atm_vcc {
+- atm_vcc_flags_t flags; /* VCC flags (ATM_VF_*) */
++ unsigned long flags; /* VCC flags (ATM_VF_*) */
+ unsigned char family; /* address family; 0 if unused */
+ short vpi; /* VPI and VCI (types must be equal */
+ /* with sockaddr) */
+@@ -302,7 +297,6 @@
+ int (*send)(struct atm_vcc *vcc,struct sk_buff *skb);
+ void *dev_data; /* per-device data */
+ void *proto_data; /* per-protocol data */
+- struct timeval timestamp; /* AAL timestamps */
+ struct sk_buff_head recvq; /* receive queue */
+ struct k_atm_aal_stats *stats; /* pointer to AAL stats group */
+ wait_queue_head_t sleep; /* if socket is busy */
+@@ -331,10 +325,6 @@
+ struct atm_dev_addr *next; /* next address */
+ };
+
+-
+-typedef struct { unsigned int bits; } atm_dev_flags_t;
+-
+-
+ struct atm_dev {
+ const struct atmdev_ops *ops; /* device operations; NULL if unused */
+ const struct atmphy_ops *phy; /* PHY operations, may be undefined */
+@@ -345,7 +335,7 @@
+ struct atm_vcc *last; /* last VCC (or undefined) */
+ void *dev_data; /* per-device data */
+ void *phy_data; /* private PHY date */
+- atm_dev_flags_t flags; /* device flags (ATM_DF_*) */
++ unsigned long flags; /* device flags (ATM_DF_*) */
+ struct atm_dev_addr *local; /* local ATM addresses */
+ unsigned char esi[ESI_LEN]; /* ESI ("MAC" addr) */
+ struct atm_cirange ci_range; /* VPI/VCI range */
+@@ -415,7 +405,7 @@
+ #define ATM_SKB(skb) (((struct atm_skb_data *) (skb)->cb))
+
+ struct atm_dev *atm_dev_register(const char *type,const struct atmdev_ops *ops,
+- int number,atm_dev_flags_t *flags); /* number == -1: pick first available */
++ int number,unsigned long *flags); /* number == -1: pick first available */
+ struct atm_dev *atm_find_dev(int number);
+ void atm_dev_deregister(struct atm_dev *dev);
+ void shutdown_atm_dev(struct atm_dev *dev);
+diff -urN linux-2.4.20/net/atm/clip.c linux-2.5.63/net/atm/clip.c
+--- linux-2.4.20/net/atm/clip.c Thu Jun 28 02:10:55 2001
++++ linux-2.5.63/net/atm/clip.c Mon Feb 24 20:06:01 2003
+@@ -710,7 +711,7 @@
+ 999, /* dummy device number */
+ NULL,NULL, /* pretend not to have any VCCs */
+ NULL,NULL, /* no data */
+- { 0 }, /* no flags */
++ 0, /* no flags */
+ NULL, /* no local address */
+ { 0 } /* no ESI, no statistics */
+ };
+diff -urN linux-2.4.20/net/atm/common.c linux-2.5.63/net/atm/common.c
+--- linux-2.4.20/net/atm/common.c Sat Aug 3 02:39:46 2002
++++ linux-2.5.63/net/atm/common.c Mon Feb 24 20:06:03 2003
+@@ -112,7 +105,7 @@
+ sock->sk = NULL;
+ if (sock->type == SOCK_STREAM) return -EINVAL;
+ if (!(sk = alloc_atm_vcc_sk(family))) return -ENOMEM;
+- vcc = sk->protinfo.af_atm;
++ vcc = atm_sk(sk);
+ memset(&vcc->flags,0,sizeof(vcc->flags));
+ vcc->dev = NULL;
+ vcc->family = sock->ops->family;
+@@ -128,7 +121,6 @@
+ vcc->push_oam = NULL;
+ vcc->vpi = vcc->vci = 0; /* no VCI/VPI yet */
+ vcc->atm_options = vcc->aal_options = 0;
+- vcc->timestamp.tv_sec = vcc->timestamp.tv_usec = 0;
+ init_waitqueue_head(&vcc->sleep);
+ skb_queue_head_init(&vcc->recvq);
+ skb_queue_head_init(&vcc->listenq);
+@@ -140,10 +132,9 @@
+
+ void atm_release_vcc_sk(struct sock *sk,int free_sk)
+ {
+- struct atm_vcc *vcc;
++ struct atm_vcc *vcc = atm_sk(sk);
+ struct sk_buff *skb;
+
+- vcc = sk->protinfo.af_atm;
+ clear_bit(ATM_VF_READY,&vcc->flags);
+ if (vcc->dev) {
+ if (vcc->dev->ops->close) vcc->dev->ops->close(vcc);
+@@ -295,7 +286,7 @@
+ if (vpi != ATM_VPI_UNSPEC && vci != ATM_VCI_UNSPEC)
+ clear_bit(ATM_VF_PARTIAL,&vcc->flags);
+ else if (test_bit(ATM_VF_PARTIAL,&vcc->flags)) return -EINVAL;
+- printk(KERN_DEBUG "atm_connect (TX: cl %d,bw %d-%d,sdu %d; "
++ DPRINTK("atm_connect (TX: cl %d,bw %d-%d,sdu %d; "
+ "RX: cl %d,bw %d-%d,sdu %d,AAL %s%d)\n",
+ vcc->qos.txtp.traffic_class,vcc->qos.txtp.min_pcr,
+ vcc->qos.txtp.max_pcr,vcc->qos.txtp.max_sdu,
+@@ -387,7 +378,7 @@
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&vcc->sleep,&wait);
+ if (error <= 0) return error;
+- vcc->timestamp = skb->stamp;
++ sock_recv_timestamp(m, vcc->sk, skb);
+ eff_len = skb->len > size ? size : skb->len;
+ if (skb->len > size) /* Not fit ? Report it... */
+ m->msg_flags |= MSG_TRUNC;
+@@ -619,13 +610,11 @@
+ kfree(tmp_buf);
+ goto done;
+ case SIOCGSTAMP: /* borrowed from IP */
+- if (!vcc->timestamp.tv_sec) {
++ if (!vcc->sk->stamp.tv_sec) {
+ ret_val = -ENOENT;
+ goto done;
+ }
+- vcc->timestamp.tv_sec += vcc->timestamp.tv_usec/1000000;
+- vcc->timestamp.tv_usec %= 1000000;
+- ret_val = copy_to_user((void *) arg,&vcc->timestamp,
++ ret_val = copy_to_user((void *) arg, &vcc->sk->stamp,
+ sizeof(struct timeval)) ? -EFAULT : 0;
+ goto done;
+ case ATM_SETSC:
+diff -urN linux-2.4.20/net/atm/lec.c linux-2.5.63/net/atm/lec.c
+--- linux-2.4.20/net/atm/lec.c Sun Sep 30 21:26:08 2001
++++ linux-2.5.63/net/atm/lec.c Mon Feb 24 20:05:34 2003
+@@ -20,6 +20,7 @@
+ #include <net/arp.h>
+ #include <net/dst.h>
+ #include <linux/proc_fs.h>
++#include <linux/spinlock.h>
+
+ /* TokenRing if needed */
+ #ifdef CONFIG_TR
+@@ -35,6 +36,10 @@
+ #include <linux/if_bridge.h>
+ #include "../bridge/br_private.h"
+ static unsigned char bridge_ula_lec[] = {0x01, 0x80, 0xc2, 0x00, 0x00};
++
++extern struct net_bridge_fdb_entry *(*br_fdb_get_hook)(struct net_bridge *br,
++ unsigned char *addr);
++extern void (*br_fdb_put_hook)(struct net_bridge_fdb_entry *ent);
+ #endif
+
+ /* Modular too */
+@@ -51,10 +56,7 @@
+ #define DPRINTK(format,args...)
+ #endif
+
+-extern struct net_bridge_fdb_entry *(*br_fdb_get_hook)(struct net_bridge *br,
+- unsigned char *addr);
+-extern void (*br_fdb_put_hook)(struct net_bridge_fdb_entry *ent);
+-
++static spinlock_t lec_arp_spinlock = SPIN_LOCK_UNLOCKED;
+
+ #define DUMP_PACKETS 0 /* 0 = None,
+ * 1 = 30 first bytes
+@@ -196,6 +198,23 @@
+ return 0;
+ }
+
++static __inline__ void
++lec_send(struct atm_vcc *vcc, struct sk_buff *skb, struct lec_priv *priv)
++{
++ if (atm_may_send(vcc, skb->len)) {
++ atomic_add(skb->truesize, &vcc->tx_inuse);
++ ATM_SKB(skb)->vcc = vcc;
++ ATM_SKB(skb)->iovcnt = 0;
++ ATM_SKB(skb)->atm_options = vcc->atm_options;
++ priv->stats.tx_packets++;
++ priv->stats.tx_bytes += skb->len;
++ vcc->send(vcc, skb);
++ } else {
++ priv->stats.tx_dropped++;
++ dev_kfree_skb(skb);
++ }
++}
++
+ static int
+ lec_send_packet(struct sk_buff *skb, struct net_device *dev)
+ {
+@@ -341,34 +360,10 @@
+ DPRINTK("MAC address 0x%02x:%02x:%02x:%02x:%02x:%02x\n",
+ lec_h->h_dest[0], lec_h->h_dest[1], lec_h->h_dest[2],
+ lec_h->h_dest[3], lec_h->h_dest[4], lec_h->h_dest[5]);
+- ATM_SKB(skb2)->vcc = send_vcc;
+- ATM_SKB(skb2)->iovcnt = 0;
+- ATM_SKB(skb2)->atm_options = send_vcc->atm_options;
+- DPRINTK("%s:sending to vpi:%d vci:%d\n", dev->name,
+- send_vcc->vpi, send_vcc->vci);
+- if (atm_may_send(send_vcc, skb2->len)) {
+- atomic_add(skb2->truesize, &send_vcc->tx_inuse);
+- priv->stats.tx_packets++;
+- priv->stats.tx_bytes += skb2->len;
+- send_vcc->send(send_vcc, skb2);
+- } else {
+- priv->stats.tx_dropped++;
+- dev_kfree_skb(skb2);
+- }
++ lec_send(send_vcc, skb2, priv);
+ }
+
+- ATM_SKB(skb)->vcc = send_vcc;
+- ATM_SKB(skb)->iovcnt = 0;
+- ATM_SKB(skb)->atm_options = send_vcc->atm_options;
+- if (atm_may_send(send_vcc, skb->len)) {
+- atomic_add(skb->truesize, &send_vcc->tx_inuse);
+- priv->stats.tx_packets++;
+- priv->stats.tx_bytes += skb->len;
+- send_vcc->send(send_vcc, skb);
+- } else {
+- priv->stats.tx_dropped++;
+- dev_kfree_skb(skb);
+- }
++ lec_send(send_vcc, skb, priv);
+
+ #if 0
+ /* Should we wait for card's device driver to notify us? */
+@@ -564,7 +559,7 @@
+ 999, /*dummy device number*/
+ NULL,NULL, /*no VCCs*/
+ NULL,NULL, /*no data*/
+- { 0 }, /*no flags*/
++ 0, /*no flags*/
+ NULL, /* no local address*/
+ { 0 } /*no ESI or rest of the atm_dev struct things*/
+ };
+@@ -1044,15 +1039,15 @@
+ #define HASH(ch) (ch & (LEC_ARP_TABLE_SIZE -1))
+
+ static __inline__ void
+-lec_arp_lock(struct lec_priv *priv)
++lec_arp_get(struct lec_priv *priv)
+ {
+- atomic_inc(&priv->lec_arp_lock_var);
++ atomic_inc(&priv->lec_arp_users);
+ }
+
+ static __inline__ void
+-lec_arp_unlock(struct lec_priv *priv)
++lec_arp_put(struct lec_priv *priv)
+ {
+- atomic_dec(&priv->lec_arp_lock_var);
++ atomic_dec(&priv->lec_arp_users);
+ }
+
+ /*
+@@ -1103,33 +1098,33 @@
+ * LANE2: Add to the end of the list to satisfy 8.1.13
+ */
+ static __inline__ void
+-lec_arp_put(struct lec_arp_table **lec_arp_tables,
+- struct lec_arp_table *to_put)
++lec_arp_add(struct lec_arp_table **lec_arp_tables,
++ struct lec_arp_table *to_add)
+ {
+- unsigned short place;
+ unsigned long flags;
++ unsigned short place;
+ struct lec_arp_table *tmp;
+
+- save_flags(flags);
+- cli();
++ spin_lock_irqsave(&lec_arp_spinlock, flags);
+
+- place = HASH(to_put->mac_addr[ETH_ALEN-1]);
++ place = HASH(to_add->mac_addr[ETH_ALEN-1]);
+ tmp = lec_arp_tables[place];
+- to_put->next = NULL;
++ to_add->next = NULL;
+ if (tmp == NULL)
+- lec_arp_tables[place] = to_put;
++ lec_arp_tables[place] = to_add;
+
+ else { /* add to the end */
+ while (tmp->next)
+ tmp = tmp->next;
+- tmp->next = to_put;
++ tmp->next = to_add;
+ }
+
+- restore_flags(flags);
++ spin_unlock_irqrestore(&lec_arp_spinlock, flags);
++
+ DPRINTK("LEC_ARP: Added entry:%2.2x %2.2x %2.2x %2.2x %2.2x %2.2x\n",
+- 0xff&to_put->mac_addr[0], 0xff&to_put->mac_addr[1],
+- 0xff&to_put->mac_addr[2], 0xff&to_put->mac_addr[3],
+- 0xff&to_put->mac_addr[4], 0xff&to_put->mac_addr[5]);
++ 0xff&to_add->mac_addr[0], 0xff&to_add->mac_addr[1],
++ 0xff&to_add->mac_addr[2], 0xff&to_add->mac_addr[3],
++ 0xff&to_add->mac_addr[4], 0xff&to_add->mac_addr[5]);
+ }
+
+ /*
+@@ -1139,16 +1134,15 @@
+ lec_arp_remove(struct lec_arp_table **lec_arp_tables,
+ struct lec_arp_table *to_remove)
+ {
++ unsigned long flags;
+ unsigned short place;
+ struct lec_arp_table *tmp;
+- unsigned long flags;
+ int remove_vcc=1;
+
+- save_flags(flags);
+- cli();
++ spin_lock_irqsave(&lec_arp_spinlock, flags);
+
+ if (!to_remove) {
+- restore_flags(flags);
++ spin_unlock_irqrestore(&lec_arp_spinlock, flags);
+ return -1;
+ }
+ place = HASH(to_remove->mac_addr[ETH_ALEN-1]);
+@@ -1160,7 +1154,7 @@
+ tmp = tmp->next;
+ }
+ if (!tmp) {/* Entry was not found */
+- restore_flags(flags);
++ spin_unlock_irqrestore(&lec_arp_spinlock, flags);
+ return -1;
+ }
+ }
+@@ -1186,7 +1180,9 @@
+ lec_arp_clear_vccs(to_remove);
+ }
+ skb_queue_purge(&to_remove->tx_wait); /* FIXME: good place for this? */
+- restore_flags(flags);
++
++ spin_unlock_irqrestore(&lec_arp_spinlock, flags);
++
+ DPRINTK("LEC_ARP: Removed entry:%2.2x %2.2x %2.2x %2.2x %2.2x %2.2x\n",
+ 0xff&to_remove->mac_addr[0], 0xff&to_remove->mac_addr[1],
+ 0xff&to_remove->mac_addr[2], 0xff&to_remove->mac_addr[3],
+@@ -1371,12 +1367,8 @@
+ lec_arp_destroy(struct lec_priv *priv)
+ {
+ struct lec_arp_table *entry, *next;
+- unsigned long flags;
+ int i;
+
+- save_flags(flags);
+- cli();
+-
+ del_timer(&priv->lec_arp_timer);
+
+ /*
+@@ -1419,7 +1411,6 @@
+ priv->mcast_vcc = NULL;
+ memset(priv->lec_arp_tables, 0,
+ sizeof(struct lec_arp_table*)*LEC_ARP_TABLE_SIZE);
+- restore_flags(flags);
+ }
+
+
+@@ -1436,18 +1427,18 @@
+ DPRINTK("LEC_ARP: lec_arp_find :%2.2x %2.2x %2.2x %2.2x %2.2x %2.2x\n",
+ mac_addr[0]&0xff, mac_addr[1]&0xff, mac_addr[2]&0xff,
+ mac_addr[3]&0xff, mac_addr[4]&0xff, mac_addr[5]&0xff);
+- lec_arp_lock(priv);
++ lec_arp_get(priv);
+ place = HASH(mac_addr[ETH_ALEN-1]);
+
+ to_return = priv->lec_arp_tables[place];
+ while(to_return) {
+ if (memcmp(mac_addr, to_return->mac_addr, ETH_ALEN) == 0) {
+- lec_arp_unlock(priv);
++ lec_arp_put(priv);
+ return to_return;
+ }
+ to_return = to_return->next;
+ }
+- lec_arp_unlock(priv);
++ lec_arp_put(priv);
+ return NULL;
+ }
+
+@@ -1574,11 +1565,11 @@
+ del_timer(&priv->lec_arp_timer);
+
+ DPRINTK("lec_arp_check_expire %p,%d\n",priv,
+- priv->lec_arp_lock_var.counter);
++ atomic_read(&priv->lec_arp_users));
+ DPRINTK("expire: eo:%p nf:%p\n",priv->lec_arp_empty_ones,
+ priv->lec_no_forward);
+- if (!priv->lec_arp_lock_var.counter) {
+- lec_arp_lock(priv);
++ if (!atomic_read(&priv->lec_arp_users)) {
++ lec_arp_get(priv);
+ now = jiffies;
+ for(i=0;i<LEC_ARP_TABLE_SIZE;i++) {
+ for(entry = lec_arp_tables[i];entry != NULL;) {
+@@ -1616,6 +1607,10 @@
+ &&
+ time_after_eq(now, entry->timestamp+
+ priv->path_switching_delay)) {
++ struct sk_buff *skb;
++
++ while ((skb = skb_dequeue(&entry->tx_wait)))
++ lec_send(entry->vcc, skb, entry->priv);
+ entry->last_used = jiffies;
+ entry->status =
+ ESI_FORWARD_DIRECT;
+@@ -1624,7 +1619,7 @@
+ }
+ }
+ }
+- lec_arp_unlock(priv);
++ lec_arp_put(priv);
+ }
+ priv->lec_arp_timer.expires = jiffies + LEC_ARP_REFRESH_INTERVAL;
+ add_timer(&priv->lec_arp_timer);
+@@ -1686,7 +1681,7 @@
+ if (!entry) {
+ return priv->mcast_vcc;
+ }
+- lec_arp_put(priv->lec_arp_tables, entry);
++ lec_arp_add(priv->lec_arp_tables, entry);
+ /* We want arp-request(s) to be sent */
+ entry->packets_flooded =1;
+ entry->status = ESI_ARP_PENDING;
+@@ -1711,7 +1706,7 @@
+ struct lec_arp_table *entry, *next;
+ int i;
+
+- lec_arp_lock(priv);
++ lec_arp_get(priv);
+ DPRINTK("lec_addr_delete\n");
+ for(i=0;i<LEC_ARP_TABLE_SIZE;i++) {
+ for(entry=priv->lec_arp_tables[i];entry != NULL; entry=next) {
+@@ -1722,11 +1717,11 @@
+ lec_arp_remove(priv->lec_arp_tables, entry);
+ kfree(entry);
+ }
+- lec_arp_unlock(priv);
++ lec_arp_put(priv);
+ return 0;
+ }
+ }
+- lec_arp_unlock(priv);
++ lec_arp_put(priv);
+ return -1;
+ }
+
+@@ -1751,7 +1746,7 @@
+ return; /* LANE2: ignore targetless LE_ARPs for which
+ * we have no entry in the cache. 7.1.30
+ */
+- lec_arp_lock(priv);
++ lec_arp_get(priv);
+ if (priv->lec_arp_empty_ones) {
+ entry = priv->lec_arp_empty_ones;
+ if (!memcmp(entry->atm_addr, atm_addr, ATM_ESA_LEN)) {
+@@ -1785,13 +1780,13 @@
+ entry->status = ESI_FORWARD_DIRECT;
+ memcpy(entry->mac_addr, mac_addr, ETH_ALEN);
+ entry->last_used = jiffies;
+- lec_arp_put(priv->lec_arp_tables, entry);
++ lec_arp_add(priv->lec_arp_tables, entry);
+ }
+ if (remoteflag)
+ entry->flags|=LEC_REMOTE_FLAG;
+ else
+ entry->flags&=~LEC_REMOTE_FLAG;
+- lec_arp_unlock(priv);
++ lec_arp_put(priv);
+ DPRINTK("After update\n");
+ dump_arp_table(priv);
+ return;
+@@ -1801,11 +1796,11 @@
+ if (!entry) {
+ entry = make_entry(priv, mac_addr);
+ if (!entry) {
+- lec_arp_unlock(priv);
++ lec_arp_put(priv);
+ return;
+ }
+ entry->status = ESI_UNKNOWN;
+- lec_arp_put(priv->lec_arp_tables, entry);
++ lec_arp_add(priv->lec_arp_tables, entry);
+ /* Temporary, changes before end of function */
+ }
+ memcpy(entry->atm_addr, atm_addr, ATM_ESA_LEN);
+@@ -1840,7 +1835,7 @@
+ }
+ DPRINTK("After update2\n");
+ dump_arp_table(priv);
+- lec_arp_unlock(priv);
++ lec_arp_put(priv);
+ }
+
+ /*
+@@ -1854,7 +1849,7 @@
+ struct lec_arp_table *entry;
+ int i, found_entry=0;
+
+- lec_arp_lock(priv);
++ lec_arp_get(priv);
+ if (ioc_data->receive == 2) {
+ /* Vcc for Multicast Forward. No timer, LANEv2 7.1.20 and 2.3.5.3 */
+
+@@ -1863,7 +1858,7 @@
+ entry = lec_arp_find(priv, bus_mac);
+ if (!entry) {
+ printk("LEC_ARP: Multicast entry not found!\n");
+- lec_arp_unlock(priv);
++ lec_arp_put(priv);
+ return;
+ }
+ memcpy(entry->atm_addr, ioc_data->atm_addr, ATM_ESA_LEN);
+@@ -1872,7 +1867,7 @@
+ #endif
+ entry = make_entry(priv, bus_mac);
+ if (entry == NULL) {
+- lec_arp_unlock(priv);
++ lec_arp_put(priv);
+ return;
+ }
+ del_timer(&entry->timer);
+@@ -1881,7 +1876,7 @@
+ entry->old_recv_push = old_push;
+ entry->next = priv->mcast_fwds;
+ priv->mcast_fwds = entry;
+- lec_arp_unlock(priv);
++ lec_arp_put(priv);
+ return;
+ } else if (ioc_data->receive == 1) {
+ /* Vcc which we don't want to make default vcc, attach it
+@@ -1899,7 +1894,7 @@
+ ioc_data->atm_addr[18],ioc_data->atm_addr[19]);
+ entry = make_entry(priv, bus_mac);
+ if (entry == NULL) {
+- lec_arp_unlock(priv);
++ lec_arp_put(priv);
+ return;
+ }
+ memcpy(entry->atm_addr, ioc_data->atm_addr, ATM_ESA_LEN);
+@@ -1912,7 +1907,7 @@
+ add_timer(&entry->timer);
+ entry->next = priv->lec_no_forward;
+ priv->lec_no_forward = entry;
+- lec_arp_unlock(priv);
++ lec_arp_put(priv);
+ dump_arp_table(priv);
+ return;
+ }
+@@ -1971,7 +1966,7 @@
+ }
+ }
+ if (found_entry) {
+- lec_arp_unlock(priv);
++ lec_arp_put(priv);
+ DPRINTK("After vcc was added\n");
+ dump_arp_table(priv);
+ return;
+@@ -1980,7 +1975,7 @@
+ this vcc */
+ entry = make_entry(priv, bus_mac);
+ if (!entry) {
+- lec_arp_unlock(priv);
++ lec_arp_put(priv);
+ return;
+ }
+ entry->vcc = vcc;
+@@ -1993,7 +1988,7 @@
+ entry->timer.expires = jiffies + priv->vcc_timeout_period;
+ entry->timer.function = lec_arp_expire_vcc;
+ add_timer(&entry->timer);
+- lec_arp_unlock(priv);
++ lec_arp_put(priv);
+ DPRINTK("After vcc was added\n");
+ dump_arp_table(priv);
+ }
+@@ -2009,6 +2004,10 @@
+ for (entry=priv->lec_arp_tables[i];entry;entry=entry->next) {
+ if (entry->flush_tran_id == tran_id &&
+ entry->status == ESI_FLUSH_PENDING) {
++ struct sk_buff *skb;
++
++ while ((skb = skb_dequeue(&entry->tx_wait)))
++ lec_send(entry->vcc, skb, entry->priv);
+ entry->status = ESI_FORWARD_DIRECT;
+ DPRINTK("LEC_ARP: Flushed\n");
+ }
+@@ -2039,10 +2038,10 @@
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+ struct lec_arp_table *to_add;
+
+- lec_arp_lock(priv);
++ lec_arp_get(priv);
+ to_add = make_entry(priv, mac_addr);
+ if (!to_add) {
+- lec_arp_unlock(priv);
++ lec_arp_put(priv);
+ return -ENOMEM;
+ }
+ memcpy(to_add->atm_addr, vcc->remote.sas_addr.prv, ATM_ESA_LEN);
+@@ -2052,8 +2051,8 @@
+ to_add->old_push = vcc->push;
+ vcc->push = lec_push;
+ priv->mcast_vcc = vcc;
+- lec_arp_put(priv->lec_arp_tables, to_add);
+- lec_arp_unlock(priv);
++ lec_arp_add(priv->lec_arp_tables, to_add);
++ lec_arp_put(priv);
+ return 0;
+ }
+
+@@ -2065,7 +2064,7 @@
+
+ DPRINTK("LEC_ARP: lec_vcc_close vpi:%d vci:%d\n",vcc->vpi,vcc->vci);
+ dump_arp_table(priv);
+- lec_arp_lock(priv);
++ lec_arp_get(priv);
+ for(i=0;i<LEC_ARP_TABLE_SIZE;i++) {
+ for(entry = priv->lec_arp_tables[i];entry; entry=next) {
+ next = entry->next;
+@@ -2127,7 +2126,7 @@
+ entry = next;
+ }
+
+- lec_arp_unlock(priv);
++ lec_arp_put(priv);
+ dump_arp_table(priv);
+ }
+
+@@ -2135,9 +2134,9 @@
+ lec_arp_check_empties(struct lec_priv *priv,
+ struct atm_vcc *vcc, struct sk_buff *skb)
+ {
++ unsigned long flags;
+ struct lec_arp_table *entry, *prev;
+ struct lecdatahdr_8023 *hdr = (struct lecdatahdr_8023 *)skb->data;
+- unsigned long flags;
+ unsigned char *src;
+ #ifdef CONFIG_TR
+ struct lecdatahdr_8025 *tr_hdr = (struct lecdatahdr_8025 *)skb->data;
+@@ -2147,26 +2146,26 @@
+ #endif
+ src = hdr->h_source;
+
+- lec_arp_lock(priv);
++ lec_arp_get(priv);
+ entry = priv->lec_arp_empty_ones;
+ if (vcc == entry->vcc) {
+- save_flags(flags);
+- cli();
++ spin_lock_irqsave(&lec_arp_spinlock, flags);
+ del_timer(&entry->timer);
+ memcpy(entry->mac_addr, src, ETH_ALEN);
+ entry->status = ESI_FORWARD_DIRECT;
+ entry->last_used = jiffies;
+ priv->lec_arp_empty_ones = entry->next;
+- restore_flags(flags);
++ spin_unlock_irqrestore(&lec_arp_spinlock, flags);
+ /* We might have got an entry */
+ if ((prev=lec_arp_find(priv,src))) {
+ lec_arp_remove(priv->lec_arp_tables, prev);
+ kfree(prev);
+ }
+- lec_arp_put(priv->lec_arp_tables, entry);
+- lec_arp_unlock(priv);
++ lec_arp_add(priv->lec_arp_tables, entry);
++ lec_arp_put(priv);
+ return;
+ }
++ spin_lock_irqsave(&lec_arp_spinlock, flags);
+ prev = entry;
+ entry = entry->next;
+ while (entry && entry->vcc != vcc) {
+@@ -2175,22 +2174,21 @@
+ }
+ if (!entry) {
+ DPRINTK("LEC_ARP: Arp_check_empties: entry not found!\n");
+- lec_arp_unlock(priv);
++ lec_arp_put(priv);
++ spin_unlock_irqrestore(&lec_arp_spinlock, flags);
+ return;
+ }
+- save_flags(flags);
+- cli();
+ del_timer(&entry->timer);
+ memcpy(entry->mac_addr, src, ETH_ALEN);
+ entry->status = ESI_FORWARD_DIRECT;
+ entry->last_used = jiffies;
+ prev->next = entry->next;
+- restore_flags(flags);
++ spin_unlock_irqrestore(&lec_arp_spinlock, flags);
+ if ((prev = lec_arp_find(priv, src))) {
+ lec_arp_remove(priv->lec_arp_tables,prev);
+ kfree(prev);
+ }
+- lec_arp_put(priv->lec_arp_tables,entry);
+- lec_arp_unlock(priv);
++ lec_arp_add(priv->lec_arp_tables,entry);
++ lec_arp_put(priv);
+ }
+ MODULE_LICENSE("GPL");
+diff -urN linux-2.4.20/net/atm/lec.h linux-2.5.63/net/atm/lec.h
+--- linux-2.4.20/net/atm/lec.h Fri Feb 9 20:34:13 2001
++++ linux-2.5.63/net/atm/lec.h Mon Feb 24 20:05:15 2003
+@@ -98,7 +98,7 @@
+ establishes multiple Multicast Forward VCCs to us. This list
+ collects all those VCCs. LANEv1 client has only one item in this
+ list. These entries are not aged out. */
+- atomic_t lec_arp_lock_var;
++ atomic_t lec_arp_users;
+ struct atm_vcc *mcast_vcc; /* Default Multicast Send VCC */
+ struct atm_vcc *lecd;
+ struct timer_list lec_arp_timer;
+diff -urN linux-2.4.20/net/atm/mpc.c linux-2.5.63/net/atm/mpc.c
+--- linux-2.4.20/net/atm/mpc.c Sun Sep 30 21:26:08 2001
++++ linux-2.5.63/net/atm/mpc.c Mon Feb 24 20:05:40 2003
+@@ -13,7 +13,7 @@
+ #include <linux/ip.h>
+ #include <asm/byteorder.h>
+ #include <asm/uaccess.h>
+-#include <asm/checksum.h> /* for ip_fast_csum() */
++#include <net/checksum.h> /* for ip_fast_csum() */
+ #include <net/arp.h>
+ #include <net/dst.h>
+ #include <linux/proc_fs.h>
+@@ -741,18 +741,10 @@
+ };
+
+ static struct atm_dev mpc_dev = {
+- &mpc_ops, /* device operations */
+- NULL, /* PHY operations */
+- "mpc", /* device type name */
+- 42, /* device index (dummy) */
+- NULL, /* VCC table */
+- NULL, /* last VCC */
+- NULL, /* per-device data */
+- NULL, /* private PHY data */
+- { 0 }, /* device flags */
+- NULL, /* local ATM address */
+- { 0 } /* no ESI */
+- /* rest of the members will be 0 */
++ ops: &mpc_ops,
++ type: "mpc",
++ number: 42,
++ /* members not explicitely initialised will be 0 */
+ };
+
+ int atm_mpoa_mpoad_attach (struct atm_vcc *vcc, int arg)
+@@ -1432,10 +1424,6 @@
+ struct atm_mpoa_qos *qos, *nextqos;
+ struct lec_priv *priv;
+
+- if (MOD_IN_USE) {
+- printk("mpc.c: module in use\n");
+- return;
+- }
+ #ifdef CONFIG_PROC_FS
+ mpc_proc_clean();
+ #endif
+diff -urN linux-2.4.20/net/atm/mpoa_proc.c linux-2.5.63/net/atm/mpoa_proc.c
+--- linux-2.4.20/net/atm/mpoa_proc.c Wed Jul 4 20:50:38 2001
++++ linux-2.5.63/net/atm/mpoa_proc.c Mon Feb 24 20:05:39 2003
+@@ -111,7 +111,7 @@
+ unsigned char ip_string[16];
+ if(count == 0)
+ return 0;
+- page = get_free_page(GFP_KERNEL);
++ page = get_zeroed_page(GFP_KERNEL);
+ if(!page)
+ return -ENOMEM;
+ atm_mpoa_disp_qos((char *)page, &length);
+diff -urN linux-2.4.20/net/atm/proc.c linux-2.5.63/net/atm/proc.c
+--- linux-2.4.20/net/atm/proc.c Fri Nov 29 00:53:15 2002
++++ linux-2.5.63/net/atm/proc.c Mon Feb 24 20:05:14 2003
+@@ -220,7 +220,7 @@
+ default:
+ here += sprintf(here,"%3d",vcc->family);
+ }
+- here += sprintf(here," %04lx %5d %7d/%7d %7d/%7d\n",vcc->flags.bits,
++ here += sprintf(here," %04lx %5d %7d/%7d %7d/%7d\n",vcc->flags,
+ vcc->reply,
+ atomic_read(&vcc->tx_inuse),vcc->sk->sndbuf,
+ atomic_read(&vcc->rx_inuse),vcc->sk->rcvbuf);
+@@ -496,7 +496,7 @@
+ int length;
+
+ if (count == 0) return 0;
+- page = get_free_page(GFP_KERNEL);
++ page = get_zeroed_page(GFP_KERNEL);
+ if (!page) return -ENOMEM;
+ dev = ((struct proc_dir_entry *) file->f_dentry->d_inode->u.generic_ip)
+ ->data;
+@@ -521,7 +520,7 @@
+ ->data;
+
+ if (count == 0) return 0;
+- page = get_free_page(GFP_KERNEL);
++ page = get_zeroed_page(GFP_KERNEL);
+ if (!page) return -ENOMEM;
+ length = (*info)(*pos,(char *) page);
+ if (length > count) length = -EINVAL;
+@@ -552,7 +550,7 @@
+ for (num = dev->number; num; num /= 10) digits++;
+ if (!digits) digits++;
+
+- dev->proc_name = kmalloc(strlen(dev->type) + digits + 2, GFP_ATOMIC);
++ dev->proc_name = kmalloc(strlen(dev->type) + digits + 2, GFP_KERNEL);
+ if (!dev->proc_name)
+ goto fail1;
+ sprintf(dev->proc_name,"%s:%d",dev->type, dev->number);
+diff -urN linux-2.4.20/net/atm/pvc.c linux-2.5.63/net/atm/pvc.c
+--- linux-2.4.20/net/atm/pvc.c Thu Apr 12 21:11:39 2001
++++ linux-2.5.63/net/atm/pvc.c Mon Feb 24 20:05:14 2003
+@@ -111,11 +111,8 @@
+
+
+ static struct net_proto_family pvc_family_ops = {
+- PF_ATMPVC,
+- pvc_create,
+- 0, /* no authentication */
+- 0, /* no encryption */
+- 0 /* no encrypt_net */
++ family: PF_ATMPVC,
++ create: pvc_create,
+ };
+
+
+diff -urN linux-2.4.20/net/atm/resources.c linux-2.5.63/net/atm/resources.c
+--- linux-2.4.20/net/atm/resources.c Fri Nov 29 00:53:15 2002
++++ linux-2.5.63/net/atm/resources.c Mon Feb 24 20:05:45 2003
+@@ -2,6 +2,11 @@
+
+ /* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
+
++/* Fixes
++ * Arnaldo Carvalho de Melo <acme@conectiva.com.br>
++ * 2002/01 - don't free the whole struct sock on sk->destruct time,
++ * use the default destruct function initialized by sock_init_data */
++
+
+ #include <linux/config.h>
+ #include <linux/ctype.h>
+@@ -11,7 +16,6 @@
+ #include <linux/module.h>
+ #include <linux/bitops.h>
+ #include <net/sock.h> /* for struct sock */
+-#include <asm/segment.h> /* for get_fs_long and put_fs_long */
+
+ #include "common.h"
+ #include "resources.h"
+@@ -27,14 +31,15 @@
+ struct atm_vcc *nodev_vccs = NULL;
+ extern spinlock_t atm_dev_lock;
+
+-
+-static struct atm_dev *alloc_atm_dev(const char *type)
++/* Caller must hold atm_dev_lock. */
++static struct atm_dev *__alloc_atm_dev(const char *type)
+ {
+ struct atm_dev *dev;
+
+ dev = kmalloc(sizeof(*dev), GFP_ATOMIC);
+- if (!dev) return NULL;
+- memset(dev,0,sizeof(*dev));
++ if (!dev)
++ return NULL;
++ memset(dev, 0, sizeof(*dev));
+ dev->type = type;
+ dev->signal = ATM_PHY_SIG_UNKNOWN;
+ dev->link_rate = ATM_OC3_PCR;
+@@ -42,39 +47,49 @@
+
+ dev->prev = last_dev;
+
+- if (atm_devs) last_dev->next = dev;
+- else atm_devs = dev;
++ if (atm_devs)
++ last_dev->next = dev;
++ else
++ atm_devs = dev;
+ last_dev = dev;
++
+ return dev;
+ }
+
+-
+-static void free_atm_dev(struct atm_dev *dev)
++/* Caller must hold atm_dev_lock. */
++static void __free_atm_dev(struct atm_dev *dev)
+ {
+- if (dev->prev) dev->prev->next = dev->next;
+- else atm_devs = dev->next;
+- if (dev->next) dev->next->prev = dev->prev;
+- else last_dev = dev->prev;
++ if (dev->prev)
++ dev->prev->next = dev->next;
++ else
++ atm_devs = dev->next;
++ if (dev->next)
++ dev->next->prev = dev->prev;
++ else
++ last_dev = dev->prev;
+ kfree(dev);
+ }
+
++/* Caller must hold atm_dev_lock. */
+ struct atm_dev *atm_find_dev(int number)
+ {
+ struct atm_dev *dev;
+
+ for (dev = atm_devs; dev; dev = dev->next)
+- if (dev->ops && dev->number == number) return dev;
++ if (dev->ops && dev->number == number)
++ return dev;
+ return NULL;
+ }
+
+-struct atm_dev *atm_dev_register(const char *type,const struct atmdev_ops *ops,
+- int number,atm_dev_flags_t *flags)
++
++struct atm_dev *atm_dev_register(const char *type, const struct atmdev_ops *ops,
++ int number, unsigned long *flags)
+ {
+- struct atm_dev *dev = NULL;
++ struct atm_dev *dev;
+
+ spin_lock(&atm_dev_lock);
+
+- dev = alloc_atm_dev(type);
++ dev = __alloc_atm_dev(type);
+ if (!dev) {
+ printk(KERN_ERR "atm_dev_register: no space for dev %s\n",
+ type);
+@@ -82,31 +97,37 @@
+ }
+ if (number != -1) {
+ if (atm_find_dev(number)) {
+- free_atm_dev(dev);
+- return NULL;
++ __free_atm_dev(dev);
++ dev = NULL;
++ goto done;
+ }
+ dev->number = number;
+ } else {
+ dev->number = 0;
+- while (atm_find_dev(dev->number)) dev->number++;
++ while (atm_find_dev(dev->number))
++ dev->number++;
+ }
+ dev->vccs = dev->last = NULL;
+ dev->dev_data = NULL;
+ barrier();
+ dev->ops = ops;
+- if (flags)
++ if (flags)
+ dev->flags = *flags;
+- else
+- memset(&dev->flags,0,sizeof(dev->flags));
+- memset((void *) &dev->stats,0,sizeof(dev->stats));
++ else
++ memset(&dev->flags, 0, sizeof(dev->flags));
++ memset(&dev->stats, 0, sizeof(dev->stats));
++
+ #ifdef CONFIG_PROC_FS
+- if (ops->proc_read)
++ if (ops->proc_read) {
+ if (atm_proc_dev_register(dev) < 0) {
+ printk(KERN_ERR "atm_dev_register: "
+- "atm_proc_dev_register failed for dev %s\n",type);
+- free_atm_dev(dev);
++ "atm_proc_dev_register failed for dev %s\n",
++ type);
++ __free_atm_dev(dev);
++ dev = NULL;
+ goto done;
+ }
++ }
+ #endif
+
+ done:
+@@ -118,47 +139,50 @@
+ void atm_dev_deregister(struct atm_dev *dev)
+ {
+ #ifdef CONFIG_PROC_FS
+- if (dev->ops->proc_read) atm_proc_dev_deregister(dev);
++ if (dev->ops->proc_read)
++ atm_proc_dev_deregister(dev);
+ #endif
+ spin_lock(&atm_dev_lock);
+- free_atm_dev(dev);
++ __free_atm_dev(dev);
+ spin_unlock(&atm_dev_lock);
+ }
+
+ void shutdown_atm_dev(struct atm_dev *dev)
+ {
+ if (dev->vccs) {
+- set_bit(ATM_DF_CLOSE,&dev->flags);
++ set_bit(ATM_DF_CLOSE, &dev->flags);
+ return;
+ }
+- if (dev->ops->dev_close) dev->ops->dev_close(dev);
++ if (dev->ops->dev_close)
++ dev->ops->dev_close(dev);
+ atm_dev_deregister(dev);
+ }
+
+-
+ /* Handler for sk->destruct, invoked by sk_free() */
+ static void atm_free_sock(struct sock *sk)
+ {
+ kfree(sk->protinfo.af_atm);
+ }
+
+ struct sock *alloc_atm_vcc_sk(int family)
+ {
+ struct sock *sk;
+ struct atm_vcc *vcc;
+
+- sk = sk_alloc(family, GFP_KERNEL, 1);
+- if (!sk) return NULL;
+- vcc = sk->protinfo.af_atm = kmalloc(sizeof(*vcc),GFP_KERNEL);
++ sk = sk_alloc(family, GFP_KERNEL, 1);
++ if (!sk)
++ return NULL;
++ vcc = atm_sk(sk) = kmalloc(sizeof(*vcc), GFP_KERNEL);
+ if (!vcc) {
+ sk_free(sk);
+ return NULL;
+ }
+- sock_init_data(NULL,sk);
+ sk->destruct = atm_free_sock;
+- memset(vcc,0,sizeof(*vcc));
++ sock_init_data(NULL, sk);
++ memset(vcc, 0, sizeof(*vcc));
+ vcc->sk = sk;
+- if (nodev_vccs) nodev_vccs->prev = vcc;
++ if (nodev_vccs)
++ nodev_vccs->prev = vcc;
+ vcc->prev = NULL;
+ vcc->next = nodev_vccs;
+ nodev_vccs = vcc;
+@@ -168,11 +185,16 @@
+
+ static void unlink_vcc(struct atm_vcc *vcc,struct atm_dev *hold_dev)
+ {
+- if (vcc->prev) vcc->prev->next = vcc->next;
+- else if (vcc->dev) vcc->dev->vccs = vcc->next;
+- else nodev_vccs = vcc->next;
+- if (vcc->next) vcc->next->prev = vcc->prev;
+- else if (vcc->dev) vcc->dev->last = vcc->prev;
++ if (vcc->prev)
++ vcc->prev->next = vcc->next;
++ else if (vcc->dev)
++ vcc->dev->vccs = vcc->next;
++ else
++ nodev_vccs = vcc->next;
++ if (vcc->next)
++ vcc->next->prev = vcc->prev;
++ else if (vcc->dev)
++ vcc->dev->last = vcc->prev;
+ if (vcc->dev && vcc->dev != hold_dev && !vcc->dev->vccs &&
+ test_bit(ATM_DF_CLOSE,&vcc->dev->flags))
+ shutdown_atm_dev(vcc->dev);
+@@ -181,11 +203,10 @@
+
+ void free_atm_vcc_sk(struct sock *sk)
+ {
+- unlink_vcc(sk->protinfo.af_atm,NULL);
++ unlink_vcc(atm_sk(sk), NULL);
+ sk_free(sk);
+ }
+
+-
+ void bind_vcc(struct atm_vcc *vcc,struct atm_dev *dev)
+ {
+ unlink_vcc(vcc,dev);
+@@ -193,19 +214,20 @@
+ if (dev) {
+ vcc->next = NULL;
+ vcc->prev = dev->last;
+- if (dev->vccs) dev->last->next = vcc;
+- else dev->vccs = vcc;
++ if (dev->vccs)
++ dev->last->next = vcc;
++ else
++ dev->vccs = vcc;
+ dev->last = vcc;
+- }
+- else {
+- if (nodev_vccs) nodev_vccs->prev = vcc;
++ } else {
++ if (nodev_vccs)
++ nodev_vccs->prev = vcc;
+ vcc->next = nodev_vccs;
+ vcc->prev = NULL;
+ nodev_vccs = vcc;
+ }
+ }
+
+-
+ EXPORT_SYMBOL(atm_dev_register);
+ EXPORT_SYMBOL(atm_dev_deregister);
+ EXPORT_SYMBOL(atm_find_dev);
+diff -urN linux-2.4.20/net/atm/signaling.c linux-2.5.63/net/atm/signaling.c
+--- linux-2.4.20/net/atm/signaling.c Thu Jun 28 02:10:55 2001
++++ linux-2.5.63/net/atm/signaling.c Mon Feb 24 20:05:39 2003
+@@ -239,7 +239,7 @@
+ 999, /* dummy device number */
+ NULL,NULL, /* pretend not to have any VCCs */
+ NULL,NULL, /* no data */
+- { 0 }, /* no flags */
++ 0, /* no flags */
+ NULL, /* no local address */
+ { 0 } /* no ESI, no statistics */
+ };
+diff -urN linux-2.4.20/net/atm/svc.c linux-2.5.63/net/atm/svc.c
+--- linux-2.4.20/net/atm/svc.c Thu Apr 12 21:11:39 2001
++++ linux-2.5.63/net/atm/svc.c Mon Feb 24 20:05:36 2003
+@@ -430,11 +430,8 @@
+
+
+ static struct net_proto_family svc_family_ops = {
+- PF_ATMSVC,
+- svc_create,
+- 0, /* no authentication */
+- 0, /* no encryption */
+- 0 /* no encrypt_net */
++ family: PF_ATMSVC,
++ create: svc_create,
+ };
+
+
--- /dev/null
+--- linux-2.4.20/net/atm/Makefile.orig Sat Aug 3 02:39:46 2002
++++ linux-2.4.20/net/atm/Makefile Wed Mar 5 23:15:50 2003
+@@ -14,32 +14,18 @@
+ list-multi := mpoa.o
+ mpoa-objs := mpc.o mpoa_caches.o mpoa_proc.o
+
+-obj-$(CONFIG_ATM) := addr.o pvc.o signaling.o svc.o common.o atm_misc.o raw.o resources.o
++obj-y := addr.o pvc.o signaling.o svc.o common.o atm_misc.o raw.o resources.o ipcommon.o
+
+-ifeq ($(CONFIG_ATM_CLIP),y)
+-obj-y += clip.o
+-NEED_IPCOM = ipcommon.o
++ifeq ($(CONFIG_ATM),m)
++obj-m += $(O_TARGET)
+ endif
+
+-ifeq ($(CONFIG_ATM_BR2684),y)
+- NEED_IPCOM = ipcommon.o
+-else
+- ifeq ($(CONFIG_ATM_BR2684),m)
+- NEED_IPCOM = ipcommon.o
+- endif
+-endif
+-obj-$(CONFIG_ATM_BR2684) += br2684.o
+-
+-ifeq ($(CONFIG_NET_SCH_ATM),y)
+-NEED_IPCOM = ipcommon.o
+-endif
+-
+-obj-y += $(NEED_IPCOM)
+-
+ ifeq ($(CONFIG_PROC_FS),y)
+ obj-y += proc.o
+ endif
+
++obj-$(CONFIG_ATM_CLIP) += clip.o
++obj-$(CONFIG_ATM_BR2684) += br2684.o
+ obj-$(CONFIG_ATM_LANE) += lec.o
+ obj-$(CONFIG_ATM_MPOA) += mpoa.o
+ obj-$(CONFIG_PPPOATM) += pppoatm.o
--- /dev/null
+the minimum frame size for token ring (802.5) pdus is 16 octets. also,
+count the pdus that failed to copy as dropped. also, skb_copy_expand()
+now seems to be the right way to do this.
+
+Index: linux/net/atm/lec.c
+===================================================================
+RCS file: /home/chas/CVSROOT/linux/net/atm/lec.c,v
+retrieving revision 1.5
+retrieving revision 1.6
+diff -u -d -b -w -r1.5 -r1.6
+--- linux/net/atm/lec.c 22 Feb 2003 19:23:22 -0000 1.5
++++ linux/net/atm/lec.c 24 Feb 2003 13:24:45 -0000 1.6
+@@ -223,7 +223,8 @@
+ struct lecdatahdr_8023 *lec_h;
+ struct atm_vcc *send_vcc;
+ struct lec_arp_table *entry;
+- unsigned char *nb, *dst;
++ unsigned char *dst;
++ int min_frame_size;
+ #ifdef CONFIG_TR
+ unsigned char rdesc[ETH_ALEN]; /* Token Ring route descriptor */
+ #endif
+@@ -294,26 +295,24 @@
+ #endif /* DUMP_PACKETS > 0 */
+
+ /* Minimum ethernet-frame size */
+- if (skb->len <62) {
+- if (skb->truesize < 62) {
+- printk("%s:data packet %d / %d\n",
+- dev->name,
+- skb->len,skb->truesize);
+- nb=(unsigned char*)kmalloc(64, GFP_ATOMIC);
+- if (nb == NULL) {
++#ifdef CONFIG_TR
++ if (priv->is_trdev)
++ min_frame_size = LEC_MINIMUM_8025_SIZE;
++ else
++#endif
++ min_frame_size = LEC_MINIMUM_8023_SIZE;
++ if (skb->len < min_frame_size) {
++ if (skb->truesize < min_frame_size) {
++ skb2 = skb_copy_expand(skb, 0,
++ min_frame_size - skb->truesize, GFP_ATOMIC);
+ dev_kfree_skb(skb);
++ if (skb2 == NULL) {
++ priv->stats.tx_dropped++;
+ return 0;
+ }
+- memcpy(nb,skb->data,skb->len);
+- kfree(skb->head);
+- skb->head = skb->data = nb;
+- skb->tail = nb+62;
+- skb->end = nb+64;
+- skb->len=62;
+- skb->truesize = 64;
+- } else {
+- skb->len = 62;
++ skb = skb2;
+ }
++ skb_put(skb, min_frame_size - skb->len);
+ }
+
+ /* Send to right vcc */
+Index: linux/net/atm/lec.h
+===================================================================
+RCS file: /home/chas/CVSROOT/linux/net/atm/lec.h,v
+retrieving revision 1.2
+retrieving revision 1.3
+diff -u -d -b -w -r1.2 -r1.3
+--- linux/net/atm/lec.h 22 Feb 2003 19:29:27 -0000 1.2
++++ linux/net/atm/lec.h 24 Feb 2003 13:24:46 -0000 1.3
+@@ -38,6 +38,9 @@
+ unsigned char h_source[ETH_ALEN];
+ };
+
++#define LEC_MINIMUM_8023_SIZE 62
++#define LEC_MINIMUM_8025_SIZE 16
++
+ /*
+ * Operations that LANE2 capable device can do. Two first functions
+ * are used to make the device do things. See spec 3.1.3 and 3.1.4.
+-
+To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
+the body of a message to majordomo@vger.kernel.org
+More majordomo info at http://vger.kernel.org/majordomo-info.html
+Please read the FAQ at http://www.tux.org/lkml/
--- /dev/null
+the ip layer uses the presence of the .set_multicast_list to determine
+if the underlying network device supports multicast.
+
+Index: linux/net/atm/lec.c
+===================================================================
+RCS file: /home/chas/CVSROOT/linux/net/atm/lec.c,v
+retrieving revision 1.7
+diff -u -d -b -w -r1.7 lec.c
+--- linux/net/atm/lec.c 24 Feb 2003 13:34:43 -0000 1.7
++++ linux/net/atm/lec.c 25 Feb 2003 11:49:42 -0000
+@@ -617,6 +617,14 @@
+ return 0;
+ }
+
++static void lec_set_multicast_list(struct net_device *dev)
++{
++ /* by default, all multicast frames arrive over the bus.
++ * eventually support selective multicast service
++ */
++ return;
++}
++
+ static void
+ lec_init(struct net_device *dev)
+ {
+@@ -626,7 +634,7 @@
+ dev->hard_start_xmit = lec_send_packet;
+
+ dev->get_stats = lec_get_stats;
+- dev->set_multicast_list = NULL;
++ dev->set_multicast_list = lec_set_multicast_list;
+ dev->do_ioctl = NULL;
+ printk("%s: Initialized!\n",dev->name);
+ return;
+-
+To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
+the body of a message to majordomo@vger.kernel.org
+More majordomo info at http://vger.kernel.org/majordomo-info.html
+Please read the FAQ at http://www.tux.org/lkml/
--- /dev/null
+In message <20030223.214513.120185268.davem@redhat.com>,"David S. Miller" writes:
+>Don't try to modify skb->{data,len} by hands, let the skb_*()
+>interfaces do it. Use skb_pull() in this case.
+
+missed that function when i went looking for it. again, the right way:
+
+Index: linux/net/atm/lec.c
+===================================================================
+RCS file: /home/chas/CVSROOT/linux/net/atm/lec.c,v
+retrieving revision 1.1
+retrieving revision 1.7
+diff -u -d -b -w -r1.1 -r1.7
+--- linux/net/atm/lec.c 20 Feb 2003 13:46:30 -0000 1.1
++++ linux/net/atm/lec.c 24 Feb 2003 13:34:43 -0000 1.7
+@@ -711,7 +705,7 @@
+ lec_arp_check_empties(priv, vcc, skb);
+ }
+ skb->dev = dev;
+- skb->data += 2; /* skip lec_id */
++ skb_pull(skb, 2); /* skip lec_id */
+ #ifdef CONFIG_TR
+ if (priv->is_trdev) skb->protocol = tr_type_trans(skb, dev);
+ else
+-
+To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
+the body of a message to majordomo@vger.kernel.org
+More majordomo info at http://vger.kernel.org/majordomo-info.html
+Please read the FAQ at http://www.tux.org/lkml/
--- /dev/null
+
+ [PATCH][2.4] convert atm_dev_lock from spinlock to semaphore
+
+From: chas williams (chas@locutus.cmf.nrl.navy.mil)
+Date: Tue Feb 18 2003 - 16:31:55 EST
+
+same as the 2.5 patch just for 2.4 kernels.
+
+Index: linux/net/atm/addr.c
+===================================================================
+RCS file: /afs/cmf/project/cvsroot/linux/net/atm/addr.c,v
+retrieving revision 1.2
+retrieving revision 1.3
+diff -u -d -b -w -r1.2 -r1.3
+--- linux/net/atm/addr.c 5 Mar 2002 13:41:26 -0000 1.2
++++ linux/net/atm/addr.c 14 Feb 2003 17:02:40 -0000 1.3
+@@ -42,7 +42,6 @@
+ */
+
+ static DECLARE_MUTEX(local_lock);
+-extern spinlock_t atm_dev_lock;
+
+ static void notify_sigd(struct atm_dev *dev)
+ {
+Index: linux/net/atm/common.c
+===================================================================
+RCS file: /afs/cmf/project/cvsroot/linux/net/atm/common.c,v
+retrieving revision 1.5
+retrieving revision 1.6
+diff -u -d -b -w -r1.5 -r1.6
+--- linux/net/atm/common.c 14 Feb 2003 16:59:38 -0000 1.5
++++ linux/net/atm/common.c 14 Feb 2003 17:02:40 -0000 1.6
+@@ -18,6 +18,7 @@
+ #include <linux/capability.h>
+ #include <linux/mm.h> /* verify_area */
+ #include <linux/sched.h>
++#include <linux/sem.h>
+ #include <linux/time.h> /* struct timeval */
+ #include <linux/skbuff.h>
+ #include <linux/bitops.h>
+@@ -27,6 +28,7 @@
+ #include <asm/atomic.h>
+ #include <asm/poll.h>
+ #include <asm/ioctls.h>
++#include <asm/semaphore.h>
+
+ #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
+ #include <linux/atmlec.h>
+@@ -86,7 +88,7 @@
+ #define DPRINTK(format,args...)
+ #endif
+
+-spinlock_t atm_dev_lock = SPIN_LOCK_UNLOCKED;
++DECLARE_MUTEX(atm_dev_sem);
+
+ static struct sk_buff *alloc_tx(struct atm_vcc *vcc,unsigned int size)
+ {
+@@ -154,7 +156,7 @@
+ vcc->dev->ops->free_rx_skb(vcc,skb);
+ else kfree_skb(skb);
+ }
+- spin_lock (&atm_dev_lock);
++ down(&atm_dev_sem);
+ fops_put (vcc->dev->ops);
+ if (atomic_read(&vcc->rx_inuse))
+ printk(KERN_WARNING "atm_release_vcc: strange ... "
+@@ -162,11 +164,11 @@
+ atomic_read(&vcc->rx_inuse));
+ bind_vcc(vcc,NULL);
+ } else
+- spin_lock (&atm_dev_lock);
++ down(&atm_dev_sem);
+
+ if (free_sk) free_atm_vcc_sk(sk);
+
+- spin_unlock (&atm_dev_lock);
++ up(&atm_dev_sem);
+ }
+
+
+@@ -277,14 +279,14 @@
+ struct atm_dev *dev;
+ int return_val;
+
+- spin_lock (&atm_dev_lock);
++ down(&atm_dev_sem);
+ dev = atm_find_dev(itf);
+ if (!dev)
+ return_val = -ENODEV;
+ else
+ return_val = atm_do_connect_dev(vcc,dev,vpi,vci);
+
+- spin_unlock (&atm_dev_lock);
++ up(&atm_dev_sem);
+
+ return return_val;
+ }
+@@ -316,10 +318,10 @@
+ else {
+ struct atm_dev *dev;
+
+- spin_lock (&atm_dev_lock);
++ down(&atm_dev_sem);
+ for (dev = atm_devs; dev; dev = dev->next)
+ if (!atm_do_connect_dev(vcc,dev,vpi,vci)) break;
+- spin_unlock (&atm_dev_lock);
++ up(&atm_dev_sem);
+ if (!dev) return -ENODEV;
+ }
+ if (vpi == ATM_VPI_UNSPEC || vci == ATM_VCI_UNSPEC)
+@@ -561,7 +563,7 @@
+ int error,len,size,number, ret_val;
+
+ ret_val = 0;
+- spin_lock (&atm_dev_lock);
++ down(&atm_dev_sem);
+ vcc = ATM_SD(sock);
+ switch (cmd) {
+ case SIOCOUTQ:
+@@ -961,7 +963,7 @@
+ ret_val = 0;
+
+ done:
+- spin_unlock (&atm_dev_lock);
++ up(&atm_dev_sem);
+ return ret_val;
+ }
+
+Index: linux/net/atm/resources.c
+===================================================================
+RCS file: /afs/cmf/project/cvsroot/linux/net/atm/resources.c,v
+retrieving revision 1.2
+retrieving revision 1.3
+diff -u -d -b -w -r1.2 -r1.3
+--- linux/net/atm/resources.c 2 May 2002 16:56:23 -0000 1.2
++++ linux/net/atm/resources.c 14 Feb 2003 17:02:40 -0000 1.3
+@@ -25,7 +25,7 @@
+ struct atm_dev *atm_devs = NULL;
+ static struct atm_dev *last_dev = NULL;
+ struct atm_vcc *nodev_vccs = NULL;
+-extern spinlock_t atm_dev_lock;
++extern struct semaphore atm_dev_sem;
+
+
+ static struct atm_dev *alloc_atm_dev(const char *type)
+@@ -72,7 +72,7 @@
+ {
+ struct atm_dev *dev = NULL;
+
+- spin_lock(&atm_dev_lock);
++ down(&atm_dev_sem);
+
+ dev = alloc_atm_dev(type);
+ if (!dev) {
+@@ -110,7 +110,7 @@
+ #endif
+
+ done:
+- spin_unlock(&atm_dev_lock);
++ up(&atm_dev_sem);
+ return dev;
+ }
+
+@@ -120,9 +120,9 @@
+ #ifdef CONFIG_PROC_FS
+ if (dev->ops->proc_read) atm_proc_dev_deregister(dev);
+ #endif
+- spin_lock(&atm_dev_lock);
++ down(&atm_dev_sem);
+ __free_atm_dev(dev);
+- spin_unlock(&atm_dev_lock);
++ up(&atm_dev_sem);
+ }
+
+ void shutdown_atm_dev(struct atm_dev *dev)
+Index: linux/net/atm/signaling.c
+===================================================================
+RCS file: /afs/cmf/project/cvsroot/linux/net/atm/signaling.c,v
+retrieving revision 1.2
+retrieving revision 1.3
+diff -u -d -b -w -r1.2 -r1.3
+--- linux/net/atm/signaling.c 12 Feb 2003 20:57:47 -0000 1.2
++++ linux/net/atm/signaling.c 14 Feb 2003 17:02:40 -0000 1.3
+@@ -33,7 +33,7 @@
+ struct atm_vcc *sigd = NULL;
+ static DECLARE_WAIT_QUEUE_HEAD(sigd_sleep);
+
+-extern spinlock_t atm_dev_lock;
++extern struct semaphore atm_dev_sem;
+
+ static void sigd_put_skb(struct sk_buff *skb)
+ {
+@@ -220,9 +220,9 @@
+ skb_queue_purge(&vcc->recvq);
+ purge_vccs(nodev_vccs);
+
+- spin_lock (&atm_dev_lock);
++ down(&atm_dev_sem);
+ for (dev = atm_devs; dev; dev = dev->next) purge_vccs(dev->vccs);
+- spin_unlock (&atm_dev_lock);
++ up(&atm_dev_sem);
+ }
+
+
+To unsubscribe from this list: send the line "unsubscribe
+linux-kernel" in
+the body of a message to [8]majordomo@vger.kernel.org
+More majordomo info at [9]http://vger.kernel.org/majordomo-info.html
+Please read the FAQ at [10]http://www.tux.org/lkml/
--- /dev/null
+lets try this again. for now there are two owner pointers, the original
+in atmdev_ops and another in the struct shared between the common code
+and the lane code. the shared one pointers to the private owner.
+would that be the 'right' way or should i just ignore the owner field
+in atmdev_ops.
+
+Index: linux/net/atm/lec.h
+===================================================================
+RCS file: /home/chas/CVSROOT/linux/net/atm/lec.h,v
+retrieving revision 1.3
+retrieving revision 1.4
+diff -u -r1.3 -r1.4
+--- linux/net/atm/lec.h 24 Feb 2003 13:24:46 -0000 1.3
++++ linux/net/atm/lec.h 26 Feb 2003 15:52:44 -0000 1.4
+@@ -65,6 +65,7 @@
+ int (*mcast_attach)(struct atm_vcc *vcc, int arg);
+ int (*vcc_attach)(struct atm_vcc *vcc, void *arg);
+ struct net_device **(*get_lecs)(void);
++ struct module *owner;
+ };
+
+ /*
+Index: linux/net/atm/proc.c
+===================================================================
+RCS file: /home/chas/CVSROOT/linux/net/atm/proc.c,v
+retrieving revision 1.1
+retrieving revision 1.2
+diff -u -r1.1 -r1.2
+--- linux/net/atm/proc.c 20 Feb 2003 13:46:30 -0000 1.1
++++ linux/net/atm/proc.c 26 Feb 2003 15:52:44 -0000 1.2
+@@ -444,8 +444,11 @@
+ }
+ if (atm_lane_ops.get_lecs == NULL)
+ return 0; /* the lane module is not there yet */
+- else
+- dev_lec = atm_lane_ops.get_lecs();
++
++ if (!try_module_get(atm_lane_ops.owner))
++ return 0;
++
++ dev_lec = atm_lane_ops.get_lecs();
+
+ count = pos;
+ for(d=0;d<MAX_LEC_ITF;d++) {
+@@ -458,6 +461,7 @@
+ e=sprintf(buf,"%s ",
+ dev_lec[d]->name);
+ lec_info(entry,buf+e);
++ module_put(atm_lane_ops.owner);
+ return strlen(buf);
+ }
+ }
+@@ -466,6 +470,7 @@
+ if (--count) continue;
+ e=sprintf(buf,"%s ",dev_lec[d]->name);
+ lec_info(entry, buf+e);
++ module_put(atm_lane_ops.owner);
+ return strlen(buf);
+ }
+ for(entry=priv->lec_no_forward; entry;
+@@ -473,6 +478,7 @@
+ if (--count) continue;
+ e=sprintf(buf,"%s ",dev_lec[d]->name);
+ lec_info(entry, buf+e);
++ module_put(atm_lane_ops.owner);
+ return strlen(buf);
+ }
+ for(entry=priv->mcast_fwds; entry;
+@@ -480,9 +486,11 @@
+ if (--count) continue;
+ e=sprintf(buf,"%s ",dev_lec[d]->name);
+ lec_info(entry, buf+e);
++ module_put(atm_lane_ops.owner);
+ return strlen(buf);
+ }
+ }
++ module_put(atm_lane_ops.owner);
+ return 0;
+ }
+ #endif
+Index: linux/net/atm/common.c
+===================================================================
+RCS file: /home/chas/CVSROOT/linux/net/atm/common.c,v
+retrieving revision 1.4
+retrieving revision 1.5
+diff -u -r1.4 -r1.5
+--- linux/net/atm/common.c 25 Feb 2003 20:06:54 -0000 1.4
++++ linux/net/atm/common.c 26 Feb 2003 15:52:44 -0000 1.5
+@@ -685,12 +685,15 @@
+ }
+ if (atm_lane_ops.lecd_attach == NULL)
+ atm_lane_init();
+- if (atm_lane_ops.lecd_attach == NULL) { /* try again */
++ if (!try_module_get(atm_lane_ops.owner)) { /* try again */
+ ret_val = -ENOSYS;
+ goto done;
+ }
+ error = atm_lane_ops.lecd_attach(vcc, (int)arg);
+- if (error >= 0) sock->state = SS_CONNECTED;
++ if (error >= 0)
++ sock->state = SS_CONNECTED;
++ else
++ module_put(atm_lane_ops.owner);
+ ret_val = error;
+ goto done;
+ case ATMLEC_MCAST:
+Index: linux/net/atm/lec.c
+===================================================================
+RCS file: /home/chas/CVSROOT/linux/net/atm/lec.c,v
+retrieving revision 1.8
+retrieving revision 1.10
+diff -u -r1.8 -r1.10
+--- linux/net/atm/lec.c 25 Feb 2003 11:59:08 -0000 1.8
++++ linux/net/atm/lec.c 26 Feb 2003 15:52:44 -0000 1.10
+@@ -543,12 +543,12 @@
+ }
+
+ printk("%s: Shut down!\n", dev->name);
+- MOD_DEC_USE_COUNT;
+ }
+
+ static struct atmdev_ops lecdev_ops = {
+ close: lec_atm_close,
+- send: lec_atm_send
++ send: lec_atm_send,
++ owner: THIS_MODULE
+ };
+
+ static struct atm_dev lecatm_dev = {
+@@ -824,7 +824,6 @@
+ if (dev_lec[i]->flags & IFF_UP) {
+ netif_start_queue(dev_lec[i]);
+ }
+- MOD_INC_USE_COUNT;
+ return i;
+ }
+
+@@ -834,6 +833,7 @@
+ ops->mcast_attach = lec_mcast_attach;
+ ops->vcc_attach = lec_vcc_attach;
+ ops->get_lecs = get_dev_lec;
++ ops->owner = lecdev_ops.owner;
+
+ printk("lec.c: " __DATE__ " " __TIME__ " initialized\n");
+
+@@ -859,6 +859,7 @@
+ atm_lane_ops.mcast_attach = NULL;
+ atm_lane_ops.vcc_attach = NULL;
+ atm_lane_ops.get_lecs = NULL;
++ atm_lane_ops.owner = NULL;
+
+ for (i = 0; i < MAX_LEC_ITF; i++) {
+ if (dev_lec[i] != NULL) {
+-
+To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
+the body of a message to majordomo@vger.kernel.org
+More majordomo info at http://vger.kernel.org/majordomo-info.html
+Please read the FAQ at http://www.tux.org/lkml/
--- /dev/null
+exporting a symbol declared as __init is bogus. additonally, suni
+doesnt need to modify its ref counts, to quote:
+
+Q: My code use "MOD_INC_USE_COUNT". Do I still need to adjust my
+ module count when someone calls one of my functions?
+A: No ...
+ ... It could be another module using one of your
+ EXPORT_SYMBOL'ed functions, in which case you cannot be removed
+ since they would have to be removed first. ...
+
+this is certainly the case for suni which is used by the various
+atm drivers.
+
+Index: linux/drivers/atm/suni.c
+===================================================================
+RCS file: /home/chas/CVSROOT/linux/drivers/atm/suni.c,v
+retrieving revision 1.1
+retrieving revision 1.3
+diff -u -r1.1 -r1.3
+--- linux/drivers/atm/suni.c 20 Feb 2003 13:45:03 -0000 1.1
++++ linux/drivers/atm/suni.c 26 Feb 2003 15:43:30 -0000 1.3
+@@ -233,8 +233,6 @@
+ if (!(PRIV(dev) = kmalloc(sizeof(struct suni_priv),GFP_KERNEL)))
+ return -ENOMEM;
+
+- MOD_INC_USE_COUNT;
+-
+ PRIV(dev)->dev = dev;
+ spin_lock_irqsave(&sunis_lock,flags);
+ first = !sunis;
+@@ -280,7 +278,6 @@
+ spin_unlock_irqrestore(&sunis_lock,flags);
+ kfree(PRIV(dev));
+
+- MOD_DEC_USE_COUNT;
+ return 0;
+ }
+
+@@ -293,7 +290,7 @@
+ };
+
+
+-int __init suni_init(struct atm_dev *dev)
++int suni_init(struct atm_dev *dev)
+ {
+ unsigned char mri;
+
+-
+To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
+the body of a message to majordomo@vger.kernel.org
+More majordomo info at http://vger.kernel.org/majordomo-info.html
+Please read the FAQ at http://www.tux.org/lkml/
--- /dev/null
+the following patch makes atm a module. this patch also fixes a
+number of other problems: MOD_INC/MOD_DEC are gone in lec and mpc
+(however i needed to use module_put() inside lec, mpc and clip due to
+the way the user space 'clients' unregister from the modules), clip
+is a module (i wrote the clip module_exit() function -- its *probably*
+correct), structs are now exported as pointers (if someone has a
+compelling reason it should remain the old way let me know), ipcommon.c
+is included by clip.c and doesnt export symbols -- it should probably
+just be split eventually with only llc_oui residing in the kernel
+(and exported) and skb_migrate moved to clip -- this really depends
+on the status of CONFIG_NET_SCH_ATM.
+
+Index: linux/net/atm/clip.c
+===================================================================
+RCS file: /home/chas/CVSROOT/linux/net/atm/clip.c,v
+retrieving revision 1.1.1.1
+diff -u -r1.1.1.1 clip.c
+--- linux/net/atm/clip.c 20 Feb 2003 13:46:30 -0000 1.1.1.1
++++ linux/net/atm/clip.c 3 Mar 2003 19:33:38 -0000
+@@ -7,6 +7,7 @@
+ #include <linux/string.h>
+ #include <linux/errno.h>
+ #include <linux/kernel.h> /* for UINT_MAX */
++#include <linux/module.h>
+ #include <linux/netdevice.h>
+ #include <linux/skbuff.h>
+ #include <linux/wait.h>
+@@ -48,6 +49,7 @@
+ static struct timer_list idle_timer;
+ static int start_timer = 1;
+
++#include "ipcommon.c"
+
+ static int to_atmarpd(enum atmarp_ctrl_type type,int itf,unsigned long ip)
+ {
+@@ -696,6 +698,7 @@
+ "pending\n");
+ skb_queue_purge(&vcc->recvq);
+ DPRINTK("(done)\n");
++ MOD_DEC_USE_COUNT;
+ }
+
+
+@@ -747,10 +750,50 @@
+ return 0;
+ }
+
++static struct atm_clip_ops __atm_clip_ops = {
++ clip_create: clip_create,
++ clip_mkip: clip_mkip,
++ clip_setentry: clip_setentry,
++ clip_encap: clip_encap,
++ clip_push: clip_push,
++ atm_init_atmarp: atm_init_atmarp,
++ clip_tbl: &clip_tbl,
++ owner: THIS_MODULE
++};
+
+-void atm_clip_init(void)
++static int __init atm_clip_init(void)
+ {
++ extern struct atm_clip_ops *atm_clip_ops;
++
+ clip_tbl.lock = RW_LOCK_UNLOCKED;
+ clip_tbl.kmem_cachep = kmem_cache_create(clip_tbl.id,
+ clip_tbl.entry_size, 0, SLAB_HWCACHE_ALIGN, NULL, NULL);
++
++ atm_clip_ops = &__atm_clip_ops;
++
++ return 0;
+ }
++
++static void __exit atm_clip_exit(void)
++{
++ extern struct atm_clip_ops *atm_clip_ops;
++ struct net_device *dev, *next;
++
++ atm_clip_ops = NULL;
++
++ next = clip_devs;
++ while (next) {
++ dev = next;
++ next = PRIV(dev)->next;
++ unregister_netdev(dev);
++ kfree(dev);
++ }
++ if (start_timer == 0) del_timer(&idle_timer);
++
++ kmem_cache_destroy(clip_tbl.kmem_cachep);
++}
++
++module_init(atm_clip_init);
++module_exit(atm_clip_exit);
++
++MODULE_LICENSE("GPL");
+Index: linux/net/atm/common.c
+===================================================================
+RCS file: /home/chas/CVSROOT/linux/net/atm/common.c,v
+retrieving revision 1.5
+diff -u -r1.5 common.c
+--- linux/net/atm/common.c 26 Feb 2003 15:52:44 -0000 1.5
++++ linux/net/atm/common.c 3 Mar 2003 19:41:21 -0000
+@@ -33,16 +33,16 @@
+ #include <linux/atmlec.h>
+ #include "lec.h"
+ #include "lec_arpc.h"
+-struct atm_lane_ops atm_lane_ops;
+-#endif
++struct atm_lane_ops *atm_lane_ops = NULL;
+ #ifdef CONFIG_ATM_LANE_MODULE
+ EXPORT_SYMBOL(atm_lane_ops);
+ #endif
++#endif
+
+ #if defined(CONFIG_ATM_MPOA) || defined(CONFIG_ATM_MPOA_MODULE)
+ #include <linux/atmmpc.h>
+ #include "mpc.h"
+-struct atm_mpoa_ops atm_mpoa_ops;
++struct atm_mpoa_ops *atm_mpoa_ops = NULL;
+ #endif
+ #ifdef CONFIG_ATM_MPOA_MODULE
+ EXPORT_SYMBOL(atm_mpoa_ops);
+@@ -59,6 +59,14 @@
+ #endif
+ #endif
+
++#if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE)
++#include <net/atmclip.h>
++struct atm_clip_ops *atm_clip_ops = NULL;
++#ifdef CONFIG_ATM_CLIP_MODULE
++EXPORT_SYMBOL(atm_clip_ops);
++#endif
++#endif
++
+ #if defined(CONFIG_PPPOATM) || defined(CONFIG_PPPOATM_MODULE)
+ int (*pppoatm_ioctl_hook)(struct atm_vcc *, unsigned int, unsigned long);
+ EXPORT_SYMBOL(pppoatm_ioctl_hook);
+@@ -68,9 +76,6 @@
+ #include "common.h" /* prototypes */
+ #include "protocols.h" /* atm_init_<transport> */
+ #include "addr.h" /* address registry */
+-#ifdef CONFIG_ATM_CLIP
+-#include <net/atmclip.h> /* for clip_create */
+-#endif
+ #include "signaling.h" /* for WAITING and sigd_attach */
+
+
+@@ -642,39 +647,50 @@
+ if (!error) sock->state = SS_CONNECTED;
+ ret_val = error;
+ goto done;
+-#ifdef CONFIG_ATM_CLIP
++#if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE)
+ case SIOCMKCLIP:
+ if (!capable(CAP_NET_ADMIN))
+ ret_val = -EPERM;
+ else
+- ret_val = clip_create(arg);
++ ret_val = atm_clip_ops->clip_create(arg);
+ goto done;
+ case ATMARPD_CTRL:
+ if (!capable(CAP_NET_ADMIN)) {
+ ret_val = -EPERM;
+ goto done;
+ }
+- error = atm_init_atmarp(vcc);
+- if (!error) sock->state = SS_CONNECTED;
++#if defined(CONFIG_ATM_CLIP_MODULE)
++ if (atm_clip_ops == NULL)
++ request_module("clip");
++#endif
++ if (atm_clip_ops && !try_inc_mod_count(atm_clip_ops->owner)) {
++ ret_val = -ENOSYS;
++ goto done;
++ }
++ error = atm_clip_ops->atm_init_atmarp(vcc);
++ if (!error)
++ sock->state = SS_CONNECTED;
++ else if (atm_clip_ops->owner)
++ __MOD_DEC_USE_COUNT(atm_clip_ops->owner);
+ ret_val = error;
+ goto done;
+ case ATMARP_MKIP:
+ if (!capable(CAP_NET_ADMIN))
+ ret_val = -EPERM;
+ else
+- ret_val = clip_mkip(vcc,arg);
++ ret_val = atm_clip_ops->clip_mkip(vcc,arg);
+ goto done;
+ case ATMARP_SETENTRY:
+ if (!capable(CAP_NET_ADMIN))
+ ret_val = -EPERM;
+ else
+- ret_val = clip_setentry(vcc,arg);
++ ret_val = atm_clip_ops->clip_setentry(vcc,arg);
+ goto done;
+ case ATMARP_ENCAP:
+ if (!capable(CAP_NET_ADMIN))
+ ret_val = -EPERM;
+ else
+- ret_val = clip_encap(vcc,arg);
++ ret_val = atm_clip_ops->clip_encap(vcc,arg);
+ goto done;
+ #endif
+ #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
+@@ -683,30 +699,32 @@
+ ret_val = -EPERM;
+ goto done;
+ }
+- if (atm_lane_ops.lecd_attach == NULL)
+- atm_lane_init();
+- if (!try_module_get(atm_lane_ops.owner)) { /* try again */
++#if defined(CONFIG_ATM_LANE_MODULE)
++ if (atm_lane_ops == NULL)
++ request_module("lec");
++#endif
++ if (atm_lane_ops && !try_inc_mod_count(atm_lane_ops->owner)) {
+ ret_val = -ENOSYS;
+ goto done;
+ }
+- error = atm_lane_ops.lecd_attach(vcc, (int)arg);
++ error = atm_lane_ops->lecd_attach(vcc, (int)arg);
+ if (error >= 0)
+ sock->state = SS_CONNECTED;
+- else
+- module_put(atm_lane_ops.owner);
++ else if (atm_lane_ops->owner)
++ __MOD_DEC_USE_COUNT(atm_lane_ops->owner);
+ ret_val = error;
+ goto done;
+ case ATMLEC_MCAST:
+ if (!capable(CAP_NET_ADMIN))
+ ret_val = -EPERM;
+ else
+- ret_val = atm_lane_ops.mcast_attach(vcc, (int)arg);
++ ret_val = atm_lane_ops->mcast_attach(vcc, (int)arg);
+ goto done;
+ case ATMLEC_DATA:
+ if (!capable(CAP_NET_ADMIN))
+ ret_val = -EPERM;
+ else
+- ret_val = atm_lane_ops.vcc_attach(vcc, (void*)arg);
++ ret_val = atm_lane_ops->vcc_attach(vcc, (void*)arg);
+ goto done;
+ #endif
+ #if defined(CONFIG_ATM_MPOA) || defined(CONFIG_ATM_MPOA_MODULE)
+@@ -715,21 +733,26 @@
+ ret_val = -EPERM;
+ goto done;
+ }
+- if (atm_mpoa_ops.mpoad_attach == NULL)
+- atm_mpoa_init();
+- if (atm_mpoa_ops.mpoad_attach == NULL) { /* try again */
++#if defined(CONFIG_ATM_MPOA_MODULE)
++ if (atm_mpoa_ops == NULL)
++ request_module("mpoa");
++#endif
++ if (atm_mpoa_ops && !try_inc_mod_count(atm_mpoa_ops->owner)) {
+ ret_val = -ENOSYS;
+ goto done;
+ }
+- error = atm_mpoa_ops.mpoad_attach(vcc, (int)arg);
+- if (error >= 0) sock->state = SS_CONNECTED;
++ error = atm_mpoa_ops->mpoad_attach(vcc, (int)arg);
++ if (error >= 0)
++ sock->state = SS_CONNECTED;
++ else if (atm_mpoa_ops->owner)
++ __MOD_DEC_USE_COUNT(atm_mpoa_ops->owner);
+ ret_val = error;
+ goto done;
+ case ATMMPC_DATA:
+ if (!capable(CAP_NET_ADMIN))
+ ret_val = -EPERM;
+ else
+- ret_val = atm_mpoa_ops.vcc_attach(vcc, arg);
++ ret_val = atm_mpoa_ops->vcc_attach(vcc, arg);
+ goto done;
+ #endif
+ #if defined(CONFIG_ATM_TCP) || defined(CONFIG_ATM_TCP_MODULE)
+@@ -1105,40 +1128,6 @@
+ }
+
+
+-/*
+- * lane_mpoa_init.c: A couple of helper functions
+- * to make modular LANE and MPOA client easier to implement
+- */
+-
+-/*
+- * This is how it goes:
+- *
+- * if xxxx is not compiled as module, call atm_xxxx_init_ops()
+- * from here
+- * else call atm_mpoa_init_ops() from init_module() within
+- * the kernel when xxxx module is loaded
+- *
+- * In either case function pointers in struct atm_xxxx_ops
+- * are initialized to their correct values. Either they
+- * point to functions in the module or in the kernel
+- */
+-
+-extern struct atm_mpoa_ops atm_mpoa_ops; /* in common.c */
+-extern struct atm_lane_ops atm_lane_ops; /* in common.c */
+-
+-#if defined(CONFIG_ATM_MPOA) || defined(CONFIG_ATM_MPOA_MODULE)
+-void atm_mpoa_init(void)
+-{
+-#ifndef CONFIG_ATM_MPOA_MODULE /* not module */
+- atm_mpoa_init_ops(&atm_mpoa_ops);
+-#else
+- request_module("mpoa");
+-#endif
+-
+- return;
+-}
+-#endif
+-
+ #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ struct net_bridge_fdb_entry *(*br_fdb_get_hook)(struct net_bridge *br,
+@@ -1149,15 +1138,33 @@
+ EXPORT_SYMBOL(br_fdb_put_hook);
+ #endif /* defined(CONFIG_ATM_LANE_MODULE) || defined(CONFIG_BRIDGE_MODULE) */
+ #endif /* defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE) */
++#endif /* defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE) */
++
+
+-void atm_lane_init(void)
++static int __init atm_init(void)
+ {
+-#ifndef CONFIG_ATM_LANE_MODULE /* not module */
+- atm_lane_init_ops(&atm_lane_ops);
+-#else
+- request_module("lec");
++ int error = 0;
++
++ if (atmpvc_init() < 0)
++ return -1;
++ if (atmsvc_init() < 0)
++ return -1;
++#ifdef CONFIG_PROC_FS
++ error = atm_proc_init();
++ if (error) printk("atm_proc_init fails with %d\n",error);
+ #endif
++ return error;
++}
+
+- return;
+-}
++static void __exit atm_exit(void)
++{
++#ifdef CONFIG_PROC_FS
++ atm_proc_exit();
+ #endif
++ atmsvc_exit();
++ atmpvc_exit();
++}
++
++module_init(atm_init);
++module_exit(atm_exit);
++MODULE_LICENSE("GPL");
+Index: linux/net/atm/common.h
+===================================================================
+RCS file: /home/chas/CVSROOT/linux/net/atm/common.h,v
+retrieving revision 1.1.1.1
+diff -u -r1.1.1.1 common.h
+--- linux/net/atm/common.h 20 Feb 2003 13:46:30 -0000 1.1.1.1
++++ linux/net/atm/common.h 27 Feb 2003 16:47:15 -0000
+@@ -28,7 +28,14 @@
+ void atm_release_vcc_sk(struct sock *sk,int free_sk);
+ void atm_shutdown_dev(struct atm_dev *dev);
+
++int atmsvc_init(void);
++void atmsvc_exit(void);
++
++int atmpvc_init(void);
++void atmpvc_exit(void);
++
+ int atm_proc_init(void);
++void atm_proc_exit(void);
+
+ /* SVC */
+
+Index: linux/net/atm/ipcommon.c
+===================================================================
+RCS file: /home/chas/CVSROOT/linux/net/atm/ipcommon.c,v
+retrieving revision 1.1.1.1
+diff -u -r1.1.1.1 ipcommon.c
+--- linux/net/atm/ipcommon.c 20 Feb 2003 13:46:30 -0000 1.1.1.1
++++ linux/net/atm/ipcommon.c 3 Mar 2003 16:51:32 -0000
+@@ -65,6 +65,3 @@
+ from->qlen = 0;
+ spin_unlock_irqrestore(&from->lock,flags);
+ }
+-
+-
+-EXPORT_SYMBOL(skb_migrate);
+Index: linux/net/atm/ipcommon.h
+===================================================================
+RCS file: /home/chas/CVSROOT/linux/net/atm/ipcommon.h,v
+retrieving revision 1.1.1.1
+diff -u -r1.1.1.1 ipcommon.h
+--- linux/net/atm/ipcommon.h 20 Feb 2003 13:46:30 -0000 1.1.1.1
++++ linux/net/atm/ipcommon.h 3 Mar 2003 16:52:24 -0000
+@@ -13,8 +13,6 @@
+ #include <linux/atmdev.h>
+
+
+-extern struct net_device *clip_devs;
+-
+ /*
+ * Appends all skbs from "from" to "to". The operation is atomic with respect
+ * to all other skb operations on "from" or "to".
+Index: linux/net/atm/lec.c
+===================================================================
+RCS file: /home/chas/CVSROOT/linux/net/atm/lec.c,v
+retrieving revision 1.11
+diff -u -r1.11 lec.c
+--- linux/net/atm/lec.c 3 Mar 2003 21:25:05 -0000 1.11
++++ linux/net/atm/lec.c 3 Mar 2003 21:27:05 -0000
+@@ -790,7 +790,6 @@
+ dev_lec[i] = init_etherdev(NULL, size);
+ if (!dev_lec[i])
+ return -ENOMEM;
+-
+ priv = dev_lec[i]->priv;
+ priv->is_trdev = is_trdev;
+ sprintf(dev_lec[i]->name, "lec%d", i);
+@@ -828,24 +827,20 @@
+ return i;
+ }
+
+-void atm_lane_init_ops(struct atm_lane_ops *ops)
+-{
+- ops->lecd_attach = lecd_attach;
+- ops->mcast_attach = lec_mcast_attach;
+- ops->vcc_attach = lec_vcc_attach;
+- ops->get_lecs = get_dev_lec;
+- ops->owner = lecdev_ops.owner;
+-
+- printk("lec.c: " __DATE__ " " __TIME__ " initialized\n");
+-
+- return;
+-}
++static struct atm_lane_ops __atm_lane_ops = {
++ lecd_attach: lecd_attach,
++ mcast_attach: lec_mcast_attach,
++ vcc_attach: lec_vcc_attach,
++ get_lecs: get_dev_lec,
++ owner: THIS_MODULE
++};
+
+ static int __init lane_module_init(void)
+ {
+- extern struct atm_lane_ops atm_lane_ops;
++ extern struct atm_lane_ops *atm_lane_ops;
+
+- atm_lane_init_ops(&atm_lane_ops);
++ atm_lane_ops = &__atm_lane_ops;
++ printk("lec.c: " __DATE__ " " __TIME__ " initialized\n");
+
+ return 0;
+ }
+@@ -852,14 +847,10 @@
+ static void __exit lane_module_cleanup(void)
+ {
+ int i;
+- extern struct atm_lane_ops atm_lane_ops;
++ extern struct atm_lane_ops *atm_lane_ops;
+ struct lec_priv *priv;
+
+- atm_lane_ops.lecd_attach = NULL;
+- atm_lane_ops.mcast_attach = NULL;
+- atm_lane_ops.vcc_attach = NULL;
+- atm_lane_ops.get_lecs = NULL;
+- atm_lane_ops.owner = NULL;
++ atm_lane_ops = NULL;
+
+ for (i = 0; i < MAX_LEC_ITF; i++) {
+ if (dev_lec[i] != NULL) {
+@@ -871,7 +863,7 @@
+ unregister_netdev(dev_lec[i]);
+ kfree(dev_lec[i]);
+ dev_lec[i] = NULL;
+- }
++ }
+ }
+
+ return;
+@@ -1021,7 +1013,7 @@
+ #include <linux/timer.h>
+ #include <asm/param.h>
+ #include <asm/atomic.h>
+-#include <linux/inetdevice.h>
++#include <linux/netdevice.h>
+ #include <net/route.h>
+
+
+Index: linux/net/atm/lec.h
+===================================================================
+RCS file: /home/chas/CVSROOT/linux/net/atm/lec.h,v
+retrieving revision 1.5
+diff -u -r1.5 lec.h
+--- linux/net/atm/lec.h 3 Mar 2003 21:25:05 -0000 1.5
++++ linux/net/atm/lec.h 3 Mar 2003 21:25:17 -0000
+@@ -155,7 +156,5 @@
+ unsigned char *atm_addr, struct sk_buff *data);
+ void lec_push(struct atm_vcc *vcc, struct sk_buff *skb);
+
+-void atm_lane_init(void);
+-void atm_lane_init_ops(struct atm_lane_ops *ops);
+ #endif /* _LEC_H_ */
+
+Index: linux/net/atm/mpc.c
+===================================================================
+RCS file: /home/chas/CVSROOT/linux/net/atm/mpc.c,v
+retrieving revision 1.3
+diff -u -r1.3 mpc.c
+--- linux/net/atm/mpc.c 3 Mar 2003 21:25:05 -0000 1.3
++++ linux/net/atm/mpc.c 3 Mar 2003 21:25:19 -0000
+@@ -799,7 +799,6 @@
+ send_set_mps_ctrl_addr(mpc->mps_ctrl_addr, mpc);
+ }
+
+- MOD_INC_USE_COUNT;
+ return arg;
+ }
+
+@@ -1390,11 +1389,17 @@
+ return;
+ }
+
+-void atm_mpoa_init_ops(struct atm_mpoa_ops *ops)
++struct atm_mpoa_ops __atm_mpoa_ops = {
++ mpoad_attach: atm_mpoa_mpoad_attach,
++ vcc_attach: atm_mpoa_vcc_attach,
++ owner: THIS_MODULE
++};
++
++static int __init atm_mpoa_init(void)
+ {
+- ops->mpoad_attach = atm_mpoa_mpoad_attach;
+- ops->vcc_attach = atm_mpoa_vcc_attach;
++ extern struct atm_mpoa_ops *atm_mpoa_ops;
+
++ atm_mpoa_ops = &__atm_mpoa_ops;
+ #ifdef CONFIG_PROC_FS
+ if(mpc_proc_init() != 0)
+ printk(KERN_INFO "mpoa: failed to initialize /proc/mpoa\n");
+@@ -1404,22 +1409,12 @@
+
+ printk("mpc.c: " __DATE__ " " __TIME__ " initialized\n");
+
+- return;
+-}
+-
+-#ifdef MODULE
+-int init_module(void)
+-{
+- extern struct atm_mpoa_ops atm_mpoa_ops;
+-
+- atm_mpoa_init_ops(&atm_mpoa_ops);
+-
+ return 0;
+ }
+
+-void cleanup_module(void)
++static void __exit atm_mpoa_cleanup(void)
+ {
+- extern struct atm_mpoa_ops atm_mpoa_ops;
++ extern struct atm_mpoa_ops *atm_mpoa_ops;
+ struct mpoa_client *mpc, *tmp;
+ struct atm_mpoa_qos *qos, *nextqos;
+ struct lec_priv *priv;
+@@ -1430,8 +1425,7 @@
+
+ del_timer(&mpc_timer);
+ unregister_netdevice_notifier(&mpoa_notifier);
+- atm_mpoa_ops.mpoad_attach = NULL;
+- atm_mpoa_ops.vcc_attach = NULL;
++ atm_mpoa_ops = NULL;
+
+ mpc = mpcs;
+ mpcs = NULL;
+@@ -1463,8 +1457,9 @@
+ kfree(qos);
+ qos = nextqos;
+ }
+-
+- return;
+ }
+-#endif /* MODULE */
++
++module_init(atm_mpoa_init);
++module_exit(atm_mpoa_cleanup);
++
+ MODULE_LICENSE("GPL");
+Index: linux/net/atm/mpc.h
+===================================================================
+RCS file: /home/chas/CVSROOT/linux/net/atm/mpc.h,v
+retrieving revision 1.1.1.1
+diff -u -r1.1.1.1 mpc.h
+--- linux/net/atm/mpc.h 20 Feb 2003 13:46:30 -0000 1.1.1.1
++++ linux/net/atm/mpc.h 1 Mar 2003 13:37:05 -0000
+@@ -48,11 +48,8 @@
+ struct atm_mpoa_ops {
+ int (*mpoad_attach)(struct atm_vcc *vcc, int arg); /* attach mpoa daemon */
+ int (*vcc_attach)(struct atm_vcc *vcc, long arg); /* attach shortcut vcc */
++ struct module *owner;
+ };
+-
+-/* Boot/module initialization function */
+-void atm_mpoa_init(void);
+-void atm_mpoa_init_ops(struct atm_mpoa_ops *ops);
+
+ /* MPOA QoS operations */
+ struct atm_mpoa_qos *atm_mpoa_add_qos(uint32_t dst_ip, struct atm_qos *qos);
+Index: linux/net/atm/proc.c
+===================================================================
+RCS file: /home/chas/CVSROOT/linux/net/atm/proc.c,v
+retrieving revision 1.2
+diff -u -r1.2 proc.c
+--- linux/net/atm/proc.c 26 Feb 2003 15:52:44 -0000 1.2
++++ linux/net/atm/proc.c 1 Mar 2003 14:39:12 -0000
+@@ -39,16 +39,15 @@
+ #include "common.h" /* atm_proc_init prototype */
+ #include "signaling.h" /* to get sigd - ugly too */
+
+-#ifdef CONFIG_ATM_CLIP
++#if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE)
+ #include <net/atmclip.h>
+-#include "ipcommon.h"
+-extern void clip_push(struct atm_vcc *vcc,struct sk_buff *skb);
++extern struct atm_clip_ops *atm_clip_ops; /* in common.c */
+ #endif
+
+ #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
+ #include "lec.h"
+ #include "lec_arpc.h"
+-extern struct atm_lane_ops atm_lane_ops; /* in common.c */
++extern struct atm_lane_ops *atm_lane_ops; /* in common.c */
+ #endif
+
+ static ssize_t proc_dev_atm_read(struct file *file,char *buf,size_t count,
+@@ -89,7 +88,7 @@
+ }
+
+
+-#ifdef CONFIG_ATM_CLIP
++#if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE)
+
+
+ static int svc_addr(char *buf,struct sockaddr_atmsvc *addr)
+@@ -178,8 +177,8 @@
+ aal_name[vcc->qos.aal],vcc->qos.rxtp.min_pcr,
+ class_name[vcc->qos.rxtp.traffic_class],vcc->qos.txtp.min_pcr,
+ class_name[vcc->qos.txtp.traffic_class]);
+-#ifdef CONFIG_ATM_CLIP
+- if (vcc->push == clip_push) {
++#if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE)
++ if (atm_clip_ops && (vcc->push == atm_clip_ops->clip_push)) {
+ struct clip_vcc *clip_vcc = CLIP_VCC(vcc);
+ struct net_device *dev;
+
+@@ -393,7 +392,7 @@
+ return 0;
+ }
+
+-#ifdef CONFIG_ATM_CLIP
++#if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE)
+ static int atm_arp_info(loff_t pos,char *buf)
+ {
+ struct neighbour *n;
+@@ -403,28 +402,30 @@
+ return sprintf(buf,"IPitf TypeEncp Idle IP address "
+ "ATM address\n");
+ }
++ if (!atm_clip_ops)
++ return 0;
+ count = pos;
+- read_lock_bh(&clip_tbl.lock);
++ read_lock_bh(&atm_clip_ops->clip_tbl->lock);
+ for (i = 0; i <= NEIGH_HASHMASK; i++)
+- for (n = clip_tbl.hash_buckets[i]; n; n = n->next) {
++ for (n = atm_clip_ops->clip_tbl->hash_buckets[i]; n; n = n->next) {
+ struct atmarp_entry *entry = NEIGH2ENTRY(n);
+ struct clip_vcc *vcc;
+
+ if (!entry->vccs) {
+ if (--count) continue;
+ atmarp_info(n->dev,entry,NULL,buf);
+- read_unlock_bh(&clip_tbl.lock);
++ read_unlock_bh(&atm_clip_ops->clip_tbl->lock);
+ return strlen(buf);
+ }
+ for (vcc = entry->vccs; vcc;
+ vcc = vcc->next) {
+ if (--count) continue;
+ atmarp_info(n->dev,entry,vcc,buf);
+- read_unlock_bh(&clip_tbl.lock);
++ read_unlock_bh(&atm_clip_ops->clip_tbl->lock);
+ return strlen(buf);
+ }
+ }
+- read_unlock_bh(&clip_tbl.lock);
++ read_unlock_bh(&atm_clip_ops->clip_tbl->lock);
+ return 0;
+ }
+ #endif
+@@ -442,13 +443,13 @@
+ " Status Flags "
+ "VPI/VCI Recv VPI/VCI\n");
+ }
+- if (atm_lane_ops.get_lecs == NULL)
++ if (!atm_lane_ops)
+ return 0; /* the lane module is not there yet */
+
+- if (!try_module_get(atm_lane_ops.owner))
++ if (!try_inc_mod_count(atm_lane_ops->owner))
+ return 0;
+
+- dev_lec = atm_lane_ops.get_lecs();
++ dev_lec = atm_lane_ops->get_lecs();
+
+ count = pos;
+ for(d=0;d<MAX_LEC_ITF;d++) {
+@@ -461,7 +462,8 @@
+ e=sprintf(buf,"%s ",
+ dev_lec[d]->name);
+ lec_info(entry,buf+e);
+- module_put(atm_lane_ops.owner);
++ if (atm_lane_ops->owner)
++ __MOD_DEC_USE_COUNT(atm_lane_ops->owner);
+ return strlen(buf);
+ }
+ }
+@@ -470,7 +471,8 @@
+ if (--count) continue;
+ e=sprintf(buf,"%s ",dev_lec[d]->name);
+ lec_info(entry, buf+e);
+- module_put(atm_lane_ops.owner);
++ if (atm_lane_ops->owner)
++ __MOD_DEC_USE_COUNT(atm_lane_ops->owner);
+ return strlen(buf);
+ }
+ for(entry=priv->lec_no_forward; entry;
+@@ -478,7 +479,8 @@
+ if (--count) continue;
+ e=sprintf(buf,"%s ",dev_lec[d]->name);
+ lec_info(entry, buf+e);
+- module_put(atm_lane_ops.owner);
++ if (atm_lane_ops->owner)
++ __MOD_DEC_USE_COUNT(atm_lane_ops->owner);
+ return strlen(buf);
+ }
+ for(entry=priv->mcast_fwds; entry;
+@@ -486,11 +487,13 @@
+ if (--count) continue;
+ e=sprintf(buf,"%s ",dev_lec[d]->name);
+ lec_info(entry, buf+e);
+- module_put(atm_lane_ops.owner);
++ if (atm_lane_ops->owner)
++ __MOD_DEC_USE_COUNT(atm_lane_ops->owner);
+ return strlen(buf);
+ }
+ }
+- module_put(atm_lane_ops.owner);
++ if (atm_lane_ops->owner)
++ __MOD_DEC_USE_COUNT(atm_lane_ops->owner);
+ return 0;
+ }
+ #endif
+@@ -591,12 +592,11 @@
+ name->proc_fops = &proc_spec_atm_operations; \
+ name->owner = THIS_MODULE
+
++struct proc_dir_entry *devices = NULL, *pvc = NULL, *svc = NULL;
++struct proc_dir_entry *arp = NULL, *lec = NULL, *vc = NULL;
+
+ int __init atm_proc_init(void)
+ {
+- struct proc_dir_entry *devices = NULL,*pvc = NULL,*svc = NULL;
+- struct proc_dir_entry *arp = NULL,*lec = NULL,*vc = NULL;
+-
+ atm_proc_root = proc_mkdir("net/atm",NULL);
+ if (!atm_proc_root)
+ return -ENOMEM;
+@@ -604,7 +604,7 @@
+ CREATE_ENTRY(pvc);
+ CREATE_ENTRY(svc);
+ CREATE_ENTRY(vc);
+-#ifdef CONFIG_ATM_CLIP
++#if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE)
+ CREATE_ENTRY(arp);
+ #endif
+ #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
+@@ -621,4 +621,15 @@
+ if (vc) remove_proc_entry("vc",atm_proc_root);
+ remove_proc_entry("net/atm",NULL);
+ return -ENOMEM;
++}
++
++void __exit atm_proc_exit(void)
++{
++ if (vc) remove_proc_entry("vc",atm_proc_root);
++ if (lec) remove_proc_entry("lec",atm_proc_root);
++ if (arp) remove_proc_entry("arp",atm_proc_root);
++ if (svc) remove_proc_entry("svc",atm_proc_root);
++ if (pvc) remove_proc_entry("pvc",atm_proc_root);
++ if (devices) remove_proc_entry("devices",atm_proc_root);
++ remove_proc_entry("net/atm",NULL);
+ }
+Index: linux/net/atm/pvc.c
+===================================================================
+RCS file: /home/chas/CVSROOT/linux/net/atm/pvc.c,v
+retrieving revision 1.1.1.1
+diff -u -r1.1.1.1 pvc.c
+--- linux/net/atm/pvc.c 20 Feb 2003 13:46:30 -0000 1.1.1.1
++++ linux/net/atm/pvc.c 1 Mar 2003 14:11:17 -0000
+@@ -15,9 +15,6 @@
+ #include <linux/skbuff.h>
+ #include <linux/bitops.h>
+ #include <net/sock.h> /* for sock_no_* */
+-#ifdef CONFIG_ATM_CLIP
+-#include <net/atmclip.h>
+-#endif
+
+ #include "resources.h" /* devs and vccs */
+ #include "common.h" /* common for PVCs and SVCs */
+@@ -121,23 +118,20 @@
+ */
+
+
+-static int __init atmpvc_init(void)
++int __init atmpvc_init(void)
+ {
+ int error;
+
+ error = sock_register(&pvc_family_ops);
+ if (error < 0) {
+- printk(KERN_ERR "ATMPVC: can't register (%d)",error);
++ printk(KERN_ERR "ATMPVC: can't register (%d)", error);
+ return error;
+ }
+-#ifdef CONFIG_ATM_CLIP
+- atm_clip_init();
+-#endif
+-#ifdef CONFIG_PROC_FS
+- error = atm_proc_init();
+- if (error) printk("atm_proc_init fails with %d\n",error);
+-#endif
+- return 0;
++
++ return error;
+ }
+
+-module_init(atmpvc_init);
++void __exit atmpvc_exit(void)
++{
++ sock_unregister(PF_ATMPVC);
++}
+Index: linux/net/atm/svc.c
+===================================================================
+RCS file: /home/chas/CVSROOT/linux/net/atm/svc.c,v
+retrieving revision 1.1.1.1
+diff -u -r1.1.1.1 svc.c
+--- linux/net/atm/svc.c 20 Feb 2003 13:46:30 -0000 1.1.1.1
++++ linux/net/atm/svc.c 27 Feb 2003 16:48:54 -0000
+@@ -439,13 +439,19 @@
+ * Initialize the ATM SVC protocol family
+ */
+
+-static int __init atmsvc_init(void)
++int __init atmsvc_init(void)
+ {
+- if (sock_register(&svc_family_ops) < 0) {
+- printk(KERN_ERR "ATMSVC: can't register");
++ int error;
++
++ error = sock_register(&svc_family_ops);
++ if (error < 0) {
++ printk(KERN_ERR "ATMSVC: can't register (%d)\n", error);
+ return -1;
+ }
+ return 0;
+ }
+
+-module_init(atmsvc_init);
++void __exit atmsvc_exit(void)
++{
++ sock_unregister(PF_ATMSVC);
++}
+Index: linux/include/net/atmclip.h
+===================================================================
+RCS file: /home/chas/CVSROOT/linux/include/net/atmclip.h,v
+retrieving revision 1.1.1.1
+diff -u -r1.1.1.1 atmclip.h
+--- linux/include/net/atmclip.h 20 Feb 2003 13:45:58 -0000 1.1.1.1
++++ linux/include/net/atmclip.h 1 Mar 2003 13:59:31 -0000
+@@ -62,6 +62,16 @@
+ int clip_mkip(struct atm_vcc *vcc,int timeout);
+ int clip_setentry(struct atm_vcc *vcc,u32 ip);
+ int clip_encap(struct atm_vcc *vcc,int mode);
+-void atm_clip_init(void);
++
++struct atm_clip_ops {
++ int (*clip_create)(int number);
++ int (*clip_mkip)(struct atm_vcc *vcc,int timeout);
++ int (*clip_setentry)(struct atm_vcc *vcc,u32 ip);
++ int (*clip_encap)(struct atm_vcc *vcc,int mode);
++ void (*clip_push)(struct atm_vcc *vcc,struct sk_buff *skb);
++ int (*atm_init_atmarp)(struct atm_vcc *vcc);
++ struct neigh_table *clip_tbl;
++ struct module *owner;
++};
+
+ #endif
+--- linux-2.4.20/net/atm/clip.c.orig Wed Mar 5 22:54:57 2003
++++ linux-2.4.20/net/atm/clip.c Thu Mar 6 00:03:50 2003
+@@ -49,7 +49,13 @@
+ static struct timer_list idle_timer;
+ static int start_timer = 1;
+
+-#include "ipcommon.c"
++const unsigned char llc_oui[] = {
++ 0xaa, /* DSAP: non-ISO */
++ 0xaa, /* SSAP: non-ISO */
++ 0x03, /* Ctrl: Unnumbered Information Command PDU */
++ 0x00, /* OUI: EtherType */
++ 0x00,
++ 0x00 };
+
+ static int to_atmarpd(enum atmarp_ctrl_type type,int itf,unsigned long ip)
+ {
+--- linux-2.4.20/net/atm/ipcommon.c.orig Wed Mar 5 22:54:57 2003
++++ linux-2.4.20/net/atm/ipcommon.c Thu Mar 6 00:03:48 2003
+@@ -22,15 +22,6 @@
+ #endif
+
+
+-const unsigned char llc_oui[] = {
+- 0xaa, /* DSAP: non-ISO */
+- 0xaa, /* SSAP: non-ISO */
+- 0x03, /* Ctrl: Unnumbered Information Command PDU */
+- 0x00, /* OUI: EtherType */
+- 0x00,
+- 0x00 };
+-
+-
+ /*
+ * skb_migrate appends the list at "from" to "to", emptying "from" in the
+ * process. skb_migrate is atomic with respect to all other skb operations on
+@@ -65,3 +56,5 @@
+ from->qlen = 0;
+ spin_unlock_irqrestore(&from->lock,flags);
+ }
++
++EXPORT_SYMBOL(skb_migrate);
+--- linux-2.4.20/net/Config.in.orig Sat Aug 3 02:39:46 2002
++++ linux-2.4.20/net/Config.in Wed Mar 5 03:16:08 2003
+@@ -31,23 +31,13 @@
+ fi
+ fi
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+- bool 'Asynchronous Transfer Mode (ATM) (EXPERIMENTAL)' CONFIG_ATM
+- if [ "$CONFIG_ATM" = "y" ]; then
+- if [ "$CONFIG_INET" = "y" ]; then
+- bool ' Classical IP over ATM' CONFIG_ATM_CLIP
+- if [ "$CONFIG_ATM_CLIP" = "y" ]; then
+- bool ' Do NOT send ICMP if no neighbour' CONFIG_ATM_CLIP_NO_ICMP
+- fi
+- fi
+- tristate ' LAN Emulation (LANE) support' CONFIG_ATM_LANE
+- if [ "$CONFIG_INET" = "y" -a "$CONFIG_ATM_LANE" != "n" ]; then
+- tristate ' Multi-Protocol Over ATM (MPOA) support' CONFIG_ATM_MPOA
+- fi
+- tristate ' RFC1483/2684 Bridged protocols' CONFIG_ATM_BR2684
+- if [ "$CONFIG_ATM_BR2684" != "n" ]; then
+- bool ' Per-VC IP filter kludge' CONFIG_ATM_BR2684_IPFILTER
+- fi
+- fi
++ tristate 'Asynchronous Transfer Mode (ATM) (EXPERIMENTAL)' CONFIG_ATM
++ dep_tristate ' Classical IP over ATM' CONFIG_ATM_CLIP $CONFIG_ATM $CONFIG_INET
++ dep_mbool ' Do NOT send ICMP if no neighbour' CONFIG_ATM_CLIP_NO_ICMP $CONFIG_ATM_CLIP
++ dep_tristate ' LAN Emulation (LANE) support' CONFIG_ATM_LANE $CONFIG_ATM $CONFIG_INET
++ dep_tristate ' Multi-Protocol Over ATM (MPOA) support' CONFIG_ATM_MPOA $CONFIG_ATM_LANE
++ dep_tristate ' RFC1483/2684 Bridged protocols' CONFIG_ATM_BR2684 $CONFIG_ATM $CONFIG_INET
++ dep_mbool ' Per-VC IP filter kludge' CONFIG_ATM_BR2684_IPFILTER $CONFIG_ATM_BR2684
+ fi
+ tristate '802.1Q VLAN Support' CONFIG_VLAN_8021Q
+
+--- linux/arch/alpha/config.in.orig Wed Mar 5 22:24:02 2003
++++ linux/arch/alpha/config.in Wed Mar 5 22:24:10 2003
+@@ -344,7 +344,7 @@
+ bool 'Network device support' CONFIG_NETDEVICES
+ if [ "$CONFIG_NETDEVICES" = "y" ]; then
+ source drivers/net/Config.in
+- if [ "$CONFIG_ATM" = "y" ]; then
++ if [ "$CONFIG_ATM" != "n" ]; then
+ source drivers/atm/Config.in
+ fi
+ fi
+--- linux/arch/cris/config.in.orig Wed Mar 5 22:24:24 2003
++++ linux/arch/cris/config.in Wed Mar 5 22:24:31 2003
+@@ -198,7 +198,7 @@
+ bool 'Network device support' CONFIG_NETDEVICES
+ if [ "$CONFIG_NETDEVICES" = "y" ]; then
+ source drivers/net/Config.in
+- if [ "$CONFIG_ATM" = "y" ]; then
++ if [ "$CONFIG_ATM" != "n" ]; then
+ source drivers/atm/Config.in
+ fi
+ fi
+--- linux/arch/i386/config.in.orig Wed Mar 5 22:24:33 2003
++++ linux/arch/i386/config.in Wed Mar 5 22:24:39 2003
+@@ -406,7 +406,7 @@
+ bool 'Network device support' CONFIG_NETDEVICES
+ if [ "$CONFIG_NETDEVICES" = "y" ]; then
+ source drivers/net/Config.in
+- if [ "$CONFIG_ATM" = "y" ]; then
++ if [ "$CONFIG_ATM" != "n" ]; then
+ source drivers/atm/Config.in
+ fi
+ fi
+--- linux/arch/parisc/config.in.orig Wed Mar 5 22:25:34 2003
++++ linux/arch/parisc/config.in Wed Mar 5 22:25:40 2003
+@@ -144,7 +144,7 @@
+
+ if [ "$CONFIG_NETDEVICES" = "y" ]; then
+ source drivers/net/Config.in
+- if [ "$CONFIG_ATM" = "y" ]; then
++ if [ "$CONFIG_ATM" != "n" ]; then
+ source drivers/atm/Config.in
+ fi
+ fi
+--- linux/arch/ppc/config.in.orig Wed Mar 5 22:25:42 2003
++++ linux/arch/ppc/config.in Wed Mar 5 22:25:48 2003
+@@ -292,7 +292,7 @@
+ bool 'Network device support' CONFIG_NETDEVICES
+ if [ "$CONFIG_NETDEVICES" = "y" ]; then
+ source drivers/net/Config.in
+- if [ "$CONFIG_ATM" = "y" ]; then
++ if [ "$CONFIG_ATM" != "n" ]; then
+ source drivers/atm/Config.in
+ fi
+ fi
+--- linux/arch/ppc64/config.in.orig Wed Mar 5 22:25:50 2003
++++ linux/arch/ppc64/config.in Wed Mar 5 22:25:55 2003
+@@ -138,7 +138,7 @@
+ bool 'Network device support' CONFIG_NETDEVICES
+ if [ "$CONFIG_NETDEVICES" = "y" ]; then
+ source drivers/net/Config.in
+- if [ "$CONFIG_ATM" = "y" ]; then
++ if [ "$CONFIG_ATM" != "n" ]; then
+ source drivers/atm/Config.in
+ fi
+ fi
+--- linux/arch/sh/config.in.orig Wed Mar 5 22:26:08 2003
++++ linux/arch/sh/config.in Wed Mar 5 22:26:14 2003
+@@ -264,7 +264,7 @@
+ bool 'Network device support' CONFIG_NETDEVICES
+ if [ "$CONFIG_NETDEVICES" = "y" ]; then
+ source drivers/net/Config.in
+- if [ "$CONFIG_ATM" = "y" ]; then
++ if [ "$CONFIG_ATM" != "n" ]; then
+ source drivers/atm/Config.in
+ fi
+ fi
+--- linux/arch/sparc/config.in.orig Wed Mar 5 22:26:16 2003
++++ linux/arch/sparc/config.in Wed Mar 5 22:26:32 2003
+@@ -217,7 +217,7 @@
+ dep_tristate ' PPP BSD-Compress compression' CONFIG_PPP_BSDCOMP m
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ dep_tristate ' PPP over Ethernet (EXPERIMENTAL)' CONFIG_PPPOE $CONFIG_PPP
+- if [ "$CONFIG_ATM" = "y" ]; then
++ if [ "$CONFIG_ATM" != "n" ]; then
+ dep_tristate ' PPP over ATM (EXPERIMENTAL)' CONFIG_PPPOATM $CONFIG_PPP
+ fi
+ fi
+@@ -243,7 +243,7 @@
+ # if [ "$CONFIG_FDDI" = "y" ]; then
+ # fi
+
+- if [ "$CONFIG_ATM" = "y" ]; then
++ if [ "$CONFIG_ATM" != "n" ]; then
+ source drivers/atm/Config.in
+ fi
+ fi
+--- linux/arch/sparc64/config.in.orig Wed Mar 5 22:26:39 2003
++++ linux/arch/sparc64/config.in Wed Mar 5 22:26:45 2003
+@@ -242,7 +242,7 @@
+ bool 'Network device support' CONFIG_NETDEVICES
+ if [ "$CONFIG_NETDEVICES" = "y" ]; then
+ source drivers/net/Config.in
+- if [ "$CONFIG_ATM" = "y" ]; then
++ if [ "$CONFIG_ATM" != "n" ]; then
+ source drivers/atm/Config.in
+ fi
+ fi
+--- linux/arch/x86_64/config.in.orig Wed Mar 5 22:26:51 2003
++++ linux/arch/x86_64/config.in Wed Mar 5 22:27:11 2003
+@@ -173,7 +173,7 @@
+ if [ "$CONFIG_NETDEVICES" = "y" ]; then
+ source drivers/net/Config.in
+ # seems to be largely not 64bit safe
+-# if [ "$CONFIG_ATM" = "y" ]; then
++# if [ "$CONFIG_ATM" != "n" ]; then
+ # source drivers/atm/Config.in
+ # fi
+ fi
+--- linux/arch/mips/config-shared.in.orig Wed Mar 5 22:25:13 2003
++++ linux/arch/mips/config-shared.in Wed Mar 5 22:25:19 2003
+@@ -691,7 +691,7 @@
+ bool 'Network device support' CONFIG_NETDEVICES
+ if [ "$CONFIG_NETDEVICES" = "y" ]; then
+ source drivers/net/Config.in
+- if [ "$CONFIG_ATM" = "y" ]; then
++ if [ "$CONFIG_ATM" != "n" ]; then
+ source drivers/atm/Config.in
+ fi
+ fi
+-
+To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
+the body of a message to majordomo@vger.kernel.org
+More majordomo info at http://vger.kernel.org/majordomo-info.html
+Please read the FAQ at http://www.tux.org/lkml/
--- /dev/null
+init_etherdev() allocates and registers the network device in one
+step. it uses eth%d as the template for the device names. this
+conflicts with already registered ethernet devices, like eth0. since
+we want a fixed (and different name) this patch uses alloc_etherdev,
+rewrites the device name and then registers our interface.
+
+Index: linux/net/atm/lec.c
+===================================================================
+RCS file: /home/chas/CVSROOT/linux/net/atm/lec.c,v
+retrieving revision 1.12
+diff -u -r1.12 lec.c
+--- linux/net/atm/lec.c 3 Mar 2003 22:23:13 -0000 1.12
++++ linux/net/atm/lec.c 3 Mar 2003 22:29:02 -0000
+@@ -784,15 +784,19 @@
+ size = sizeof(struct lec_priv);
+ #ifdef CONFIG_TR
+ if (is_trdev)
+- dev_lec[i] = init_trdev(NULL, size);
++ dev_lec[i] = alloc_trdev(size);
+ else
+ #endif
+- dev_lec[i] = init_etherdev(NULL, size);
++ dev_lec[i] = alloc_etherdev(size);
+ if (!dev_lec[i])
+ return -ENOMEM;
++ snprintf(dev_lec[i]->name, IFNAMSIZ, "lec%d", i);
++ if (register_netdev(dev_lec[i])) {
++ kfree(dev_lec[i]);
++ return -EINVAL;
++ }
+ priv = dev_lec[i]->priv;
+ priv->is_trdev = is_trdev;
+- sprintf(dev_lec[i]->name, "lec%d", i);
+ lec_init(dev_lec[i]);
+ } else {
+ priv = dev_lec[i]->priv;
+-
+To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
+the body of a message to majordomo@vger.kernel.org
+More majordomo info at http://vger.kernel.org/majordomo-info.html
+Please read the FAQ at http://www.tux.org/lkml/
--- /dev/null
+According to chas williams:
+> >> so wherever clip_start_xmit() calls dev_free_skb() it should return 1
+> >> instead of 0?
+> >> did i follow that correctly...?
+>
+> i believe that you should only return non zero if you want the ip
+> stack to requeue the skb and try to send it again (perhaps your
+> driver was busy). in practice this is rarely done. if you drop
+> the skb and return 1 this is going to create trouble. check out
+> Documentation/networking/driver.txt. it looks like br2684 should
+> simple not return 1 at the end of br2684_xmit_vcc.
+
+But that's br2684, not clip... and I posted the same thing about
+a week ago ;)
+
+--- linux-2.4.21-pre4/net/atm/br2684.c.orig 2002-08-03 02:39:46.000000000 +0200
++++ linux-2.4.21-pre4/net/atm/br2684.c 2003-02-26 15:11:48.000000000 +0100
+@@ -219,17 +219,19 @@
+ /* netif_stop_queue(dev); */
+ dev_kfree_skb(skb);
+ read_unlock(&devs_lock);
+- return -EUNATCH;
++ /* Pretend we succeeded so the packet is dropped. */
++ return 0;
+ }
+ if (!br2684_xmit_vcc(skb, brdev, brvcc)) {
+ /*
+ * We should probably use netif_*_queue() here, but that
+ * involves added complication. We need to walk before
+ * we can run
++ *
++ * Note: at this point, skb is no longer valid.
++ * br2684_xmit_vcc() might have realloced or freed it.
++ * We don't need to worry about that.
+ */
+- /* don't free here! this pointer might be no longer valid!
+- dev_kfree_skb(skb);
+- */
+ brdev->stats.tx_errors++;
+ brdev->stats.tx_fifo_errors++;
+ }
+
+
+Mike.
+--
+Anyone who is capable of getting themselves made President should
+on no account be allowed to do the job -- Douglas Adams.
+
+
+-------------------------------------------------------
+This SF.net email is sponsored by: Etnus, makers of TotalView, The debugger
+for complex code. Debugging C/C++ programs can leave you feeling lost and
+disoriented. TotalView can help you find your way. Available on major UNIX
+and Linux platforms. Try it free. www.etnus.com
+_______________________________________________
+Linux-atm-general mailing list
+Linux-atm-general@lists.sourceforge.net
+https://lists.sourceforge.net/lists/listinfo/linux-atm-general
--- /dev/null
+it has been suggested that atm_vcc has redundant members with
+struct sock.
+
+this patch removes family, tx_inuse, rx_inuse, and recvq from
+atm_vcc in favor of sk->family, sk->wmem_alloc, sk->rmem_alloc,
+and sk->receive_queue (respectively). listenq and backlog_quota
+should be removed as well but i need to think about them for a
+bit. also, atm_dev now uses the list manipulation routines (converting
+the vccs lists is a bit more involved since some the atm drivers know
+too much)
+
+
+Index: linux/include/linux/atmdev.h
+===================================================================
+RCS file: /home/chas/CVSROOT/linux/include/linux/atmdev.h,v
+retrieving revision 1.2
+diff -u -r1.2 atmdev.h
+--- linux/include/linux/atmdev.h 20 Feb 2003 20:17:59 -0000 1.2
++++ linux/include/linux/atmdev.h 5 Mar 2003 00:05:46 -0000
+@@ -277,7 +277,6 @@
+
+ struct atm_vcc {
+ unsigned long flags; /* VCC flags (ATM_VF_*) */
+- unsigned char family; /* address family; 0 if unused */
+ short vpi; /* VPI and VCI (types must be equal */
+ /* with sockaddr) */
+ int vci;
+@@ -286,7 +285,6 @@
+ struct atm_dev *dev; /* device back pointer */
+ struct atm_qos qos; /* QOS */
+ struct atm_sap sap; /* SAP */
+- atomic_t tx_inuse,rx_inuse; /* buffer space in use */
+ void (*push)(struct atm_vcc *vcc,struct sk_buff *skb);
+ void (*pop)(struct atm_vcc *vcc,struct sk_buff *skb); /* optional */
+ struct sk_buff *(*alloc_tx)(struct atm_vcc *vcc,unsigned int size);
+@@ -297,7 +295,6 @@
+ int (*send)(struct atm_vcc *vcc,struct sk_buff *skb);
+ void *dev_data; /* per-device data */
+ void *proto_data; /* per-protocol data */
+- struct sk_buff_head recvq; /* receive queue */
+ struct k_atm_aal_stats *stats; /* pointer to AAL stats group */
+ wait_queue_head_t sleep; /* if socket is busy */
+ struct sock *sk; /* socket backpointer */
+@@ -346,7 +343,7 @@
+ struct proc_dir_entry *proc_entry; /* proc entry */
+ char *proc_name; /* proc entry name */
+ #endif
+- struct atm_dev *prev,*next; /* linkage */
++ struct list_head dev_list; /* linkage */
+ };
+
+
+@@ -425,19 +422,19 @@
+
+ static __inline__ void atm_force_charge(struct atm_vcc *vcc,int truesize)
+ {
+- atomic_add(truesize+ATM_PDU_OVHD,&vcc->rx_inuse);
++ atomic_add(truesize+ATM_PDU_OVHD,&vcc->sk->rmem_alloc);
+ }
+
+
+ static __inline__ void atm_return(struct atm_vcc *vcc,int truesize)
+ {
+- atomic_sub(truesize+ATM_PDU_OVHD,&vcc->rx_inuse);
++ atomic_sub(truesize+ATM_PDU_OVHD,&vcc->sk->rmem_alloc);
+ }
+
+
+ static __inline__ int atm_may_send(struct atm_vcc *vcc,unsigned int size)
+ {
+- return size+atomic_read(&vcc->tx_inuse)+ATM_PDU_OVHD < vcc->sk->sndbuf;
++ return size+atomic_read(&vcc->sk->wmem_alloc)+ATM_PDU_OVHD < vcc->sk->sndbuf;
+ }
+
+
+Index: linux/net/atm/atm_misc.c
+===================================================================
+RCS file: /home/chas/CVSROOT/linux/net/atm/atm_misc.c,v
+retrieving revision 1.1.1.1
+diff -u -r1.1.1.1 atm_misc.c
+--- linux/net/atm/atm_misc.c 20 Feb 2003 13:46:30 -0000 1.1.1.1
++++ linux/net/atm/atm_misc.c 4 Mar 2003 23:29:50 -0000
+@@ -16,7 +16,7 @@
+ int atm_charge(struct atm_vcc *vcc,int truesize)
+ {
+ atm_force_charge(vcc,truesize);
+- if (atomic_read(&vcc->rx_inuse) <= vcc->sk->rcvbuf) return 1;
++ if (atomic_read(&vcc->sk->rmem_alloc) <= vcc->sk->rcvbuf) return 1;
+ atm_return(vcc,truesize);
+ atomic_inc(&vcc->stats->rx_drop);
+ return 0;
+@@ -29,11 +29,11 @@
+ int guess = atm_guess_pdu2truesize(pdu_size);
+
+ atm_force_charge(vcc,guess);
+- if (atomic_read(&vcc->rx_inuse) <= vcc->sk->rcvbuf) {
++ if (atomic_read(&vcc->sk->rmem_alloc) <= vcc->sk->rcvbuf) {
+ struct sk_buff *skb = alloc_skb(pdu_size,gfp_flags);
+
+ if (skb) {
+- atomic_add(skb->truesize-guess,&vcc->rx_inuse);
++ atomic_add(skb->truesize-guess,&vcc->sk->rmem_alloc);
+ return skb;
+ }
+ }
+Index: linux/net/atm/clip.c
+===================================================================
+RCS file: /home/chas/CVSROOT/linux/net/atm/clip.c,v
+retrieving revision 1.3
+diff -u -r1.3 clip.c
+--- linux/net/atm/clip.c 4 Mar 2003 20:48:04 -0000 1.3
++++ linux/net/atm/clip.c 4 Mar 2003 23:45:45 -0000
+@@ -63,7 +63,7 @@
+ ctrl->itf_num = itf;
+ ctrl->ip = ip;
+ atm_force_charge(atmarpd,skb->truesize);
+- skb_queue_tail(&atmarpd->recvq,skb);
++ skb_queue_tail(&atmarpd->sk->receive_queue,skb);
+ wake_up(&atmarpd->sleep);
+ return 0;
+ }
+@@ -426,7 +426,7 @@
+ memcpy(here,llc_oui,sizeof(llc_oui));
+ ((u16 *) here)[3] = skb->protocol;
+ }
+- atomic_add(skb->truesize,&vcc->tx_inuse);
++ atomic_add(skb->truesize,&vcc->sk->wmem_alloc);
+ ATM_SKB(skb)->iovcnt = 0;
+ ATM_SKB(skb)->atm_options = vcc->atm_options;
+ entry->vccs->last_use = jiffies;
+@@ -485,7 +485,7 @@
+ vcc->push = clip_push;
+ vcc->pop = clip_pop;
+ skb_queue_head_init(©);
+- skb_migrate(&vcc->recvq,©);
++ skb_migrate(&vcc->sk->receive_queue,©);
+ /* re-process everything received between connection setup and MKIP */
+ while ((skb = skb_dequeue(©)))
+ if (!clip_devs) {
+@@ -691,10 +691,10 @@
+ barrier();
+ unregister_inetaddr_notifier(&clip_inet_notifier);
+ unregister_netdevice_notifier(&clip_dev_notifier);
+- if (skb_peek(&vcc->recvq))
++ if (skb_peek(&vcc->sk->receive_queue))
+ printk(KERN_ERR "atmarpd_close: closing with requests "
+ "pending\n");
+- skb_queue_purge(&vcc->recvq);
++ skb_queue_purge(&vcc->sk->receive_queue);
+ DPRINTK("(done)\n");
+ module_put(THIS_MODULE);
+ }
+Index: linux/net/atm/common.c
+===================================================================
+RCS file: /home/chas/CVSROOT/linux/net/atm/common.c,v
+retrieving revision 1.6
+diff -u -r1.6 common.c
+--- linux/net/atm/common.c 3 Mar 2003 22:23:13 -0000 1.6
++++ linux/net/atm/common.c 5 Mar 2003 00:15:53 -0000
+@@ -91,14 +91,14 @@
+ {
+ struct sk_buff *skb;
+
+- if (atomic_read(&vcc->tx_inuse) && !atm_may_send(vcc,size)) {
+- DPRINTK("Sorry: tx_inuse = %d, size = %d, sndbuf = %d\n",
+- atomic_read(&vcc->tx_inuse),size,vcc->sk->sndbuf);
++ if (atomic_read(&vcc->sk->wmem_alloc) && !atm_may_send(vcc,size)) {
++ DPRINTK("Sorry: wmem_alloc = %d, size = %d, sndbuf = %d\n",
++ atomic_read(&vcc->sk->wmem_alloc),size,vcc->sk->sndbuf);
+ return NULL;
+ }
+ while (!(skb = alloc_skb(size,GFP_KERNEL))) schedule();
+- DPRINTK("AlTx %d += %d\n",atomic_read(&vcc->tx_inuse),skb->truesize);
+- atomic_add(skb->truesize+ATM_PDU_OVHD,&vcc->tx_inuse);
++ DPRINTK("AlTx %d += %d\n",atomic_read(&vcc->sk->wmem_alloc),skb->truesize);
++ atomic_add(skb->truesize+ATM_PDU_OVHD,&vcc->sk->wmem_alloc);
+ return skb;
+ }
+
+@@ -114,21 +114,19 @@
+ vcc = atm_sk(sk);
+ memset(&vcc->flags,0,sizeof(vcc->flags));
+ vcc->dev = NULL;
+- vcc->family = sock->ops->family;
+ vcc->alloc_tx = alloc_tx;
+ vcc->callback = NULL;
+ memset(&vcc->local,0,sizeof(struct sockaddr_atmsvc));
+ memset(&vcc->remote,0,sizeof(struct sockaddr_atmsvc));
+ vcc->qos.txtp.max_sdu = 1 << 16; /* for meta VCs */
+- atomic_set(&vcc->tx_inuse,0);
+- atomic_set(&vcc->rx_inuse,0);
++ atomic_set(&vcc->sk->wmem_alloc,0);
++ atomic_set(&vcc->sk->rmem_alloc,0);
+ vcc->push = NULL;
+ vcc->pop = NULL;
+ vcc->push_oam = NULL;
+ vcc->vpi = vcc->vci = 0; /* no VCI/VPI yet */
+ vcc->atm_options = vcc->aal_options = 0;
+ init_waitqueue_head(&vcc->sleep);
+- skb_queue_head_init(&vcc->recvq);
+ skb_queue_head_init(&vcc->listenq);
+ sk->sleep = &vcc->sleep;
+ sock->sk = sk;
+@@ -145,7 +143,7 @@
+ if (vcc->dev) {
+ if (vcc->dev->ops->close) vcc->dev->ops->close(vcc);
+ if (vcc->push) vcc->push(vcc,NULL); /* atmarpd has no push */
+- while ((skb = skb_dequeue(&vcc->recvq))) {
++ while ((skb = skb_dequeue(&vcc->sk->receive_queue))) {
+ atm_return(vcc,skb->truesize);
+ if (vcc->dev->ops->free_rx_skb)
+ vcc->dev->ops->free_rx_skb(vcc,skb);
+@@ -153,10 +151,10 @@
+ }
+ down(&atm_dev_sem);
+ fops_put (vcc->dev->ops);
+- if (atomic_read(&vcc->rx_inuse))
++ if (atomic_read(&vcc->sk->rmem_alloc))
+ printk(KERN_WARNING "atm_release_vcc: strange ... "
+- "rx_inuse == %d after closing\n",
+- atomic_read(&vcc->rx_inuse));
++ "rmem_alloc == %d after closing\n",
++ atomic_read(&vcc->sk->rmem_alloc));
+ bind_vcc(vcc,NULL);
+ } else
+ down(&atm_dev_sem);
+@@ -311,11 +309,15 @@
+ if (error) return error;
+ }
+ else {
+- struct atm_dev *dev;
++ struct atm_dev *dev = NULL;
++ struct list_head *p;
+
+ down(&atm_dev_sem);
+- for (dev = atm_devs; dev; dev = dev->next)
++ list_for_each(p, &atm_devs) {
++ dev = list_entry(p, struct atm_dev, dev_list);
+ if (!atm_do_connect_dev(vcc,dev,vpi,vci)) break;
++ dev = NULL;
++ }
+ up(&atm_dev_sem);
+ if (!dev) return -ENODEV;
+ }
+@@ -360,7 +362,7 @@
+ add_wait_queue(&vcc->sleep,&wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+ error = 1; /* <= 0 is error */
+- while (!(skb = skb_dequeue(&vcc->recvq))) {
++ while (!(skb = skb_dequeue(&vcc->sk->receive_queue))) {
+ if (test_bit(ATM_VF_RELEASED,&vcc->flags) ||
+ test_bit(ATM_VF_CLOSE,&vcc->flags)) {
+ error = vcc->reply;
+@@ -391,7 +393,7 @@
+ if (vcc->dev->ops->feedback)
+ vcc->dev->ops->feedback(vcc,skb,(unsigned long) skb->data,
+ (unsigned long) buff,eff_len);
+- DPRINTK("RcvM %d -= %d\n",atomic_read(&vcc->rx_inuse),skb->truesize);
++ DPRINTK("RcvM %d -= %d\n",atomic_read(&vcc->sk->rmem_alloc),skb->truesize);
+ atm_return(vcc,skb->truesize);
+ if (ATM_SKB(skb)->iovcnt) { /* @@@ hack */
+ /* iovcnt set, use scatter-gather for receive */
+@@ -494,14 +496,14 @@
+ vcc = ATM_SD(sock);
+ poll_wait(file,&vcc->sleep,wait);
+ mask = 0;
+- if (skb_peek(&vcc->recvq) || skb_peek(&vcc->listenq))
++ if (skb_peek(&vcc->sk->receive_queue) || skb_peek(&vcc->listenq))
+ mask |= POLLIN | POLLRDNORM;
+ if (test_bit(ATM_VF_RELEASED,&vcc->flags) ||
+ test_bit(ATM_VF_CLOSE,&vcc->flags))
+ mask |= POLLHUP;
+ if (sock->state != SS_CONNECTING) {
+ if (vcc->qos.txtp.traffic_class != ATM_NONE &&
+- vcc->qos.txtp.max_sdu+atomic_read(&vcc->tx_inuse)+
++ vcc->qos.txtp.max_sdu+atomic_read(&vcc->sk->wmem_alloc)+
+ ATM_PDU_OVHD <= vcc->sk->sndbuf)
+ mask |= POLLOUT | POLLWRNORM;
+ }
+@@ -552,6 +554,7 @@
+ int atm_ioctl(struct socket *sock,unsigned int cmd,unsigned long arg)
+ {
+ struct atm_dev *dev;
++ struct list_head *p;
+ struct atm_vcc *vcc;
+ int *tmp_buf, *tmp_p;
+ void *buf;
+@@ -568,7 +571,7 @@
+ goto done;
+ }
+ ret_val = put_user(vcc->sk->sndbuf-
+- atomic_read(&vcc->tx_inuse)-ATM_PDU_OVHD,
++ atomic_read(&vcc->sk->wmem_alloc)-ATM_PDU_OVHD,
+ (int *) arg) ? -EFAULT : 0;
+ goto done;
+ case SIOCINQ:
+@@ -579,7 +582,7 @@
+ ret_val = -EINVAL;
+ goto done;
+ }
+- skb = skb_peek(&vcc->recvq);
++ skb = skb_peek(&vcc->sk->receive_queue);
+ ret_val = put_user(skb ? skb->len : 0,(int *) arg)
+ ? -EFAULT : 0;
+ goto done;
+@@ -596,7 +599,7 @@
+ goto done;
+ }
+ size = 0;
+- for (dev = atm_devs; dev; dev = dev->next)
++ list_for_each(p, &atm_devs)
+ size += sizeof(int);
+ if (size > len) {
+ ret_val = -E2BIG;
+@@ -608,8 +611,10 @@
+ goto done;
+ }
+ tmp_p = tmp_buf;
+- for (dev = atm_devs; dev; dev = dev->next)
++ list_for_each(p, &atm_devs) {
++ dev = list_entry(p, struct atm_dev, dev_list);
+ *tmp_p++ = dev->number;
++ }
+ ret_val = ((copy_to_user(buf, tmp_buf, size)) ||
+ put_user(size, &((struct atm_iobuf *) arg)->length)
+ ) ? -EFAULT : 0;
+@@ -990,7 +995,7 @@
+ if (!error) error = adjust_tp(&qos->rxtp,qos->aal);
+ if (error) return error;
+ if (!vcc->dev->ops->change_qos) return -EOPNOTSUPP;
+- if (vcc->family == AF_ATMPVC)
++ if (vcc->sk->family == AF_ATMPVC)
+ return vcc->dev->ops->change_qos(vcc,qos,ATM_MF_SET);
+ return svc_change_qos(vcc,qos);
+ }
+Index: linux/net/atm/lec.c
+===================================================================
+RCS file: /home/chas/CVSROOT/linux/net/atm/lec.c,v
+retrieving revision 1.14
+diff -u -r1.14 lec.c
+--- linux/net/atm/lec.c 4 Mar 2003 20:48:28 -0000 1.14
++++ linux/net/atm/lec.c 4 Mar 2003 23:42:21 -0000
+@@ -125,7 +125,7 @@
+
+ priv = (struct lec_priv *)dev->priv;
+ atm_force_charge(priv->lecd, skb2->truesize);
+- skb_queue_tail(&priv->lecd->recvq, skb2);
++ skb_queue_tail(&priv->lecd->sk->receive_queue, skb2);
+ wake_up(&priv->lecd->sleep);
+ }
+
+@@ -202,7 +202,7 @@
+ lec_send(struct atm_vcc *vcc, struct sk_buff *skb, struct lec_priv *priv)
+ {
+ if (atm_may_send(vcc, skb->len)) {
+- atomic_add(skb->truesize, &vcc->tx_inuse);
++ atomic_add(skb->truesize, &vcc->sk->wmem_alloc);
+ ATM_SKB(skb)->vcc = vcc;
+ ATM_SKB(skb)->iovcnt = 0;
+ ATM_SKB(skb)->atm_options = vcc->atm_options;
+@@ -399,7 +399,7 @@
+ int i;
+ char *tmp; /* FIXME */
+
+- atomic_sub(skb->truesize+ATM_PDU_OVHD, &vcc->tx_inuse);
++ atomic_sub(skb->truesize+ATM_PDU_OVHD, &vcc->sk->wmem_alloc);
+ mesg = (struct atmlec_msg *)skb->data;
+ tmp = skb->data;
+ tmp += sizeof(struct atmlec_msg);
+@@ -505,7 +505,7 @@
+ skb2->len = sizeof(struct atmlec_msg);
+ memcpy(skb2->data, mesg, sizeof(struct atmlec_msg));
+ atm_force_charge(priv->lecd, skb2->truesize);
+- skb_queue_tail(&priv->lecd->recvq, skb2);
++ skb_queue_tail(&priv->lecd->sk->receive_queue, skb2);
+ wake_up(&priv->lecd->sleep);
+ }
+ if (f != NULL) br_fdb_put_hook(f);
+@@ -534,10 +534,10 @@
+ netif_stop_queue(dev);
+ lec_arp_destroy(priv);
+
+- if (skb_peek(&vcc->recvq))
++ if (skb_peek(&vcc->sk->receive_queue))
+ printk("%s lec_atm_close: closing with messages pending\n",
+ dev->name);
+- while ((skb = skb_dequeue(&vcc->recvq))) {
++ while ((skb = skb_dequeue(&vcc->sk->receive_queue))) {
+ atm_return(vcc, skb->truesize);
+ dev_kfree_skb(skb);
+ }
+@@ -595,13 +595,13 @@
+ memcpy(&mesg->content.normal.atm_addr, atm_addr, ATM_ESA_LEN);
+
+ atm_force_charge(priv->lecd, skb->truesize);
+- skb_queue_tail(&priv->lecd->recvq, skb);
++ skb_queue_tail(&priv->lecd->sk->receive_queue, skb);
+ wake_up(&priv->lecd->sleep);
+
+ if (data != NULL) {
+ DPRINTK("lec: about to send %d bytes of data\n", data->len);
+ atm_force_charge(priv->lecd, data->truesize);
+- skb_queue_tail(&priv->lecd->recvq, data);
++ skb_queue_tail(&priv->lecd->sk->receive_queue, data);
+ wake_up(&priv->lecd->sleep);
+ }
+
+@@ -683,7 +683,7 @@
+ #endif /* DUMP_PACKETS > 0 */
+ if (memcmp(skb->data, lec_ctrl_magic, 4) ==0) { /* Control frame, to daemon*/
+ DPRINTK("%s: To daemon\n",dev->name);
+- skb_queue_tail(&vcc->recvq, skb);
++ skb_queue_tail(&vcc->sk->receive_queue, skb);
+ wake_up(&vcc->sleep);
+ } else { /* Data frame, queue to protocol handlers */
+ unsigned char *dst;
+Index: linux/net/atm/mpc.c
+===================================================================
+RCS file: /home/chas/CVSROOT/linux/net/atm/mpc.c,v
+retrieving revision 1.4
+diff -u -r1.4 mpc.c
+--- linux/net/atm/mpc.c 3 Mar 2003 22:23:13 -0000 1.4
++++ linux/net/atm/mpc.c 4 Mar 2003 23:42:27 -0000
+@@ -520,7 +520,7 @@
+ memcpy(skb->data, &llc_snap_mpoa_data, sizeof(struct llc_snap_hdr));
+ }
+
+- atomic_add(skb->truesize, &entry->shortcut->tx_inuse);
++ atomic_add(skb->truesize, &entry->shortcut->sk->wmem_alloc);
+ ATM_SKB(skb)->iovcnt = 0; /* just to be safe ... */
+ ATM_SKB(skb)->atm_options = entry->shortcut->atm_options;
+ entry->shortcut->send(entry->shortcut, skb);
+@@ -665,7 +665,7 @@
+ skb->dev = dev;
+ if (memcmp(skb->data, &llc_snap_mpoa_ctrl, sizeof(struct llc_snap_hdr)) == 0) {
+ dprintk("mpoa: (%s) mpc_push: control packet arrived\n", dev->name);
+- skb_queue_tail(&vcc->recvq, skb); /* Pass control packets to daemon */
++ skb_queue_tail(&vcc->sk->receive_queue, skb); /* Pass control packets to daemon */
+ wake_up(&vcc->sleep);
+ return;
+ }
+@@ -840,7 +840,7 @@
+ mpc->in_ops->destroy_cache(mpc);
+ mpc->eg_ops->destroy_cache(mpc);
+
+- while ( (skb = skb_dequeue(&vcc->recvq)) ){
++ while ( (skb = skb_dequeue(&vcc->sk->receive_queue)) ){
+ atm_return(vcc, skb->truesize);
+ kfree_skb(skb);
+ }
+@@ -860,7 +860,7 @@
+
+ struct mpoa_client *mpc = find_mpc_by_vcc(vcc);
+ struct k_message *mesg = (struct k_message*)skb->data;
+- atomic_sub(skb->truesize+ATM_PDU_OVHD, &vcc->tx_inuse);
++ atomic_sub(skb->truesize+ATM_PDU_OVHD, &vcc->sk->wmem_alloc);
+
+ if (mpc == NULL) {
+ printk("mpoa: msg_from_mpoad: no mpc found\n");
+@@ -937,7 +937,7 @@
+ skb_put(skb, sizeof(struct k_message));
+ memcpy(skb->data, mesg, sizeof(struct k_message));
+ atm_force_charge(mpc->mpoad_vcc, skb->truesize);
+- skb_queue_tail(&mpc->mpoad_vcc->recvq, skb);
++ skb_queue_tail(&mpc->mpoad_vcc->sk->receive_queue, skb);
+ wake_up(&mpc->mpoad_vcc->sleep);
+
+ return 0;
+@@ -1214,7 +1214,7 @@
+ purge_msg->content.eg_info = entry->ctrl_info;
+
+ atm_force_charge(vcc, skb->truesize);
+- skb_queue_tail(&vcc->recvq, skb);
++ skb_queue_tail(&vcc->sk->receive_queue, skb);
+ wake_up(&vcc->sleep);
+ dprintk("mpoa: purge_egress_shortcut: exiting:\n");
+
+Index: linux/net/atm/proc.c
+===================================================================
+RCS file: /home/chas/CVSROOT/linux/net/atm/proc.c,v
+retrieving revision 1.3
+diff -u -r1.3 proc.c
+--- linux/net/atm/proc.c 3 Mar 2003 22:23:13 -0000 1.3
++++ linux/net/atm/proc.c 5 Mar 2003 00:19:32 -0000
+@@ -134,7 +134,7 @@
+ unsigned char *ip;
+ int svc,off,ip_len;
+
+- svc = !clip_vcc || clip_vcc->vcc->family == AF_ATMSVC;
++ svc = !clip_vcc || clip_vcc->vcc->sk->family == AF_ATMSVC;
+ off = sprintf(buf,"%-6s%-4s%-4s%5ld ",dev->name,svc ? "SVC" : "PVC",
+ !clip_vcc || clip_vcc->encap ? "LLC" : "NULL",
+ (jiffies-(clip_vcc ? clip_vcc->last_use : entry->neigh->used))/
+@@ -209,7 +209,7 @@
+ if (!vcc->dev) here += sprintf(here,"Unassigned ");
+ else here += sprintf(here,"%3d %3d %5d ",vcc->dev->number,vcc->vpi,
+ vcc->vci);
+- switch (vcc->family) {
++ switch (vcc->sk->family) {
+ case AF_ATMPVC:
+ here += sprintf(here,"PVC");
+ break;
+@@ -217,12 +217,12 @@
+ here += sprintf(here,"SVC");
+ break;
+ default:
+- here += sprintf(here,"%3d",vcc->family);
++ here += sprintf(here,"%3d",vcc->sk->family);
+ }
+ here += sprintf(here," %04lx %5d %7d/%7d %7d/%7d\n",vcc->flags,
+ vcc->reply,
+- atomic_read(&vcc->tx_inuse),vcc->sk->sndbuf,
+- atomic_read(&vcc->rx_inuse),vcc->sk->rcvbuf);
++ atomic_read(&vcc->sk->wmem_alloc),vcc->sk->sndbuf,
++ atomic_read(&vcc->sk->rmem_alloc),vcc->sk->rcvbuf);
+ }
+
+
+@@ -302,6 +302,7 @@
+ static int atm_devices_info(loff_t pos,char *buf)
+ {
+ struct atm_dev *dev;
++ struct list_head *p;
+ int left;
+
+ if (!pos) {
+@@ -309,10 +310,14 @@
+ "AAL(TX,err,RX,err,drop) ...\n");
+ }
+ left = pos-1;
+- for (dev = atm_devs; dev && left; dev = dev->next) left--;
+- if (!dev) return 0;
+- dev_info(dev,buf);
+- return strlen(buf);
++ list_for_each(p, &atm_devs) {
++ dev = list_entry(p, struct atm_dev, dev_list);
++ if (left-- == 0) {
++ dev_info(dev,buf);
++ return strlen(buf);
++ }
++ }
++ return 0;
+ }
+
+ /*
+@@ -323,6 +328,7 @@
+ static int atm_pvc_info(loff_t pos,char *buf)
+ {
+ struct atm_dev *dev;
++ struct list_head *p;
+ struct atm_vcc *vcc;
+ int left;
+
+@@ -331,13 +337,15 @@
+ "TX(PCR,Class)\n");
+ }
+ left = pos-1;
+- for (dev = atm_devs; dev; dev = dev->next)
++ list_for_each(p, &atm_devs) {
++ dev = list_entry(p, struct atm_dev, dev_list);
+ for (vcc = dev->vccs; vcc; vcc = vcc->next)
+- if (vcc->family == PF_ATMPVC &&
++ if (vcc->sk->family == PF_ATMPVC &&
+ vcc->dev && !left--) {
+ pvc_info(vcc,buf);
+ return strlen(buf);
+ }
++ }
+ return 0;
+ }
+
+@@ -345,6 +353,7 @@
+ static int atm_vc_info(loff_t pos,char *buf)
+ {
+ struct atm_dev *dev;
++ struct list_head *p;
+ struct atm_vcc *vcc;
+ int left;
+
+@@ -353,12 +362,14 @@
+ "Address"," Itf VPI VCI Fam Flags Reply Send buffer"
+ " Recv buffer\n");
+ left = pos-1;
+- for (dev = atm_devs; dev; dev = dev->next)
++ list_for_each(p, &atm_devs) {
++ dev = list_entry(p, struct atm_dev, dev_list);
+ for (vcc = dev->vccs; vcc; vcc = vcc->next)
+ if (!left--) {
+ vc_info(vcc,buf);
+ return strlen(buf);
+ }
++ }
+ for (vcc = nodev_vccs; vcc; vcc = vcc->next)
+ if (!left--) {
+ vc_info(vcc,buf);
+@@ -372,20 +383,23 @@
+ static int atm_svc_info(loff_t pos,char *buf)
+ {
+ struct atm_dev *dev;
++ struct list_head *p;
+ struct atm_vcc *vcc;
+ int left;
+
+ if (!pos)
+ return sprintf(buf,"Itf VPI VCI State Remote\n");
+ left = pos-1;
+- for (dev = atm_devs; dev; dev = dev->next)
++ list_for_each(p, &atm_devs) {
++ dev = list_entry(p, struct atm_dev, dev_list);
+ for (vcc = dev->vccs; vcc; vcc = vcc->next)
+- if (vcc->family == PF_ATMSVC && !left--) {
++ if (vcc->sk->family == PF_ATMSVC && !left--) {
+ svc_info(vcc,buf);
+ return strlen(buf);
+ }
++ }
+ for (vcc = nodev_vccs; vcc; vcc = vcc->next)
+- if (vcc->family == PF_ATMSVC && !left--) {
++ if (vcc->sk->family == PF_ATMSVC && !left--) {
+ svc_info(vcc,buf);
+ return strlen(buf);
+ }
+Index: linux/net/atm/raw.c
+===================================================================
+RCS file: /home/chas/CVSROOT/linux/net/atm/raw.c,v
+retrieving revision 1.1.1.1
+diff -u -r1.1.1.1 raw.c
+--- linux/net/atm/raw.c 20 Feb 2003 13:46:30 -0000 1.1.1.1
++++ linux/net/atm/raw.c 4 Mar 2003 23:42:30 -0000
+@@ -28,7 +28,7 @@
+ void atm_push_raw(struct atm_vcc *vcc,struct sk_buff *skb)
+ {
+ if (skb) {
+- skb_queue_tail(&vcc->recvq,skb);
++ skb_queue_tail(&vcc->sk->receive_queue,skb);
+ wake_up(&vcc->sleep);
+ }
+ }
+@@ -36,8 +36,8 @@
+
+ static void atm_pop_raw(struct atm_vcc *vcc,struct sk_buff *skb)
+ {
+- DPRINTK("APopR (%d) %d -= %d\n",vcc->vci,vcc->tx_inuse,skb->truesize);
+- atomic_sub(skb->truesize+ATM_PDU_OVHD,&vcc->tx_inuse);
++ DPRINTK("APopR (%d) %d -= %d\n",vcc->vci,vcc->sk->wmem_alloc,skb->truesize);
++ atomic_sub(skb->truesize+ATM_PDU_OVHD,&vcc->sk->wmem_alloc);
+ dev_kfree_skb_any(skb);
+ wake_up(&vcc->sleep);
+ }
+Index: linux/net/atm/resources.c
+===================================================================
+RCS file: /home/chas/CVSROOT/linux/net/atm/resources.c,v
+retrieving revision 1.2
+diff -u -r1.2 resources.c
+--- linux/net/atm/resources.c 25 Feb 2003 20:03:53 -0000 1.2
++++ linux/net/atm/resources.c 5 Mar 2003 00:10:01 -0000
+@@ -26,8 +26,7 @@
+ #endif
+
+
+-struct atm_dev *atm_devs = NULL;
+-static struct atm_dev *last_dev = NULL;
++LIST_HEAD(atm_devs);
+ struct atm_vcc *nodev_vccs = NULL;
+ extern struct semaphore atm_dev_sem;
+
+@@ -43,15 +42,7 @@
+ dev->type = type;
+ dev->signal = ATM_PHY_SIG_UNKNOWN;
+ dev->link_rate = ATM_OC3_PCR;
+- dev->next = NULL;
+-
+- dev->prev = last_dev;
+-
+- if (atm_devs)
+- last_dev->next = dev;
+- else
+- atm_devs = dev;
+- last_dev = dev;
++ list_add_tail(&dev->dev_list, &atm_devs);
+
+ return dev;
+ }
+@@ -59,14 +50,7 @@
+ /* Caller must hold atm_dev_sem. */
+ static void __free_atm_dev(struct atm_dev *dev)
+ {
+- if (dev->prev)
+- dev->prev->next = dev->next;
+- else
+- atm_devs = dev->next;
+- if (dev->next)
+- dev->next->prev = dev->prev;
+- else
+- last_dev = dev->prev;
++ list_del(&dev->dev_list);
+ kfree(dev);
+ }
+
+@@ -74,10 +58,13 @@
+ struct atm_dev *atm_find_dev(int number)
+ {
+ struct atm_dev *dev;
++ struct list_head *p;
+
+- for (dev = atm_devs; dev; dev = dev->next)
++ list_for_each(p, &atm_devs) {
++ dev = list_entry(p, struct atm_dev, dev_list);
+ if (dev->ops && dev->number == number)
+ return dev;
++ }
+ return NULL;
+ }
+
+Index: linux/net/atm/resources.h
+===================================================================
+RCS file: /home/chas/CVSROOT/linux/net/atm/resources.h,v
+retrieving revision 1.1.1.1
+diff -u -r1.1.1.1 resources.h
+--- linux/net/atm/resources.h 20 Feb 2003 13:46:30 -0000 1.1.1.1
++++ linux/net/atm/resources.h 5 Mar 2003 00:11:51 -0000
+@@ -10,7 +10,7 @@
+ #include <linux/atmdev.h>
+
+
+-extern struct atm_dev *atm_devs;
++extern struct list_head atm_devs;
+ extern struct atm_vcc *nodev_vccs; /* VCCs not linked to any device */
+
+
+Index: linux/net/atm/signaling.c
+===================================================================
+RCS file: /home/chas/CVSROOT/linux/net/atm/signaling.c,v
+retrieving revision 1.2
+diff -u -r1.2 signaling.c
+--- linux/net/atm/signaling.c 25 Feb 2003 20:03:53 -0000 1.2
++++ linux/net/atm/signaling.c 5 Mar 2003 00:21:45 -0000
+@@ -61,7 +61,7 @@
+ }
+ #endif
+ atm_force_charge(sigd,skb->truesize);
+- skb_queue_tail(&sigd->recvq,skb);
++ skb_queue_tail(&sigd->sk->receive_queue,skb);
+ wake_up(&sigd->sleep);
+ }
+
+@@ -98,7 +98,7 @@
+ struct atm_vcc *session_vcc;
+
+ msg = (struct atmsvc_msg *) skb->data;
+- atomic_sub(skb->truesize+ATM_PDU_OVHD,&vcc->tx_inuse);
++ atomic_sub(skb->truesize+ATM_PDU_OVHD,&vcc->sk->wmem_alloc);
+ DPRINTK("sigd_send %d (0x%lx)\n",(int) msg->type,
+ (unsigned long) msg->vcc);
+ vcc = *(struct atm_vcc **) &msg->vcc;
+@@ -198,7 +198,7 @@
+ static void purge_vccs(struct atm_vcc *vcc)
+ {
+ while (vcc) {
+- if (vcc->family == PF_ATMSVC &&
++ if (vcc->sk->family == PF_ATMSVC &&
+ !test_bit(ATM_VF_META,&vcc->flags)) {
+ set_bit(ATM_VF_RELEASED,&vcc->flags);
+ vcc->reply = -EUNATCH;
+@@ -212,16 +212,20 @@
+ static void sigd_close(struct atm_vcc *vcc)
+ {
+ struct atm_dev *dev;
++ struct list_head *p;
+
+ DPRINTK("sigd_close\n");
+ sigd = NULL;
+- if (skb_peek(&vcc->recvq))
++ if (skb_peek(&vcc->sk->receive_queue))
+ printk(KERN_ERR "sigd_close: closing with requests pending\n");
+- skb_queue_purge(&vcc->recvq);
++ skb_queue_purge(&vcc->sk->receive_queue);
+ purge_vccs(nodev_vccs);
+
+ down(&atm_dev_sem);
+- for (dev = atm_devs; dev; dev = dev->next) purge_vccs(dev->vccs);
++ list_for_each(p, &atm_devs) {
++ dev = list_entry(p, struct atm_dev, dev_list);
++ purge_vccs(dev->vccs);
++ }
+ up(&atm_dev_sem);
+ }
+
+--- linux-2.4.20/drivers/atm/idt77252.c.orig Fri Dec 21 18:41:53 2001
++++ linux-2.4.20/drivers/atm/idt77252.c Wed Mar 5 20:27:00 2003
+@@ -730,7 +730,7 @@
+ struct atm_vcc *vcc = vc->tx_vcc;
+
+ vc->estimator->cells += (skb->len + 47) / 48;
+- if (atomic_read(&vcc->tx_inuse) > (vcc->sk->sndbuf >> 1)) {
++ if (atomic_read(&vcc->sk->wmem_alloc) > (vcc->sk->sndbuf >> 1)) {
+ u32 cps = vc->estimator->maxcps;
+
+ vc->estimator->cps = cps;
+@@ -2025,7 +2025,7 @@
+ atomic_inc(&vcc->stats->tx_err);
+ return -ENOMEM;
+ }
+- atomic_add(skb->truesize + ATM_PDU_OVHD, &vcc->tx_inuse);
++ atomic_add(skb->truesize + ATM_PDU_OVHD, &vcc->sk->wmem_alloc);
+ ATM_SKB(skb)->iovcnt = 0;
+
+ memcpy(skb_put(skb, 52), cell, 52);
+--- linux-2.4.20/net/atm/br2684.c.orig Wed Mar 5 22:11:10 2003
++++ linux-2.4.20/net/atm/br2684.c Wed Mar 5 22:42:20 2003
+@@ -188,7 +188,7 @@
+ dev_kfree_skb(skb);
+ return 0;
+ }
+- atomic_add(skb->truesize, &atmvcc->tx_inuse);
++ atomic_add(skb->truesize, &atmvcc->sk->wmem_alloc);
+ ATM_SKB(skb)->iovcnt = 0;
+ ATM_SKB(skb)->atm_options = atmvcc->atm_options;
+ brdev->stats.tx_packets++;
+@@ -553,7 +553,7 @@
+ barrier();
+ atmvcc->push = br2684_push;
+ skb_queue_head_init(©);
+- skb_migrate(&atmvcc->recvq, ©);
++ skb_migrate(&atmvcc->sk->receive_queue, ©);
+ while ((skb = skb_dequeue(©))) {
+ BRPRIV(skb->dev)->stats.rx_bytes -= skb->len;
+ BRPRIV(skb->dev)->stats.rx_packets--;
+--- linux-2.4.20/net/atm/pppoatm.c~ Sat Aug 3 02:39:46 2002
++++ linux-2.4.20/net/atm/pppoatm.c Wed Mar 5 22:43:51 2003
+@@ -231,7 +231,7 @@
+ kfree_skb(skb);
+ return 1;
+ }
+- atomic_add(skb->truesize, &ATM_SKB(skb)->vcc->tx_inuse);
++ atomic_add(skb->truesize, &ATM_SKB(skb)->vcc->sk->wmem_alloc);
+ ATM_SKB(skb)->iovcnt = 0;
+ ATM_SKB(skb)->atm_options = ATM_SKB(skb)->vcc->atm_options;
+ DPRINTK("(unit %d): atm_skb(%p)->vcc(%p)->dev(%p)\n",
+-
+To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
+the body of a message to majordomo@vger.kernel.org
+More majordomo info at http://vger.kernel.org/majordomo-info.html
+Please read the FAQ at http://www.tux.org/lkml/
--- /dev/null
+In message <20030225131546.GL7685@fs.tum.de>,Adrian Bunk writes:
+>drivers/atm/fore200e.o drivers/atm/fore200e.c
+>drivers/atm/fore200e.c: In function `fore200e_push_rpd':
+>drivers/atm/fore200e.c:1135: structure has no member named `timestamp'
+>drivers/atm/fore200e.c:1136: structure has no member named `timestamp'
+
+it shouldnt be doing that. you only need to set the timestamp in the
+skb. i see the eni driver does the same thing. i will see about
+a patch for that shortly.
+
+Index: linux/drivers/atm/fore200e.c
+===================================================================
+RCS file: /home/chas/CVSROOT/linux/drivers/atm/fore200e.c,v
+retrieving revision 1.1.1.1
+diff -u -d -b -w -r1.1.1.1 fore200e.c
+--- linux/drivers/atm/fore200e.c 20 Feb 2003 13:45:03 -0000 1.1.1.1
++++ linux/drivers/atm/fore200e.c 25 Feb 2003 14:42:06 -0000
+@@ -1135,7 +1135,7 @@
+ return;
+ }
+
+- skb->stamp = vcc->timestamp = xtime;
++ do_gettimeofday(&skb->stamp);
+
+ #ifdef FORE200E_52BYTE_AAL0_SDU
+ if (cell_header) {
+-
+To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
+the body of a message to majordomo@vger.kernel.org
+More majordomo info at http://vger.kernel.org/majordomo-info.html
+Please read the FAQ at http://www.tux.org/lkml/
--- /dev/null
+--- linux-2.4.0-test9-orig/drivers/atm/fore200e.c
++++ linux-2.4.0-test9/drivers/atm/fore200e.c Fri Dec 1 16:18:31 2000
+@@ -2,7 +2,7 @@
+ $Id$
+
+ A FORE Systems 200E-series driver for ATM on Linux.
+- Christophe Lizzi (lizzi@cnam.fr), October 1999-March 2000.
++ Christophe Lizzi (lizzi@cnam.fr), October 1999-December 2000.
+
+ Based on the PCA-200E driver from Uwe Dannowski (Uwe.Dannowski@inf.tu-dresden.de).
+
+@@ -34,6 +34,8 @@
+ #include <linux/sched.h>
+ #include <linux/interrupt.h>
+ #include <linux/bitops.h>
++#include <linux/pci.h>
++#include <linux/module.h>
+ #include <linux/atmdev.h>
+ #include <linux/sonet.h>
+ #include <linux/atm_suni.h>
+@@ -46,7 +48,6 @@
+ #include <asm/byteorder.h>
+ #include <asm/uaccess.h>
+ #include <asm/atomic.h>
+-#include <linux/pci.h>
+
+ #ifdef CONFIG_ATM_FORE200E_SBA
+ #include <asm/idprom.h>
+@@ -56,25 +57,31 @@
+ #include <asm/pgtable.h>
+ #endif
+
+-#include <linux/module.h>
+
+-#include "fore200e.h"
+-#include "suni.h"
++#if 0 /* defer intr work to a tasklet */
++#define FORE200E_USE_TASKLET
++#endif
++
++#if 0 /* enable the debugging code of the buffer supply queues */
++#define FORE200E_BSQ_DEBUG
++#endif
+
+ #if 1 /* ensure correct handling of 52-byte AAL0 SDUs used by atmdump-like apps */
+ #define FORE200E_52BYTE_AAL0_SDU
+ #endif
+
+-#define FORE200E_VERSION "0.2d"
++#include "fore200e.h"
++#include "suni.h"
+
++#define FORE200E_VERSION "0.3"
+
+ #define FORE200E "fore200e: "
+
+ #if defined(CONFIG_ATM_FORE200E_DEBUG) && (CONFIG_ATM_FORE200E_DEBUG > 0)
+ #define DPRINTK(level, format, args...) do { if (CONFIG_ATM_FORE200E_DEBUG >= (level)) \
+- printk(FORE200E format, ##args); } while(0)
++ printk(FORE200E format, ##args); } while (0)
+ #else
+-#define DPRINTK(level, format, args...) while(0)
++#define DPRINTK(level, format, args...) do {} while (0)
+ #endif
+
+
+@@ -503,6 +510,7 @@
+ {
+ #if defined(__sparc_v9__)
+ /* returned chunks are page-aligned */
++ chunk->alloc_size = size * nbr;
+ chunk->alloc_addr = pci_alloc_consistent((struct pci_dev*)fore200e->bus_dev,
+ chunk->alloc_size,
+ &chunk->dma_addr);
+@@ -589,7 +597,7 @@
+ {
+ DPRINTK(2, "device %s being unmapped from memory\n", fore200e->name);
+
+- /* XXX iounmap() does nothing on PowerPC (at least in 2.2.12 and 2.3.41),
++ /* XXX iounmap() does nothing on PowerPC (at least in 2.2.x and 2.3.x),
+ this leads to a kernel panic if the module is loaded and unloaded several times */
+ if (fore200e->virt_base != NULL)
+ iounmap(fore200e->virt_base);
+@@ -989,44 +997,89 @@
+ static void
+ fore200e_irq_tx(struct fore200e* fore200e)
+ {
++ struct host_txq* txq = &fore200e->host_txq;
+ struct host_txq_entry* entry;
+- int i;
+-
+- entry = fore200e->host_txq.host_entry;
+
+- for (i = 0; i < QUEUE_SIZE_TX; i++) {
++ for (;;) {
+
+- if (*entry->status & STATUS_COMPLETE) {
++ entry = &txq->host_entry[ txq->tail ];
+
+- DPRINTK(3, "TX COMPLETED: entry = %p, vcc = %p, skb = %p\n", entry, entry->vcc, entry->skb);
++ if (!(*entry->status & STATUS_COMPLETE)) {
++ break;
++ }
+
+- /* free copy of misaligned data */
+- if (entry->data)
+- kfree(entry->data);
++ DPRINTK(3, "TX COMPLETED: entry = %p [tail = %d], vcc = %p, skb = %p\n",
++ entry, txq->tail, entry->vcc, entry->skb);
+
+- /* remove DMA mapping */
+- fore200e->bus->dma_unmap(fore200e, entry->tpd->tsd[ 0 ].buffer, entry->tpd->tsd[ 0 ].length,
+- FORE200E_DMA_TODEVICE);
++ /* free copy of misaligned data */
++ if (entry->data)
++ kfree(entry->data);
++
++ /* remove DMA mapping */
++ fore200e->bus->dma_unmap(fore200e, entry->tpd->tsd[ 0 ].buffer, entry->tpd->tsd[ 0 ].length,
++ FORE200E_DMA_TODEVICE);
++
++ /* notify tx completion */
++ if (entry->vcc->pop)
++ entry->vcc->pop(entry->vcc, entry->skb);
++ else
++ dev_kfree_skb_irq(entry->skb);
++
++ /* check error condition */
++ if (*entry->status & STATUS_ERROR)
++ atomic_inc(&entry->vcc->stats->tx_err);
++ else
++ atomic_inc(&entry->vcc->stats->tx);
++
++ *entry->status = STATUS_FREE;
++
++ fore200e->host_txq.txing--;
+
+- /* notify tx completion */
+- if (entry->vcc->pop)
+- entry->vcc->pop(entry->vcc, entry->skb);
+- else
+- dev_kfree_skb_irq(entry->skb);
++ FORE200E_NEXT_ENTRY(txq->tail, QUEUE_SIZE_TX);
++ }
++}
+
+- /* check error condition */
+- if (*entry->status & STATUS_ERROR)
+- atomic_inc(&entry->vcc->stats->tx_err);
+- else
+- atomic_inc(&entry->vcc->stats->tx);
+
+- *entry->status = STATUS_FREE;
+-
+- fore200e->host_txq.txing--;
++#ifdef FORE200E_BSQ_DEBUG
++int bsq_audit(int where, struct host_bsq* bsq, int scheme, int magn) /* FFF */
++{
++ struct buffer* buffer;
++ int count = 0;
++
++ buffer = bsq->freebuf;
++ while (buffer) {
++
++ if (buffer->supplied) {
++ printk(FORE200E "bsq_audit(%d): queue %d.%d, buffer %ld supplied but in free list!\n",
++ where, scheme, magn, buffer->index);
+ }
+- entry++;
++
++ if (buffer->magn != magn) {
++ printk(FORE200E "bsq_audit(%d): queue %d.%d, buffer %ld, unexpected magn = %d\n",
++ where, scheme, magn, buffer->index, buffer->magn);
++ }
++
++ if (buffer->scheme != scheme) {
++ printk(FORE200E "bsq_audit(%d): queue %d.%d, buffer %ld, unexpected scheme = %d\n",
++ where, scheme, magn, buffer->index, buffer->scheme);
++ }
++
++ if (buffer->index < 0 || buffer->index >= fore200e_rx_buf_nbr[ scheme ][ magn ]) {
++ printk(FORE200E "bsq_audit(%d): queue %d.%d, out of range buffer index = %ld !\n",
++ where, scheme, magn, buffer->index);
++ }
++
++ count++;
++ buffer = buffer->next;
++ }
++
++ if (count != bsq->freebuf_count) {
++ printk(FORE200E "bsq_audit(%d): queue %d.%d, %d bufs in free list, but freebuf_count = %d\n",
++ where, scheme, magn, count, bsq->freebuf_count);
+ }
++ return 0;
+ }
++#endif
+
+
+ static void
+@@ -1043,28 +1096,44 @@
+
+ bsq = &fore200e->host_bsq[ scheme ][ magn ];
+
+- if (fore200e_rx_buf_nbr[ scheme ][ magn ] - bsq->count > RBD_BLK_SIZE) {
++#ifdef FORE200E_BSQ_DEBUG
++ bsq_audit(1, bsq, scheme, magn);
++#endif
+
+- DPRINTK(2, "supplying rx buffers to queue %d / %d, count = %d\n",
+- scheme, magn, bsq->count);
++ while (bsq->freebuf_count >= RBD_BLK_SIZE) {
++
++ DPRINTK(2, "supplying %d rx buffers to queue %d / %d, freebuf_count = %d\n",
++ RBD_BLK_SIZE, scheme, magn, bsq->freebuf_count);
+
+ entry = &bsq->host_entry[ bsq->head ];
+-
+- FORE200E_NEXT_ENTRY(bsq->head, QUEUE_SIZE_BS);
+
+ for (i = 0; i < RBD_BLK_SIZE; i++) {
+
+- buffer = &bsq->buffer[ bsq->free ];
+-
+- FORE200E_NEXT_ENTRY(bsq->free, fore200e_rx_buf_nbr[ scheme ][ magn ]);
++ /* take the first buffer in the free buffer list */
++ buffer = bsq->freebuf;
++ if (!buffer) {
++ printk(FORE200E "no more free bufs in queue %d.%d, but freebuf_count = %d\n",
++ scheme, magn, bsq->freebuf_count);
++ return;
++ }
++ bsq->freebuf = buffer->next;
+
++#ifdef FORE200E_BSQ_DEBUG
++ if (buffer->supplied)
++ printk(FORE200E "queue %d.%d, buffer %lu already supplied\n",
++ scheme, magn, buffer->index);
++ buffer->supplied = 1;
++#endif
++
+ entry->rbd_block->rbd[ i ].buffer_haddr = buffer->data.dma_addr;
+ entry->rbd_block->rbd[ i ].handle = FORE200E_BUF2HDL(buffer);
+ }
+
+- /* increase the number of supplied rx buffers */
+- bsq->count += RBD_BLK_SIZE;
+-
++ FORE200E_NEXT_ENTRY(bsq->head, QUEUE_SIZE_BS);
++
++ /* decrease accordingly the number of free rx buffers */
++ bsq->freebuf_count -= RBD_BLK_SIZE;
++
+ *entry->status = STATUS_PENDING;
+ fore200e->bus->write(entry->rbd_block_dma, &entry->cp_entry->rbd_block_haddr);
+ }
+@@ -1073,7 +1142,6 @@
+ }
+
+
+-
+ static struct atm_vcc*
+ fore200e_find_vcc(struct fore200e* fore200e, struct rpd* rpd)
+ {
+@@ -1089,7 +1157,7 @@
+ }
+
+
+-static void
++static int
+ fore200e_push_rpd(struct fore200e* fore200e, struct rpd* rpd)
+ {
+ struct atm_vcc* vcc;
+@@ -1103,10 +1171,15 @@
+
+ vcc = fore200e_find_vcc(fore200e, rpd);
+ if (vcc == NULL) {
+-
+- printk(FORE200E "no vcc found for PDU received on %d.%d.%d\n",
+- fore200e->atm_dev->number, rpd->atm_header.vpi, rpd->atm_header.vci);
+- return;
++ DPRINTK(1, "no vcc found for PDU received on %d.%d.%d\n",
++ fore200e->atm_dev->number, rpd->atm_header.vpi, rpd->atm_header.vci);
++ return -EINVAL;
++ }
++
++
++ if (!test_bit(ATM_VF_READY, &vcc->flags)) {
++ DPRINTK(1, "vcc %d.%d.%d not ready for rx\n", vcc->itf, vcc->vpi, vcc->vpi);
++ return -EINVAL;
+ }
+
+ fore200e_vcc = FORE200E_VCC(vcc);
+@@ -1129,10 +1202,9 @@
+
+ skb = alloc_skb(pdu_len, GFP_ATOMIC);
+ if (skb == NULL) {
+-
+ printk(FORE200E "unable to alloc new skb, rx PDU length = %d\n", pdu_len);
+ atomic_inc(&vcc->stats->rx_drop);
+- return;
++ return -ENOMEM;
+ }
+
+ do_gettimeofday(&skb->stamp);
+@@ -1169,11 +1241,13 @@
+ vcc->itf, vcc->vpi, vcc->vci);
+
+ dev_kfree_skb_irq(skb);
+- return;
++ return -ENOMEM;
+ }
+
+ vcc->push(vcc, skb);
+ atomic_inc(&vcc->stats->rx);
++
++ return 0;
+ }
+
+
+@@ -1183,13 +1257,31 @@
+ struct buffer* buffer;
+ int i;
+
++ struct host_bsq* bsq;
++
++
+ for (i = 0; i < rpd->nseg; i++) {
+
+ /* rebuild rx buffer address from rsd handle */
+ buffer = FORE200E_HDL2BUF(rpd->rsd[ i ].handle);
+
+- /* decrease the number of supplied rx buffers */
+- fore200e->host_bsq[ buffer->scheme ][ buffer->magn ].count--;
++ bsq = &fore200e->host_bsq[ buffer->scheme ][ buffer->magn ];
++
++#ifdef FORE200E_BSQ_DEBUG
++ bsq_audit(2, bsq, buffer->scheme, buffer->magn);
++
++ if (buffer->supplied == 0)
++ printk(FORE200E "queue %d.%d, buffer %ld was not supplied\n",
++ buffer->scheme, buffer->magn, buffer->index);
++ buffer->supplied = 0;
++#endif
++
++ /* re-insert the buffer into the free buffer list */
++ buffer->next = bsq->freebuf;
++ bsq->freebuf = buffer;
++
++ /* then increment the number of free rx buffers */
++ bsq->freebuf_count++;
+ }
+ }
+
+@@ -1208,8 +1300,6 @@
+ if ((*entry->status & STATUS_COMPLETE) == 0)
+ break;
+
+- FORE200E_NEXT_ENTRY(rxq->head, QUEUE_SIZE_RX);
+-
+ if ((*entry->status & STATUS_ERROR) == 0) {
+
+ fore200e_push_rpd(fore200e, entry->rpd);
+@@ -1219,6 +1309,8 @@
+ fore200e->atm_dev->number, entry->rpd->atm_header.vpi, entry->rpd->atm_header.vci);
+ }
+
++ FORE200E_NEXT_ENTRY(rxq->head, QUEUE_SIZE_RX);
++
+ fore200e_collect_rpd(fore200e, entry->rpd);
+
+ fore200e_supply(fore200e);
+@@ -1230,6 +1322,23 @@
+ }
+
+
++static void
++fore200e_irq(struct fore200e* fore200e)
++{
++ fore200e_irq_rx(fore200e);
++
++ if (fore200e->host_txq.txing) {
++
++ /* give up if someone else is playing with the tx queue */
++ if (test_bit(0, &fore200e->tx_bit))
++ return;
++
++ fore200e_irq_tx(fore200e);
++ }
++}
++
++
++
+ static void
+ fore200e_interrupt(int irq, void* dev, struct pt_regs* regs)
+ {
+@@ -1242,23 +1351,25 @@
+ }
+ DPRINTK(3, "valid interrupt on device %c\n", fore200e->name[9]);
+
++#ifdef FORE200E_USE_TASKLET
+ tasklet_schedule(&fore200e->tasklet);
++#else
++ fore200e_irq(fore200e);
++#endif
+
+ fore200e->bus->irq_ack(fore200e);
+ }
+
+
++#ifdef FORE200E_USE_TASKLET
+ static void
+ fore200e_tasklet(unsigned long data)
+ {
+- struct fore200e* fore200e = (struct fore200e*) data;
++ DPRINTK(3, "tasklet scheduled for device %c\n", fore200e->name[9]);
+
+- fore200e_irq_rx(fore200e);
+-
+- if (fore200e->host_txq.txing)
+- fore200e_irq_tx(fore200e);
++ fore200e_irq((struct fore200e*) data);
+ }
+-
++#endif
+
+
+ static int
+@@ -1268,7 +1379,7 @@
+
+ #if 1
+ /* fairly balance VCs over (identical) buffer schemes */
+- scheme = vcc->vci % 2 ? BUFFER_SCHEME_ONE : BUFFER_SCHEME_TWO;
++ scheme = vcc->vci % 2 ? BUFFER_SCHEME_ONE : BUFFER_SCHEME_TWO;
+ #else
+ /* bit 7 of VPI magically selects the second buffer scheme */
+ if (vcc->vpi & (1<<7)) {
+@@ -1419,7 +1530,7 @@
+ return 0;
+
+ set_bit(ATM_VF_ADDR, &vcc->flags);
+- vcc->itf = vcc->dev->number;
++ vcc->itf = vcc->dev->number;
+
+ DPRINTK(2, "opening %d.%d.%d:%d QoS = (tx: cl=%s, pcr=%d-%d, cdv=%d, max_sdu=%d; "
+ "rx: cl=%s, pcr=%d-%d, cdv=%d, max_sdu=%d)\n",
+@@ -1438,7 +1549,7 @@
+ }
+ /* reserving the pseudo-CBR bandwidth at this point grants us
+ to reduce the length of the critical section protected
+- by 'rate_sf'. in counterpart, we have to reset the available
++ by 'rate_sf'. in counterpart, we have to restore the available
+ bandwidth if we later encounter an error */
+
+ fore200e->available_cell_rate -= vcc->qos.txtp.max_pcr;
+@@ -1490,6 +1601,8 @@
+
+ DPRINTK(2, "closing %d.%d.%d:%d\n", vcc->itf, vcc->vpi, vcc->vci, fore200e_atm2fore_aal(vcc->qos.aal));
+
++ clear_bit(ATM_VF_READY, &vcc->flags);
++
+ fore200e_activate_vcin(fore200e, 0, vcc, 0);
+
+ kfree(FORE200E_VCC(vcc));
+@@ -1499,8 +1610,6 @@
+ fore200e->available_cell_rate += vcc->qos.txtp.max_pcr;
+ up(&fore200e->rate_sf);
+ }
+-
+- clear_bit(ATM_VF_READY, &vcc->flags);
+ }
+
+
+@@ -1518,7 +1627,6 @@
+ struct host_txq_entry* entry;
+ struct tpd* tpd;
+ struct tpd_haddr tpd_haddr;
+- //unsigned long flags;
+ int retry = CONFIG_ATM_FORE200E_TX_RETRY;
+ int tx_copy = 0;
+ int tx_len = skb->len;
+@@ -1526,6 +1634,11 @@
+ unsigned char* skb_data;
+ int skb_len;
+
++ if (!test_bit(ATM_VF_READY, &vcc->flags)) {
++ DPRINTK(1, "vcc %d.%d.%d not ready for tx\n", vcc->itf, vcc->vpi, vcc->vpi);
++ return -EINVAL;
++ }
++
+ #ifdef FORE200E_52BYTE_AAL0_SDU
+ if ((vcc->qos.aal == ATM_AAL0) && (vcc->qos.txtp.max_sdu == ATM_AAL0_SDU)) {
+ cell_header = (u32*) skb->data;
+@@ -1543,18 +1656,18 @@
+
+ retry_here:
+
+- tasklet_disable(&fore200e->tasklet);
++ set_bit(0, &fore200e->tx_bit);
+
+ entry = &txq->host_entry[ txq->head ];
+
+- if (*entry->status != STATUS_FREE) {
++ if ((*entry->status != STATUS_FREE) || (txq->txing >= QUEUE_SIZE_TX - 2)) {
+
+ /* try to free completed tx queue entries */
+ fore200e_irq_tx(fore200e);
+
+ if (*entry->status != STATUS_FREE) {
+
+- tasklet_enable(&fore200e->tasklet);
++ clear_bit(0, &fore200e->tx_bit);
+
+ /* retry once again? */
+ if(--retry > 0)
+@@ -1562,13 +1675,16 @@
+
+ atomic_inc(&vcc->stats->tx_err);
+
+- printk(FORE200E "tx queue of device %s is saturated, PDU dropped - heartbeat is %08x\n",
++ fore200e->tx_sat++;
++ DPRINTK(2, "tx queue of device %s is saturated, PDU dropped - heartbeat is %08x\n",
+ fore200e->name, fore200e->cp_queues->heartbeat);
+ if (vcc->pop)
+ vcc->pop(vcc, skb);
+ else
+ dev_kfree_skb(skb);
+- return -EIO;
++
++ clear_bit(0, &fore200e->tx_bit);
++ return -ENOBUFS;
+ }
+ }
+
+@@ -1594,7 +1710,8 @@
+ entry->data = kmalloc(tx_len, GFP_ATOMIC | GFP_DMA);
+ if (entry->data == NULL) {
+
+- tasklet_enable(&fore200e->tasklet);
++ clear_bit(0, &fore200e->tx_bit);
++
+ if (vcc->pop)
+ vcc->pop(vcc, skb);
+ else
+@@ -1618,7 +1735,7 @@
+ FORE200E_NEXT_ENTRY(txq->head, QUEUE_SIZE_TX);
+ txq->txing++;
+
+- tasklet_enable(&fore200e->tasklet);
++ clear_bit(0, &fore200e->tx_bit);
+
+ /* ensure DMA synchronisation */
+ fore200e->bus->dma_sync(fore200e, tpd->tsd[ 0 ].buffer, tpd->tsd[ 0 ].length, FORE200E_DMA_TODEVICE);
+@@ -1959,6 +2076,11 @@
+ struct fore200e_vcc* fore200e_vcc = FORE200E_VCC(vcc);
+ struct fore200e* fore200e = FORE200E_DEV(vcc->dev);
+
++ if (!test_bit(ATM_VF_READY, &vcc->flags)) {
++ DPRINTK(1, "vcc %d.%d.%d not ready for QoS change\n", vcc->itf, vcc->vpi, vcc->vpi);
++ return -EINVAL;
++ }
++
+ DPRINTK(2, "change_qos %d.%d.%d, "
+ "(tx: cl=%s, pcr=%d-%d, cdv=%d, max_sdu=%d; "
+ "rx: cl=%s, pcr=%d-%d, cdv=%d, max_sdu=%d), flags = 0x%x\n"
+@@ -2008,7 +2130,9 @@
+ printk(FORE200E "IRQ %s reserved for device %s\n",
+ fore200e_irq_itoa(fore200e->irq), fore200e->name);
+
++#ifdef FORE200E_USE_TASKLET
+ tasklet_init(&fore200e->tasklet, fore200e_tasklet, (unsigned long)fore200e);
++#endif
+
+ fore200e->state = FORE200E_STATE_IRQ;
+ return 0;
+@@ -2070,10 +2194,16 @@
+ if (buffer == NULL)
+ return -ENOMEM;
+
++ bsq->freebuf = NULL;
++
+ for (i = 0; i < nbr; i++) {
+
+ buffer[ i ].scheme = scheme;
+ buffer[ i ].magn = magn;
++#ifdef FORE200E_BSQ_DEBUG
++ buffer[ i ].index = i;
++ buffer[ i ].supplied = 0;
++#endif
+
+ /* allocate the receive buffer body */
+ if (fore200e_chunk_alloc(fore200e,
+@@ -2086,9 +2216,17 @@
+
+ return -ENOMEM;
+ }
++
++ /* insert the buffer into the free buffer list */
++ buffer[ i ].next = bsq->freebuf;
++ bsq->freebuf = &buffer[ i ];
+ }
+- /* set next free buffer index */
+- bsq->free = 0;
++ /* all the buffers are free, initially */
++ bsq->freebuf_count = nbr;
++
++#ifdef FORE200E_BDQ_DEBUG
++ bsq_audit(3, bsq, scheme, magn);
++#endif
+ }
+ }
+
+@@ -2145,9 +2283,9 @@
+ FORE200E_INDEX(bsq->rbd_block.align_addr, struct rbd_block, i);
+ bsq->host_entry[ i ].rbd_block_dma =
+ FORE200E_DMA_INDEX(bsq->rbd_block.dma_addr, struct rbd_block, i);
+- bsq->host_entry[ i ].cp_entry = &cp_entry[ i ];
++ bsq->host_entry[ i ].cp_entry = &cp_entry[ i ];
+
+- *bsq->host_entry[ i ].status = STATUS_FREE;
++ *bsq->host_entry[ i ].status = STATUS_FREE;
+
+ fore200e->bus->write(FORE200E_DMA_INDEX(bsq->status.dma_addr, enum status, i),
+ &cp_entry[ i ].status_haddr);
+@@ -2276,8 +2414,9 @@
+ operation to detect that a new pdu has been submitted for tx */
+ }
+
+- /* set the head entry of the queue */
++ /* set the head and tail entries of the queue */
+ txq->head = 0;
++ txq->tail = 0;
+
+ fore200e->state = FORE200E_STATE_INIT_TXQ;
+ return 0;
+@@ -2591,7 +2730,7 @@
+ struct fore200e* fore200e;
+ int index, link;
+
+- printk(FORE200E "FORE Systems 200E-series driver - version " FORE200E_VERSION "\n");
++ printk(FORE200E "FORE Systems 200E-series ATM driver - version " FORE200E_VERSION "\n");
+
+ /* for each configured bus interface */
+ for (link = 0, bus = fore200e_bus; bus->model_name; bus++) {
+@@ -2674,14 +2817,15 @@
+
+ if (!left--)
+ return sprintf(page,
+- " supplied small bufs (1):\t%d\n"
+- " supplied large bufs (1):\t%d\n"
+- " supplied small bufs (2):\t%d\n"
+- " supplied large bufs (2):\t%d\n",
+- fore200e->host_bsq[ BUFFER_SCHEME_ONE ][ BUFFER_MAGN_SMALL ].count,
+- fore200e->host_bsq[ BUFFER_SCHEME_ONE ][ BUFFER_MAGN_LARGE ].count,
+- fore200e->host_bsq[ BUFFER_SCHEME_TWO ][ BUFFER_MAGN_SMALL ].count,
+- fore200e->host_bsq[ BUFFER_SCHEME_TWO ][ BUFFER_MAGN_LARGE ].count);
++ " free small bufs, scheme 1:\t%d\n"
++ " free large bufs, scheme 1:\t%d\n"
++ " free small bufs, scheme 2:\t%d\n"
++ " free large bufs, scheme 2:\t%d\n",
++ fore200e->host_bsq[ BUFFER_SCHEME_ONE ][ BUFFER_MAGN_SMALL ].freebuf_count,
++ fore200e->host_bsq[ BUFFER_SCHEME_ONE ][ BUFFER_MAGN_LARGE ].freebuf_count,
++ fore200e->host_bsq[ BUFFER_SCHEME_TWO ][ BUFFER_MAGN_SMALL ].freebuf_count,
++ fore200e->host_bsq[ BUFFER_SCHEME_TWO ][ BUFFER_MAGN_LARGE ].freebuf_count);
++
+ if (!left--) {
+ u32 hb = fore200e->bus->read(&fore200e->cp_queues->heartbeat);
+
+@@ -2867,13 +3011,15 @@
+ " large b1:\t\t\t%10u\n"
+ " small b2:\t\t\t%10u\n"
+ " large b2:\t\t\t%10u\n"
+- " RX PDUs:\t\t\t%10u\n",
++ " RX PDUs:\t\t\t%10u\n"
++ " TX PDUs:\t\t\t%10lu\n",
+ fore200e_swap(fore200e->stats->aux.small_b1_failed),
+ fore200e_swap(fore200e->stats->aux.large_b1_failed),
+ fore200e_swap(fore200e->stats->aux.small_b2_failed),
+ fore200e_swap(fore200e->stats->aux.large_b2_failed),
+- fore200e_swap(fore200e->stats->aux.rpd_alloc_failed));
+-
++ fore200e_swap(fore200e->stats->aux.rpd_alloc_failed),
++ fore200e->tx_sat);
++
+ if (!left--)
+ return sprintf(page,"\n"
+ " receive carrier:\t\t\t%s\n",
+--- linux-2.4.0-test9-orig/drivers/atm/fore200e.h
++++ linux-2.4.0-test9/drivers/atm/fore200e.h Fri Dec 1 16:27:20 2000
+@@ -11,7 +11,7 @@
+ #define LARGE_BUFFER_SIZE 4032 /* size of large buffers (multiple of 48 (PCA) and 64 (SBA) bytes) */
+
+
+-#define RBD_BLK_SIZE 32 /* nbr of supplied rx buffers per rbd */
++#define RBD_BLK_SIZE 16 /* nbr of supplied rx buffers per rbd */
+
+
+ #define MAX_PDU_SIZE 65535 /* maximum PDU size supported by AALs */
+@@ -23,17 +23,17 @@
+ #define BUFFER_S2_SIZE SMALL_BUFFER_SIZE /* size of small buffers, scheme 2 */
+ #define BUFFER_L2_SIZE LARGE_BUFFER_SIZE /* size of large buffers, scheme 2 */
+
+-#define BUFFER_S1_NBR (RBD_BLK_SIZE * 2)
+-#define BUFFER_L1_NBR (RBD_BLK_SIZE * 2)
++#define BUFFER_S1_NBR (RBD_BLK_SIZE * 6)
++#define BUFFER_L1_NBR (RBD_BLK_SIZE * 4)
+
+-#define BUFFER_S2_NBR (RBD_BLK_SIZE * 2)
+-#define BUFFER_L2_NBR (RBD_BLK_SIZE * 2)
++#define BUFFER_S2_NBR (RBD_BLK_SIZE * 6)
++#define BUFFER_L2_NBR (RBD_BLK_SIZE * 4)
+
+
+ #define QUEUE_SIZE_CMD 16 /* command queue capacity */
+ #define QUEUE_SIZE_RX 64 /* receive queue capacity */
+ #define QUEUE_SIZE_TX 256 /* transmit queue capacity */
+-#define QUEUE_SIZE_BS 16 /* buffer supply queue capacity */
++#define QUEUE_SIZE_BS 32 /* buffer supply queue capacity */
+
+ #define NBR_CONNECT 1024 /* number of ATM connections */
+
+@@ -576,6 +576,10 @@
+ enum buffer_scheme scheme; /* buffer scheme */
+ enum buffer_magn magn; /* buffer magnitude */
+ struct chunk data; /* data buffer */
++#ifdef FORE200E_BSQ_DEBUG
++ unsigned long index; /* buffer # in queue */
++ int supplied; /* 'buffer supplied' flag */
++#endif
+ } buffer_t;
+
+
+@@ -602,6 +606,7 @@
+ typedef struct host_txq {
+ struct host_txq_entry host_entry[ QUEUE_SIZE_TX ]; /* host resident tx queue entries */
+ int head; /* head of tx queue */
++ int tail; /* tail of tx queue */
+ struct chunk tpd; /* array of tpds */
+ struct chunk status; /* arry of completion status */
+ int txing; /* number of pending PDUs in tx queue */
+@@ -626,8 +631,8 @@
+ struct chunk rbd_block; /* array of rbds */
+ struct chunk status; /* array of completion status */
+ struct buffer* buffer; /* array of rx buffers */
+- int free; /* index of first free rx buffer */
+- volatile int count; /* count of supplied rx buffers */
++ struct buffer* freebuf; /* list of free rx buffers */
++ volatile int freebuf_count; /* count of free rx buffers */
+ } host_bsq_t;
+
+
+@@ -879,8 +884,11 @@
+ struct stats* stats; /* last snapshot of the stats */
+
+ struct semaphore rate_sf; /* protects rate reservation ops */
++#ifdef FORE200E_USE_TASKLET
+ struct tasklet_struct tasklet; /* performs interrupt work */
+-
++#endif
++ volatile int tx_bit; /* 'in tx code' flag */
++ unsigned long tx_sat; /* tx queue saturation count */
+ } fore200e_t;
+
+
+--- /dev/null Thu Jan 1 01:00:00 1970
++++ linux-2.4.20/drivers/atm/fore200e.CHANGES Mon Dec 4 14:34:31 2000
+@@ -0,0 +1,207 @@
++Version 0.3 (December, 1, 2000)
++-------------------------------
++
++ this version should fix some of the bugs introduced
++ in the preceding releases (and thus should introduce new ones :-)
++
++ - fix the bug in the sparc64-specific part of
++ fore200e_pca_dma_chunk_alloc(): chunk->alloc_size was
++ left uninitialized.
++ - rewrite the management of the buffer supply queues:
++ now use per-queue linked-lists of free buffers.
++ - the debugging code that audits the sanity of the free buffers
++ lists can be enabled by the FORE200E_BSQ_DEBUG define.
++ - check that a VCC is ready before playing with it.
++ - fix bugs in the management of the tx queue. note that ENOBUFS is
++ now returned in case of tx queue saturation: this prevent
++ ttcp_atm to give up immediately if this event occurs.
++ - count of tx queue saturation occurrences appended to the
++ auxiliary statistics.
++ - minor cosmetic changes.
++
++
++Version 0.2g (July, 16, 2000)
++-----------------------------
++
++ this release includes some experimental changes to
++ the tx/interrupt code. It's an attempt to improve
++ the schedulability of the driver and its performances.
++
++ - interrupts are no longer disabled to protect the
++ critical section of the tx code. As a consequence,
++ the rx processing may no longer be delayed by the
++ tx processing.
++ - handling of completed tx entries is improved
++ thanks to a tail queue pointer.
++ - interrupt work can be performed by the intr handler
++ or can be deferred to a tasklet according to the
++ FORE200E_USE_TASKLET define.
++
++
++Version 0.2f (May, 21, 2000)
++----------------------------
++
++ this is just the 'official' release of the preceding version.
++ It also fixes a buglet spotted by Mitchell Blank Jr.
++
++ - the driver no longer mess with vcc->timestamp.
++
++
++Version 0.2e (May, 8, 2000)
++---------------------------
++
++ this is an experimental version, intended as a first attempt
++ to fix the lockup problems encountered by a few users
++ under heavy loads/SMP (probably introduced with the tasklet
++ code).
++
++ - use the spin_lock_bh() machinery to guard the
++ critical section of the tx code.
++ - minor fix to the Makefile test that tells the target
++ endianess from <asm/byteorder.h>.
++
++
++Version 0.2d (Mar, 28, 2000)
++----------------------------
++
++ - fix fatal bug in fore200e_open(): the ATM_VF_READY flag
++ should be set, not cleared!
++ - fix SBUS DMA direction flags.
++ - move interrupt handler work to tasklet.
++ - the compile no longer fails when the driver is compiled
++ without any hardware support (i.e. with both PCA and SBA
++ support disabled) (spotted by Arjan van de Ven). Also display
++ a message to warn the user.
++
++
++Version 0.2c (Mar, 23, 2000)
++----------------------------
++
++ this is a minor interim release. It does not provide significant
++ improvement to the driver code.
++
++ - initiate transition to the new-style PCI driver support.
++ - minor cosmetic changes.
++ - fix typos in Documentation/networking/fore200e.txt.
++
++
++Version 0.2b (Feb, 23, 2000)
++----------------------------
++
++ - update of PCI and SBUS DVMA code to 2.3.47-7 (DVMA functions
++ now have a 'direction' argument).
++
++
++Version 0.2a (Feb, 11, 2000)
++----------------------------
++
++ this release completes the transition of the driver to the new
++ DVMA interface. Many thanks to Marconi Networks (formerly FORE
++ Systems) for having placed their binary firmware images under GPL.
++
++ - switch to the new PCI interface (requires 2.3.42+).
++ - minor code cleanup.
++ - add FORE firmware copyright notice.
++
++
++Version 0.2 (Jan, 22, 2000)
++---------------------------
++
++ as the driver is now shipped with the regular linux-atm
++ distributions, it is now released as a diff against the latest
++ linux-atm code.
++
++ - drop support of 2.2.x and pre-2.3.36 kernels.
++ - switch to the new SBUS interface.
++
++
++Version 0.1f (Jan, 22, 2000)
++----------------------------
++
++ this is an interim release that makes the driver ready for the
++ important 2.3.35+ transition. Significant parts of the driver
++ have been rewritten to comply to the principles of the new SBUS
++ interface introduced by the latest 2.3 kernels.
++
++ - clean code.
++ - rewrite memory management routines.
++ - rewrite DMA/DVMA management routines.
++ - rewrite I/O management routines (and fix design weakness).
++ - fix a bug in the initialization of the buffer supply queue.
++ - cmd polling now gives up immediately if an error condition
++ is detected.
++
++ note that the driver remains useable with 2.2 and earlier 2.3 kernels,
++ but future releases will drop support of pre-2.3.36 kernels.
++
++
++Version 0.1e (Dec, 23, 1999)
++----------------------------
++
++ - support of S/UNI hardware loopback modes via SUNI_GETLOOP/SUNI_SETLOOP
++ ioctls.
++ - add the related ioctl entries in arch/sparc64/kernel/ioctl32.c.
++ - add a simple 'sunimode' utility to get/set the S/UNI loopback mode
++ (should also work with all the drivers that use suni.c/suni.h).
++ - fix handling of 52-byte AAL0 SDUs used by atmdump-like applications:
++ * if qos.txtp.max_sdu == 52, the following tx SDU format is expected
++ by the driver:
++ <4-byte cell header><48-byte AAL0 payload>[<48-byte AAL0 payload>...]
++ i.e.
++ <--- 52-byte AAL0 SDU used by atmdump --->[<- opt. extra payloads ->]
++ otherwise, the following tx SDU format is assumed:
++ <48-byte AAL0 payload>[<48-byte AAL0 payload>...]
++ * if qos.txtp.max_sdu == 52, the rx SDUs are delivered as follows:
++ <4-byte cell header><48-byte AAL0 payload>
++ i.e.
++ <- 52-byte AAL0 SDU expected by atmdump ->
++ otherwise, the SDUs are delivered as: <48-byte AAL0 payload>
++
++
++Version 0.1d (Dec, 16, 1999)
++----------------------------
++
++ - add a retry mechanism so that the driver does not immediately give up
++ if the tx queue is saturated. Saturation of the transmit queue may occur
++ under extreme conditions, when a fast host continuously submits very
++ small frames or raw AAL0 cells.
++ - add a new config option to tune the retry mecanism.
++ - improve AAL0 support. Attempting to transmit incomplete cell payloads
++ over an AAL0 VC simply used to nuke the PCA board.
++
++
++Version 0.1c (Nov, 22, 1999)
++----------------------------
++
++ - endian-dependent code no longer depends on arch-specific definitions
++ (e.g. __powerpc__ or __sparc_v9__) but on __BIG_ENDIAN/__LITTLE_ENDIAN
++ - the right PCA-200E firmware is no longer selected according to
++ a list of well-known archs in Config.in, but is guessed from
++ <asm/byteorder.h> in Makefile.
++ - rbd/rsd handles now use the FORE200E_BUF2HDL() and FORE200E_HDL2BUF()
++ macros.
++ - minor cosmetic changes.
++ - spend some time playing with AAL0. Seems to work with MTU = 48 bytes.
++
++
++Version 0.1b (Nov, 16, 1999)
++----------------------------
++
++ - improve the configuration process in order to avoid confusion
++ caused by the firmware stuff.
++ - update accordingly the build process and the help file.
++
++ (note that the driver code is unchanged)
++
++
++Version 0.1a (Nov, 2, 1999)
++---------------------------
++
++ - use new mutex initializer in 2.3.1+ kernels.
++ - add CHANGES file.
++
++
++Version 0.1 (Oct, 25, 1999)
++---------------------------
++
++ - first public release
+--- /dev/null Thu Jan 1 01:00:00 1970
++++ linux-2.4.20/drivers/atm/fore200e.README Mon Dec 4 14:34:31 2000
+@@ -0,0 +1,79 @@
++
++linux-2.4.0-test9-fore200e-0.3.tar
++----------------------------------
++
++This package updates the support of the FORE Systems 200E-series ATM
++adapters by the Linux operating system. Note that the driver itself is
++now distributed with the regular Linux kernel distribution.
++
++It is based on the earlier PCA-200E driver written by Uwe Dannowski.
++
++This device driver simultaneously supports PCA-200E and SBA-200E adapters on
++i386, alpha (untested), powerpc, sparc and sparc64 hosts.
++
++The intent is to enable the use of different models of FORE adapters at the
++same time, by servers that have several bus interfaces (such as PCI+SBUS,
++PCI+MCA or PCI+EISA).
++Only PCI and SBUS devices are currently supported by the driver, but support
++for other bus interfaces such as EISA should not be too hard to add (this may
++be more tricky for the MCA bus, though, as FORE made some MCA-specific
++modifications to the adapter's AALI interface).
++
++The driver is shipped with firmware data being uploaded to the ATM adapters
++at system boot time or at module loading time. The supplied firmware images
++should work with all adapters.
++
++However, if you encounter problems (the firmware doesn't start or the driver
++is unable to read the PROM data), you may consider trying another firmware
++version. Alternative binary firmware images can be found somewhere on the
++ForeThough CD-ROM supplied with your adapter by FORE Systems.
++
++You can also get the latest firmware images from FORE Systems at
++http://www.fore.com. Register TACTics Online and go to
++the 'software updates' pages. The firmware binaries are part of
++the various ForeThough software distributions.
++
++Notice that different versions of the PCA-200E firmware exist, depending
++on the endianess of the host architecture. The driver is shipped with
++both little and big endian PCA firmware images.
++
++Name and location of the alternative firmware images can be set at kernel
++configuration time. See the INSTALL file for details.
++
++Also note that the firmware binaries are the intellectual property
++of FORE Systems, Inc. Please read the fore200e_firmware_copyright file.
++
++
++
++Driver features summary
++-----------------------
++
++ - simultaneously supports PCA-200E and SBA-200E adapters;
++ - works on i386, powerpc, SBUS-based sparc and
++ SBUS or PCI-based sparc64 architectures;
++ - now targeted to 2.4.0-test9 kernels;
++ - uses new DVMA PCI and SBUS interfaces;
++ - useable as a removable kernel module;
++ - supports FORE pseudo-CBR rate control;
++ - the reserved CBR rate can be changed after VC setup;
++ - supports AAL0, AAL3/4 and AAL5;
++ - supports S/UNI hardware loopback modes;
++ - internal design focuses on portability and extensibility.
++
++
++Restrictions / Known bugs
++-------------------------
++
++ - only VP=0 is supported;
++ - 'insmod' coredumps when attempting to load the driver on sparc64
++ hosts (at least using the old Ultrapenguin 1.1.9 distribution);
++ - some memory resources cannot be freed on sparc, sparc64 and powerpc,
++ because the required support is still missing in 2.2 and 2.3 kernels.
++ You shouldn't try to unload the module driver on these platforms.
++
++
++Feedback
++--------
++
++Feedback is welcome. Please send success stories/bug reports/
++patches/improvement/comments/flames to <lizzi@cnam.fr>.