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