|
@@ -7,6 +7,7 @@
|
|
|
*/
|
|
|
|
|
|
#include <linux/bug.h>
|
|
|
+#include <linux/delay.h>
|
|
|
#include <linux/device.h>
|
|
|
#include <linux/firewire.h>
|
|
|
#include <linux/firewire-constants.h>
|
|
@@ -26,8 +27,14 @@
|
|
|
#include <asm/unaligned.h>
|
|
|
#include <net/arp.h>
|
|
|
|
|
|
-#define FWNET_MAX_FRAGMENTS 25 /* arbitrary limit */
|
|
|
-#define FWNET_ISO_PAGE_COUNT (PAGE_SIZE < 16 * 1024 ? 4 : 2)
|
|
|
+/* rx limits */
|
|
|
+#define FWNET_MAX_FRAGMENTS 30 /* arbitrary, > TX queue depth */
|
|
|
+#define FWNET_ISO_PAGE_COUNT (PAGE_SIZE < 16*1024 ? 4 : 2)
|
|
|
+
|
|
|
+/* tx limits */
|
|
|
+#define FWNET_MAX_QUEUED_DATAGRAMS 20 /* < 64 = number of tlabels */
|
|
|
+#define FWNET_MIN_QUEUED_DATAGRAMS 10 /* should keep AT DMA busy enough */
|
|
|
+#define FWNET_TX_QUEUE_LEN FWNET_MAX_QUEUED_DATAGRAMS /* ? */
|
|
|
|
|
|
#define IEEE1394_BROADCAST_CHANNEL 31
|
|
|
#define IEEE1394_ALL_NODES (0xffc0 | 0x003f)
|
|
@@ -169,15 +176,8 @@ struct fwnet_device {
|
|
|
struct fw_address_handler handler;
|
|
|
u64 local_fifo;
|
|
|
|
|
|
- /* List of packets to be sent */
|
|
|
- struct list_head packet_list;
|
|
|
- /*
|
|
|
- * List of packets that were broadcasted. When we get an ISO interrupt
|
|
|
- * one of them has been sent
|
|
|
- */
|
|
|
- struct list_head broadcasted_list;
|
|
|
- /* List of packets that have been sent but not yet acked */
|
|
|
- struct list_head sent_list;
|
|
|
+ /* Number of tx datagrams that have been queued but not yet acked */
|
|
|
+ int queued_datagrams;
|
|
|
|
|
|
struct list_head peer_list;
|
|
|
struct fw_card *card;
|
|
@@ -195,7 +195,7 @@ struct fwnet_peer {
|
|
|
unsigned pdg_size; /* pd_list size */
|
|
|
|
|
|
u16 datagram_label; /* outgoing datagram label */
|
|
|
- unsigned max_payload; /* includes RFC2374_FRAG_HDR_SIZE overhead */
|
|
|
+ u16 max_payload; /* includes RFC2374_FRAG_HDR_SIZE overhead */
|
|
|
int node_id;
|
|
|
int generation;
|
|
|
unsigned speed;
|
|
@@ -203,22 +203,18 @@ struct fwnet_peer {
|
|
|
|
|
|
/* This is our task struct. It's used for the packet complete callback. */
|
|
|
struct fwnet_packet_task {
|
|
|
- /*
|
|
|
- * ptask can actually be on dev->packet_list, dev->broadcasted_list,
|
|
|
- * or dev->sent_list depending on its current state.
|
|
|
- */
|
|
|
- struct list_head pt_link;
|
|
|
struct fw_transaction transaction;
|
|
|
struct rfc2734_header hdr;
|
|
|
struct sk_buff *skb;
|
|
|
struct fwnet_device *dev;
|
|
|
|
|
|
int outstanding_pkts;
|
|
|
- unsigned max_payload;
|
|
|
u64 fifo_addr;
|
|
|
u16 dest_node;
|
|
|
+ u16 max_payload;
|
|
|
u8 generation;
|
|
|
u8 speed;
|
|
|
+ u8 enqueued;
|
|
|
};
|
|
|
|
|
|
/*
|
|
@@ -650,8 +646,6 @@ static int fwnet_finish_incoming_packet(struct net_device *net,
|
|
|
net->stats.rx_packets++;
|
|
|
net->stats.rx_bytes += skb->len;
|
|
|
}
|
|
|
- if (netif_queue_stopped(net))
|
|
|
- netif_wake_queue(net);
|
|
|
|
|
|
return 0;
|
|
|
|
|
@@ -660,8 +654,6 @@ static int fwnet_finish_incoming_packet(struct net_device *net,
|
|
|
net->stats.rx_dropped++;
|
|
|
|
|
|
dev_kfree_skb_any(skb);
|
|
|
- if (netif_queue_stopped(net))
|
|
|
- netif_wake_queue(net);
|
|
|
|
|
|
return -ENOENT;
|
|
|
}
|
|
@@ -793,15 +785,10 @@ static int fwnet_incoming_packet(struct fwnet_device *dev, __be32 *buf, int len,
|
|
|
* Datagram is not complete, we're done for the
|
|
|
* moment.
|
|
|
*/
|
|
|
- spin_unlock_irqrestore(&dev->lock, flags);
|
|
|
-
|
|
|
- return 0;
|
|
|
+ retval = 0;
|
|
|
fail:
|
|
|
spin_unlock_irqrestore(&dev->lock, flags);
|
|
|
|
|
|
- if (netif_queue_stopped(net))
|
|
|
- netif_wake_queue(net);
|
|
|
-
|
|
|
return retval;
|
|
|
}
|
|
|
|
|
@@ -901,11 +888,19 @@ static void fwnet_free_ptask(struct fwnet_packet_task *ptask)
|
|
|
kmem_cache_free(fwnet_packet_task_cache, ptask);
|
|
|
}
|
|
|
|
|
|
+/* Caller must hold dev->lock. */
|
|
|
+static void dec_queued_datagrams(struct fwnet_device *dev)
|
|
|
+{
|
|
|
+ if (--dev->queued_datagrams == FWNET_MIN_QUEUED_DATAGRAMS)
|
|
|
+ netif_wake_queue(dev->netdev);
|
|
|
+}
|
|
|
+
|
|
|
static int fwnet_send_packet(struct fwnet_packet_task *ptask);
|
|
|
|
|
|
static void fwnet_transmit_packet_done(struct fwnet_packet_task *ptask)
|
|
|
{
|
|
|
struct fwnet_device *dev = ptask->dev;
|
|
|
+ struct sk_buff *skb = ptask->skb;
|
|
|
unsigned long flags;
|
|
|
bool free;
|
|
|
|
|
@@ -914,10 +909,14 @@ static void fwnet_transmit_packet_done(struct fwnet_packet_task *ptask)
|
|
|
ptask->outstanding_pkts--;
|
|
|
|
|
|
/* Check whether we or the networking TX soft-IRQ is last user. */
|
|
|
- free = (ptask->outstanding_pkts == 0 && !list_empty(&ptask->pt_link));
|
|
|
+ free = (ptask->outstanding_pkts == 0 && ptask->enqueued);
|
|
|
+ if (free)
|
|
|
+ dec_queued_datagrams(dev);
|
|
|
|
|
|
- if (ptask->outstanding_pkts == 0)
|
|
|
- list_del(&ptask->pt_link);
|
|
|
+ if (ptask->outstanding_pkts == 0) {
|
|
|
+ dev->netdev->stats.tx_packets++;
|
|
|
+ dev->netdev->stats.tx_bytes += skb->len;
|
|
|
+ }
|
|
|
|
|
|
spin_unlock_irqrestore(&dev->lock, flags);
|
|
|
|
|
@@ -926,7 +925,6 @@ static void fwnet_transmit_packet_done(struct fwnet_packet_task *ptask)
|
|
|
u16 fg_off;
|
|
|
u16 datagram_label;
|
|
|
u16 lf;
|
|
|
- struct sk_buff *skb;
|
|
|
|
|
|
/* Update the ptask to point to the next fragment and send it */
|
|
|
lf = fwnet_get_hdr_lf(&ptask->hdr);
|
|
@@ -953,7 +951,7 @@ static void fwnet_transmit_packet_done(struct fwnet_packet_task *ptask)
|
|
|
datagram_label = fwnet_get_hdr_dgl(&ptask->hdr);
|
|
|
break;
|
|
|
}
|
|
|
- skb = ptask->skb;
|
|
|
+
|
|
|
skb_pull(skb, ptask->max_payload);
|
|
|
if (ptask->outstanding_pkts > 1) {
|
|
|
fwnet_make_sf_hdr(&ptask->hdr, RFC2374_HDR_INTFRAG,
|
|
@@ -970,6 +968,31 @@ static void fwnet_transmit_packet_done(struct fwnet_packet_task *ptask)
|
|
|
fwnet_free_ptask(ptask);
|
|
|
}
|
|
|
|
|
|
+static void fwnet_transmit_packet_failed(struct fwnet_packet_task *ptask)
|
|
|
+{
|
|
|
+ struct fwnet_device *dev = ptask->dev;
|
|
|
+ unsigned long flags;
|
|
|
+ bool free;
|
|
|
+
|
|
|
+ spin_lock_irqsave(&dev->lock, flags);
|
|
|
+
|
|
|
+ /* One fragment failed; don't try to send remaining fragments. */
|
|
|
+ ptask->outstanding_pkts = 0;
|
|
|
+
|
|
|
+ /* Check whether we or the networking TX soft-IRQ is last user. */
|
|
|
+ free = ptask->enqueued;
|
|
|
+ if (free)
|
|
|
+ dec_queued_datagrams(dev);
|
|
|
+
|
|
|
+ dev->netdev->stats.tx_dropped++;
|
|
|
+ dev->netdev->stats.tx_errors++;
|
|
|
+
|
|
|
+ spin_unlock_irqrestore(&dev->lock, flags);
|
|
|
+
|
|
|
+ if (free)
|
|
|
+ fwnet_free_ptask(ptask);
|
|
|
+}
|
|
|
+
|
|
|
static void fwnet_write_complete(struct fw_card *card, int rcode,
|
|
|
void *payload, size_t length, void *data)
|
|
|
{
|
|
@@ -977,11 +1000,12 @@ static void fwnet_write_complete(struct fw_card *card, int rcode,
|
|
|
|
|
|
ptask = data;
|
|
|
|
|
|
- if (rcode == RCODE_COMPLETE)
|
|
|
+ if (rcode == RCODE_COMPLETE) {
|
|
|
fwnet_transmit_packet_done(ptask);
|
|
|
- else
|
|
|
+ } else {
|
|
|
fw_error("fwnet_write_complete: failed: %x\n", rcode);
|
|
|
- /* ??? error recovery */
|
|
|
+ fwnet_transmit_packet_failed(ptask);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
static int fwnet_send_packet(struct fwnet_packet_task *ptask)
|
|
@@ -1039,9 +1063,11 @@ static int fwnet_send_packet(struct fwnet_packet_task *ptask)
|
|
|
spin_lock_irqsave(&dev->lock, flags);
|
|
|
|
|
|
/* If the AT tasklet already ran, we may be last user. */
|
|
|
- free = (ptask->outstanding_pkts == 0 && list_empty(&ptask->pt_link));
|
|
|
+ free = (ptask->outstanding_pkts == 0 && !ptask->enqueued);
|
|
|
if (!free)
|
|
|
- list_add_tail(&ptask->pt_link, &dev->broadcasted_list);
|
|
|
+ ptask->enqueued = true;
|
|
|
+ else
|
|
|
+ dec_queued_datagrams(dev);
|
|
|
|
|
|
spin_unlock_irqrestore(&dev->lock, flags);
|
|
|
|
|
@@ -1056,9 +1082,11 @@ static int fwnet_send_packet(struct fwnet_packet_task *ptask)
|
|
|
spin_lock_irqsave(&dev->lock, flags);
|
|
|
|
|
|
/* If the AT tasklet already ran, we may be last user. */
|
|
|
- free = (ptask->outstanding_pkts == 0 && list_empty(&ptask->pt_link));
|
|
|
+ free = (ptask->outstanding_pkts == 0 && !ptask->enqueued);
|
|
|
if (!free)
|
|
|
- list_add_tail(&ptask->pt_link, &dev->sent_list);
|
|
|
+ ptask->enqueued = true;
|
|
|
+ else
|
|
|
+ dec_queued_datagrams(dev);
|
|
|
|
|
|
spin_unlock_irqrestore(&dev->lock, flags);
|
|
|
|
|
@@ -1224,6 +1252,15 @@ static netdev_tx_t fwnet_tx(struct sk_buff *skb, struct net_device *net)
|
|
|
struct fwnet_peer *peer;
|
|
|
unsigned long flags;
|
|
|
|
|
|
+ spin_lock_irqsave(&dev->lock, flags);
|
|
|
+
|
|
|
+ /* Can this happen? */
|
|
|
+ if (netif_queue_stopped(dev->netdev)) {
|
|
|
+ spin_unlock_irqrestore(&dev->lock, flags);
|
|
|
+
|
|
|
+ return NETDEV_TX_BUSY;
|
|
|
+ }
|
|
|
+
|
|
|
ptask = kmem_cache_alloc(fwnet_packet_task_cache, GFP_ATOMIC);
|
|
|
if (ptask == NULL)
|
|
|
goto fail;
|
|
@@ -1242,9 +1279,6 @@ static netdev_tx_t fwnet_tx(struct sk_buff *skb, struct net_device *net)
|
|
|
proto = hdr_buf.h_proto;
|
|
|
dg_size = skb->len;
|
|
|
|
|
|
- /* serialize access to peer, including peer->datagram_label */
|
|
|
- spin_lock_irqsave(&dev->lock, flags);
|
|
|
-
|
|
|
/*
|
|
|
* Set the transmission type for the packet. ARP packets and IP
|
|
|
* broadcast packets are sent via GASP.
|
|
@@ -1266,7 +1300,7 @@ static netdev_tx_t fwnet_tx(struct sk_buff *skb, struct net_device *net)
|
|
|
|
|
|
peer = fwnet_peer_find_by_guid(dev, be64_to_cpu(guid));
|
|
|
if (!peer || peer->fifo == FWNET_NO_FIFO_ADDR)
|
|
|
- goto fail_unlock;
|
|
|
+ goto fail;
|
|
|
|
|
|
generation = peer->generation;
|
|
|
dest_node = peer->node_id;
|
|
@@ -1320,18 +1354,21 @@ static netdev_tx_t fwnet_tx(struct sk_buff *skb, struct net_device *net)
|
|
|
max_payload += RFC2374_FRAG_HDR_SIZE;
|
|
|
}
|
|
|
|
|
|
+ if (++dev->queued_datagrams == FWNET_MAX_QUEUED_DATAGRAMS)
|
|
|
+ netif_stop_queue(dev->netdev);
|
|
|
+
|
|
|
spin_unlock_irqrestore(&dev->lock, flags);
|
|
|
|
|
|
ptask->max_payload = max_payload;
|
|
|
- INIT_LIST_HEAD(&ptask->pt_link);
|
|
|
+ ptask->enqueued = 0;
|
|
|
|
|
|
fwnet_send_packet(ptask);
|
|
|
|
|
|
return NETDEV_TX_OK;
|
|
|
|
|
|
- fail_unlock:
|
|
|
- spin_unlock_irqrestore(&dev->lock, flags);
|
|
|
fail:
|
|
|
+ spin_unlock_irqrestore(&dev->lock, flags);
|
|
|
+
|
|
|
if (ptask)
|
|
|
kmem_cache_free(fwnet_packet_task_cache, ptask);
|
|
|
|
|
@@ -1377,7 +1414,7 @@ static void fwnet_init_dev(struct net_device *net)
|
|
|
net->addr_len = FWNET_ALEN;
|
|
|
net->hard_header_len = FWNET_HLEN;
|
|
|
net->type = ARPHRD_IEEE1394;
|
|
|
- net->tx_queue_len = 10;
|
|
|
+ net->tx_queue_len = FWNET_TX_QUEUE_LEN;
|
|
|
}
|
|
|
|
|
|
/* caller must hold fwnet_device_mutex */
|
|
@@ -1457,14 +1494,9 @@ static int fwnet_probe(struct device *_dev)
|
|
|
dev->broadcast_rcv_context = NULL;
|
|
|
dev->broadcast_xmt_max_payload = 0;
|
|
|
dev->broadcast_xmt_datagramlabel = 0;
|
|
|
-
|
|
|
dev->local_fifo = FWNET_NO_FIFO_ADDR;
|
|
|
-
|
|
|
- INIT_LIST_HEAD(&dev->packet_list);
|
|
|
- INIT_LIST_HEAD(&dev->broadcasted_list);
|
|
|
- INIT_LIST_HEAD(&dev->sent_list);
|
|
|
+ dev->queued_datagrams = 0;
|
|
|
INIT_LIST_HEAD(&dev->peer_list);
|
|
|
-
|
|
|
dev->card = card;
|
|
|
dev->netdev = net;
|
|
|
|
|
@@ -1522,7 +1554,7 @@ static int fwnet_remove(struct device *_dev)
|
|
|
struct fwnet_peer *peer = dev_get_drvdata(_dev);
|
|
|
struct fwnet_device *dev = peer->dev;
|
|
|
struct net_device *net;
|
|
|
- struct fwnet_packet_task *ptask, *pt_next;
|
|
|
+ int i;
|
|
|
|
|
|
mutex_lock(&fwnet_device_mutex);
|
|
|
|
|
@@ -1540,21 +1572,9 @@ static int fwnet_remove(struct device *_dev)
|
|
|
dev->card);
|
|
|
fw_iso_context_destroy(dev->broadcast_rcv_context);
|
|
|
}
|
|
|
- list_for_each_entry_safe(ptask, pt_next,
|
|
|
- &dev->packet_list, pt_link) {
|
|
|
- dev_kfree_skb_any(ptask->skb);
|
|
|
- kmem_cache_free(fwnet_packet_task_cache, ptask);
|
|
|
- }
|
|
|
- list_for_each_entry_safe(ptask, pt_next,
|
|
|
- &dev->broadcasted_list, pt_link) {
|
|
|
- dev_kfree_skb_any(ptask->skb);
|
|
|
- kmem_cache_free(fwnet_packet_task_cache, ptask);
|
|
|
- }
|
|
|
- list_for_each_entry_safe(ptask, pt_next,
|
|
|
- &dev->sent_list, pt_link) {
|
|
|
- dev_kfree_skb_any(ptask->skb);
|
|
|
- kmem_cache_free(fwnet_packet_task_cache, ptask);
|
|
|
- }
|
|
|
+ for (i = 0; dev->queued_datagrams && i < 5; i++)
|
|
|
+ ssleep(1);
|
|
|
+ WARN_ON(dev->queued_datagrams);
|
|
|
list_del(&dev->dev_link);
|
|
|
|
|
|
free_netdev(net);
|