netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC usbnet.c] - Add packet aggregation capability
@ 2009-06-10 17:57 David Hollis
  2009-06-11 12:38 ` David Miller
  0 siblings, 1 reply; 3+ messages in thread
From: David Hollis @ 2009-06-10 17:57 UTC (permalink / raw)
  To: David Brownell; +Cc: netdev, mark, donald, Louis

[-- Attachment #1: Type: text/plain, Size: 1111 bytes --]

The ASIX AX88772 and AX88178 devices have the ability to transmit up 16K
worth of packets in a single bulk transfer.  The asix.c driver currently
doesn't support aggregating multiple packets into this buffer so that
packets can be transmitted in a single transfer.  On some systems -
notably ARM - high cpu utilization is experienced when transmitting
large numbers of packets.

The attached set of patches provided by ASIX add this capability to the
usbnet.c driver and add the implementation for the asix.c driver.  

These patches are not yet ready for merging but we are very interested
in any review and comments that can be provided at this time.

One item that I have is how to handle the portion in usbnet_probe() that
sets the hard_start_xmit handler with the change to netdevice_ops:

-	net->hard_start_xmit = usbnet_start_xmit;
+	if (info->tx_gather)
+		net->hard_start_xmit = usbnet_bundle_xmit;
+	else
+		net->hard_start_xmit = usbnet_start_xmit;


Would it be the most appropriate to merge the
usbnet_aggregate_skb_xmit() pieces into the existing usbnet_start_xmit
with appropriate conditionals?


[-- Attachment #2: usbnet.h.patch --]
[-- Type: text/x-patch, Size: 1126 bytes --]

--- ../../linux-2.6.29.4/include/linux/usb/usbnet.h	2009-05-19 07:52:34.000000000 +0800
+++ usbnet.h	2009-06-08 13:27:16.000000000 +0800
@@ -48,11 +48,15 @@
 	u32			xid;
 	u32			hard_mtu;	/* count any extra framing */
 	size_t			rx_urb_size;	/* size for rx urbs */
+	size_t			tx_threshold;
 	struct mii_if_info	mii;
 
 	/* various kinds of pending driver work */
 	struct sk_buff_head	rxq;
 	struct sk_buff_head	txq;
+	struct sk_buff_head	tx_waitq;
+	u32			tx_wait_bytes;
+	u32			tx_busy_pkt;
 	struct sk_buff_head	done;
 	struct urb		*interrupt;
 	struct tasklet_struct	bh;
@@ -114,6 +118,10 @@
 	struct sk_buff	*(*tx_fixup)(struct usbnet *dev,
 				struct sk_buff *skb, gfp_t flags);
 
+	/* aggregate tx packets */
+	struct sk_buff	*(*tx_gather)(struct usbnet *dev,
+				struct sk_buff_head *q, size_t *actual_len);
+
 	/* early initialization code, can sleep. This is for minidrivers
 	 * having 'subminidrivers' that need to do extra initialization
 	 * right after minidriver have initialized hardware. */
@@ -174,6 +182,7 @@
 	struct usbnet		*dev;
 	enum skb_state		state;
 	size_t			length;
+	size_t			pkt_cnt;
 };
 
 

[-- Attachment #3: usbnet.c.patch --]
[-- Type: text/x-patch, Size: 6363 bytes --]

--- ../../linux-2.6.29.4/drivers/net/usb/usbnet.c	2009-05-19 07:52:34.000000000 +0800
+++ usbnet.c	2009-06-10 10:18:18.000000000 +0800
@@ -45,6 +45,8 @@
 
 #define DRIVER_VERSION		"22-Aug-2005"
 
+static int usbnet_aggreate_skb_ximt (struct usbnet *dev,
+			struct sk_buff_head *q);
 
 /*-------------------------------------------------------------------------*/
 
@@ -551,6 +553,7 @@
 static int usbnet_stop (struct net_device *net)
 {
 	struct usbnet		*dev = netdev_priv(net);
+	struct sk_buff		*skb;
 	int			temp;
 	DECLARE_WAIT_QUEUE_HEAD_ONSTACK (unlink_wakeup);
 	DECLARE_WAITQUEUE (wait, current);
@@ -579,6 +582,11 @@
 	dev->wait = NULL;
 	remove_wait_queue (&unlink_wakeup, &wait);
 
+	while (!skb_queue_empty (&dev->tx_waitq)) {
+		skb = skb_dequeue(&dev->tx_waitq);
+		dev_kfree_skb (skb);
+	}
+
 	usb_kill_urb(dev->interrupt);
 
 	/* deferred work (task, timer, softirq) must also stop.
@@ -863,13 +871,20 @@
 	struct sk_buff		*skb = (struct sk_buff *) urb->context;
 	struct skb_data		*entry = (struct skb_data *) skb->cb;
 	struct usbnet		*dev = entry->dev;
+	struct driver_info	*info = dev->driver_info;
+	unsigned long		flags;
+
+	if (info->tx_gather) {
+		spin_lock_irqsave (&dev->tx_waitq.lock, flags);
+		dev->tx_busy_pkt -= entry->pkt_cnt;
+		spin_unlock (&dev->tx_waitq.lock);
+	}
 
 	if (urb->status == 0) {
-		dev->stats.tx_packets++;
 		dev->stats.tx_bytes += entry->length;
+		dev->stats.tx_packets += entry->pkt_cnt;
 	} else {
-		dev->stats.tx_errors++;
-
+		dev->stats.tx_errors += entry->pkt_cnt;
 		switch (urb->status) {
 		case -EPIPE:
 			usbnet_defer_kevent (dev, EVENT_TX_HALT);
@@ -920,6 +935,113 @@
 
 /*-------------------------------------------------------------------------*/
 
+static int usbnet_aggreate_skb_xmit (struct usbnet *dev, struct sk_buff_head *q)
+{
+
+	struct sk_buff		*skb = NULL;//, *skbnext = NULL;
+	int			pkt_cnt = 0;
+	struct skb_data		*entry;
+	int			retval = NET_XMIT_SUCCESS;
+	struct urb		*urb = NULL;
+	struct driver_info	*info = dev->driver_info;
+	size_t			actual_len = 0;
+
+	if (skb_queue_empty (q)) {
+		return retval;
+	}
+
+	spin_lock (&q->lock);
+
+	pkt_cnt = skb_queue_len (q);
+	skb = info->tx_gather (dev, q, &actual_len);
+
+	dev->tx_wait_bytes -= actual_len;
+	dev->tx_busy_pkt += pkt_cnt;
+
+	spin_unlock (&q->lock);	
+
+	if (!skb){
+		goto drop;
+	}
+
+	if (!(urb = usb_alloc_urb (0, GFP_ATOMIC))) {
+		if (netif_msg_tx_err (dev))
+			devdbg (dev, "no urb");	
+		goto drop;
+	}else{
+		entry = (struct skb_data *) skb->cb;
+		entry->length = actual_len;
+		entry->pkt_cnt = pkt_cnt;
+		entry->urb = urb;
+		entry->dev = dev;
+		entry->state = tx_start;
+
+		usb_fill_bulk_urb (urb, dev->udev, dev->out,
+				skb->data, skb->len, tx_complete, skb);
+	}
+
+	switch ((retval = usb_submit_urb (urb, GFP_ATOMIC))) {		
+	case -EPIPE:
+		netif_stop_queue (dev->net);
+		usbnet_defer_kevent (dev, EVENT_TX_HALT);
+		break;
+	default:
+		if (netif_msg_tx_err (dev))
+			devdbg (dev, "tx: submit urb err %d", retval);
+		break;
+	case 0:
+		dev->net->trans_start = jiffies;
+		__skb_queue_tail (&dev->txq, skb);
+	}
+
+	if (retval) {
+		if (netif_msg_tx_err (dev))
+			devdbg (dev, "drop, code %d", retval);
+drop:
+		spin_lock (&q->lock);
+		dev->tx_busy_pkt -= pkt_cnt;
+		spin_unlock (&q->lock);
+
+		retval = NET_XMIT_SUCCESS;
+		dev->stats.tx_dropped += pkt_cnt;
+		if (skb)
+			dev_kfree_skb_any (skb);
+		usb_free_urb (urb);
+	} else if (netif_msg_tx_queued (dev)) {
+		devdbg (dev, "> tx, len %d, type 0x%x",
+			skb->len, skb->protocol);
+	}
+
+	return retval;
+}
+
+static int usbnet_bundle_xmit (struct sk_buff *skb, struct net_device *net)
+{
+	struct usbnet		*dev = netdev_priv(net);
+	int			retval = NET_XMIT_SUCCESS;
+	unsigned long		flags;
+
+	spin_lock_irqsave (&dev->tx_waitq.lock, flags);
+	__skb_queue_tail (&dev->tx_waitq, skb);
+	dev->tx_wait_bytes += skb->len;
+	spin_unlock (&dev->tx_waitq.lock);
+
+	spin_lock (&dev->txq.lock);
+
+	if (skb_queue_empty (&dev->txq) ||
+	    (dev->tx_wait_bytes >= dev->tx_threshold) ||
+	    (dev->tx_waitq.qlen > 16)) {
+		retval = usbnet_aggreate_skb_xmit (dev, &dev->tx_waitq);
+	}
+
+	if ((dev->tx_busy_pkt + dev->tx_waitq.qlen) >= TX_QLEN (dev))
+		netif_stop_queue (net);
+
+	spin_unlock_irqrestore (&dev->txq.lock, flags);
+
+	return 	retval;
+}
+
 static int usbnet_start_xmit (struct sk_buff *skb, struct net_device *net)
 {
 	struct usbnet		*dev = netdev_priv(net);
@@ -953,6 +1075,7 @@
 	entry->dev = dev;
 	entry->state = tx_start;
 	entry->length = length;
+	entry->pkt_cnt = 1;
 
 	usb_fill_bulk_urb (urb, dev->udev, dev->out,
 			skb->data, skb->len, tx_complete, skb);
@@ -1014,6 +1137,8 @@
 	struct usbnet		*dev = (struct usbnet *) param;
 	struct sk_buff		*skb;
 	struct skb_data		*entry;
+	struct driver_info	*info = dev->driver_info;
+	unsigned long		flags;
 
 	while ((skb = skb_dequeue (&dev->done))) {
 		entry = (struct skb_data *) skb->cb;
@@ -1062,8 +1187,22 @@
 			if (dev->rxq.qlen < qlen)
 				tasklet_schedule (&dev->bh);
 		}
-		if (dev->txq.qlen < TX_QLEN (dev))
-			netif_wake_queue (dev->net);
+
+		if (info->tx_gather) {
+			spin_lock_irqsave (&dev->txq.lock, flags);
+			if (dev->txq.qlen == 0) {
+				usbnet_aggreate_skb_ximt(dev, &dev->tx_waitq);
+			}
+
+			if ((dev->tx_busy_pkt + dev->tx_waitq.qlen) <
+					 TX_QLEN (dev))
+				netif_wake_queue (dev->net);
+
+			spin_unlock_irqrestore(&dev->txq.lock, flags);
+		} else {
+			if (dev->txq.qlen < TX_QLEN (dev))
+				netif_wake_queue (dev->net);
+		}
 	}
 }
 
@@ -1156,6 +1295,7 @@
 	skb_queue_head_init (&dev->rxq);
 	skb_queue_head_init (&dev->txq);
 	skb_queue_head_init (&dev->done);
+	skb_queue_head_init (&dev->tx_waitq);
 	dev->bh.func = usbnet_bh;
 	dev->bh.data = (unsigned long) dev;
 	INIT_WORK (&dev->kevent, kevent);
@@ -1181,7 +1321,10 @@
 
 	net->change_mtu = usbnet_change_mtu;
 	net->get_stats = usbnet_get_stats;
-	net->hard_start_xmit = usbnet_start_xmit;
+	if (info->tx_gather)
+		net->hard_start_xmit = usbnet_bundle_xmit;
+	else
+		net->hard_start_xmit = usbnet_start_xmit;
 	net->open = usbnet_open;
 	net->stop = usbnet_stop;
 	net->watchdog_timeo = TX_TIMEOUT_JIFFIES;
@@ -1228,6 +1371,8 @@
 
 	if (!dev->rx_urb_size)
 		dev->rx_urb_size = dev->hard_mtu;
+	if (!dev->tx_threshold)
+		dev->tx_threshold = dev->rx_urb_size;
 	dev->maxpacket = usb_maxpacket (dev->udev, dev->out, 1);
 
 	SET_NETDEV_DEV(net, &udev->dev);

[-- Attachment #4: asix.c.patch --]
[-- Type: text/x-patch, Size: 2760 bytes --]

--- ../../../linux-2.6.29.4/drivers/net/usb/asix.c	2009-05-19 07:52:34.000000000 +0800
+++ asix.c	2009-06-10 10:15:17.000000000 +0800
@@ -354,6 +354,85 @@
 	return 1;
 }
 
+static struct sk_buff *
+ax88772_tx_gather (struct usbnet *dev, struct sk_buff_head *q,
+			size_t *actual_len)
+{
+	struct sk_buff		*skb = NULL, *skb_next = NULL;
+	int			pkt_cnt;
+	u16			pkt_size;
+	u32			skb_len = 0;
+
+	u32			packet_hdr;
+	u32			padbytes = 0xffff0000;
+	u8			padneeded = 0;
+	struct driver_info	*info = dev->driver_info;
+
+	pkt_cnt = skb_queue_len (q);
+
+	*actual_len = 0;
+	for (skb_next = q->next; skb_next != (struct sk_buff *) q;
+	     skb_next = skb_next->next){
+		*actual_len += skb_next->len;
+		pkt_size = (skb_next->len + 4 + 1) & 0xFFFE;
+		skb_len += pkt_size;
+	}
+
+	if ((skb_len % 512) == 0) {
+		skb_len += sizeof (padbytes);
+		padneeded = 1;
+	}
+
+	if (pkt_cnt > 1){
+
+		skb = dev_alloc_skb (skb_len); 
+
+		if (!skb){
+
+			while (pkt_cnt--) {
+				skb_next = __skb_dequeue(q);
+				dev_kfree_skb_any (skb_next);
+			}
+
+			devdbg (dev, "skb is NULL, len %d", skb_len);
+			return skb;
+		}
+
+		while (pkt_cnt--) {
+
+			skb_next = __skb_dequeue(q);
+
+			packet_hdr = ((skb_next->len ^ 0x0000ffff) << 16) +
+						skb_next->len;
+
+			le32_to_cpus(&packet_hdr);
+			memcpy (skb_put(skb, 4),  &packet_hdr, 4);
+			memcpy (skb_put(skb, skb_next->len),  skb_next->data,
+						skb_next->len);
+			if (skb->len & 1)
+				skb_put (skb, 1);
+			dev_kfree_skb_any (skb_next);
+		}
+
+		if (padneeded) {
+			memcpy (skb_put(skb, sizeof (padbytes)),
+					&padbytes, sizeof (padbytes));
+		}
+		
+	}else{ //only one packet to send
+		skb = __skb_dequeue(q);
+
+		skb = info->tx_fixup (dev, skb, GFP_ATOMIC);
+		if (!skb) {
+			if (netif_msg_tx_err (dev))
+				devdbg (dev, "can't tx_fixup skb");
+			return skb;
+		}
+	}
+
+	return skb;
+}
+
 static struct sk_buff *asix_tx_fixup(struct usbnet *dev, struct sk_buff *skb,
 					gfp_t flags)
 {
@@ -1011,6 +1090,7 @@
 		/* hard_mtu  is still the default - the device does not support
 		   jumbo eth frames */
 		dev->rx_urb_size = 2048;
+		dev->tx_threshold = 16384;
 	}
 	return 0;
 
@@ -1281,6 +1361,7 @@
 		/* hard_mtu  is still the default - the device does not support
 		   jumbo eth frames */
 		dev->rx_urb_size = 2048;
+		dev->tx_threshold = 16384;
 	}
 	return 0;
 
@@ -1337,6 +1418,7 @@
 	.flags = FLAG_ETHER | FLAG_FRAMING_AX,
 	.rx_fixup = asix_rx_fixup,
 	.tx_fixup = asix_tx_fixup,
+	.tx_gather = ax88772_tx_gather,
 };
 
 static const struct driver_info ax88178_info = {
@@ -1348,6 +1430,7 @@
 	.flags = FLAG_ETHER | FLAG_FRAMING_AX,
 	.rx_fixup = asix_rx_fixup,
 	.tx_fixup = asix_tx_fixup,
+	.tx_gather = ax88772_tx_gather,
 };
 
 static const struct usb_device_id	products [] = {

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [RFC usbnet.c] - Add packet aggregation capability
  2009-06-10 17:57 [RFC usbnet.c] - Add packet aggregation capability David Hollis
@ 2009-06-11 12:38 ` David Miller
  2009-06-11 13:30   ` David Hollis
  0 siblings, 1 reply; 3+ messages in thread
From: David Miller @ 2009-06-11 12:38 UTC (permalink / raw)
  To: dhollis; +Cc: david-b, netdev, mark, donald, louis

From: David Hollis <dhollis@davehollis.com>
Date: Wed, 10 Jun 2009 13:57:39 -0400

> One item that I have is how to handle the portion in usbnet_probe() that
> sets the hard_start_xmit handler with the change to netdevice_ops:
> 
> -	net->hard_start_xmit = usbnet_start_xmit;
> +	if (info->tx_gather)
> +		net->hard_start_xmit = usbnet_bundle_xmit;
> +	else
> +		net->hard_start_xmit = usbnet_start_xmit;
> 
> 
> Would it be the most appropriate to merge the
> usbnet_aggregate_skb_xmit() pieces into the existing usbnet_start_xmit
> with appropriate conditionals?

In the current tree you cannot even make this assignment.

Everything must go through a net_device_ops set of methods,
the pointer of which is const.

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [RFC usbnet.c] - Add packet aggregation capability
  2009-06-11 12:38 ` David Miller
