ieee1394: eth1394: handle tlabel exhaustion
When eth1394 was unable to acquire a transaction label, it just dropped outgoing packets without attempt to resend them later. The transmit queue is now halted if no tlabel is available to ->hard_start_xmit(). A workqueue job is then scheduled to catch the moment when ieee1394 recycled the next lot of tlabels. Fixes http://bugzilla.kernel.org/show_bug.cgi?id=8402 Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
This commit is contained in:
parent
69c29fa7d1
commit
7a97bc03e0
|
@ -47,6 +47,7 @@
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
|
#include <linux/workqueue.h>
|
||||||
|
|
||||||
#include <linux/netdevice.h>
|
#include <linux/netdevice.h>
|
||||||
#include <linux/inetdevice.h>
|
#include <linux/inetdevice.h>
|
||||||
|
@ -235,6 +236,9 @@ static int ether1394_open(struct net_device *dev)
|
||||||
/* This is called after an "ifdown" */
|
/* This is called after an "ifdown" */
|
||||||
static int ether1394_stop(struct net_device *dev)
|
static int ether1394_stop(struct net_device *dev)
|
||||||
{
|
{
|
||||||
|
/* flush priv->wake */
|
||||||
|
flush_scheduled_work();
|
||||||
|
|
||||||
netif_stop_queue(dev);
|
netif_stop_queue(dev);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -530,6 +534,37 @@ static void ether1394_init_dev(struct net_device *dev)
|
||||||
dev->tx_queue_len = 1000;
|
dev->tx_queue_len = 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Wake the queue up after commonly encountered transmit failure conditions are
|
||||||
|
* hopefully over. Currently only tlabel exhaustion is accounted for.
|
||||||
|
*/
|
||||||
|
static void ether1394_wake_queue(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct eth1394_priv *priv;
|
||||||
|
struct hpsb_packet *packet;
|
||||||
|
|
||||||
|
priv = container_of(work, struct eth1394_priv, wake);
|
||||||
|
packet = hpsb_alloc_packet(0);
|
||||||
|
|
||||||
|
/* This is really bad, but unjam the queue anyway. */
|
||||||
|
if (!packet)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
packet->host = priv->host;
|
||||||
|
packet->node_id = priv->wake_node;
|
||||||
|
/*
|
||||||
|
* A transaction label is all we really want. If we get one, it almost
|
||||||
|
* always means we can get a lot more because the ieee1394 core recycled
|
||||||
|
* a whole batch of tlabels, at last.
|
||||||
|
*/
|
||||||
|
if (hpsb_get_tlabel(packet) == 0)
|
||||||
|
hpsb_free_tlabel(packet);
|
||||||
|
|
||||||
|
hpsb_free_packet(packet);
|
||||||
|
out:
|
||||||
|
netif_wake_queue(priv->wake_dev);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This function is called every time a card is found. It is generally called
|
* This function is called every time a card is found. It is generally called
|
||||||
* when the module is installed. This is where we add all of our ethernet
|
* when the module is installed. This is where we add all of our ethernet
|
||||||
|
@ -574,6 +609,8 @@ static void ether1394_add_host(struct hpsb_host *host)
|
||||||
spin_lock_init(&priv->lock);
|
spin_lock_init(&priv->lock);
|
||||||
priv->host = host;
|
priv->host = host;
|
||||||
priv->local_fifo = fifo_addr;
|
priv->local_fifo = fifo_addr;
|
||||||
|
INIT_WORK(&priv->wake, ether1394_wake_queue);
|
||||||
|
priv->wake_dev = dev;
|
||||||
|
|
||||||
hi = hpsb_create_hostinfo(ð1394_highlevel, host, sizeof(*hi));
|
hi = hpsb_create_hostinfo(ð1394_highlevel, host, sizeof(*hi));
|
||||||
if (hi == NULL) {
|
if (hi == NULL) {
|
||||||
|
@ -1390,22 +1427,17 @@ static int ether1394_prep_write_packet(struct hpsb_packet *p,
|
||||||
u64 addr, void *data, int tx_len)
|
u64 addr, void *data, int tx_len)
|
||||||
{
|
{
|
||||||
p->node_id = node;
|
p->node_id = node;
|
||||||
p->data = NULL;
|
|
||||||
|
if (hpsb_get_tlabel(p))
|
||||||
|
return -EAGAIN;
|
||||||
|
|
||||||
p->tcode = TCODE_WRITEB;
|
p->tcode = TCODE_WRITEB;
|
||||||
p->header[1] = host->node_id << 16 | addr >> 32;
|
|
||||||
p->header[2] = addr & 0xffffffff;
|
|
||||||
|
|
||||||
p->header_size = 16;
|
p->header_size = 16;
|
||||||
p->expect_response = 1;
|
p->expect_response = 1;
|
||||||
|
|
||||||
if (hpsb_get_tlabel(p)) {
|
|
||||||
ETH1394_PRINT_G(KERN_ERR, "Out of tlabels\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
p->header[0] =
|
p->header[0] =
|
||||||
p->node_id << 16 | p->tlabel << 10 | 1 << 8 | TCODE_WRITEB << 4;
|
p->node_id << 16 | p->tlabel << 10 | 1 << 8 | TCODE_WRITEB << 4;
|
||||||
|
p->header[1] = host->node_id << 16 | addr >> 32;
|
||||||
|
p->header[2] = addr & 0xffffffff;
|
||||||
p->header[3] = tx_len << 16;
|
p->header[3] = tx_len << 16;
|
||||||
p->data_size = (tx_len + 3) & ~3;
|
p->data_size = (tx_len + 3) & ~3;
|
||||||
p->data = data;
|
p->data = data;
|
||||||
|
@ -1451,7 +1483,7 @@ static int ether1394_send_packet(struct packet_task *ptask, unsigned int tx_len)
|
||||||
|
|
||||||
packet = ether1394_alloc_common_packet(priv->host);
|
packet = ether1394_alloc_common_packet(priv->host);
|
||||||
if (!packet)
|
if (!packet)
|
||||||
return -1;
|
return -ENOMEM;
|
||||||
|
|
||||||
if (ptask->tx_type == ETH1394_GASP) {
|
if (ptask->tx_type == ETH1394_GASP) {
|
||||||
int length = tx_len + 2 * sizeof(quadlet_t);
|
int length = tx_len + 2 * sizeof(quadlet_t);
|
||||||
|
@ -1462,7 +1494,7 @@ static int ether1394_send_packet(struct packet_task *ptask, unsigned int tx_len)
|
||||||
ptask->addr, ptask->skb->data,
|
ptask->addr, ptask->skb->data,
|
||||||
tx_len)) {
|
tx_len)) {
|
||||||
hpsb_free_packet(packet);
|
hpsb_free_packet(packet);
|
||||||
return -1;
|
return -EAGAIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
ptask->packet = packet;
|
ptask->packet = packet;
|
||||||
|
@ -1471,7 +1503,7 @@ static int ether1394_send_packet(struct packet_task *ptask, unsigned int tx_len)
|
||||||
|
|
||||||
if (hpsb_send_packet(packet) < 0) {
|
if (hpsb_send_packet(packet) < 0) {
|
||||||
ether1394_free_packet(packet);
|
ether1394_free_packet(packet);
|
||||||
return -1;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1514,13 +1546,18 @@ static void ether1394_complete_cb(void *__ptask)
|
||||||
|
|
||||||
ptask->outstanding_pkts--;
|
ptask->outstanding_pkts--;
|
||||||
if (ptask->outstanding_pkts > 0 && !fail) {
|
if (ptask->outstanding_pkts > 0 && !fail) {
|
||||||
int tx_len;
|
int tx_len, err;
|
||||||
|
|
||||||
/* Add the encapsulation header to the fragment */
|
/* Add the encapsulation header to the fragment */
|
||||||
tx_len = ether1394_encapsulate(ptask->skb, ptask->max_payload,
|
tx_len = ether1394_encapsulate(ptask->skb, ptask->max_payload,
|
||||||
&ptask->hdr);
|
&ptask->hdr);
|
||||||
if (ether1394_send_packet(ptask, tx_len))
|
err = ether1394_send_packet(ptask, tx_len);
|
||||||
|
if (err) {
|
||||||
|
if (err == -EAGAIN)
|
||||||
|
ETH1394_PRINT_G(KERN_ERR, "Out of tlabels\n");
|
||||||
|
|
||||||
ether1394_dg_complete(ptask, 1);
|
ether1394_dg_complete(ptask, 1);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
ether1394_dg_complete(ptask, fail);
|
ether1394_dg_complete(ptask, fail);
|
||||||
}
|
}
|
||||||
|
@ -1633,8 +1670,17 @@ static int ether1394_tx(struct sk_buff *skb, struct net_device *dev)
|
||||||
/* Add the encapsulation header to the fragment */
|
/* Add the encapsulation header to the fragment */
|
||||||
tx_len = ether1394_encapsulate(skb, max_payload, &ptask->hdr);
|
tx_len = ether1394_encapsulate(skb, max_payload, &ptask->hdr);
|
||||||
dev->trans_start = jiffies;
|
dev->trans_start = jiffies;
|
||||||
if (ether1394_send_packet(ptask, tx_len))
|
if (ether1394_send_packet(ptask, tx_len)) {
|
||||||
goto fail;
|
if (dest_node == (LOCAL_BUS | ALL_NODES))
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
/* Most failures of ether1394_send_packet are recoverable. */
|
||||||
|
netif_stop_queue(dev);
|
||||||
|
priv->wake_node = dest_node;
|
||||||
|
schedule_work(&priv->wake);
|
||||||
|
kmem_cache_free(packet_task_cache, ptask);
|
||||||
|
return NETDEV_TX_BUSY;
|
||||||
|
}
|
||||||
|
|
||||||
return NETDEV_TX_OK;
|
return NETDEV_TX_OK;
|
||||||
fail:
|
fail:
|
||||||
|
|
|
@ -66,6 +66,10 @@ struct eth1394_priv {
|
||||||
int bc_dgl; /* Outgoing broadcast datagram label */
|
int bc_dgl; /* Outgoing broadcast datagram label */
|
||||||
struct list_head ip_node_list; /* List of IP capable nodes */
|
struct list_head ip_node_list; /* List of IP capable nodes */
|
||||||
struct unit_directory *ud_list[ALL_NODES]; /* Cached unit dir list */
|
struct unit_directory *ud_list[ALL_NODES]; /* Cached unit dir list */
|
||||||
|
|
||||||
|
struct work_struct wake; /* Wake up after xmit failure */
|
||||||
|
struct net_device *wake_dev; /* Stupid backlink for .wake */
|
||||||
|
nodeid_t wake_node; /* Destination of failed xmit */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue