netdev
[Top] [All Lists]

[PATCH][ATM]: [lec] put back pressure on network stack

To: davem@xxxxxxxxxx
Subject: [PATCH][ATM]: [lec] put back pressure on network stack
From: "chas williams (contractor)" <chas@xxxxxxxxxxxxxxxx>
Date: Wed, 25 Feb 2004 18:54:09 -0500
Cc: netdev@xxxxxxxxxxx
Reply-to: chas3@xxxxxxxxxxxxxxxxxxxxx
Sender: netdev-bounce@xxxxxxxxxxx
backport of similar patch for 2.6.  

please apply to 2.4 -- thanks

# This is a BitKeeper generated patch for the following project:
# Project Name: Linux kernel tree
# This patch format is intended for GNU patch command version 2.5 or higher.
# This patch includes the following deltas:
#                  ChangeSet    1.1355  -> 1.1356 
#              net/atm/lec.h    1.6     -> 1.7    
#              net/atm/lec.c    1.22    -> 1.23   
#
# The following is the BitKeeper ChangeSet Log
# --------------------------------------------
# 04/02/25      chas@xxxxxxxxxxxxxxxxxxxxxx     1.1356
# [ATM]: [lec] put back pressure on network stack
# --------------------------------------------
#
diff -Nru a/net/atm/lec.c b/net/atm/lec.c
--- a/net/atm/lec.c     Wed Feb 25 15:40:52 2004
+++ b/net/atm/lec.c     Wed Feb 25 15:40:52 2004
@@ -67,7 +67,7 @@
                                single destination while waiting for SVC */
 
 static int lec_open(struct net_device *dev);
-static int lec_send_packet(struct sk_buff *skb, struct net_device *dev);
+static int lec_start_xmit(struct sk_buff *skb, struct net_device *dev);
 static int lec_close(struct net_device *dev);
 static struct net_device_stats *lec_get_stats(struct net_device *dev);
 static void lec_init(struct net_device *dev);
@@ -211,26 +211,34 @@
 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->sk->wmem_alloc);
-               ATM_SKB(skb)->vcc = vcc;
-               ATM_SKB(skb)->atm_options = vcc->atm_options;
-               priv->stats.tx_packets++;
-               priv->stats.tx_bytes += skb->len;
-               vcc->send(vcc, skb);
-       } else {
+       ATM_SKB(skb)->vcc = vcc;
+       ATM_SKB(skb)->atm_options = vcc->atm_options;
+
+       atomic_add(skb->truesize, &vcc->sk->wmem_alloc);
+       if (vcc->send(vcc, skb) < 0) {
                priv->stats.tx_dropped++;
-               dev_kfree_skb(skb);
+               return;
        }
+
+       priv->stats.tx_packets++;
+       priv->stats.tx_bytes += skb->len;
+}
+
+static void
+lec_tx_timeout(struct net_device *dev)
+{
+       printk(KERN_INFO "%s: tx timeout\n", dev->name);
+       dev->trans_start = jiffies;
+       netif_wake_queue(dev);
 }
 
 static int 
-lec_send_packet(struct sk_buff *skb, struct net_device *dev)
+lec_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
         struct sk_buff *skb2;
         struct lec_priv *priv = (struct lec_priv *)dev->priv;
         struct lecdatahdr_8023 *lec_h;
-        struct atm_vcc *send_vcc;
+        struct atm_vcc *vcc;
        struct lec_arp_table *entry;
         unsigned char *dst;
        int min_frame_size;
@@ -243,7 +251,7 @@
         int i=0;
 #endif /* DUMP_PACKETS >0 */
         
