patch-2_6_7-vs1_9_1_12
[linux-2.6.git] / drivers / net / iseries_veth.c
index 97fd269..634cb04 100644 (file)
@@ -461,6 +461,11 @@ static void veth_statemachine(void *p)
                if (cnx->msgs)
                        for (i = 0; i < VETH_NUMBUFFERS; ++i)
                                veth_recycle_msg(cnx, cnx->msgs + i);
+               spin_unlock_irq(&cnx->lock);
+               veth_flush_pending(cnx);
+               spin_lock_irq(&cnx->lock);
+               if (cnx->state & VETH_STATE_RESET)
+                       goto restart;
        }
 
        if (cnx->state & VETH_STATE_SHUTDOWN)
@@ -796,6 +801,48 @@ static int veth_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
        return -EOPNOTSUPP;
 }
 
+static void veth_tx_timeout(struct net_device *dev)
+{
+       struct veth_port *port = (struct veth_port *)dev->priv;
+       struct net_device_stats *stats = &port->stats;
+       unsigned long flags;
+       int i;
+
+       stats->tx_errors++;
+
+       spin_lock_irqsave(&port->pending_gate, flags);
+
+       printk(KERN_WARNING "%s: Tx timeout!  Resetting lp connections: %08x\n",
+              dev->name, port->pending_lpmask);
+
+       /* If we've timed out the queue must be stopped, which should
+        * only ever happen when there is a pending packet. */
+       WARN_ON(! port->pending_lpmask);
+
+       for (i = 0; i < HVMAXARCHITECTEDLPS; i++) {
+               struct veth_lpar_connection *cnx = veth_cnx[i];
+
+               if (! (port->pending_lpmask & (1<<i)))
+                       continue;
+
+               /* If we're pending on it, we must be connected to it,
+                * so we should certainly have a structure for it. */
+               BUG_ON(! cnx);
+
+               /* Theoretically we could be kicking a connection
+                * which doesn't deserve it, but in practice if we've
+                * had a Tx timeout, the pending_lpmask will have
+                * exactly one bit set - the connection causing the
+                * problem. */
+               spin_lock(&cnx->lock);
+               cnx->state |= VETH_STATE_RESET;
+               veth_kick_statemachine(cnx);
+               spin_unlock(&cnx->lock);
+       }
+
+       spin_unlock_irqrestore(&port->pending_gate, flags);
+}
+
 struct net_device * __init veth_probe_one(int vlan)
 {
        struct net_device *dev;
@@ -843,6 +890,9 @@ struct net_device * __init veth_probe_one(int vlan)
        dev->set_multicast_list = veth_set_multicast_list;
        dev->do_ioctl = veth_ioctl;
 
+       dev->watchdog_timeo = 2 * (VETH_ACKTIMEOUT * HZ / 1000000);
+       dev->tx_timeout = veth_tx_timeout;
+
        rc = register_netdev(dev);
        if (rc != 0) {
                veth_printk(KERN_ERR,
@@ -938,19 +988,10 @@ static HvLpIndexMap veth_transmit_to_many(struct sk_buff *skb,
        int rc;
 
        for (i = 0; i < HVMAXARCHITECTEDLPS; i++) {
-               struct sk_buff *clone;
-
-               if (! lpmask & (1<<i))
+               if ((lpmask & (1 << i)) == 0)
                        continue;
 
-               clone = skb_clone(skb, GFP_ATOMIC);
-               if (! clone) {
-                       veth_error("%s: skb_clone failed %p\n",
-                                  dev->name, skb);
-                       continue;
-               }
-
-               rc = veth_transmit_to_one(clone, i, dev);
+               rc = veth_transmit_to_one(skb_get(skb), i, dev);
                if (! rc)
                        lpmask &= ~(1<<i);
        }
@@ -984,26 +1025,28 @@ static int veth_start_xmit(struct sk_buff *skb, struct net_device *dev)
                lpmask = port->lpar_map;
        }
 
+       spin_lock_irqsave(&port->pending_gate, flags);
+
        lpmask = veth_transmit_to_many(skb, lpmask, dev);
 
        if (! lpmask) {
                dev_kfree_skb(skb);
        } else {
-               spin_lock_irqsave(&port->pending_gate, flags);
                if (port->pending_skb) {
                        veth_error("%s: Tx while skb was pending!\n",
                                   dev->name);
                        dev_kfree_skb(skb);
+                       spin_unlock_irqrestore(&port->pending_gate, flags);
                        return 1;
                }
 
                port->pending_skb = skb;
                port->pending_lpmask = lpmask;
                netif_stop_queue(dev);
-
-               spin_unlock_irqrestore(&port->pending_gate, flags);
        }
 
+       spin_unlock_irqrestore(&port->pending_gate, flags);
+
        return 0;
 }
 
@@ -1057,7 +1100,7 @@ static void veth_flush_pending(struct veth_lpar_connection *cnx)
                        if (! port->pending_lpmask) {
                                dev_kfree_skb_any(port->pending_skb);
                                port->pending_skb = NULL;
-                               netif_start_queue(dev);
+                               netif_wake_queue(dev);
                        }
                }
                spin_unlock_irqrestore(&port->pending_gate, flags);