@ 2009-06-11 13:30   ` David Hollis
  0 siblings, 0 replies; 3+ messages in thread
From: David Hollis @ 2009-06-11 13:30 UTC (permalink / raw)
  To: David Miller; +Cc: david-b, netdev, mark, donald, louis

On Thu, 2009-06-11 at 05:38 -0700, David Miller wrote:
> From: David Hollis <dhollis@davehollis.com>
> Date: Wed, 10 Jun 2009 13:57:39 -0400
> 
> > One item that I have is how to handle the portion in usbnet_probe() that
> > sets the hard_start_xmit handler with the change to netdevice_ops:
> > 
> > -	net->hard_start_xmit = usbnet_start_xmit;
> > +	if (info->tx_gather)
> > +		net->hard_start_xmit = usbnet_bundle_xmit;
> > +	else
> > +		net->hard_start_xmit = usbnet_start_xmit;
> > 
> > 
> > Would it be the most appropriate to merge the
> > usbnet_aggregate_skb_xmit() pieces into the existing usbnet_start_xmit
> > with appropriate conditionals?
> 
> In the current tree you cannot even make this assignment.
> 
> Everything must go through a net_device_ops set of methods,
> the pointer of which is const.

So I'd need to have to separate net_device_ops and set
net->hard_start_xmit to the appropriate one based on info->tx_gather
being defined.

Not a problem.


> --
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2009-06-11 14:07 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-06-10 17:57 [RFC usbnet.c] - Add packet aggregation capability David Hollis
2009-06-11 12:38 ` David Miller
2009-06-11 13:30   ` David Hollis

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).