-        DPRINTK("Lec_send_packet called\n");  
+        DPRINTK("lec_start_xmit called\n");  
         if (!priv->lecd) {
                 printk("%s:No lecd attached\n",dev->name);
                 priv->stats.tx_errors++;
@@ -262,7 +270,7 @@
         /* Make sure we have room for lec_id */
         if (skb_headroom(skb) < 2) {
 
-                DPRINTK("lec_send_packet: reallocating skb\n");
+                DPRINTK("lec_start_xmit: reallocating skb\n");
                 skb2 = skb_realloc_headroom(skb, LEC_HEADER_LEN);
                 kfree_skb(skb);
                 if (skb2 == NULL) return 0;
@@ -337,18 +345,18 @@
         }
 #endif
         entry = NULL;
-        send_vcc = lec_arp_resolve(priv, dst, is_rdesc, &entry);
-        DPRINTK("%s:send_vcc:%p vcc_flags:%x, entry:%p\n", dev->name,
-                send_vcc, send_vcc?send_vcc->flags:0, entry);
-        if (!send_vcc || !test_bit(ATM_VF_READY,&send_vcc->flags)) {    
+        vcc = lec_arp_resolve(priv, dst, is_rdesc, &entry);
+        DPRINTK("%s:vcc:%p vcc_flags:%x, entry:%p\n", dev->name,
+                vcc, vcc?vcc->flags:0, entry);
+        if (!vcc || !test_bit(ATM_VF_READY,&vcc->flags)) {    
                 if (entry && (entry->tx_wait.qlen < LEC_UNRES_QUE_LEN)) {
-                        DPRINTK("%s:lec_send_packet: queuing packet, ", 
dev->name);
+                        DPRINTK("%s:lec_start_xmit: queuing packet, ", 
dev->name);
                         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]);
                         skb_queue_tail(&entry->tx_wait, skb);
                 } else {
-                        DPRINTK("%s:lec_send_packet: tx queue full or no arp 
entry, dropping, ", dev->name);
+                        DPRINTK("%s:lec_start_xmit: tx queue full or no arp 
entry, dropping, ", dev->name);
                         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]);
@@ -360,7 +368,7 @@
                 
 #if DUMP_PACKETS > 0                    
         printk("%s:sending to vpi:%d vci:%d\n", dev->name,
-               send_vcc->vpi, send_vcc->vci);       
+               vcc->vpi, vcc->vci);       
 #endif /* DUMP_PACKETS > 0 */
                 
         while (entry && (skb2 = skb_dequeue(&entry->tx_wait))) {
@@ -368,14 +376,28 @@
                 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]);
-                lec_send(send_vcc, skb2, priv);
+                lec_send(vcc, skb2, priv);
         }
 
-        lec_send(send_vcc, skb, priv);
-#if 0
-        /* Should we wait for card's device driver to notify us? */
-        dev->tbusy=0;
-#endif        
+        lec_send(vcc, skb, priv);
+
+       if (!atm_may_send(vcc, 0)) {
+               struct lec_vcc_priv *vpriv = LEC_VCC_PRIV(vcc);
+
+               vpriv->xoff = 1;
+               netif_stop_queue(dev);
+
+               /*
+                * vcc->pop() might have occurred in between, making
+                * the vcc usuable again.  Since xmit is serialized,
+                * this is the only situation we have to re-test.
+                */
+
+               if (atm_may_send(vcc, 0))
+                       netif_wake_queue(dev);
+       }
+
+       dev->trans_start = jiffies;
         return 0;
 }
 
@@ -634,7 +656,8 @@
         dev->change_mtu = lec_change_mtu;
         dev->open = lec_open;
         dev->stop = lec_close;
-        dev->hard_start_xmit = lec_send_packet;
+        dev->hard_start_xmit = lec_start_xmit;
+       dev->tx_timeout = lec_tx_timeout;
 
         dev->get_stats = lec_get_stats;
         dev->set_multicast_list = lec_set_multicast_list;
@@ -729,9 +752,30 @@
         }
 }
 
