diff options
Diffstat (limited to 'drivers/firewire/net.c')
| -rw-r--r-- | drivers/firewire/net.c | 160 | 
1 files changed, 90 insertions, 70 deletions
diff --git a/drivers/firewire/net.c b/drivers/firewire/net.c index 18fdd9703b4..1a467a91fb0 100644 --- a/drivers/firewire/net.c +++ b/drivers/firewire/net.c @@ -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);  |