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);
|