+void
+lec_pop(struct atm_vcc *vcc, struct sk_buff *skb)
+{
+       struct lec_vcc_priv *vpriv = LEC_VCC_PRIV(vcc);
+       struct net_device *dev = skb->dev;
+
+       if (vpriv == NULL) {
+               printk("lec_pop(): vpriv = NULL!?!?!?\n");
+               return;
+       }
+
+       vpriv->old_pop(vcc, skb);
+
+       if (vpriv->xoff && atm_may_send(vcc, 0)) {
+               vpriv->xoff = 0;
+               if (netif_running(dev) && netif_queue_stopped(dev))
+                       netif_wake_queue(dev);
+       }
+}
+
 int 
 lec_vcc_attach(struct atm_vcc *vcc, void *arg)
 {
+       struct lec_vcc_priv *vpriv;
         int bytes_left;
         struct atmlec_ioc ioc_data;
 
@@ -744,6 +788,12 @@
         if (ioc_data.dev_num < 0 || ioc_data.dev_num >= MAX_LEC_ITF || 
             !dev_lec[ioc_data.dev_num])
                 return -EINVAL;
+       if (!(vpriv = kmalloc(sizeof(struct lec_vcc_priv), GFP_KERNEL)))
+               return -ENOMEM;
+       vpriv->xoff = 0;
+       vpriv->old_pop = vcc->pop;
+       LEC_VCC_PRIV(vcc) = vpriv;
+       vcc->pop = lec_pop;
         lec_vcc_added(dev_lec[ioc_data.dev_num]->priv, 
                       &ioc_data, vcc, vcc->push);
         vcc->push = lec_push;
@@ -1080,22 +1130,21 @@
 lec_arp_clear_vccs(struct lec_arp_table *entry)
 {
         if (entry->vcc) {
-                entry->vcc->push = entry->old_push;
-#if 0 /* August 6, 1998 */
-                set_bit(ATM_VF_RELEASED,&entry->vcc->flags);
-               clear_bit(ATM_VF_READY,&entry->vcc->flags);
-                entry->vcc->push(entry->vcc, NULL);
-#endif
-               vcc_release_async(entry->vcc, -EPIPE);
-                entry->vcc = NULL;
+               struct atm_vcc *vcc = entry->vcc;
+               struct lec_vcc_priv *vpriv = LEC_VCC_PRIV(vcc);
+               struct net_device *dev = (struct net_device*) vcc->proto_data;
+
+                vcc->pop = vpriv->old_pop;
+               if (vpriv->xoff)
+                       netif_wake_queue(dev);
+               kfree(vpriv);
+               LEC_VCC_PRIV(vcc) = NULL;
+                vcc->push = entry->old_push;
+               vcc_release_async(vcc, -EPIPE);
+                vcc = NULL;
         }
         if (entry->recv_vcc) {
                 entry->recv_vcc->push = entry->old_recv_push;
-#if 0
-                set_bit(ATM_VF_RELEASED,&entry->recv_vcc->flags);
-               clear_bit(ATM_VF_READY,&entry->recv_vcc->flags);
-                entry->recv_vcc->push(entry->recv_vcc, NULL);
-#endif
                vcc_release_async(entry->recv_vcc, -EPIPE);
                 entry->recv_vcc = NULL;
         }        
@@ -2037,11 +2086,20 @@
         unsigned char mac_addr[] = {
                 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
         struct lec_arp_table *to_add;
+       struct lec_vcc_priv *vpriv;
   
+       if (!(vpriv = kmalloc(sizeof(struct lec_vcc_priv), GFP_KERNEL)))
+               return -ENOMEM;
+       vpriv->xoff = 0;
+       vpriv->old_pop = vcc->pop;
+       LEC_VCC_PRIV(vcc) = vpriv;
+        vcc->pop = lec_pop;
         lec_arp_get(priv);
         to_add = make_entry(priv, mac_addr);
         if (!to_add) {
                 lec_arp_put(priv);
+               vcc->pop = vpriv->old_pop;
+               kfree(vpriv);
                 return -ENOMEM;
         }
         memcpy(to_add->atm_addr, vcc->remote.sas_addr.prv, ATM_ESA_LEN);
diff -Nru a/net/atm/lec.h b/net/atm/lec.h
--- a/net/atm/lec.h     Wed Feb 25 15:40:52 2004
+++ b/net/atm/lec.h     Wed Feb 25 15:40:52 2004
@@ -147,6 +147,13 @@
         int is_trdev;      /* Device type, 0 = Ethernet, 1 = TokenRing */
 };
 
+struct lec_vcc_priv {
+       void (*old_pop)(struct atm_vcc *vcc, struct sk_buff *skb);
+       int xoff;
+};
+
+#define LEC_VCC_PRIV(vcc)      ((struct lec_vcc_priv *)((vcc)->user_back))
+
 int lecd_attach(struct atm_vcc *vcc, int arg);
 int lec_vcc_attach(struct atm_vcc *vcc, void *arg);
 int lec_mcast_attach(struct atm_vcc *vcc, int arg);

<Prev in Thread] Current Thread [Next in Thread>