Netdev List
 help / color / mirror / Atom feed
* Re: [7/8,RFC] CAIF Protocol Stack
From: Stefano Babic @ 2009-10-08  9:48 UTC (permalink / raw)
  To: Sjur Brændeland; +Cc: netdev, Kim Lilliestierna XX
In-Reply-To: <61D8D34BB13CFE408D154529C120E07902ED671E@eseldmw101.eemea.ericsson.se>

Sjur Brændeland wrote:
> Yes, I'll fix this documentation in a new PATCH delivery (hopefully) this week.

Ok, I will test it again when you will send to the ML.

> Hmm, the hanging might be due to a tight loop in the phyif_ser.c:ser_phy_tx function.

Agree. I can trace what happens and I can check that the tty write
function returns always 0. However, I can check that the ops field of
pser_tty is correctly set to the uart_* functions in serial_core.c and
that pser_tty->index points to the serial I chose. At least the
connection to the physical interface seems right.

> [snip]
> 		do {
> 			tty_wr =
> 			    pser_tty->ops->write(pser_tty, bufp, actual_len);

Yes, tty_wr is always 0.

> If pser_tty->ops->write() returns zero it will loop infinetly.
> I guess the proper solution would be not to loop, but to implement a write_wakeup function for the tty...?

Agree, but this is not the problem now, because pser_tty->ops->write
returns always 0.
I have supposed that "clocal" was not set on the serial, but I have
found something different.

In phyif_ser.c, I traced the result of the extract function:

[snip]
do {
                char *bufp;
                /* By default we assume that we will extract
                 * all data in one go. */
                cont = false;

                /* Extract data from the packet. */
                f.cfpkt_extract(cfpkt, sbuf_wr, WRITE_BUF_SIZE,
&actual_len);

I can state that actual_len is always wrong (at least, the first time
.ser_phy_txis called). I get a completely wrong value for it, as if this
variable is not initialized:

actual_len -1090496676

Then the uart_write function cannot work with negative numbers and
explains why it returns 0. So it seems to me at the moment there is a
bug in the extract function. What do you think about ?

Best regards,
Stefano Babic

-- 
stefano <stefano.babic@babic.homelinux.org>
GPG Key: 0x55814DDE
Fingerprint 4E85 2A66 4CBA 497A 2A7B D3BF 5973 F216 5581 4DDE

^ permalink raw reply

* Re: Nested function in drivers/of/of_mdio.c
From: Gabriel Paubert @ 2009-10-08 11:14 UTC (permalink / raw)
  To: Jérôme Pouiller
  Cc: netdev, linuxppc-dev, Andy Fleming, David S. Miller
In-Reply-To: <200910081045.12590.j.pouiller@sysmic.fr>

On Thu, Oct 08, 2009 at 10:45:12AM +0200, Jérôme Pouiller wrote:
> I did some grep on codebase. I have not found any other instances of 
> nested functions, but my regexps are not enough to be 100% sure.

>From Documentation/CodingStyle, written by the Head Penguin himself:

"Heretic people all over the world have claimed that this inconsistency
is ...  well ...  inconsistent, but all right-thinking people know that
(a) K&R are _right_ and (b) K&R are right.  Besides, functions are
special anyway (you can't nest them in C)."
                ^^^^^^^^^^^^^^^^^^^^^^^^

I interpret it as a clear prohibition of using nested functions
in the kernel.  

	Regards,
	Gabriel

^ permalink raw reply

* Re: [RFC] multiqueue changes
From: Jarek Poplawski @ 2009-10-08 12:00 UTC (permalink / raw)
  To: Eric Dumazet; +Cc: David S. Miller, Patrick McHardy, Linux Netdev List
In-Reply-To: <20091008090344.GA7409@ff.dom.local>

On Thu, Oct 08, 2009 at 09:03:44AM +0000, Jarek Poplawski wrote:
...
> num_tx_queues. But it seems we should rather use more often
> real_num_tx_queue in schedulers code like dumps and maybe more
> (like e.g. sch_multiq does).

...i.e. probably everywhere between dev_activate and dev_deactivate
all qdisc operations could use real_num_tx_queues (including a test
like: netif_is_real_multique), unless I miss something.

Jarek P.

^ permalink raw reply

* Re: [RFC] multiqueue changes
From: Eric Dumazet @ 2009-10-08 12:13 UTC (permalink / raw)
  To: Jarek Poplawski; +Cc: David S. Miller, Patrick McHardy, Linux Netdev List
In-Reply-To: <20091008120039.GA8691@ff.dom.local>

Jarek Poplawski a écrit :
> On Thu, Oct 08, 2009 at 09:03:44AM +0000, Jarek Poplawski wrote:
> ...
>> num_tx_queues. But it seems we should rather use more often
>> real_num_tx_queue in schedulers code like dumps and maybe more
>> (like e.g. sch_multiq does).
> 
> ...i.e. probably everywhere between dev_activate and dev_deactivate
> all qdisc operations could use real_num_tx_queues (including a test
> like: netif_is_real_multique), unless I miss something.

I am not sure David intent was being able to dynamically adjust real_num_tx_queue
between 1 and num_tx_queue.

For low/moderate traffic, its better to use one queue, to lower IRQ activations,
and let some cpus sleep longer.

^ permalink raw reply

* [PATCH] net: Fix OF platform drivers coldplug/hotplug when compiled as modules
From: Anton Vorontsov @ 2009-10-08 12:15 UTC (permalink / raw)
  To: David Miller; +Cc: linuxppc-dev, netdev

Some OF platform drivers are missing module device tables, so they won't
load automatically on boot. This patch fixes the issue by adding proper
MODULE_DEVICE_TABLE() macros to the drivers.

Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
---
 drivers/net/can/sja1000/sja1000_of_platform.c |    1 +
 drivers/net/fec_mpc52xx_phy.c                 |    1 +
 drivers/net/fs_enet/fs_enet-main.c            |    1 +
 drivers/net/fs_enet/mii-bitbang.c             |    1 +
 drivers/net/fs_enet/mii-fec.c                 |    1 +
 drivers/net/fsl_pq_mdio.c                     |    1 +
 drivers/net/gianfar.c                         |    4 +---
 drivers/net/ibm_newemac/core.c                |    2 ++
 drivers/net/phy/mdio-gpio.c                   |    1 +
 9 files changed, 10 insertions(+), 3 deletions(-)

diff --git a/drivers/net/can/sja1000/sja1000_of_platform.c b/drivers/net/can/sja1000/sja1000_of_platform.c
index 3373560..9dd076a 100644
--- a/drivers/net/can/sja1000/sja1000_of_platform.c
+++ b/drivers/net/can/sja1000/sja1000_of_platform.c
@@ -213,6 +213,7 @@ static struct of_device_id __devinitdata sja1000_ofp_table[] = {
 	{.compatible = "nxp,sja1000"},
 	{},
 };
+MODULE_DEVICE_TABLE(of, sja1000_ofp_table);
 
 static struct of_platform_driver sja1000_ofp_driver = {
 	.owner = THIS_MODULE,
diff --git a/drivers/net/fec_mpc52xx_phy.c b/drivers/net/fec_mpc52xx_phy.c
index 31e6d62..ee0f3c6 100644
--- a/drivers/net/fec_mpc52xx_phy.c
+++ b/drivers/net/fec_mpc52xx_phy.c
@@ -155,6 +155,7 @@ static struct of_device_id mpc52xx_fec_mdio_match[] = {
 	{ .compatible = "mpc5200b-fec-phy", },
 	{}
 };
+MODULE_DEVICE_TABLE(of, mpc52xx_fec_mdio_match);
 
 struct of_platform_driver mpc52xx_fec_mdio_driver = {
 	.name = "mpc5200b-fec-phy",
diff --git a/drivers/net/fs_enet/fs_enet-main.c b/drivers/net/fs_enet/fs_enet-main.c
index 2bc2d2b..ec2f503 100644
--- a/drivers/net/fs_enet/fs_enet-main.c
+++ b/drivers/net/fs_enet/fs_enet-main.c
@@ -1110,6 +1110,7 @@ static struct of_device_id fs_enet_match[] = {
 #endif
 	{}
 };
+MODULE_DEVICE_TABLE(of, fs_enet_match);
 
 static struct of_platform_driver fs_enet_driver = {
 	.name	= "fs_enet",
diff --git a/drivers/net/fs_enet/mii-bitbang.c b/drivers/net/fs_enet/mii-bitbang.c
index 93b481b..24ff9f4 100644
--- a/drivers/net/fs_enet/mii-bitbang.c
+++ b/drivers/net/fs_enet/mii-bitbang.c
@@ -221,6 +221,7 @@ static struct of_device_id fs_enet_mdio_bb_match[] = {
 	},
 	{},
 };
+MODULE_DEVICE_TABLE(of, fs_enet_mdio_bb_match);
 
 static struct of_platform_driver fs_enet_bb_mdio_driver = {
 	.name = "fsl-bb-mdio",
diff --git a/drivers/net/fs_enet/mii-fec.c b/drivers/net/fs_enet/mii-fec.c
index a2d69c1..96eba42 100644
--- a/drivers/net/fs_enet/mii-fec.c
+++ b/drivers/net/fs_enet/mii-fec.c
@@ -219,6 +219,7 @@ static struct of_device_id fs_enet_mdio_fec_match[] = {
 #endif
 	{},
 };
+MODULE_DEVICE_TABLE(of, fs_enet_mdio_fec_match);
 
 static struct of_platform_driver fs_enet_fec_mdio_driver = {
 	.name = "fsl-fec-mdio",
diff --git a/drivers/net/fsl_pq_mdio.c b/drivers/net/fsl_pq_mdio.c
index d167090..6ac4648 100644
--- a/drivers/net/fsl_pq_mdio.c
+++ b/drivers/net/fsl_pq_mdio.c
@@ -407,6 +407,7 @@ static struct of_device_id fsl_pq_mdio_match[] = {
 	},
 	{},
 };
+MODULE_DEVICE_TABLE(of, fsl_pq_mdio_match);
 
 static struct of_platform_driver fsl_pq_mdio_driver = {
 	.name = "fsl-pq_mdio",
diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c
index 1e5289f..5bf31f1 100644
--- a/drivers/net/gianfar.c
+++ b/drivers/net/gianfar.c
@@ -2325,9 +2325,6 @@ static irqreturn_t gfar_error(int irq, void *dev_id)
 	return IRQ_HANDLED;
 }
 
-/* work with hotplug and coldplug */
-MODULE_ALIAS("platform:fsl-gianfar");
-
 static struct of_device_id gfar_match[] =
 {
 	{
@@ -2336,6 +2333,7 @@ static struct of_device_id gfar_match[] =
 	},
 	{},
 };
+MODULE_DEVICE_TABLE(of, gfar_match);
 
 /* Structure for a device driver */
 static struct of_platform_driver gfar_driver = {
diff --git a/drivers/net/ibm_newemac/core.c b/drivers/net/ibm_newemac/core.c
index 89c82c5..4baa37c 100644
--- a/drivers/net/ibm_newemac/core.c
+++ b/drivers/net/ibm_newemac/core.c
@@ -24,6 +24,7 @@
  *
  */
 
+#include <linux/module.h>
 #include <linux/sched.h>
 #include <linux/string.h>
 #include <linux/errno.h>
@@ -2985,6 +2986,7 @@ static struct of_device_id emac_match[] =
 	},
 	{},
 };
+MODULE_DEVICE_TABLE(of, emac_match);
 
 static struct of_platform_driver emac_driver = {
 	.name = "emac",
diff --git a/drivers/net/phy/mdio-gpio.c b/drivers/net/phy/mdio-gpio.c
index 250e10f..8659d34 100644
--- a/drivers/net/phy/mdio-gpio.c
+++ b/drivers/net/phy/mdio-gpio.c
@@ -238,6 +238,7 @@ static struct of_device_id mdio_ofgpio_match[] = {
 	},
 	{},
 };
+MODULE_DEVICE_TABLE(of, mdio_ofgpio_match);
 
 static struct of_platform_driver mdio_ofgpio_driver = {
 	.name = "mdio-gpio",
-- 
1.6.3.3

^ permalink raw reply related

* Re: [RFC] multiqueue changes
From: Jarek Poplawski @ 2009-10-08 12:53 UTC (permalink / raw)
  To: Eric Dumazet; +Cc: David S. Miller, Patrick McHardy, Linux Netdev List
In-Reply-To: <4ACDD762.9080101@gmail.com>

On Thu, Oct 08, 2009 at 02:13:22PM +0200, Eric Dumazet wrote:
> Jarek Poplawski a écrit :
> > On Thu, Oct 08, 2009 at 09:03:44AM +0000, Jarek Poplawski wrote:
> > ...
> >> num_tx_queues. But it seems we should rather use more often
> >> real_num_tx_queue in schedulers code like dumps and maybe more
> >> (like e.g. sch_multiq does).
> > 
> > ...i.e. probably everywhere between dev_activate and dev_deactivate
> > all qdisc operations could use real_num_tx_queues (including a test
> > like: netif_is_real_multique), unless I miss something.
> 
> I am not sure David intent was being able to dynamically adjust real_num_tx_queue
> between 1 and num_tx_queue.

If so, then it looks like some drivers could misuse this intent.

Thanks for the explanation,
Jarek P.

^ permalink raw reply

* [PATCH] net: Use routing mark from skb in multicast forwarding routing lookups
From: Atis Elsts @ 2009-10-08 13:19 UTC (permalink / raw)
  To: David Miller; +Cc: netdev, panther, eric.dumazet, brian.haley, zenczykowski
In-Reply-To: <20091007.135627.96995518.davem@davemloft.net>

On Wednesday 07 October 2009 23:56:27 David Miller wrote:
> 
> Ok submit just the else part and we'll have a look at it.
> 
Here is a try.

Use routing mark from skb in routing lookup in IPv4 and IPv6 multicast forwarding code.
Signed-off-by: Atis Elsts <atis@mikrotik.com>
---
 net/ipv4/ipmr.c  |    1 +
 net/ipv6/ip6mr.c |    1 +
 2 files changed, 2 insertions(+)

diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c
index 630a56d..5522cf8 100644
--- a/net/ipv4/ipmr.c
+++ b/net/ipv4/ipmr.c
@@ -1248,6 +1248,7 @@ static void ipmr_queue_xmit(struct sk_buff *skb, struct mfc_cache *c, int vifi)
 		encap = sizeof(struct iphdr);
 	} else {
 		struct flowi fl = { .oif = vif->link,
+				    .mark = skb->mark,
 				    .nl_u = { .ip4_u =
 					      { .daddr = iph->daddr,
 						.tos = RT_TOS(iph->tos) } },
diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c
index 7161539..d98df54 100644
--- a/net/ipv6/ip6mr.c
+++ b/net/ipv6/ip6mr.c
@@ -1523,6 +1523,7 @@ static int ip6mr_forward2(struct sk_buff *skb, struct mfc6_cache *c, int vifi)
 
 	fl = (struct flowi) {
 		.oif = vif->link,
+		.mark = skb->mark,
 		.nl_u = { .ip6_u =
 				{ .daddr = ipv6h->daddr, }
 		}

^ permalink raw reply related

* Re: [PATCH] Generalize socket rx gap / receive queue overflow cmsg
From: Neil Horman @ 2009-10-08 13:54 UTC (permalink / raw)
  To: Eric Dumazet; +Cc: netdev, davem, socketcan
In-Reply-To: <4ACD3AC8.608@gmail.com>

> 
> > +	if (check_drops) {
> > +		skb = skb_recv_datagram(sk, flags|MSG_PEEK,
> > +				flags & MSG_DONTWAIT, &err);
> 
> 	Ouch, this is too expensive, please find another way :)
> 
> > +		if (skb) {
> > +			gap = skb->dropcount;
> > +			consume_skb(skb);
> > +		}
> > +	}
> > +
I'm not sure that I see the expense here, and what expense there is, I don't see
how it avoidable.  In order to do this reporting at the socket level, we need to
look at the skb at the head of the receive queue.  But we need to do so in a way
thats consistent with the flags being passed in (i.e. if this is a blocking
socket, we need to block here until something is available to read).  Then its
just an atomic_inc on skb->users, followed by a dec in the consume_skb.  I could
implement the logic for DONTWAIT myself, and skip the atomic_inc/dec, but I'm
not sure thats much of a savings.  If you have another thought, I'm certainly
open to it.

Neil

> > +	rc = sock->ops->recvmsg(iocb, sock, msg, size, flags);
> > +
> > +	if (check_drops && (rc > 0))
> 
> 		&& gap != 0
> 
> > +		put_cmsg(msg, SOL_SOCKET, SO_RXQ_OVFL, sizeof(__u32), &gap);
> > +
> 
> 

^ permalink raw reply

* Re: [7/8,RFC] CAIF Protocol Stack
From: Stefano Babic @ 2009-10-08 14:03 UTC (permalink / raw)
  To: Sjur Brændeland; +Cc: netdev, Kim Lilliestierna XX
In-Reply-To: <61D8D34BB13CFE408D154529C120E07902ED671E@eseldmw101.eemea.ericsson.se>

Sjur Brændeland wrote:

> If pser_tty->ops->write() returns zero it will loop infinetly.
> I guess the proper solution would be not to loop, but to implement a write_wakeup function for the tty...?

I discovered there is a production bug in the Makefile and the setup of
the extract function in cfcnfg_get_packet_funcs() is inconsistent.
Indeed, I traced the address of the extract function and I can find that
the address does not point to cfpkt_extract(), as I assumed.

The problem is due to the usage of the define CAIF_USE_SKB. This is used
in caif_layer.h, but some files (net/caif/*) are compiled with the macro
defined, while the drivers (drivers/net/caif/*) not.
Rather I did not get an "oops", because a valid pointer was set....but
to the wrong function !

I have also seen that CAIF_USE_SKB is not used in
cfpkt_get_packet_funcs, and this generates a problem if CAIF_USE_SKB is
not set, because the "fromnative" and "tonative" functions are always
set, even if they do not belong to the structure.

IMHO should be possible to get rid of the usage of CAIF_USE_SKB in the
structure definition (in caif_layer.h) and to provide always the same
structure definition in both case. I would prefer to set the values of
cfpkt_fromnative and cfpkt_tonative to NULL, instead of reducing the
size of the structure.

I mean, something like this:

void cfpkt_get_packet_funcs(caif_packet_funcs_t *f)
{
#ifdef CAIF_USE_SKB
        f->cfpkt_fromnative = cfpkt_fromnative;
        f->cfpkt_tonative = cfpkt_tonative;
#else
	f->cfpkt_fromnative = NULL;
        f->cfpkt_tonative = NULL;
#endif

I am not sure I understood the meaning of using this structure, because
at the moment the setup is fixed and I cannot find any point in code
where the structure is assigned to another set of functions. Probably
you arrange to have multiple choices in future, I can suppose.

What about to pass directly the pointer to the structure instead of
copying returning its value ? It seems not necessary to me, I changed
cfpkt_get_packet_funcs in this direction.

Meanwhile, it seems some bytes are sent now to the physical interface.

<caif_chropen:797, TRACE> [caif_chropen:797] WAIT FOR CONNECT RESPONSE
<caif_chropen:820, TRACE> caif_open: connect timed out

However, I get no connection, but probably this is another problem....

Best Regards,
Stefano Babic

-- 
stefano <stefano.babic@babic.homelinux.org>
GPG Key: 0x55814DDE
Fingerprint 4E85 2A66 4CBA 497A 2A7B D3BF 5973 F216 5581 4DDE


^ permalink raw reply

* [PATCH -next][resubmit] cdc_ether: additional Ericsson MBM PID's to the whitelist
From: Torgny Johansson @ 2009-10-08 14:20 UTC (permalink / raw)
  To: davem; +Cc: netdev

Hi!

This is a new attempt to submit a patch that adds seven more PID's to the whitelist set of device. Been having some problems submitting patches with Outlook (yes I know that is a baaad idea :)) but hopefully this will get sent right.

Devices added to the whitelist:
 - Ericsson Mobile Broadband variants (F3607gw and F3307)
 - Dell F3607gw variants
 - Toshiba F3607gw variants

Regards 
Torgny Johansson

Signed-off-by: Torgny Johansson <torgny.johansson@ericsson.com>

--

diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig
index c47237c..32d9356 100644
--- a/drivers/net/usb/Kconfig
+++ b/drivers/net/usb/Kconfig
@@ -174,7 +174,7 @@ config USB_NET_CDCETHER
 	    * Ericsson Mobile Broadband Module (all variants)
  	    * Motorola (DM100 and SB4100)
  	    * Broadcom Cable Modem (reference design)
-	    * Toshiba (PCX1100U and F3507g)
+	    * Toshiba (PCX1100U and F3507g/F3607gw)
 	    * ...
 
 	  This driver creates an interface named "ethX", where X depends on
diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c
index 4a6aff5..5d99106 100644
--- a/drivers/net/usb/cdc_ether.c
+++ b/drivers/net/usb/cdc_ether.c
@@ -544,20 +544,55 @@ static const struct usb_device_id	products [] = {
 			USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE),
 	.driver_info = (unsigned long) &cdc_info,
 }, {
-	/* Ericsson F3307 */
+	/* Ericsson F3607gw ver 2 */
+	USB_DEVICE_AND_INTERFACE_INFO(0x0bdb, 0x1905, USB_CLASS_COMM,
+			USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE),
+	.driver_info = (unsigned long) &cdc_info,
+}, {
+	/* Ericsson F3607gw ver 3 */
 	USB_DEVICE_AND_INTERFACE_INFO(0x0bdb, 0x1906, USB_CLASS_COMM,
 			USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE),
 	.driver_info = (unsigned long) &cdc_info,
 }, {
+	/* Ericsson F3307 */
+	USB_DEVICE_AND_INTERFACE_INFO(0x0bdb, 0x190a, USB_CLASS_COMM,
+			USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE),
+	.driver_info = (unsigned long) &cdc_info,
+}, {
+	/* Ericsson F3307 ver 2 */
+	USB_DEVICE_AND_INTERFACE_INFO(0x0bdb, 0x1909, USB_CLASS_COMM,
+			USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE),
+	.driver_info = (unsigned long) &cdc_info,
+}, {
 	/* Toshiba F3507g */
 	USB_DEVICE_AND_INTERFACE_INFO(0x0930, 0x130b, USB_CLASS_COMM,
 			USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE),
 	.driver_info = (unsigned long) &cdc_info,
 }, {
+	/* Toshiba F3607gw */
+	USB_DEVICE_AND_INTERFACE_INFO(0x0930, 0x130c, USB_CLASS_COMM,
+			USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE),
+	.driver_info = (unsigned long) &cdc_info,
+}, {
+	/* Toshiba F3607gw ver 2 */
+	USB_DEVICE_AND_INTERFACE_INFO(0x0930, 0x1311, USB_CLASS_COMM,
+			USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE),
+	.driver_info = (unsigned long) &cdc_info,
+}, {
 	/* Dell F3507g */
 	USB_DEVICE_AND_INTERFACE_INFO(0x413c, 0x8147, USB_CLASS_COMM,
 			USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE),
 	.driver_info = (unsigned long) &cdc_info,
+}, {
+	/* Dell F3607gw */
+	USB_DEVICE_AND_INTERFACE_INFO(0x413c, 0x8183, USB_CLASS_COMM,
+			USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE),
+	.driver_info = (unsigned long) &cdc_info,
+}, {
+	/* Dell F3607gw ver 2 */
+	USB_DEVICE_AND_INTERFACE_INFO(0x413c, 0x8184, USB_CLASS_COMM,
+			USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE),
+	.driver_info = (unsigned long) &cdc_info,
 },
 	{ },		// END
 };

--

^ permalink raw reply related

* Re: [PATCH] Generalize socket rx gap / receive queue overflow cmsg
From: Eric Dumazet @ 2009-10-08 14:45 UTC (permalink / raw)
  To: Neil Horman; +Cc: netdev, davem, socketcan
In-Reply-To: <20091008135435.GA23928@hmsreliant.think-freely.org>

Neil Horman a écrit :
>>> +	if (check_drops) {
>>> +		skb = skb_recv_datagram(sk, flags|MSG_PEEK,
>>> +				flags & MSG_DONTWAIT, &err);
>> 	Ouch, this is too expensive, please find another way :)
>>
>>> +		if (skb) {
>>> +			gap = skb->dropcount;
>>> +			consume_skb(skb);
>>> +		}
>>> +	}
>>> +
> I'm not sure that I see the expense here, and what expense there is, I don't see
> how it avoidable.  In order to do this reporting at the socket level, we need to
> look at the skb at the head of the receive queue.  But we need to do so in a way
> thats consistent with the flags being passed in (i.e. if this is a blocking
> socket, we need to block here until something is available to read).  Then its
> just an atomic_inc on skb->users, followed by a dec in the consume_skb.  I could
> implement the logic for DONTWAIT myself, and skip the atomic_inc/dec, but I'm
> not sure thats much of a savings.  If you have another thought, I'm certainly
> open to it.

The expense is a lot of atomic ops. You forgot the lock, so thats four atomic ops.

You can do all this with no extra atomics.

All you need is some function with (struct msghdr *msg, struct sock *sk, struct sk_buff *skb)
triplet.

hint : sock_recv_timestamp(struct msghdr *msg, struct sock *sk, struct sk_buff *skb)

Could be renamed to something else if you want...

sock_recv_ts_or_drops() or whatever

^ permalink raw reply

* Re: [PATCH 2/2] [RFC] Add c/r support for connected INET sockets
From: John Dykstra @ 2009-10-08 14:47 UTC (permalink / raw)
  To: Dan Smith; +Cc: containers, netdev
In-Reply-To: <1254932945-12578-3-git-send-email-danms@us.ibm.com>

On Wed, 2009-10-07 at 09:29 -0700, Dan Smith wrote:
> This patch adds basic support for C/R of open INET sockets.  I think
> that
> all the important bits of the TCP and ICSK socket structures is saved,
> but I think there is still some additional IPv6 stuff that needs to be
> handled.

I think this patch breaks code that was already in do_sock_restore():

        struct sock *do_sock_restore(struct ckpt_ctx *ctx)
        {
        	struct ckpt_hdr_socket *h;
        	struct socket *sock;
        	int ret;
        
        	h = ckpt_read_obj_type(ctx, sizeof(*h), CKPT_HDR_SOCKET);
        	if (IS_ERR(h))
        		return ERR_PTR(PTR_ERR(h));
        
        	/* silently clear flags, e.g. SOCK_NONBLOCK or SOCK_CLOEXEC */
        	h->sock.type &= SOCK_TYPE_MASK;
        
        	ret = sock_create(h->sock_common.family, h->sock.type, 0, &sock);
        	if (ret < 0)
        		goto err;
        

You're passing 0 as the protocol value to sock_create().  This
ultimately gets passed to the address family's create() function.  

inet_create() (and its IPv6 companion) use that protocol value as the
key when they search for the proper inet_protosw, which in turn gets
mapped to the struct proto and passed to sk_prot_alloc().

In address families INET and AF_INET6, the struct sock is different
sizes for different protocols.  This is implemented by the struct proto
specifying which cache the struct sock comes from.

So by passing in 0 all the time to sock_create(), you're getting a
struct sock that may not be the right size.  Memory corruption and
madness follow.

  --  John


^ permalink raw reply

* [PATCH] net: Fix struct sock bitfield annotation
From: Eric Dumazet @ 2009-10-08 15:16 UTC (permalink / raw)
  To: David S. Miller; +Cc: Linux Netdev List, Vegard Nossum, Ingo Molnar

Since commit a98b65a3 (net: annotate struct sock bitfield), we lost 8 bytes
in struct sock on 64bits arches because of kmemcheck_bitfield_end(flags) misplacement.


struct good {
	int		begin_flags[0];
	unsigned char	a : 8;
	unsigned char	b;
	unsigned short	c;
	int		end_flags[0];
	int		sk_rcvbuf;
	void           *ptr;
};
struct bad {
	int		begin_flags[0];
	unsigned char	a : 8;
	int		end_flags[0];
	unsigned char	b;
	unsigned short	c;
	int		sk_rcvbuf;
	void           *ptr;
};
sizeof(struct good) = 16, sizeof(struct bad) = 24

Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
---
 include/net/sock.h |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/include/net/sock.h b/include/net/sock.h
index 1621935..ecfb831 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -229,9 +229,9 @@ struct sock {
 	unsigned char		sk_shutdown : 2,
 				sk_no_check : 2,
 				sk_userlocks : 4;
-	kmemcheck_bitfield_end(flags);
 	unsigned char		sk_protocol;
 	unsigned short		sk_type;
+	kmemcheck_bitfield_end(flags);
 	int			sk_rcvbuf;
 	socket_lock_t		sk_lock;
 	/*

^ permalink raw reply related

* net-2.6 build error on powerpc
From: Ron Mercer @ 2009-10-08 15:20 UTC (permalink / raw)
  To: netdev; +Cc: ron.mercer, davem

I recently grabbed the latest net-2.6 kernel for a powerpc test box and
got this compile error.

  CC      arch/powerpc/kernel/cputable.o
  CC      arch/powerpc/kernel/ptrace.o
  CC      arch/powerpc/kernel/syscalls.o
  CC      arch/powerpc/kernel/irq.o
  CC      arch/powerpc/kernel/align.o
  CC      arch/powerpc/kernel/signal_32.o
  CC      arch/powerpc/kernel/pmc.o
  CC      arch/powerpc/kernel/vdso.o
  cc1: warnings being treated as errors
  arch/powerpc/kernel/vdso.c:78: warning: alignment of
  âso_data_storeâs greater than maximum object file
  alignment.  Using 32768
  make[1]: *** [arch/powerpc/kernel/vdso.o] Error 1
  make: *** [arch/powerpc/kernel] Error 2
  [root@localhost linux-2.6-net.git]#



  Any suggestions would be appreciated.


^ permalink raw reply

* Re: [PATCH 2/2] [RFC] Add c/r support for connected INET sockets
From: Dan Smith @ 2009-10-08 15:41 UTC (permalink / raw)
  To: John Dykstra
  Cc: containers-qjLDD68F18O7TbgM5vRIOg, netdev-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1255013233.8033.14.camel@Maple>

JD> You're passing 0 as the protocol value to sock_create().  This
JD> ultimately gets passed to the address family's create() function.

The fix is to pass in the previous version of sk_protocol instead of
zero, right?  It looks like inet_create() has enough intelligence to
weed out invalid values for us too...

-- 
Dan Smith
IBM Linux Technology Center
email: danms-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org

^ permalink raw reply

* [PATCH][v2] ibm_newemac: Added 16K Tx FIFO size support for EMAC4
From: Dave Mitchell @ 2009-10-08 16:32 UTC (permalink / raw)
  To: netdev; +Cc: linuxppc-dev, Dave Mitchell

Some of the EMAC V4 implementations support 16K Tx FIFOs. This
patch adds support for this functionality and fixes typos in the
Tx FIFO size error messages.

Signed-off-by: Dave Mitchell <dmitchell@appliedmicro.com>
Acked-by: Prodyut Hazarika <phazarika@appliedmicro.com>
Acked-by: Victor Gallardo <vgallardo@appliedmicro.com>
Acked-by: Loc Ho <lho@appliedmicro.com>
---
 v1->v2: local date/time was out-of-sync and thus mail was as well
 
 drivers/net/ibm_newemac/core.c |    7 +++++--
 drivers/net/ibm_newemac/emac.h |    1 +
 2 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/drivers/net/ibm_newemac/core.c b/drivers/net/ibm_newemac/core.c
index 89c82c5..c6591cb 100644
--- a/drivers/net/ibm_newemac/core.c
+++ b/drivers/net/ibm_newemac/core.c
@@ -443,7 +443,7 @@ static u32 __emac_calc_base_mr1(struct emac_instance *dev, int tx_size, int rx_s
 		ret |= EMAC_MR1_TFS_2K;
 		break;
 	default:
-		printk(KERN_WARNING "%s: Unknown Rx FIFO size %d\n",
+		printk(KERN_WARNING "%s: Unknown Tx FIFO size %d\n",
 		       dev->ndev->name, tx_size);
 	}
 
@@ -470,6 +470,9 @@ static u32 __emac4_calc_base_mr1(struct emac_instance *dev, int tx_size, int rx_
 	DBG2(dev, "__emac4_calc_base_mr1" NL);
 
 	switch(tx_size) {
+	case 16384:
+		ret |= EMAC4_MR1_TFS_16K;
+		break;
 	case 4096:
 		ret |= EMAC4_MR1_TFS_4K;
 		break;
@@ -477,7 +480,7 @@ static u32 __emac4_calc_base_mr1(struct emac_instance *dev, int tx_size, int rx_
 		ret |= EMAC4_MR1_TFS_2K;
 		break;
 	default:
-		printk(KERN_WARNING "%s: Unknown Rx FIFO size %d\n",
+		printk(KERN_WARNING "%s: Unknown Tx FIFO size %d\n",
 		       dev->ndev->name, tx_size);
 	}
 
diff --git a/drivers/net/ibm_newemac/emac.h b/drivers/net/ibm_newemac/emac.h
index 0afc2cf..d34adf9 100644
--- a/drivers/net/ibm_newemac/emac.h
+++ b/drivers/net/ibm_newemac/emac.h
@@ -153,6 +153,7 @@ struct emac_regs {
 #define EMAC4_MR1_RFS_16K		0x00280000
 #define EMAC4_MR1_TFS_2K       		0x00020000
 #define EMAC4_MR1_TFS_4K		0x00030000
+#define EMAC4_MR1_TFS_16K		0x00050000
 #define EMAC4_MR1_TR			0x00008000
 #define EMAC4_MR1_MWSW_001		0x00001000
 #define EMAC4_MR1_JPSM			0x00000800
-- 
1.6.3.2

^ permalink raw reply related

* Re: [PATCH] Generalize socket rx gap / receive queue overflow cmsg
From: Neil Horman @ 2009-10-08 17:20 UTC (permalink / raw)
  To: Eric Dumazet; +Cc: netdev, davem, socketcan
In-Reply-To: <4ACDFB1C.4090107@gmail.com>

On Thu, Oct 08, 2009 at 04:45:48PM +0200, Eric Dumazet wrote:
> Neil Horman a écrit :
> >>> +	if (check_drops) {
> >>> +		skb = skb_recv_datagram(sk, flags|MSG_PEEK,
> >>> +				flags & MSG_DONTWAIT, &err);
> >> 	Ouch, this is too expensive, please find another way :)
> >>
> >>> +		if (skb) {
> >>> +			gap = skb->dropcount;
> >>> +			consume_skb(skb);
> >>> +		}
> >>> +	}
> >>> +
> > I'm not sure that I see the expense here, and what expense there is, I don't see
> > how it avoidable.  In order to do this reporting at the socket level, we need to
> > look at the skb at the head of the receive queue.  But we need to do so in a way
> > thats consistent with the flags being passed in (i.e. if this is a blocking
> > socket, we need to block here until something is available to read).  Then its
> > just an atomic_inc on skb->users, followed by a dec in the consume_skb.  I could
> > implement the logic for DONTWAIT myself, and skip the atomic_inc/dec, but I'm
> > not sure thats much of a savings.  If you have another thought, I'm certainly
> > open to it.
> 
> The expense is a lot of atomic ops. You forgot the lock, so thats four atomic ops.
> 
> You can do all this with no extra atomics.
> 
> All you need is some function with (struct msghdr *msg, struct sock *sk, struct sk_buff *skb)
> triplet.
> 
> hint : sock_recv_timestamp(struct msghdr *msg, struct sock *sk, struct sk_buff *skb)
> 
> Could be renamed to something else if you want...
> 
> sock_recv_ts_or_drops() or whatever
Ok, but that will require moving the flag that we're triggering this on down
into the sock structure, and not doing the check up in __sock_recvmsg, but I
suppose thats fine.  Ok, I'll repost soon.  Thanks!
Neil

> --
> 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

* Re: [PATCH 2/2] [RFC] Add c/r support for connected INET sockets
From: John Dykstra @ 2009-10-08 17:31 UTC (permalink / raw)
  To: Dan Smith
  Cc: containers-qjLDD68F18O7TbgM5vRIOg, netdev-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <87ab01gag7.fsf-FLMGYpZoEPULwtHQx/6qkW3U47Q5hpJU@public.gmane.org>

On Thu, 2009-10-08 at 08:41 -0700, Dan Smith wrote:
> JD> You're passing 0 as the protocol value to sock_create().  This
> JD> ultimately gets passed to the address family's create() function.
> 
> The fix is to pass in the previous version of sk_protocol instead of
> zero, right?

Yep.  It sort of messes up your separation between layers, though.

  --  John

^ permalink raw reply

* Re: [PATCH 2/2] [RFC] Add c/r support for connected INET sockets
From: Dan Smith @ 2009-10-08 17:34 UTC (permalink / raw)
  To: John Dykstra
  Cc: containers-qjLDD68F18O7TbgM5vRIOg, netdev-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1255023087.8033.17.camel@Maple>

JD> Yep.  It sort of messes up your separation between layers, though.

In what way?  It's just this:

diff --git a/net/checkpoint.c b/net/checkpoint.c
index 3d6da68..f74ebe4 100644
--- a/net/checkpoint.c
+++ b/net/checkpoint.c
@@ -687,7 +687,8 @@ struct sock *do_sock_restore(struct ckpt_ctx *ctx)
        /* silently clear flags, e.g. SOCK_NONBLOCK or SOCK_CLOEXEC */
        h->sock.type &= SOCK_TYPE_MASK;
 
-       ret = sock_create(h->sock_common.family, h->sock.type, 0, &sock);
+       ret = sock_create(h->sock_common.family, h->sock.type,
+                         h->sock.protocol, &sock);
        if (ret < 0)
                goto err;

Which seems like a why-didn't-I-do-that-in-the-first-place sort of
thing...   Am I missing something?

-- 
Dan Smith
IBM Linux Technology Center
email: danms-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org

^ permalink raw reply related

* Re: [PATCH 2.6.32-rc3] net: VMware virtual Ethernet NIC driver: vmxnet3
From: Shreyas Bhatewara @ 2009-10-08 17:59 UTC (permalink / raw)
  To: linux-kernel, netdev, Stephen Hemminger, David S. Miller
  Cc: pv-drivers, Greg Kroah-Hartman, virtualization, Chris Wright,
	Anthony Liguori, Andrew Morton
In-Reply-To: <alpine.LRH.2.00.0910061202510.24669@localhost.localdomain>


Hello all,

I do not mean to be bothersome but this thread has been unusually silent.
Could you please review the patch for me and reply with your comments / 
acks ?

Thanks.
->Shreyas


On Tue, 6 Oct 2009, Shreyas Bhatewara wrote:

> 
> 
> Ethernet NIC driver for VMware's vmxnet3
> 
> From: Shreyas Bhatewara <sbhatewara@vmware.com>
> 
> This patch adds driver support for VMware's virtual Ethernet NIC: vmxnet3
> Guests running on VMware hypervisors supporting vmxnet3 device will thus have
> access to improved network functionalities and performance.
> 
> Signed-off-by: Shreyas Bhatewara <sbhatewara@vmware.com>
> Signed-off-by: Bhavesh Davda <bhavesh@vmware.com>
> Signed-off-by: Ronghua Zhang <ronghua@vmware.com>
> 
> ---
> 
> VMware virtual Ethernet NIC Driver : vmxnet3 - v3
> 
> Changelog (v3-v2)
> - use ethtool instead of a module param to control hw LRO feature
> - rebase to 2.6.32-rc3
> 
> Changelog (v2-v1)
> - Rebased the patch to v2.6.32-rc1
> - Changed all uint32_t types to u32 and friends
> - Removed duplicate max queue size from upt1_defs.h
> - Replaced #defines by enum
> - uniform spacing between datatype and membername in structures
> - removed some noisy printks, eliminated some BUG_ONs
> - corrected arguments of kcalloc
> - used pc_request_selected_regions, pci_enable_dev_mem
> - elminated not-so-useful wrapper functions, used eth_op_ functions instead
> - used strlcpy
> - used get_sset_counts instead of get_stats_count
> - used net_device_stats from struct net_device
> 
> 
> Please review the patch and provide your feedback/comments for 
> upstreaming.
> 
> Thanking you
> ->Shreyas
> 
> ---
> 
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 09a2028..0509f26 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -5628,6 +5628,13 @@ S:	Maintained
>  F:	drivers/vlynq/vlynq.c
>  F:	include/linux/vlynq.h
>  
> +VMWARE VMXNET3 ETHERNET DRIVER
> +M:     Shreyas Bhatewara <sbhatewara@vmware.com>
> +M:     VMware, Inc. <pv-drivers@vmware.com>
> +L:     netdev@vger.kernel.org
> +S:     Maintained
> +F:     drivers/net/vmxnet3/
> +
>  VOLTAGE AND CURRENT REGULATOR FRAMEWORK
>  M:	Liam Girdwood <lrg@slimlogic.co.uk>
>  M:	Mark Brown <broonie@opensource.wolfsonmicro.com>
> diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
> index 7127760..9789de2 100644
> --- a/drivers/net/Kconfig
> +++ b/drivers/net/Kconfig
> @@ -3230,4 +3230,12 @@ config VIRTIO_NET
>  	  This is the virtual network driver for virtio.  It can be used with
>            lguest or QEMU based VMMs (like KVM or Xen).  Say Y or M.
>  
> +config VMXNET3
> +       tristate "VMware VMXNET3 ethernet driver"
> +       depends on PCI && X86
> +       help
> +         This driver supports VMware's vmxnet3 virtual ethernet NIC.
> +         To compile this driver as a module, choose M here: the
> +         module will be called vmxnet3.
> +
>  endif # NETDEVICES
> diff --git a/drivers/net/Makefile b/drivers/net/Makefile
> index d866b8c..d3a0418 100644
> --- a/drivers/net/Makefile
> +++ b/drivers/net/Makefile
> @@ -26,6 +26,7 @@ obj-$(CONFIG_TEHUTI) += tehuti.o
>  obj-$(CONFIG_ENIC) += enic/
>  obj-$(CONFIG_JME) += jme.o
>  obj-$(CONFIG_BE2NET) += benet/
> +obj-$(CONFIG_VMXNET3) += vmxnet3/
>  
>  gianfar_driver-objs := gianfar.o \
>  		gianfar_ethtool.o \
> diff --git a/drivers/net/vmxnet3/Makefile b/drivers/net/vmxnet3/Makefile
> new file mode 100644
> index 0000000..880f509
> --- /dev/null
> +++ b/drivers/net/vmxnet3/Makefile
> @@ -0,0 +1,35 @@
> +################################################################################
> +#
> +# Linux driver for VMware's vmxnet3 ethernet NIC.
> +#
> +# Copyright (C) 2007-2009, VMware, Inc. All Rights Reserved.
> +#
> +# This program is free software; you can redistribute it and/or modify it
> +# under the terms of the GNU General Public License as published by the
> +# Free Software Foundation; version 2 of the License and no later version.
> +#
> +# This program is distributed in the hope that it will be useful, but
> +# WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
> +# NON INFRINGEMENT.  See the GNU General Public License for more
> +# details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program; if not, write to the Free Software
> +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
> +#
> +# The full GNU General Public License is included in this distribution in
> +# the file called "COPYING".
> +#
> +# Maintained by: Shreyas Bhatewara <pv-drivers@vmware.com>
> +#
> +#
> +################################################################################
> +
> +#
> +# Makefile for the VMware vmxnet3 ethernet NIC driver
> +#
> +
> +obj-$(CONFIG_VMXNET3) += vmxnet3.o
> +
> +vmxnet3-objs := vmxnet3_drv.o vmxnet3_ethtool.o
> diff --git a/drivers/net/vmxnet3/upt1_defs.h b/drivers/net/vmxnet3/upt1_defs.h
> new file mode 100644
> index 0000000..37108fb
> --- /dev/null
> +++ b/drivers/net/vmxnet3/upt1_defs.h
> @@ -0,0 +1,96 @@
> +/*
> + * Linux driver for VMware's vmxnet3 ethernet NIC.
> + *
> + * Copyright (C) 2008-2009, VMware, Inc. All Rights Reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License and no later version.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
> + * NON INFRINGEMENT.  See the GNU General Public License for more
> + * details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
> + *
> + * The full GNU General Public License is included in this distribution in
> + * the file called "COPYING".
> + *
> + * Maintained by: Shreyas Bhatewara <pv-drivers@vmware.com>
> + *
> + */
> +
> +#ifndef _UPT1_DEFS_H
> +#define _UPT1_DEFS_H
> +
> +struct UPT1_TxStats {
> +	u64			TSOPktsTxOK;  /* TSO pkts post-segmentation */
> +	u64			TSOBytesTxOK;
> +	u64			ucastPktsTxOK;
> +	u64			ucastBytesTxOK;
> +	u64			mcastPktsTxOK;
> +	u64			mcastBytesTxOK;
> +	u64			bcastPktsTxOK;
> +	u64			bcastBytesTxOK;
> +	u64			pktsTxError;
> +	u64			pktsTxDiscard;
> +};
> +
> +struct UPT1_RxStats {
> +	u64			LROPktsRxOK;    /* LRO pkts */
> +	u64			LROBytesRxOK;   /* bytes from LRO pkts */
> +	/* the following counters are for pkts from the wire, i.e., pre-LRO */
> +	u64			ucastPktsRxOK;
> +	u64			ucastBytesRxOK;
> +	u64			mcastPktsRxOK;
> +	u64			mcastBytesRxOK;
> +	u64			bcastPktsRxOK;
> +	u64			bcastBytesRxOK;
> +	u64			pktsRxOutOfBuf;
> +	u64			pktsRxError;
> +};
> +
> +/* interrupt moderation level */
> +enum {
> +	UPT1_IML_NONE		= 0, /* no interrupt moderation */
> +	UPT1_IML_HIGHEST	= 7, /* least intr generated */
> +	UPT1_IML_ADAPTIVE	= 8, /* adpative intr moderation */
> +};
> +/* values for UPT1_RSSConf.hashFunc */
> +enum {
> +	UPT1_RSS_HASH_TYPE_NONE      = 0x0,
> +	UPT1_RSS_HASH_TYPE_IPV4      = 0x01,
> +	UPT1_RSS_HASH_TYPE_TCP_IPV4  = 0x02,
> +	UPT1_RSS_HASH_TYPE_IPV6      = 0x04,
> +	UPT1_RSS_HASH_TYPE_TCP_IPV6  = 0x08,
> +};
> +
> +enum {
> +	UPT1_RSS_HASH_FUNC_NONE      = 0x0,
> +	UPT1_RSS_HASH_FUNC_TOEPLITZ  = 0x01,
> +};
> +
> +#define UPT1_RSS_MAX_KEY_SIZE        40
> +#define UPT1_RSS_MAX_IND_TABLE_SIZE  128
> +
> +struct UPT1_RSSConf {
> +	u16			hashType;
> +	u16			hashFunc;
> +	u16			hashKeySize;
> +	u16			indTableSize;
> +	u8			hashKey[UPT1_RSS_MAX_KEY_SIZE];
> +	u8			indTable[UPT1_RSS_MAX_IND_TABLE_SIZE];
> +};
> +
> +/* features */
> +enum {
> +	UPT1_F_RXCSUM		= 0x0001,   /* rx csum verification */
> +	UPT1_F_RSS		= 0x0002,
> +	UPT1_F_RXVLAN		= 0x0004,   /* VLAN tag stripping */
> +	UPT1_F_LRO		= 0x0008,
> +};
> +#endif
> diff --git a/drivers/net/vmxnet3/vmxnet3_defs.h b/drivers/net/vmxnet3/vmxnet3_defs.h
> new file mode 100644
> index 0000000..dc8ee44
> --- /dev/null
> +++ b/drivers/net/vmxnet3/vmxnet3_defs.h
> @@ -0,0 +1,535 @@
> +/*
> + * Linux driver for VMware's vmxnet3 ethernet NIC.
> + *
> + * Copyright (C) 2008-2009, VMware, Inc. All Rights Reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License and no later version.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
> + * NON INFRINGEMENT.  See the GNU General Public License for more
> + * details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
> + *
> + * The full GNU General Public License is included in this distribution in
> + * the file called "COPYING".
> + *
> + * Maintained by: Shreyas Bhatewara <pv-drivers@vmware.com>
> + *
> + */
> +
> +#ifndef _VMXNET3_DEFS_H_
> +#define _VMXNET3_DEFS_H_
> +
> +#include "upt1_defs.h"
> +
> +/* all registers are 32 bit wide */
> +/* BAR 1 */
> +enum {
> +	VMXNET3_REG_VRRS	= 0x0,	/* Vmxnet3 Revision Report Selection */
> +	VMXNET3_REG_UVRS	= 0x8,	/* UPT Version Report Selection */
> +	VMXNET3_REG_DSAL	= 0x10,	/* Driver Shared Address Low */
> +	VMXNET3_REG_DSAH	= 0x18,	/* Driver Shared Address High */
> +	VMXNET3_REG_CMD		= 0x20,	/* Command */
> +	VMXNET3_REG_MACL	= 0x28,	/* MAC Address Low */
> +	VMXNET3_REG_MACH	= 0x30,	/* MAC Address High */
> +	VMXNET3_REG_ICR		= 0x38,	/* Interrupt Cause Register */
> +	VMXNET3_REG_ECR		= 0x40	/* Event Cause Register */
> +};
> +
> +/* BAR 0 */
> +enum {
> +	VMXNET3_REG_IMR		= 0x0,	 /* Interrupt Mask Register */
> +	VMXNET3_REG_TXPROD	= 0x600, /* Tx Producer Index */
> +	VMXNET3_REG_RXPROD	= 0x800, /* Rx Producer Index for ring 1 */
> +	VMXNET3_REG_RXPROD2	= 0xA00	 /* Rx Producer Index for ring 2 */
> +};
> +
> +#define VMXNET3_PT_REG_SIZE     4096	/* BAR 0 */
> +#define VMXNET3_VD_REG_SIZE     4096	/* BAR 1 */
> +
> +#define VMXNET3_REG_ALIGN       8	/* All registers are 8-byte aligned. */
> +#define VMXNET3_REG_ALIGN_MASK  0x7
> +
> +/* I/O Mapped access to registers */
> +#define VMXNET3_IO_TYPE_PT              0
> +#define VMXNET3_IO_TYPE_VD              1
> +#define VMXNET3_IO_ADDR(type, reg)      (((type) << 24) | ((reg) & 0xFFFFFF))
> +#define VMXNET3_IO_TYPE(addr)           ((addr) >> 24)
> +#define VMXNET3_IO_REG(addr)            ((addr) & 0xFFFFFF)
> +
> +enum {
> +	VMXNET3_CMD_FIRST_SET = 0xCAFE0000,
> +	VMXNET3_CMD_ACTIVATE_DEV = VMXNET3_CMD_FIRST_SET,
> +	VMXNET3_CMD_QUIESCE_DEV,
> +	VMXNET3_CMD_RESET_DEV,
> +	VMXNET3_CMD_UPDATE_RX_MODE,
> +	VMXNET3_CMD_UPDATE_MAC_FILTERS,
> +	VMXNET3_CMD_UPDATE_VLAN_FILTERS,
> +	VMXNET3_CMD_UPDATE_RSSIDT,
> +	VMXNET3_CMD_UPDATE_IML,
> +	VMXNET3_CMD_UPDATE_PMCFG,
> +	VMXNET3_CMD_UPDATE_FEATURE,
> +	VMXNET3_CMD_LOAD_PLUGIN,
> +
> +	VMXNET3_CMD_FIRST_GET = 0xF00D0000,
> +	VMXNET3_CMD_GET_QUEUE_STATUS = VMXNET3_CMD_FIRST_GET,
> +	VMXNET3_CMD_GET_STATS,
> +	VMXNET3_CMD_GET_LINK,
> +	VMXNET3_CMD_GET_PERM_MAC_LO,
> +	VMXNET3_CMD_GET_PERM_MAC_HI,
> +	VMXNET3_CMD_GET_DID_LO,
> +	VMXNET3_CMD_GET_DID_HI,
> +	VMXNET3_CMD_GET_DEV_EXTRA_INFO,
> +	VMXNET3_CMD_GET_CONF_INTR
> +};
> +
> +struct Vmxnet3_TxDesc {
> +	u64		addr;
> +
> +	u32		len:14;
> +	u32		gen:1;      /* generation bit */
> +	u32		rsvd:1;
> +	u32		dtype:1;    /* descriptor type */
> +	u32		ext1:1;
> +	u32		msscof:14;  /* MSS, checksum offset, flags */
> +
> +	u32		hlen:10;    /* header len */
> +	u32		om:2;       /* offload mode */
> +	u32		eop:1;      /* End Of Packet */
> +	u32		cq:1;       /* completion request */
> +	u32		ext2:1;
> +	u32		ti:1;       /* VLAN Tag Insertion */
> +	u32		tci:16;     /* Tag to Insert */
> +};
> +
> +/* TxDesc.OM values */
> +#define VMXNET3_OM_NONE		0
> +#define VMXNET3_OM_CSUM		2
> +#define VMXNET3_OM_TSO		3
> +
> +/* fields in TxDesc we access w/o using bit fields */
> +#define VMXNET3_TXD_EOP_SHIFT	12
> +#define VMXNET3_TXD_CQ_SHIFT	13
> +#define VMXNET3_TXD_GEN_SHIFT	14
> +
> +#define VMXNET3_TXD_CQ		(1 << VMXNET3_TXD_CQ_SHIFT)
> +#define VMXNET3_TXD_EOP		(1 << VMXNET3_TXD_EOP_SHIFT)
> +#define VMXNET3_TXD_GEN		(1 << VMXNET3_TXD_GEN_SHIFT)
> +
> +#define VMXNET3_HDR_COPY_SIZE   128
> +
> +
> +struct Vmxnet3_TxDataDesc {
> +	u8		data[VMXNET3_HDR_COPY_SIZE];
> +};
> +
> +
> +struct Vmxnet3_TxCompDesc {
> +	u32		txdIdx:12;    /* Index of the EOP TxDesc */
> +	u32		ext1:20;
> +
> +	u32		ext2;
> +	u32		ext3;
> +
> +	u32		rsvd:24;
> +	u32		type:7;       /* completion type */
> +	u32		gen:1;        /* generation bit */
> +};
> +
> +
> +struct Vmxnet3_RxDesc {
> +	u64		addr;
> +
> +	u32		len:14;
> +	u32		btype:1;      /* Buffer Type */
> +	u32		dtype:1;      /* Descriptor type */
> +	u32		rsvd:15;
> +	u32		gen:1;        /* Generation bit */
> +
> +	u32		ext1;
> +};
> +
> +/* values of RXD.BTYPE */
> +#define VMXNET3_RXD_BTYPE_HEAD   0    /* head only */
> +#define VMXNET3_RXD_BTYPE_BODY   1    /* body only */
> +
> +/* fields in RxDesc we access w/o using bit fields */
> +#define VMXNET3_RXD_BTYPE_SHIFT  14
> +#define VMXNET3_RXD_GEN_SHIFT    31
> +
> +
> +struct Vmxnet3_RxCompDesc {
> +	u32		rxdIdx:12;    /* Index of the RxDesc */
> +	u32		ext1:2;
> +	u32		eop:1;        /* End of Packet */
> +	u32		sop:1;        /* Start of Packet */
> +	u32		rqID:10;      /* rx queue/ring ID */
> +	u32		rssType:4;    /* RSS hash type used */
> +	u32		cnc:1;        /* Checksum Not Calculated */
> +	u32		ext2:1;
> +
> +	u32		rssHash;      /* RSS hash value */
> +
> +	u32		len:14;       /* data length */
> +	u32		err:1;        /* Error */
> +	u32		ts:1;         /* Tag is stripped */
> +	u32		tci:16;       /* Tag stripped */
> +
> +	u32		csum:16;
> +	u32		tuc:1;        /* TCP/UDP Checksum Correct */
> +	u32		udp:1;        /* UDP packet */
> +	u32		tcp:1;        /* TCP packet */
> +	u32		ipc:1;        /* IP Checksum Correct */
> +	u32		v6:1;         /* IPv6 */
> +	u32		v4:1;         /* IPv4 */
> +	u32		frg:1;        /* IP Fragment */
> +	u32		fcs:1;        /* Frame CRC correct */
> +	u32		type:7;       /* completion type */
> +	u32		gen:1;        /* generation bit */
> +};
> +
> +/* fields in RxCompDesc we access via Vmxnet3_GenericDesc.dword[3] */
> +#define VMXNET3_RCD_TUC_SHIFT	16
> +#define VMXNET3_RCD_IPC_SHIFT	19
> +
> +/* fields in RxCompDesc we access via Vmxnet3_GenericDesc.qword[1] */
> +#define VMXNET3_RCD_TYPE_SHIFT	56
> +#define VMXNET3_RCD_GEN_SHIFT	63
> +
> +/* csum OK for TCP/UDP pkts over IP */
> +#define VMXNET3_RCD_CSUM_OK (1 << VMXNET3_RCD_TUC_SHIFT | \
> +			     1 << VMXNET3_RCD_IPC_SHIFT)
> +
> +/* value of RxCompDesc.rssType */
> +enum {
> +	VMXNET3_RCD_RSS_TYPE_NONE     = 0,
> +	VMXNET3_RCD_RSS_TYPE_IPV4     = 1,
> +	VMXNET3_RCD_RSS_TYPE_TCPIPV4  = 2,
> +	VMXNET3_RCD_RSS_TYPE_IPV6     = 3,
> +	VMXNET3_RCD_RSS_TYPE_TCPIPV6  = 4,
> +};
> +
> +
> +/* a union for accessing all cmd/completion descriptors */
> +union Vmxnet3_GenericDesc {
> +	u64				qword[2];
> +	u32				dword[4];
> +	u16				word[8];
> +	struct Vmxnet3_TxDesc		txd;
> +	struct Vmxnet3_RxDesc		rxd;
> +	struct Vmxnet3_TxCompDesc	tcd;
> +	struct Vmxnet3_RxCompDesc	rcd;
> +};
> +
> +#define VMXNET3_INIT_GEN       1
> +
> +/* Max size of a single tx buffer */
> +#define VMXNET3_MAX_TX_BUF_SIZE  (1 << 14)
> +
> +/* # of tx desc needed for a tx buffer size */
> +#define VMXNET3_TXD_NEEDED(size) (((size) + VMXNET3_MAX_TX_BUF_SIZE - 1) / \
> +				  VMXNET3_MAX_TX_BUF_SIZE)
> +
> +/* max # of tx descs for a non-tso pkt */
> +#define VMXNET3_MAX_TXD_PER_PKT 16
> +
> +/* Max size of a single rx buffer */
> +#define VMXNET3_MAX_RX_BUF_SIZE  ((1 << 14) - 1)
> +/* Minimum size of a type 0 buffer */
> +#define VMXNET3_MIN_T0_BUF_SIZE  128
> +#define VMXNET3_MAX_CSUM_OFFSET  1024
> +
> +/* Ring base address alignment */
> +#define VMXNET3_RING_BA_ALIGN   512
> +#define VMXNET3_RING_BA_MASK    (VMXNET3_RING_BA_ALIGN - 1)
> +
> +/* Ring size must be a multiple of 32 */
> +#define VMXNET3_RING_SIZE_ALIGN 32
> +#define VMXNET3_RING_SIZE_MASK  (VMXNET3_RING_SIZE_ALIGN - 1)
> +
> +/* Max ring size */
> +#define VMXNET3_TX_RING_MAX_SIZE   4096
> +#define VMXNET3_TC_RING_MAX_SIZE   4096
> +#define VMXNET3_RX_RING_MAX_SIZE   4096
> +#define VMXNET3_RC_RING_MAX_SIZE   8192
> +
> +/* a list of reasons for queue stop */
> +
> +enum {
> + VMXNET3_ERR_NOEOP        = 0x80000000,  /* cannot find the EOP desc of a pkt */
> + VMXNET3_ERR_TXD_REUSE    = 0x80000001,  /* reuse TxDesc before tx completion */
> + VMXNET3_ERR_BIG_PKT      = 0x80000002,  /* too many TxDesc for a pkt */
> + VMXNET3_ERR_DESC_NOT_SPT = 0x80000003,  /* descriptor type not supported */
> + VMXNET3_ERR_SMALL_BUF    = 0x80000004,  /* type 0 buffer too small */
> + VMXNET3_ERR_STRESS       = 0x80000005,  /* stress option firing in vmkernel */
> + VMXNET3_ERR_SWITCH       = 0x80000006,  /* mode switch failure */
> + VMXNET3_ERR_TXD_INVALID  = 0x80000007,  /* invalid TxDesc */
> +};
> +
> +/* completion descriptor types */
> +#define VMXNET3_CDTYPE_TXCOMP      0    /* Tx Completion Descriptor */
> +#define VMXNET3_CDTYPE_RXCOMP      3    /* Rx Completion Descriptor */
> +
> +enum {
> +	VMXNET3_GOS_BITS_UNK    = 0,   /* unknown */
> +	VMXNET3_GOS_BITS_32     = 1,
> +	VMXNET3_GOS_BITS_64     = 2,
> +};
> +
> +#define VMXNET3_GOS_TYPE_LINUX	1
> +
> +
> +struct Vmxnet3_GOSInfo {
> +	u32				gosBits:2;	/* 32-bit or 64-bit? */
> +	u32				gosType:4;   /* which guest */
> +	u32				gosVer:16;   /* gos version */
> +	u32				gosMisc:10;  /* other info about gos */
> +};
> +
> +
> +struct Vmxnet3_DriverInfo {
> +	u32				version;
> +	struct Vmxnet3_GOSInfo		gos;
> +	u32				vmxnet3RevSpt;
> +	u32				uptVerSpt;
> +};
> +
> +
> +#define VMXNET3_REV1_MAGIC  0xbabefee1
> +
> +/*
> + * QueueDescPA must be 128 bytes aligned. It points to an array of
> + * Vmxnet3_TxQueueDesc followed by an array of Vmxnet3_RxQueueDesc.
> + * The number of Vmxnet3_TxQueueDesc/Vmxnet3_RxQueueDesc are specified by
> + * Vmxnet3_MiscConf.numTxQueues/numRxQueues, respectively.
> + */
> +#define VMXNET3_QUEUE_DESC_ALIGN  128
> +
> +
> +struct Vmxnet3_MiscConf {
> +	struct Vmxnet3_DriverInfo driverInfo;
> +	u64		uptFeatures;
> +	u64		ddPA;         /* driver data PA */
> +	u64		queueDescPA;  /* queue descriptor table PA */
> +	u32		ddLen;        /* driver data len */
> +	u32		queueDescLen; /* queue desc. table len in bytes */
> +	u32		mtu;
> +	u16		maxNumRxSG;
> +	u8		numTxQueues;
> +	u8		numRxQueues;
> +	u32		reserved[4];
> +};
> +
> +
> +struct Vmxnet3_TxQueueConf {
> +	u64		txRingBasePA;
> +	u64		dataRingBasePA;
> +	u64		compRingBasePA;
> +	u64		ddPA;         /* driver data */
> +	u64		reserved;
> +	u32		txRingSize;   /* # of tx desc */
> +	u32		dataRingSize; /* # of data desc */
> +	u32		compRingSize; /* # of comp desc */
> +	u32		ddLen;        /* size of driver data */
> +	u8		intrIdx;
> +	u8		_pad[7];
> +};
> +
> +
> +struct Vmxnet3_RxQueueConf {
> +	u64		rxRingBasePA[2];
> +	u64		compRingBasePA;
> +	u64		ddPA;            /* driver data */
> +	u64		reserved;
> +	u32		rxRingSize[2];   /* # of rx desc */
> +	u32		compRingSize;    /* # of rx comp desc */
> +	u32		ddLen;           /* size of driver data */
> +	u8		intrIdx;
> +	u8		_pad[7];
> +};
> +
> +
> +enum vmxnet3_intr_mask_mode {
> +	VMXNET3_IMM_AUTO   = 0,
> +	VMXNET3_IMM_ACTIVE = 1,
> +	VMXNET3_IMM_LAZY   = 2
> +};
> +
> +enum vmxnet3_intr_type {
> +	VMXNET3_IT_AUTO = 0,
> +	VMXNET3_IT_INTX = 1,
> +	VMXNET3_IT_MSI  = 2,
> +	VMXNET3_IT_MSIX = 3
> +};
> +
> +#define VMXNET3_MAX_TX_QUEUES  8
> +#define VMXNET3_MAX_RX_QUEUES  16
> +/* addition 1 for events */
> +#define VMXNET3_MAX_INTRS      25
> +
> +
> +struct Vmxnet3_IntrConf {
> +	bool		autoMask;
> +	u8		numIntrs;      /* # of interrupts */
> +	u8		eventIntrIdx;
> +	u8		modLevels[VMXNET3_MAX_INTRS];	/* moderation level for
> +							 * each intr */
> +	u32		reserved[3];
> +};
> +
> +/* one bit per VLAN ID, the size is in the units of u32	*/
> +#define VMXNET3_VFT_SIZE  (4096 / (sizeof(u32) * 8))
> +
> +
> +struct Vmxnet3_QueueStatus {
> +	bool		stopped;
> +	u8		_pad[3];
> +	u32		error;
> +};
> +
> +
> +struct Vmxnet3_TxQueueCtrl {
> +	u32		txNumDeferred;
> +	u32		txThreshold;
> +	u64		reserved;
> +};
> +
> +
> +struct Vmxnet3_RxQueueCtrl {
> +	bool		updateRxProd;
> +	u8		_pad[7];
> +	u64		reserved;
> +};
> +
> +enum {
> +	VMXNET3_RXM_UCAST     = 0x01,  /* unicast only */
> +	VMXNET3_RXM_MCAST     = 0x02,  /* multicast passing the filters */
> +	VMXNET3_RXM_BCAST     = 0x04,  /* broadcast only */
> +	VMXNET3_RXM_ALL_MULTI = 0x08,  /* all multicast */
> +	VMXNET3_RXM_PROMISC   = 0x10  /* promiscuous */
> +};
> +
> +struct Vmxnet3_RxFilterConf {
> +	u32		rxMode;       /* VMXNET3_RXM_xxx */
> +	u16		mfTableLen;   /* size of the multicast filter table */
> +	u16		_pad1;
> +	u64		mfTablePA;    /* PA of the multicast filters table */
> +	u32		vfTable[VMXNET3_VFT_SIZE]; /* vlan filter */
> +};
> +
> +
> +#define VMXNET3_PM_MAX_FILTERS        6
> +#define VMXNET3_PM_MAX_PATTERN_SIZE   128
> +#define VMXNET3_PM_MAX_MASK_SIZE      (VMXNET3_PM_MAX_PATTERN_SIZE / 8)
> +
> +#define VMXNET3_PM_WAKEUP_MAGIC       0x01  /* wake up on magic pkts */
> +#define VMXNET3_PM_WAKEUP_FILTER      0x02  /* wake up on pkts matching
> +					     * filters */
> +
> +
> +struct Vmxnet3_PM_PktFilter {
> +	u8		maskSize;
> +	u8		patternSize;
> +	u8		mask[VMXNET3_PM_MAX_MASK_SIZE];
> +	u8		pattern[VMXNET3_PM_MAX_PATTERN_SIZE];
> +	u8		pad[6];
> +};
> +
> +
> +struct Vmxnet3_PMConf {
> +	u16		wakeUpEvents;  /* VMXNET3_PM_WAKEUP_xxx */
> +	u8		numFilters;
> +	u8		pad[5];
> +	struct Vmxnet3_PM_PktFilter filters[VMXNET3_PM_MAX_FILTERS];
> +};
> +
> +
> +struct Vmxnet3_VariableLenConfDesc {
> +	u32		confVer;
> +	u32		confLen;
> +	u64		confPA;
> +};
> +
> +
> +struct Vmxnet3_TxQueueDesc {
> +	struct Vmxnet3_TxQueueCtrl		ctrl;
> +	struct Vmxnet3_TxQueueConf		conf;
> +
> +	/* Driver read after a GET command */
> +	struct Vmxnet3_QueueStatus		status;
> +	struct UPT1_TxStats			stats;
> +	u8					_pad[88]; /* 128 aligned */
> +};
> +
> +
> +struct Vmxnet3_RxQueueDesc {
> +	struct Vmxnet3_RxQueueCtrl		ctrl;
> +	struct Vmxnet3_RxQueueConf		conf;
> +	/* Driver read after a GET commad */
> +	struct Vmxnet3_QueueStatus		status;
> +	struct UPT1_RxStats			stats;
> +	u8				      __pad[88]; /* 128 aligned */
> +};
> +
> +
> +struct Vmxnet3_DSDevRead {
> +	/* read-only region for device, read by dev in response to a SET cmd */
> +	struct Vmxnet3_MiscConf			misc;
> +	struct Vmxnet3_IntrConf			intrConf;
> +	struct Vmxnet3_RxFilterConf		rxFilterConf;
> +	struct Vmxnet3_VariableLenConfDesc	rssConfDesc;
> +	struct Vmxnet3_VariableLenConfDesc	pmConfDesc;
> +	struct Vmxnet3_VariableLenConfDesc	pluginConfDesc;
> +};
> +
> +/* All structures in DriverShared are padded to multiples of 8 bytes */
> +struct Vmxnet3_DriverShared {
> +	u32				magic;
> +	/* make devRead start at 64bit boundaries */
> +	u32					pad;
> +	struct Vmxnet3_DSDevRead		devRead;
> +	u32					ecr;
> +	u32					reserved[5];
> +};
> +
> +
> +#define VMXNET3_ECR_RQERR       (1 << 0)
> +#define VMXNET3_ECR_TQERR       (1 << 1)
> +#define VMXNET3_ECR_LINK        (1 << 2)
> +#define VMXNET3_ECR_DIC         (1 << 3)
> +#define VMXNET3_ECR_DEBUG       (1 << 4)
> +
> +/* flip the gen bit of a ring */
> +#define VMXNET3_FLIP_RING_GEN(gen) ((gen) = (gen) ^ 0x1)
> +
> +/* only use this if moving the idx won't affect the gen bit */
> +#define VMXNET3_INC_RING_IDX_ONLY(idx, ring_size) \
> +	do {\
> +		(idx)++;\
> +		if (unlikely((idx) == (ring_size))) {\
> +			(idx) = 0;\
> +		} \
> +	} while (0)
> +
> +#define VMXNET3_SET_VFTABLE_ENTRY(vfTable, vid) \
> +	(vfTable[vid >> 5] |= (1 << (vid & 31)))
> +#define VMXNET3_CLEAR_VFTABLE_ENTRY(vfTable, vid) \
> +	(vfTable[vid >> 5] &= ~(1 << (vid & 31)))
> +
> +#define VMXNET3_VFTABLE_ENTRY_IS_SET(vfTable, vid) \
> +	((vfTable[vid >> 5] & (1 << (vid & 31))) != 0)
> +
> +#define VMXNET3_MAX_MTU     9000
> +#define VMXNET3_MIN_MTU     60
> +
> +#define VMXNET3_LINK_UP         (10000 << 16 | 1)    /* 10 Gbps, up */
> +#define VMXNET3_LINK_DOWN       0
> +
> +#endif /* _VMXNET3_DEFS_H_ */
> diff --git a/drivers/net/vmxnet3/vmxnet3_drv.c b/drivers/net/vmxnet3/vmxnet3_drv.c
> new file mode 100644
> index 0000000..a886f24
> --- /dev/null
> +++ b/drivers/net/vmxnet3/vmxnet3_drv.c
> @@ -0,0 +1,2553 @@
> +/*
> + * Linux driver for VMware's vmxnet3 ethernet NIC.
> + *
> + * Copyright (C) 2008-2009, VMware, Inc. All Rights Reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License and no later version.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
> + * NON INFRINGEMENT. See the GNU General Public License for more
> + * details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
> + *
> + * The full GNU General Public License is included in this distribution in
> + * the file called "COPYING".
> + *
> + * Maintained by: Shreyas Bhatewara <pv-drivers@vmware.com>
> + *
> + */
> +
> +#include "vmxnet3_int.h"
> +
> +char vmxnet3_driver_name[] = "vmxnet3";
> +#define VMXNET3_DRIVER_DESC "VMware vmxnet3 virtual NIC driver"
> +
> +
> +/*
> + * PCI Device ID Table
> + * Last entry must be all 0s
> + */
> +static const struct pci_device_id vmxnet3_pciid_table[] = {
> +	{PCI_VDEVICE(VMWARE, PCI_DEVICE_ID_VMWARE_VMXNET3)},
> +	{0}
> +};
> +
> +MODULE_DEVICE_TABLE(pci, vmxnet3_pciid_table);
> +
> +static atomic_t devices_found;
> +
> +
> +/*
> + *    Enable/Disable the given intr
> + */
> +static void
> +vmxnet3_enable_intr(struct vmxnet3_adapter *adapter, unsigned intr_idx)
> +{
> +	VMXNET3_WRITE_BAR0_REG(adapter, VMXNET3_REG_IMR + intr_idx * 8, 0);
> +}
> +
> +
> +static void
> +vmxnet3_disable_intr(struct vmxnet3_adapter *adapter, unsigned intr_idx)
> +{
> +	VMXNET3_WRITE_BAR0_REG(adapter, VMXNET3_REG_IMR + intr_idx * 8, 1);
> +}
> +
> +
> +/*
> + *    Enable/Disable all intrs used by the device
> + */
> +static void
> +vmxnet3_enable_all_intrs(struct vmxnet3_adapter *adapter)
> +{
> +	int i;
> +
> +	for (i = 0; i < adapter->intr.num_intrs; i++)
> +		vmxnet3_enable_intr(adapter, i);
> +}
> +
> +
> +static void
> +vmxnet3_disable_all_intrs(struct vmxnet3_adapter *adapter)
> +{
> +	int i;
> +
> +	for (i = 0; i < adapter->intr.num_intrs; i++)
> +		vmxnet3_disable_intr(adapter, i);
> +}
> +
> +
> +static void
> +vmxnet3_ack_events(struct vmxnet3_adapter *adapter, u32 events)
> +{
> +	VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_ECR, events);
> +}
> +
> +
> +static bool
> +vmxnet3_tq_stopped(struct vmxnet3_tx_queue *tq, struct vmxnet3_adapter *adapter)
> +{
> +	return netif_queue_stopped(adapter->netdev);
> +}
> +
> +
> +static void
> +vmxnet3_tq_start(struct vmxnet3_tx_queue *tq, struct vmxnet3_adapter *adapter)
> +{
> +	tq->stopped = false;
> +	netif_start_queue(adapter->netdev);
> +}
> +
> +
> +static void
> +vmxnet3_tq_wake(struct vmxnet3_tx_queue *tq, struct vmxnet3_adapter *adapter)
> +{
> +	tq->stopped = false;
> +	netif_wake_queue(adapter->netdev);
> +}
> +
> +
> +static void
> +vmxnet3_tq_stop(struct vmxnet3_tx_queue *tq, struct vmxnet3_adapter *adapter)
> +{
> +	tq->stopped = true;
> +	tq->num_stop++;
> +	netif_stop_queue(adapter->netdev);
> +}
> +
> +
> +/*
> + * Check the link state. This may start or stop the tx queue.
> + */
> +static void
> +vmxnet3_check_link(struct vmxnet3_adapter *adapter)
> +{
> +	u32 ret;
> +
> +	VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, VMXNET3_CMD_GET_LINK);
> +	ret = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_CMD);
> +	adapter->link_speed = ret >> 16;
> +	if (ret & 1) { /* Link is up. */
> +		printk(KERN_INFO "%s: NIC Link is Up %d Mbps\n",
> +		       adapter->netdev->name, adapter->link_speed);
> +		if (!netif_carrier_ok(adapter->netdev))
> +			netif_carrier_on(adapter->netdev);
> +
> +		vmxnet3_tq_start(&adapter->tx_queue, adapter);
> +	} else {
> +		printk(KERN_INFO "%s: NIC Link is Down\n",
> +		       adapter->netdev->name);
> +		if (netif_carrier_ok(adapter->netdev))
> +			netif_carrier_off(adapter->netdev);
> +
> +		vmxnet3_tq_stop(&adapter->tx_queue, adapter);
> +	}
> +}
> +
> +
> +static void
> +vmxnet3_process_events(struct vmxnet3_adapter *adapter)
> +{
> +	u32 events = adapter->shared->ecr;
> +	if (!events)
> +		return;
> +
> +	vmxnet3_ack_events(adapter, events);
> +
> +	/* Check if link state has changed */
> +	if (events & VMXNET3_ECR_LINK)
> +		vmxnet3_check_link(adapter);
> +
> +	/* Check if there is an error on xmit/recv queues */
> +	if (events & (VMXNET3_ECR_TQERR | VMXNET3_ECR_RQERR)) {
> +		VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD,
> +				       VMXNET3_CMD_GET_QUEUE_STATUS);
> +
> +		if (adapter->tqd_start->status.stopped) {
> +			printk(KERN_ERR "%s: tq error 0x%x\n",
> +			       adapter->netdev->name,
> +			       adapter->tqd_start->status.error);
> +		}
> +		if (adapter->rqd_start->status.stopped) {
> +			printk(KERN_ERR "%s: rq error 0x%x\n",
> +			       adapter->netdev->name,
> +			       adapter->rqd_start->status.error);
> +		}
> +
> +		schedule_work(&adapter->work);
> +	}
> +}
> +
> +
> +static void
> +vmxnet3_unmap_tx_buf(struct vmxnet3_tx_buf_info *tbi,
> +		     struct pci_dev *pdev)
> +{
> +	if (tbi->map_type == VMXNET3_MAP_SINGLE)
> +		pci_unmap_single(pdev, tbi->dma_addr, tbi->len,
> +				 PCI_DMA_TODEVICE);
> +	else if (tbi->map_type == VMXNET3_MAP_PAGE)
> +		pci_unmap_page(pdev, tbi->dma_addr, tbi->len,
> +			       PCI_DMA_TODEVICE);
> +	else
> +		BUG_ON(tbi->map_type != VMXNET3_MAP_NONE);
> +
> +	tbi->map_type = VMXNET3_MAP_NONE; /* to help debugging */
> +}
> +
> +
> +static int
> +vmxnet3_unmap_pkt(u32 eop_idx, struct vmxnet3_tx_queue *tq,
> +		  struct pci_dev *pdev,	struct vmxnet3_adapter *adapter)
> +{
> +	struct sk_buff *skb;
> +	int entries = 0;
> +
> +	/* no out of order completion */
> +	BUG_ON(tq->buf_info[eop_idx].sop_idx != tq->tx_ring.next2comp);
> +	BUG_ON(tq->tx_ring.base[eop_idx].txd.eop != 1);
> +
> +	skb = tq->buf_info[eop_idx].skb;
> +	BUG_ON(skb == NULL);
> +	tq->buf_info[eop_idx].skb = NULL;
> +
> +	VMXNET3_INC_RING_IDX_ONLY(eop_idx, tq->tx_ring.size);
> +
> +	while (tq->tx_ring.next2comp != eop_idx) {
> +		vmxnet3_unmap_tx_buf(tq->buf_info + tq->tx_ring.next2comp,
> +				     pdev);
> +
> +		/* update next2comp w/o tx_lock. Since we are marking more,
> +		 * instead of less, tx ring entries avail, the worst case is
> +		 * that the tx routine incorrectly re-queues a pkt due to
> +		 * insufficient tx ring entries.
> +		 */
> +		vmxnet3_cmd_ring_adv_next2comp(&tq->tx_ring);
> +		entries++;
> +	}
> +
> +	dev_kfree_skb_any(skb);
> +	return entries;
> +}
> +
> +
> +static int
> +vmxnet3_tq_tx_complete(struct vmxnet3_tx_queue *tq,
> +			struct vmxnet3_adapter *adapter)
> +{
> +	int completed = 0;
> +	union Vmxnet3_GenericDesc *gdesc;
> +
> +	gdesc = tq->comp_ring.base + tq->comp_ring.next2proc;
> +	while (gdesc->tcd.gen == tq->comp_ring.gen) {
> +		completed += vmxnet3_unmap_pkt(gdesc->tcd.txdIdx, tq,
> +					       adapter->pdev, adapter);
> +
> +		vmxnet3_comp_ring_adv_next2proc(&tq->comp_ring);
> +		gdesc = tq->comp_ring.base + tq->comp_ring.next2proc;
> +	}
> +
> +	if (completed) {
> +		spin_lock(&tq->tx_lock);
> +		if (unlikely(vmxnet3_tq_stopped(tq, adapter) &&
> +			     vmxnet3_cmd_ring_desc_avail(&tq->tx_ring) >
> +			     VMXNET3_WAKE_QUEUE_THRESHOLD(tq) &&
> +			     netif_carrier_ok(adapter->netdev))) {
> +			vmxnet3_tq_wake(tq, adapter);
> +		}
> +		spin_unlock(&tq->tx_lock);
> +	}
> +	return completed;
> +}
> +
> +
> +static void
> +vmxnet3_tq_cleanup(struct vmxnet3_tx_queue *tq,
> +		   struct vmxnet3_adapter *adapter)
> +{
> +	int i;
> +
> +	while (tq->tx_ring.next2comp != tq->tx_ring.next2fill) {
> +		struct vmxnet3_tx_buf_info *tbi;
> +		union Vmxnet3_GenericDesc *gdesc;
> +
> +		tbi = tq->buf_info + tq->tx_ring.next2comp;
> +		gdesc = tq->tx_ring.base + tq->tx_ring.next2comp;
> +
> +		vmxnet3_unmap_tx_buf(tbi, adapter->pdev);
> +		if (tbi->skb) {
> +			dev_kfree_skb_any(tbi->skb);
> +			tbi->skb = NULL;
> +		}
> +		vmxnet3_cmd_ring_adv_next2comp(&tq->tx_ring);
> +	}
> +
> +	/* sanity check, verify all buffers are indeed unmapped and freed */
> +	for (i = 0; i < tq->tx_ring.size; i++) {
> +		BUG_ON(tq->buf_info[i].skb != NULL ||
> +		       tq->buf_info[i].map_type != VMXNET3_MAP_NONE);
> +	}
> +
> +	tq->tx_ring.gen = VMXNET3_INIT_GEN;
> +	tq->tx_ring.next2fill = tq->tx_ring.next2comp = 0;
> +
> +	tq->comp_ring.gen = VMXNET3_INIT_GEN;
> +	tq->comp_ring.next2proc = 0;
> +}
> +
> +
> +void
> +vmxnet3_tq_destroy(struct vmxnet3_tx_queue *tq,
> +		   struct vmxnet3_adapter *adapter)
> +{
> +	if (tq->tx_ring.base) {
> +		pci_free_consistent(adapter->pdev, tq->tx_ring.size *
> +				    sizeof(struct Vmxnet3_TxDesc),
> +				    tq->tx_ring.base, tq->tx_ring.basePA);
> +		tq->tx_ring.base = NULL;
> +	}
> +	if (tq->data_ring.base) {
> +		pci_free_consistent(adapter->pdev, tq->data_ring.size *
> +				    sizeof(struct Vmxnet3_TxDataDesc),
> +				    tq->data_ring.base, tq->data_ring.basePA);
> +		tq->data_ring.base = NULL;
> +	}
> +	if (tq->comp_ring.base) {
> +		pci_free_consistent(adapter->pdev, tq->comp_ring.size *
> +				    sizeof(struct Vmxnet3_TxCompDesc),
> +				    tq->comp_ring.base, tq->comp_ring.basePA);
> +		tq->comp_ring.base = NULL;
> +	}
> +	kfree(tq->buf_info);
> +	tq->buf_info = NULL;
> +}
> +
> +
> +static void
> +vmxnet3_tq_init(struct vmxnet3_tx_queue *tq,
> +		struct vmxnet3_adapter *adapter)
> +{
> +	int i;
> +
> +	/* reset the tx ring contents to 0 and reset the tx ring states */
> +	memset(tq->tx_ring.base, 0, tq->tx_ring.size *
> +	       sizeof(struct Vmxnet3_TxDesc));
> +	tq->tx_ring.next2fill = tq->tx_ring.next2comp = 0;
> +	tq->tx_ring.gen = VMXNET3_INIT_GEN;
> +
> +	memset(tq->data_ring.base, 0, tq->data_ring.size *
> +	       sizeof(struct Vmxnet3_TxDataDesc));
> +
> +	/* reset the tx comp ring contents to 0 and reset comp ring states */
> +	memset(tq->comp_ring.base, 0, tq->comp_ring.size *
> +	       sizeof(struct Vmxnet3_TxCompDesc));
> +	tq->comp_ring.next2proc = 0;
> +	tq->comp_ring.gen = VMXNET3_INIT_GEN;
> +
> +	/* reset the bookkeeping data */
> +	memset(tq->buf_info, 0, sizeof(tq->buf_info[0]) * tq->tx_ring.size);
> +	for (i = 0; i < tq->tx_ring.size; i++)
> +		tq->buf_info[i].map_type = VMXNET3_MAP_NONE;
> +
> +	/* stats are not reset */
> +}
> +
> +
> +static int
> +vmxnet3_tq_create(struct vmxnet3_tx_queue *tq,
> +		  struct vmxnet3_adapter *adapter)
> +{
> +	BUG_ON(tq->tx_ring.base || tq->data_ring.base ||
> +	       tq->comp_ring.base || tq->buf_info);
> +
> +	tq->tx_ring.base = pci_alloc_consistent(adapter->pdev, tq->tx_ring.size
> +			   * sizeof(struct Vmxnet3_TxDesc),
> +			   &tq->tx_ring.basePA);
> +	if (!tq->tx_ring.base) {
> +		printk(KERN_ERR "%s: failed to allocate tx ring\n",
> +		       adapter->netdev->name);
> +		goto err;
> +	}
> +
> +	tq->data_ring.base = pci_alloc_consistent(adapter->pdev,
> +			     tq->data_ring.size *
> +			     sizeof(struct Vmxnet3_TxDataDesc),
> +			     &tq->data_ring.basePA);
> +	if (!tq->data_ring.base) {
> +		printk(KERN_ERR "%s: failed to allocate data ring\n",
> +		       adapter->netdev->name);
> +		goto err;
> +	}
> +
> +	tq->comp_ring.base = pci_alloc_consistent(adapter->pdev,
> +			     tq->comp_ring.size *
> +			     sizeof(struct Vmxnet3_TxCompDesc),
> +			     &tq->comp_ring.basePA);
> +	if (!tq->comp_ring.base) {
> +		printk(KERN_ERR "%s: failed to allocate tx comp ring\n",
> +		       adapter->netdev->name);
> +		goto err;
> +	}
> +
> +	tq->buf_info = kcalloc(tq->tx_ring.size, sizeof(tq->buf_info[0]),
> +			       GFP_KERNEL);
> +	if (!tq->buf_info) {
> +		printk(KERN_ERR "%s: failed to allocate tx bufinfo\n",
> +		       adapter->netdev->name);
> +		goto err;
> +	}
> +
> +	return 0;
> +
> +err:
> +	vmxnet3_tq_destroy(tq, adapter);
> +	return -ENOMEM;
> +}
> +
> +
> +/*
> + *    starting from ring->next2fill, allocate rx buffers for the given ring
> + *    of the rx queue and update the rx desc. stop after @num_to_alloc buffers
> + *    are allocated or allocation fails
> + */
> +
> +static int
> +vmxnet3_rq_alloc_rx_buf(struct vmxnet3_rx_queue *rq, u32 ring_idx,
> +			int num_to_alloc, struct vmxnet3_adapter *adapter)
> +{
> +	int num_allocated = 0;
> +	struct vmxnet3_rx_buf_info *rbi_base = rq->buf_info[ring_idx];
> +	struct vmxnet3_cmd_ring *ring = &rq->rx_ring[ring_idx];
> +	u32 val;
> +
> +	while (num_allocated < num_to_alloc) {
> +		struct vmxnet3_rx_buf_info *rbi;
> +		union Vmxnet3_GenericDesc *gd;
> +
> +		rbi = rbi_base + ring->next2fill;
> +		gd = ring->base + ring->next2fill;
> +
> +		if (rbi->buf_type == VMXNET3_RX_BUF_SKB) {
> +			if (rbi->skb == NULL) {
> +				rbi->skb = dev_alloc_skb(rbi->len +
> +							 NET_IP_ALIGN);
> +				if (unlikely(rbi->skb == NULL)) {
> +					rq->stats.rx_buf_alloc_failure++;
> +					break;
> +				}
> +				rbi->skb->dev = adapter->netdev;
> +
> +				skb_reserve(rbi->skb, NET_IP_ALIGN);
> +				rbi->dma_addr = pci_map_single(adapter->pdev,
> +						rbi->skb->data, rbi->len,
> +						PCI_DMA_FROMDEVICE);
> +			} else {
> +				/* rx buffer skipped by the device */
> +			}
> +			val = VMXNET3_RXD_BTYPE_HEAD << VMXNET3_RXD_BTYPE_SHIFT;
> +		} else {
> +			BUG_ON(rbi->buf_type != VMXNET3_RX_BUF_PAGE ||
> +			       rbi->len  != PAGE_SIZE);
> +
> +			if (rbi->page == NULL) {
> +				rbi->page = alloc_page(GFP_ATOMIC);
> +				if (unlikely(rbi->page == NULL)) {
> +					rq->stats.rx_buf_alloc_failure++;
> +					break;
> +				}
> +				rbi->dma_addr = pci_map_page(adapter->pdev,
> +						rbi->page, 0, PAGE_SIZE,
> +						PCI_DMA_FROMDEVICE);
> +			} else {
> +				/* rx buffers skipped by the device */
> +			}
> +			val = VMXNET3_RXD_BTYPE_BODY << VMXNET3_RXD_BTYPE_SHIFT;
> +		}
> +
> +		BUG_ON(rbi->dma_addr == 0);
> +		gd->rxd.addr = rbi->dma_addr;
> +		gd->dword[2] = (ring->gen << VMXNET3_RXD_GEN_SHIFT) | val |
> +				rbi->len;
> +
> +		num_allocated++;
> +		vmxnet3_cmd_ring_adv_next2fill(ring);
> +	}
> +	rq->uncommitted[ring_idx] += num_allocated;
> +
> +	dprintk(KERN_ERR "alloc_rx_buf: %d allocated, next2fill %u, next2comp "
> +		"%u, uncommited %u\n", num_allocated, ring->next2fill,
> +		ring->next2comp, rq->uncommitted[ring_idx]);
> +
> +	/* so that the device can distinguish a full ring and an empty ring */
> +	BUG_ON(num_allocated != 0 && ring->next2fill == ring->next2comp);
> +
> +	return num_allocated;
> +}
> +
> +
> +static void
> +vmxnet3_append_frag(struct sk_buff *skb, struct Vmxnet3_RxCompDesc *rcd,
> +		    struct vmxnet3_rx_buf_info *rbi)
> +{
> +	struct skb_frag_struct *frag = skb_shinfo(skb)->frags +
> +		skb_shinfo(skb)->nr_frags;
> +
> +	BUG_ON(skb_shinfo(skb)->nr_frags >= MAX_SKB_FRAGS);
> +
> +	frag->page = rbi->page;
> +	frag->page_offset = 0;
> +	frag->size = rcd->len;
> +	skb->data_len += frag->size;
> +	skb_shinfo(skb)->nr_frags++;
> +}
> +
> +
> +static void
> +vmxnet3_map_pkt(struct sk_buff *skb, struct vmxnet3_tx_ctx *ctx,
> +		struct vmxnet3_tx_queue *tq, struct pci_dev *pdev,
> +		struct vmxnet3_adapter *adapter)
> +{
> +	u32 dw2, len;
> +	unsigned long buf_offset;
> +	int i;
> +	union Vmxnet3_GenericDesc *gdesc;
> +	struct vmxnet3_tx_buf_info *tbi = NULL;
> +
> +	BUG_ON(ctx->copy_size > skb_headlen(skb));
> +
> +	/* use the previous gen bit for the SOP desc */
> +	dw2 = (tq->tx_ring.gen ^ 0x1) << VMXNET3_TXD_GEN_SHIFT;
> +
> +	ctx->sop_txd = tq->tx_ring.base + tq->tx_ring.next2fill;
> +	gdesc = ctx->sop_txd; /* both loops below can be skipped */
> +
> +	/* no need to map the buffer if headers are copied */
> +	if (ctx->copy_size) {
> +		ctx->sop_txd->txd.addr = tq->data_ring.basePA +
> +					tq->tx_ring.next2fill *
> +					sizeof(struct Vmxnet3_TxDataDesc);
> +		ctx->sop_txd->dword[2] = dw2 | ctx->copy_size;
> +		ctx->sop_txd->dword[3] = 0;
> +
> +		tbi = tq->buf_info + tq->tx_ring.next2fill;
> +		tbi->map_type = VMXNET3_MAP_NONE;
> +
> +		dprintk(KERN_ERR "txd[%u]: 0x%Lx 0x%x 0x%x\n",
> +			tq->tx_ring.next2fill, ctx->sop_txd->txd.addr,
> +			ctx->sop_txd->dword[2], ctx->sop_txd->dword[3]);
> +		vmxnet3_cmd_ring_adv_next2fill(&tq->tx_ring);
> +
> +		/* use the right gen for non-SOP desc */
> +		dw2 = tq->tx_ring.gen << VMXNET3_TXD_GEN_SHIFT;
> +	}
> +
> +	/* linear part can use multiple tx desc if it's big */
> +	len = skb_headlen(skb) - ctx->copy_size;
> +	buf_offset = ctx->copy_size;
> +	while (len) {
> +		u32 buf_size;
> +
> +		buf_size = len > VMXNET3_MAX_TX_BUF_SIZE ?
> +			   VMXNET3_MAX_TX_BUF_SIZE : len;
> +
> +		tbi = tq->buf_info + tq->tx_ring.next2fill;
> +		tbi->map_type = VMXNET3_MAP_SINGLE;
> +		tbi->dma_addr = pci_map_single(adapter->pdev,
> +				skb->data + buf_offset, buf_size,
> +				PCI_DMA_TODEVICE);
> +
> +		tbi->len = buf_size; /* this automatically convert 2^14 to 0 */
> +
> +		gdesc = tq->tx_ring.base + tq->tx_ring.next2fill;
> +		BUG_ON(gdesc->txd.gen == tq->tx_ring.gen);
> +
> +		gdesc->txd.addr = tbi->dma_addr;
> +		gdesc->dword[2] = dw2 | buf_size;
> +		gdesc->dword[3] = 0;
> +
> +		dprintk(KERN_ERR "txd[%u]: 0x%Lx 0x%x 0x%x\n",
> +			tq->tx_ring.next2fill, gdesc->txd.addr,
> +			gdesc->dword[2], gdesc->dword[3]);
> +		vmxnet3_cmd_ring_adv_next2fill(&tq->tx_ring);
> +		dw2 = tq->tx_ring.gen << VMXNET3_TXD_GEN_SHIFT;
> +
> +		len -= buf_size;
> +		buf_offset += buf_size;
> +	}
> +
> +	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
> +		struct skb_frag_struct *frag = &skb_shinfo(skb)->frags[i];
> +
> +		tbi = tq->buf_info + tq->tx_ring.next2fill;
> +		tbi->map_type = VMXNET3_MAP_PAGE;
> +		tbi->dma_addr = pci_map_page(adapter->pdev, frag->page,
> +					     frag->page_offset, frag->size,
> +					     PCI_DMA_TODEVICE);
> +
> +		tbi->len = frag->size;
> +
> +		gdesc = tq->tx_ring.base + tq->tx_ring.next2fill;
> +		BUG_ON(gdesc->txd.gen == tq->tx_ring.gen);
> +
> +		gdesc->txd.addr = tbi->dma_addr;
> +		gdesc->dword[2] = dw2 | frag->size;
> +		gdesc->dword[3] = 0;
> +
> +		dprintk(KERN_ERR "txd[%u]: 0x%llu %u %u\n",
> +			tq->tx_ring.next2fill, gdesc->txd.addr,
> +			gdesc->dword[2], gdesc->dword[3]);
> +		vmxnet3_cmd_ring_adv_next2fill(&tq->tx_ring);
> +		dw2 = tq->tx_ring.gen << VMXNET3_TXD_GEN_SHIFT;
> +	}
> +
> +	ctx->eop_txd = gdesc;
> +
> +	/* set the last buf_info for the pkt */
> +	tbi->skb = skb;
> +	tbi->sop_idx = ctx->sop_txd - tq->tx_ring.base;
> +}
> +
> +
> +/*
> + *    parse and copy relevant protocol headers:
> + *      For a tso pkt, relevant headers are L2/3/4 including options
> + *      For a pkt requesting csum offloading, they are L2/3 and may include L4
> + *      if it's a TCP/UDP pkt
> + *
> + * Returns:
> + *    -1:  error happens during parsing
> + *     0:  protocol headers parsed, but too big to be copied
> + *     1:  protocol headers parsed and copied
> + *
> + * Other effects:
> + *    1. related *ctx fields are updated.
> + *    2. ctx->copy_size is # of bytes copied
> + *    3. the portion copied is guaranteed to be in the linear part
> + *
> + */
> +static int
> +vmxnet3_parse_and_copy_hdr(struct sk_buff *skb, struct vmxnet3_tx_queue *tq,
> +			   struct vmxnet3_tx_ctx *ctx,
> +			   struct vmxnet3_adapter *adapter)
> +{
> +	struct Vmxnet3_TxDataDesc *tdd;
> +
> +	if (ctx->mss) {
> +		ctx->eth_ip_hdr_size = skb_transport_offset(skb);
> +		ctx->l4_hdr_size = ((struct tcphdr *)
> +				   skb_transport_header(skb))->doff * 4;
> +		ctx->copy_size = ctx->eth_ip_hdr_size + ctx->l4_hdr_size;
> +	} else {
> +		unsigned int pull_size;
> +
> +		if (skb->ip_summed == CHECKSUM_PARTIAL) {
> +			ctx->eth_ip_hdr_size = skb_transport_offset(skb);
> +
> +			if (ctx->ipv4) {
> +				struct iphdr *iph = (struct iphdr *)
> +						    skb_network_header(skb);
> +				if (iph->protocol == IPPROTO_TCP) {
> +					pull_size = ctx->eth_ip_hdr_size +
> +						    sizeof(struct tcphdr);
> +
> +					if (unlikely(!pskb_may_pull(skb,
> +								pull_size))) {
> +						goto err;
> +					}
> +					ctx->l4_hdr_size = ((struct tcphdr *)
> +					   skb_transport_header(skb))->doff * 4;
> +				} else if (iph->protocol == IPPROTO_UDP) {
> +					ctx->l4_hdr_size =
> +							sizeof(struct udphdr);
> +				} else {
> +					ctx->l4_hdr_size = 0;
> +				}
> +			} else {
> +				/* for simplicity, don't copy L4 headers */
> +				ctx->l4_hdr_size = 0;
> +			}
> +			ctx->copy_size = ctx->eth_ip_hdr_size +
> +					 ctx->l4_hdr_size;
> +		} else {
> +			ctx->eth_ip_hdr_size = 0;
> +			ctx->l4_hdr_size = 0;
> +			/* copy as much as allowed */
> +			ctx->copy_size = min((unsigned int)VMXNET3_HDR_COPY_SIZE
> +					     , skb_headlen(skb));
> +		}
> +
> +		/* make sure headers are accessible directly */
> +		if (unlikely(!pskb_may_pull(skb, ctx->copy_size)))
> +			goto err;
> +	}
> +
> +	if (unlikely(ctx->copy_size > VMXNET3_HDR_COPY_SIZE)) {
> +		tq->stats.oversized_hdr++;
> +		ctx->copy_size = 0;
> +		return 0;
> +	}
> +
> +	tdd = tq->data_ring.base + tq->tx_ring.next2fill;
> +
> +	memcpy(tdd->data, skb->data, ctx->copy_size);
> +	dprintk(KERN_ERR "copy %u bytes to dataRing[%u]\n",
> +		ctx->copy_size, tq->tx_ring.next2fill);
> +	return 1;
> +
> +err:
> +	return -1;
> +}
> +
> +
> +static void
> +vmxnet3_prepare_tso(struct sk_buff *skb,
> +		    struct vmxnet3_tx_ctx *ctx)
> +{
> +	struct tcphdr *tcph = (struct tcphdr *)skb_transport_header(skb);
> +	if (ctx->ipv4) {
> +		struct iphdr *iph = (struct iphdr *)skb_network_header(skb);
> +		iph->check = 0;
> +		tcph->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, 0,
> +						 IPPROTO_TCP, 0);
> +	} else {
> +		struct ipv6hdr *iph = (struct ipv6hdr *)skb_network_header(skb);
> +		tcph->check = ~csum_ipv6_magic(&iph->saddr, &iph->daddr, 0,
> +					       IPPROTO_TCP, 0);
> +	}
> +}
> +
> +
> +/*
> + * Transmits a pkt thru a given tq
> + * Returns:
> + *    NETDEV_TX_OK:      descriptors are setup successfully
> + *    NETDEV_TX_OK:      error occured, the pkt is dropped
> + *    NETDEV_TX_BUSY:    tx ring is full, queue is stopped
> + *
> + * Side-effects:
> + *    1. tx ring may be changed
> + *    2. tq stats may be updated accordingly
> + *    3. shared->txNumDeferred may be updated
> + */
> +
> +static int
> +vmxnet3_tq_xmit(struct sk_buff *skb, struct vmxnet3_tx_queue *tq,
> +		struct vmxnet3_adapter *adapter, struct net_device *netdev)
> +{
> +	int ret;
> +	u32 count;
> +	unsigned long flags;
> +	struct vmxnet3_tx_ctx ctx;
> +	union Vmxnet3_GenericDesc *gdesc;
> +
> +	/* conservatively estimate # of descriptors to use */
> +	count = VMXNET3_TXD_NEEDED(skb_headlen(skb)) +
> +		skb_shinfo(skb)->nr_frags + 1;
> +
> +	ctx.ipv4 = (skb->protocol == __constant_ntohs(ETH_P_IP));
> +
> +	ctx.mss = skb_shinfo(skb)->gso_size;
> +	if (ctx.mss) {
> +		if (skb_header_cloned(skb)) {
> +			if (unlikely(pskb_expand_head(skb, 0, 0,
> +						      GFP_ATOMIC) != 0)) {
> +				tq->stats.drop_tso++;
> +				goto drop_pkt;
> +			}
> +			tq->stats.copy_skb_header++;
> +		}
> +		vmxnet3_prepare_tso(skb, &ctx);
> +	} else {
> +		if (unlikely(count > VMXNET3_MAX_TXD_PER_PKT)) {
> +
> +			/* non-tso pkts must not use more than
> +			 * VMXNET3_MAX_TXD_PER_PKT entries
> +			 */
> +			if (skb_linearize(skb) != 0) {
> +				tq->stats.drop_too_many_frags++;
> +				goto drop_pkt;
> +			}
> +			tq->stats.linearized++;
> +
> +			/* recalculate the # of descriptors to use */
> +			count = VMXNET3_TXD_NEEDED(skb_headlen(skb)) + 1;
> +		}
> +	}
> +
> +	ret = vmxnet3_parse_and_copy_hdr(skb, tq, &ctx, adapter);
> +	if (ret >= 0) {
> +		BUG_ON(ret <= 0 && ctx.copy_size != 0);
> +		/* hdrs parsed, check against other limits */
> +		if (ctx.mss) {
> +			if (unlikely(ctx.eth_ip_hdr_size + ctx.l4_hdr_size >
> +				     VMXNET3_MAX_TX_BUF_SIZE)) {
> +				goto hdr_too_big;
> +			}
> +		} else {
> +			if (skb->ip_summed == CHECKSUM_PARTIAL) {
> +				if (unlikely(ctx.eth_ip_hdr_size +
> +					     skb->csum_offset >
> +					     VMXNET3_MAX_CSUM_OFFSET)) {
> +					goto hdr_too_big;
> +				}
> +			}
> +		}
> +	} else {
> +		tq->stats.drop_hdr_inspect_err++;
> +		goto drop_pkt;
> +	}
> +
> +	spin_lock_irqsave(&tq->tx_lock, flags);
> +
> +	if (count > vmxnet3_cmd_ring_desc_avail(&tq->tx_ring)) {
> +		tq->stats.tx_ring_full++;
> +		dprintk(KERN_ERR "tx queue stopped on %s, next2comp %u"
> +			" next2fill %u\n", adapter->netdev->name,
> +			tq->tx_ring.next2comp, tq->tx_ring.next2fill);
> +
> +		vmxnet3_tq_stop(tq, adapter);
> +		spin_unlock_irqrestore(&tq->tx_lock, flags);
> +		return NETDEV_TX_BUSY;
> +	}
> +
> +	/* fill tx descs related to addr & len */
> +	vmxnet3_map_pkt(skb, &ctx, tq, adapter->pdev, adapter);
> +
> +	/* setup the EOP desc */
> +	ctx.eop_txd->dword[3] = VMXNET3_TXD_CQ | VMXNET3_TXD_EOP;
> +
> +	/* setup the SOP desc */
> +	gdesc = ctx.sop_txd;
> +	if (ctx.mss) {
> +		gdesc->txd.hlen = ctx.eth_ip_hdr_size + ctx.l4_hdr_size;
> +		gdesc->txd.om = VMXNET3_OM_TSO;
> +		gdesc->txd.msscof = ctx.mss;
> +		tq->shared->txNumDeferred += (skb->len - gdesc->txd.hlen +
> +					     ctx.mss - 1) / ctx.mss;
> +	} else {
> +		if (skb->ip_summed == CHECKSUM_PARTIAL) {
> +			gdesc->txd.hlen = ctx.eth_ip_hdr_size;
> +			gdesc->txd.om = VMXNET3_OM_CSUM;
> +			gdesc->txd.msscof = ctx.eth_ip_hdr_size +
> +					    skb->csum_offset;
> +		} else {
> +			gdesc->txd.om = 0;
> +			gdesc->txd.msscof = 0;
> +		}
> +		tq->shared->txNumDeferred++;
> +	}
> +
> +	if (vlan_tx_tag_present(skb)) {
> +		gdesc->txd.ti = 1;
> +		gdesc->txd.tci = vlan_tx_tag_get(skb);
> +	}
> +
> +	wmb();
> +
> +	/* finally flips the GEN bit of the SOP desc */
> +	gdesc->dword[2] ^= VMXNET3_TXD_GEN;
> +	dprintk(KERN_ERR "txd[%u]: SOP 0x%Lx 0x%x 0x%x\n",
> +		(u32)((union Vmxnet3_GenericDesc *)ctx.sop_txd -
> +		tq->tx_ring.base), gdesc->txd.addr, gdesc->dword[2],
> +		gdesc->dword[3]);
> +
> +	spin_unlock_irqrestore(&tq->tx_lock, flags);
> +
> +	if (tq->shared->txNumDeferred >= tq->shared->txThreshold) {
> +		tq->shared->txNumDeferred = 0;
> +		VMXNET3_WRITE_BAR0_REG(adapter, VMXNET3_REG_TXPROD,
> +				       tq->tx_ring.next2fill);
> +	}
> +	netdev->trans_start = jiffies;
> +
> +	return NETDEV_TX_OK;
> +
> +hdr_too_big:
> +	tq->stats.drop_oversized_hdr++;
> +drop_pkt:
> +	tq->stats.drop_total++;
> +	dev_kfree_skb(skb);
> +	return NETDEV_TX_OK;
> +}
> +
> +
> +static netdev_tx_t
> +vmxnet3_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
> +{
> +	struct vmxnet3_adapter *adapter = netdev_priv(netdev);
> +	struct vmxnet3_tx_queue *tq = &adapter->tx_queue;
> +
> +	return vmxnet3_tq_xmit(skb, tq, adapter, netdev);
> +}
> +
> +
> +static void
> +vmxnet3_rx_csum(struct vmxnet3_adapter *adapter,
> +		struct sk_buff *skb,
> +		union Vmxnet3_GenericDesc *gdesc)
> +{
> +	if (!gdesc->rcd.cnc && adapter->rxcsum) {
> +		/* typical case: TCP/UDP over IP and both csums are correct */
> +		if ((gdesc->dword[3] & VMXNET3_RCD_CSUM_OK) ==
> +							VMXNET3_RCD_CSUM_OK) {
> +			skb->ip_summed = CHECKSUM_UNNECESSARY;
> +			BUG_ON(!(gdesc->rcd.tcp || gdesc->rcd.udp));
> +			BUG_ON(!(gdesc->rcd.v4  || gdesc->rcd.v6));
> +			BUG_ON(gdesc->rcd.frg);
> +		} else {
> +			if (gdesc->rcd.csum) {
> +				skb->csum = htons(gdesc->rcd.csum);
> +				skb->ip_summed = CHECKSUM_PARTIAL;
> +			} else {
> +				skb->ip_summed = CHECKSUM_NONE;
> +			}
> +		}
> +	} else {
> +		skb->ip_summed = CHECKSUM_NONE;
> +	}
> +}
> +
> +
> +static void
> +vmxnet3_rx_error(struct vmxnet3_rx_queue *rq, struct Vmxnet3_RxCompDesc *rcd,
> +		 struct vmxnet3_rx_ctx *ctx,  struct vmxnet3_adapter *adapter)
> +{
> +	rq->stats.drop_err++;
> +	if (!rcd->fcs)
> +		rq->stats.drop_fcs++;
> +
> +	rq->stats.drop_total++;
> +
> +	/*
> +	 * We do not unmap and chain the rx buffer to the skb.
> +	 * We basically pretend this buffer is not used and will be recycled
> +	 * by vmxnet3_rq_alloc_rx_buf()
> +	 */
> +
> +	/*
> +	 * ctx->skb may be NULL if this is the first and the only one
> +	 * desc for the pkt
> +	 */
> +	if (ctx->skb)
> +		dev_kfree_skb_irq(ctx->skb);
> +
> +	ctx->skb = NULL;
> +}
> +
> +
> +static int
> +vmxnet3_rq_rx_complete(struct vmxnet3_rx_queue *rq,
> +		       struct vmxnet3_adapter *adapter, int quota)
> +{
> +	static u32 rxprod_reg[2] = {VMXNET3_REG_RXPROD, VMXNET3_REG_RXPROD2};
> +	u32 num_rxd = 0;
> +	struct Vmxnet3_RxCompDesc *rcd;
> +	struct vmxnet3_rx_ctx *ctx = &rq->rx_ctx;
> +
> +	rcd = &rq->comp_ring.base[rq->comp_ring.next2proc].rcd;
> +	while (rcd->gen == rq->comp_ring.gen) {
> +		struct vmxnet3_rx_buf_info *rbi;
> +		struct sk_buff *skb;
> +		int num_to_alloc;
> +		struct Vmxnet3_RxDesc *rxd;
> +		u32 idx, ring_idx;
> +
> +		if (num_rxd >= quota) {
> +			/* we may stop even before we see the EOP desc of
> +			 * the current pkt
> +			 */
> +			break;
> +		}
> +		num_rxd++;
> +
> +		idx = rcd->rxdIdx;
> +		ring_idx = rcd->rqID == rq->qid ? 0 : 1;
> +
> +		rxd = &rq->rx_ring[ring_idx].base[idx].rxd;
> +		rbi = rq->buf_info[ring_idx] + idx;
> +
> +		BUG_ON(rxd->addr != rbi->dma_addr || rxd->len != rbi->len);
> +
> +		if (unlikely(rcd->eop && rcd->err)) {
> +			vmxnet3_rx_error(rq, rcd, ctx, adapter);
> +			goto rcd_done;
> +		}
> +
> +		if (rcd->sop) { /* first buf of the pkt */
> +			BUG_ON(rxd->btype != VMXNET3_RXD_BTYPE_HEAD ||
> +			       rcd->rqID != rq->qid);
> +
> +			BUG_ON(rbi->buf_type != VMXNET3_RX_BUF_SKB);
> +			BUG_ON(ctx->skb != NULL || rbi->skb == NULL);
> +
> +			if (unlikely(rcd->len == 0)) {
> +				/* Pretend the rx buffer is skipped. */
> +				BUG_ON(!(rcd->sop && rcd->eop));
> +				dprintk(KERN_ERR "rxRing[%u][%u] 0 length\n",
> +					ring_idx, idx);
> +				goto rcd_done;
> +			}
> +
> +			ctx->skb = rbi->skb;
> +			rbi->skb = NULL;
> +
> +			pci_unmap_single(adapter->pdev, rbi->dma_addr, rbi->len,
> +					 PCI_DMA_FROMDEVICE);
> +
> +			skb_put(ctx->skb, rcd->len);
> +		} else {
> +			BUG_ON(ctx->skb == NULL);
> +			/* non SOP buffer must be type 1 in most cases */
> +			if (rbi->buf_type == VMXNET3_RX_BUF_PAGE) {
> +				BUG_ON(rxd->btype != VMXNET3_RXD_BTYPE_BODY);
> +
> +				if (rcd->len) {
> +					pci_unmap_page(adapter->pdev,
> +						       rbi->dma_addr, rbi->len,
> +						       PCI_DMA_FROMDEVICE);
> +
> +					vmxnet3_append_frag(ctx->skb, rcd, rbi);
> +					rbi->page = NULL;
> +				}
> +			} else {
> +				/*
> +				 * The only time a non-SOP buffer is type 0 is
> +				 * when it's EOP and error flag is raised, which
> +				 * has already been handled.
> +				 */
> +				BUG_ON(true);
> +			}
> +		}
> +
> +		skb = ctx->skb;
> +		if (rcd->eop) {
> +			skb->len += skb->data_len;
> +			skb->truesize += skb->data_len;
> +
> +			vmxnet3_rx_csum(adapter, skb,
> +					(union Vmxnet3_GenericDesc *)rcd);
> +			skb->protocol = eth_type_trans(skb, adapter->netdev);
> +
> +			if (unlikely(adapter->vlan_grp && rcd->ts)) {
> +				vlan_hwaccel_receive_skb(skb,
> +						adapter->vlan_grp, rcd->tci);
> +			} else {
> +				netif_receive_skb(skb);
> +			}
> +
> +			adapter->netdev->last_rx = jiffies;
> +			ctx->skb = NULL;
> +		}
> +
> +rcd_done:
> +		/* device may skip some rx descs */
> +		rq->rx_ring[ring_idx].next2comp = idx;
> +		VMXNET3_INC_RING_IDX_ONLY(rq->rx_ring[ring_idx].next2comp,
> +					  rq->rx_ring[ring_idx].size);
> +
> +		/* refill rx buffers frequently to avoid starving the h/w */
> +		num_to_alloc = vmxnet3_cmd_ring_desc_avail(rq->rx_ring +
> +							   ring_idx);
> +		if (unlikely(num_to_alloc > VMXNET3_RX_ALLOC_THRESHOLD(rq,
> +							ring_idx, adapter))) {
> +			vmxnet3_rq_alloc_rx_buf(rq, ring_idx, num_to_alloc,
> +						adapter);
> +
> +			/* if needed, update the register */
> +			if (unlikely(rq->shared->updateRxProd)) {
> +				VMXNET3_WRITE_BAR0_REG(adapter,
> +					rxprod_reg[ring_idx] + rq->qid * 8,
> +					rq->rx_ring[ring_idx].next2fill);
> +				rq->uncommitted[ring_idx] = 0;
> +			}
> +		}
> +
> +		vmxnet3_comp_ring_adv_next2proc(&rq->comp_ring);
> +		rcd = &rq->comp_ring.base[rq->comp_ring.next2proc].rcd;
> +	}
> +
> +	return num_rxd;
> +}
> +
> +
> +static void
> +vmxnet3_rq_cleanup(struct vmxnet3_rx_queue *rq,
> +		   struct vmxnet3_adapter *adapter)
> +{
> +	u32 i, ring_idx;
> +	struct Vmxnet3_RxDesc *rxd;
> +
> +	for (ring_idx = 0; ring_idx < 2; ring_idx++) {
> +		for (i = 0; i < rq->rx_ring[ring_idx].size; i++) {
> +			rxd = &rq->rx_ring[ring_idx].base[i].rxd;
> +
> +			if (rxd->btype == VMXNET3_RXD_BTYPE_HEAD &&
> +					rq->buf_info[ring_idx][i].skb) {
> +				pci_unmap_single(adapter->pdev, rxd->addr,
> +						 rxd->len, PCI_DMA_FROMDEVICE);
> +				dev_kfree_skb(rq->buf_info[ring_idx][i].skb);
> +				rq->buf_info[ring_idx][i].skb = NULL;
> +			} else if (rxd->btype == VMXNET3_RXD_BTYPE_BODY &&
> +					rq->buf_info[ring_idx][i].page) {
> +				pci_unmap_page(adapter->pdev, rxd->addr,
> +					       rxd->len, PCI_DMA_FROMDEVICE);
> +				put_page(rq->buf_info[ring_idx][i].page);
> +				rq->buf_info[ring_idx][i].page = NULL;
> +			}
> +		}
> +
> +		rq->rx_ring[ring_idx].gen = VMXNET3_INIT_GEN;
> +		rq->rx_ring[ring_idx].next2fill =
> +					rq->rx_ring[ring_idx].next2comp = 0;
> +		rq->uncommitted[ring_idx] = 0;
> +	}
> +
> +	rq->comp_ring.gen = VMXNET3_INIT_GEN;
> +	rq->comp_ring.next2proc = 0;
> +}
> +
> +
> +void vmxnet3_rq_destroy(struct vmxnet3_rx_queue *rq,
> +			struct vmxnet3_adapter *adapter)
> +{
> +	int i;
> +	int j;
> +
> +	/* all rx buffers must have already been freed */
> +	for (i = 0; i < 2; i++) {
> +		if (rq->buf_info[i]) {
> +			for (j = 0; j < rq->rx_ring[i].size; j++)
> +				BUG_ON(rq->buf_info[i][j].page != NULL);
> +		}
> +	}
> +
> +
> +	kfree(rq->buf_info[0]);
> +
> +	for (i = 0; i < 2; i++) {
> +		if (rq->rx_ring[i].base) {
> +			pci_free_consistent(adapter->pdev, rq->rx_ring[i].size
> +					    * sizeof(struct Vmxnet3_RxDesc),
> +					    rq->rx_ring[i].base,
> +					    rq->rx_ring[i].basePA);
> +			rq->rx_ring[i].base = NULL;
> +		}
> +		rq->buf_info[i] = NULL;
> +	}
> +
> +	if (rq->comp_ring.base) {
> +		pci_free_consistent(adapter->pdev, rq->comp_ring.size *
> +				    sizeof(struct Vmxnet3_RxCompDesc),
> +				    rq->comp_ring.base, rq->comp_ring.basePA);
> +		rq->comp_ring.base = NULL;
> +	}
> +}
> +
> +
> +static int
> +vmxnet3_rq_init(struct vmxnet3_rx_queue *rq,
> +		struct vmxnet3_adapter  *adapter)
> +{
> +	int i;
> +
> +	/* initialize buf_info */
> +	for (i = 0; i < rq->rx_ring[0].size; i++) {
> +
> +		/* 1st buf for a pkt is skbuff */
> +		if (i % adapter->rx_buf_per_pkt == 0) {
> +			rq->buf_info[0][i].buf_type = VMXNET3_RX_BUF_SKB;
> +			rq->buf_info[0][i].len = adapter->skb_buf_size;
> +		} else { /* subsequent bufs for a pkt is frag */
> +			rq->buf_info[0][i].buf_type = VMXNET3_RX_BUF_PAGE;
> +			rq->buf_info[0][i].len = PAGE_SIZE;
> +		}
> +	}
> +	for (i = 0; i < rq->rx_ring[1].size; i++) {
> +		rq->buf_info[1][i].buf_type = VMXNET3_RX_BUF_PAGE;
> +		rq->buf_info[1][i].len = PAGE_SIZE;
> +	}
> +
> +	/* reset internal state and allocate buffers for both rings */
> +	for (i = 0; i < 2; i++) {
> +		rq->rx_ring[i].next2fill = rq->rx_ring[i].next2comp = 0;
> +		rq->uncommitted[i] = 0;
> +
> +		memset(rq->rx_ring[i].base, 0, rq->rx_ring[i].size *
> +		       sizeof(struct Vmxnet3_RxDesc));
> +		rq->rx_ring[i].gen = VMXNET3_INIT_GEN;
> +	}
> +	if (vmxnet3_rq_alloc_rx_buf(rq, 0, rq->rx_ring[0].size - 1,
> +				    adapter) == 0) {
> +		/* at least has 1 rx buffer for the 1st ring */
> +		return -ENOMEM;
> +	}
> +	vmxnet3_rq_alloc_rx_buf(rq, 1, rq->rx_ring[1].size - 1, adapter);
> +
> +	/* reset the comp ring */
> +	rq->comp_ring.next2proc = 0;
> +	memset(rq->comp_ring.base, 0, rq->comp_ring.size *
> +	       sizeof(struct Vmxnet3_RxCompDesc));
> +	rq->comp_ring.gen = VMXNET3_INIT_GEN;
> +
> +	/* reset rxctx */
> +	rq->rx_ctx.skb = NULL;
> +
> +	/* stats are not reset */
> +	return 0;
> +}
> +
> +
> +static int
> +vmxnet3_rq_create(struct vmxnet3_rx_queue *rq, struct vmxnet3_adapter *adapter)
> +{
> +	int i;
> +	size_t sz;
> +	struct vmxnet3_rx_buf_info *bi;
> +
> +	for (i = 0; i < 2; i++) {
> +
> +		sz = rq->rx_ring[i].size * sizeof(struct Vmxnet3_RxDesc);
> +		rq->rx_ring[i].base = pci_alloc_consistent(adapter->pdev, sz,
> +							&rq->rx_ring[i].basePA);
> +		if (!rq->rx_ring[i].base) {
> +			printk(KERN_ERR "%s: failed to allocate rx ring %d\n",
> +			       adapter->netdev->name, i);
> +			goto err;
> +		}
> +	}
> +
> +	sz = rq->comp_ring.size * sizeof(struct Vmxnet3_RxCompDesc);
> +	rq->comp_ring.base = pci_alloc_consistent(adapter->pdev, sz,
> +						  &rq->comp_ring.basePA);
> +	if (!rq->comp_ring.base) {
> +		printk(KERN_ERR "%s: failed to allocate rx comp ring\n",
> +		       adapter->netdev->name);
> +		goto err;
> +	}
> +
> +	sz = sizeof(struct vmxnet3_rx_buf_info) * (rq->rx_ring[0].size +
> +						   rq->rx_ring[1].size);
> +	bi = kmalloc(sz, GFP_KERNEL);
> +	if (!bi) {
> +		printk(KERN_ERR "%s: failed to allocate rx bufinfo\n",
> +		       adapter->netdev->name);
> +		goto err;
> +	}
> +	memset(bi, 0, sz);
> +	rq->buf_info[0] = bi;
> +	rq->buf_info[1] = bi + rq->rx_ring[0].size;
> +
> +	return 0;
> +
> +err:
> +	vmxnet3_rq_destroy(rq, adapter);
> +	return -ENOMEM;
> +}
> +
> +
> +static void
> +vmxnet3_do_poll(struct vmxnet3_adapter *adapter, int budget, int *txd_done,
> +		int *rxd_done)
> +{
> +	if (unlikely(adapter->shared->ecr))
> +		vmxnet3_process_events(adapter);
> +
> +	*txd_done = vmxnet3_tq_tx_complete(&adapter->tx_queue, adapter);
> +	*rxd_done = vmxnet3_rq_rx_complete(&adapter->rx_queue, adapter, budget);
> +}
> +
> +
> +static int
> +vmxnet3_poll(struct napi_struct *napi, int budget)
> +{
> +	struct vmxnet3_adapter *adapter = container_of(napi,
> +					  struct vmxnet3_adapter, napi);
> +	int rxd_done, txd_done;
> +
> +	vmxnet3_do_poll(adapter, budget, &txd_done, &rxd_done);
> +
> +	if (rxd_done < budget) {
> +		napi_complete(napi);
> +		vmxnet3_enable_intr(adapter, 0);
> +	}
> +	return rxd_done;
> +}
> +
> +
> +/* Interrupt handler for vmxnet3  */
> +static irqreturn_t
> +vmxnet3_intr(int irq, void *dev_id)
> +{
> +	struct net_device *dev = dev_id;
> +	struct vmxnet3_adapter *adapter = netdev_priv(dev);
> +
> +	if (unlikely(adapter->intr.type == VMXNET3_IT_INTX)) {
> +		u32 icr = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_ICR);
> +		if (unlikely(icr == 0))
> +			/* not ours */
> +			return IRQ_NONE;
> +	}
> +
> +
> +	/* disable intr if needed */
> +	if (adapter->intr.mask_mode == VMXNET3_IMM_ACTIVE)
> +		vmxnet3_disable_intr(adapter, 0);
> +
> +	napi_schedule(&adapter->napi);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +#ifdef CONFIG_NET_POLL_CONTROLLER
> +
> +
> +/* netpoll callback. */
> +static void
> +vmxnet3_netpoll(struct net_device *netdev)
> +{
> +	struct vmxnet3_adapter *adapter = netdev_priv(netdev);
> +	int irq;
> +
> +	if (adapter->intr.type == VMXNET3_IT_MSIX)
> +		irq = adapter->intr.msix_entries[0].vector;
> +	else
> +		irq = adapter->pdev->irq;
> +
> +	disable_irq(irq);
> +	vmxnet3_intr(irq, netdev);
> +	enable_irq(irq);
> +}
> +#endif
> +
> +static int
> +vmxnet3_request_irqs(struct vmxnet3_adapter *adapter)
> +{
> +	int err;
> +
> +	if (adapter->intr.type == VMXNET3_IT_MSIX) {
> +		/* we only use 1 MSI-X vector */
> +		err = request_irq(adapter->intr.msix_entries[0].vector,
> +				  vmxnet3_intr, 0, adapter->netdev->name,
> +				  adapter->netdev);
> +	} else if (adapter->intr.type == VMXNET3_IT_MSI) {
> +		err = request_irq(adapter->pdev->irq, vmxnet3_intr, 0,
> +				  adapter->netdev->name, adapter->netdev);
> +	} else {
> +		err = request_irq(adapter->pdev->irq, vmxnet3_intr,
> +				  IRQF_SHARED, adapter->netdev->name,
> +				  adapter->netdev);
> +	}
> +
> +	if (err)
> +		printk(KERN_ERR "Failed to request irq %s (intr type:%d), error"
> +		       ":%d\n", adapter->netdev->name, adapter->intr.type, err);
> +
> +
> +	if (!err) {
> +		int i;
> +		/* init our intr settings */
> +		for (i = 0; i < adapter->intr.num_intrs; i++)
> +			adapter->intr.mod_levels[i] = UPT1_IML_ADAPTIVE;
> +
> +		/* next setup intr index for all intr sources */
> +		adapter->tx_queue.comp_ring.intr_idx = 0;
> +		adapter->rx_queue.comp_ring.intr_idx = 0;
> +		adapter->intr.event_intr_idx = 0;
> +
> +		printk(KERN_INFO "%s: intr type %u, mode %u, %u vectors "
> +		       "allocated\n", adapter->netdev->name, adapter->intr.type,
> +		       adapter->intr.mask_mode, adapter->intr.num_intrs);
> +	}
> +
> +	return err;
> +}
> +
> +
> +static void
> +vmxnet3_free_irqs(struct vmxnet3_adapter *adapter)
> +{
> +	BUG_ON(adapter->intr.type == VMXNET3_IT_AUTO ||
> +	       adapter->intr.num_intrs <= 0);
> +
> +	switch (adapter->intr.type) {
> +	case VMXNET3_IT_MSIX:
> +	{
> +		int i;
> +
> +		for (i = 0; i < adapter->intr.num_intrs; i++)
> +			free_irq(adapter->intr.msix_entries[i].vector,
> +				 adapter->netdev);
> +		break;
> +	}
> +	case VMXNET3_IT_MSI:
> +		free_irq(adapter->pdev->irq, adapter->netdev);
> +		break;
> +	case VMXNET3_IT_INTX:
> +		free_irq(adapter->pdev->irq, adapter->netdev);
> +		break;
> +	default:
> +		BUG_ON(true);
> +	}
> +}
> +
> +
> +static void
> +vmxnet3_vlan_rx_register(struct net_device *netdev, struct vlan_group *grp)
> +{
> +	struct vmxnet3_adapter *adapter = netdev_priv(netdev);
> +	struct Vmxnet3_DriverShared *shared = adapter->shared;
> +	u32 *vfTable = adapter->shared->devRead.rxFilterConf.vfTable;
> +
> +	if (grp) {
> +		/* add vlan rx stripping. */
> +		if (adapter->netdev->features & NETIF_F_HW_VLAN_RX) {
> +			int i;
> +			struct Vmxnet3_DSDevRead *devRead = &shared->devRead;
> +			adapter->vlan_grp = grp;
> +
> +			/* update FEATURES to device */
> +			devRead->misc.uptFeatures |= UPT1_F_RXVLAN;
> +			VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD,
> +					       VMXNET3_CMD_UPDATE_FEATURE);
> +			/*
> +			 *  Clear entire vfTable; then enable untagged pkts.
> +			 *  Note: setting one entry in vfTable to non-zero turns
> +			 *  on VLAN rx filtering.
> +			 */
> +			for (i = 0; i < VMXNET3_VFT_SIZE; i++)
> +				vfTable[i] = 0;
> +
> +			VMXNET3_SET_VFTABLE_ENTRY(vfTable, 0);
> +			VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD,
> +					       VMXNET3_CMD_UPDATE_VLAN_FILTERS);
> +		} else {
> +			printk(KERN_ERR "%s: vlan_rx_register when device has "
> +			       "no NETIF_F_HW_VLAN_RX\n", netdev->name);
> +		}
> +	} else {
> +		/* remove vlan rx stripping. */
> +		struct Vmxnet3_DSDevRead *devRead = &shared->devRead;
> +		adapter->vlan_grp = NULL;
> +
> +		if (devRead->misc.uptFeatures & UPT1_F_RXVLAN) {
> +			int i;
> +
> +			for (i = 0; i < VMXNET3_VFT_SIZE; i++) {
> +				/* clear entire vfTable; this also disables
> +				 * VLAN rx filtering
> +				 */
> +				vfTable[i] = 0;
> +			}
> +			VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD,
> +					       VMXNET3_CMD_UPDATE_VLAN_FILTERS);
> +
> +			/* update FEATURES to device */
> +			devRead->misc.uptFeatures &= ~UPT1_F_RXVLAN;
> +			VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD,
> +					       VMXNET3_CMD_UPDATE_FEATURE);
> +		}
> +	}
> +}
> +
> +
> +static void
> +vmxnet3_restore_vlan(struct vmxnet3_adapter *adapter)
> +{
> +	if (adapter->vlan_grp) {
> +		u16 vid;
> +		u32 *vfTable = adapter->shared->devRead.rxFilterConf.vfTable;
> +		bool activeVlan = false;
> +
> +		for (vid = 0; vid < VLAN_GROUP_ARRAY_LEN; vid++) {
> +			if (vlan_group_get_device(adapter->vlan_grp, vid)) {
> +				VMXNET3_SET_VFTABLE_ENTRY(vfTable, vid);
> +				activeVlan = true;
> +			}
> +		}
> +		if (activeVlan) {
> +			/* continue to allow untagged pkts */
> +			VMXNET3_SET_VFTABLE_ENTRY(vfTable, 0);
> +		}
> +	}
> +}
> +
> +
> +static void
> +vmxnet3_vlan_rx_add_vid(struct net_device *netdev, u16 vid)
> +{
> +	struct vmxnet3_adapter *adapter = netdev_priv(netdev);
> +	u32 *vfTable = adapter->shared->devRead.rxFilterConf.vfTable;
> +
> +	VMXNET3_SET_VFTABLE_ENTRY(vfTable, vid);
> +	VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD,
> +			       VMXNET3_CMD_UPDATE_VLAN_FILTERS);
> +}
> +
> +
> +static void
> +vmxnet3_vlan_rx_kill_vid(struct net_device *netdev, u16 vid)
> +{
> +	struct vmxnet3_adapter *adapter = netdev_priv(netdev);
> +	u32 *vfTable = adapter->shared->devRead.rxFilterConf.vfTable;
> +
> +	VMXNET3_CLEAR_VFTABLE_ENTRY(vfTable, vid);
> +	VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD,
> +			       VMXNET3_CMD_UPDATE_VLAN_FILTERS);
> +}
> +
> +
> +static u8 *
> +vmxnet3_copy_mc(struct net_device *netdev)
> +{
> +	u8 *buf = NULL;
> +	u32 sz = netdev->mc_count * ETH_ALEN;
> +
> +	/* struct Vmxnet3_RxFilterConf.mfTableLen is u16. */
> +	if (sz <= 0xffff) {
> +		/* We may be called with BH disabled */
> +		buf = kmalloc(sz, GFP_ATOMIC);
> +		if (buf) {
> +			int i;
> +			struct dev_mc_list *mc = netdev->mc_list;
> +
> +			for (i = 0; i < netdev->mc_count; i++) {
> +				BUG_ON(!mc);
> +				memcpy(buf + i * ETH_ALEN, mc->dmi_addr,
> +				       ETH_ALEN);
> +				mc = mc->next;
> +			}
> +		}
> +	}
> +	return buf;
> +}
> +
> +
> +static void
> +vmxnet3_set_mc(struct net_device *netdev)
> +{
> +	struct vmxnet3_adapter *adapter = netdev_priv(netdev);
> +	struct Vmxnet3_RxFilterConf *rxConf =
> +					&adapter->shared->devRead.rxFilterConf;
> +	u8 *new_table = NULL;
> +	u32 new_mode = VMXNET3_RXM_UCAST;
> +
> +	if (netdev->flags & IFF_PROMISC)
> +		new_mode |= VMXNET3_RXM_PROMISC;
> +
> +	if (netdev->flags & IFF_BROADCAST)
> +		new_mode |= VMXNET3_RXM_BCAST;
> +
> +	if (netdev->flags & IFF_ALLMULTI)
> +		new_mode |= VMXNET3_RXM_ALL_MULTI;
> +	else
> +		if (netdev->mc_count > 0) {
> +			new_table = vmxnet3_copy_mc(netdev);
> +			if (new_table) {
> +				new_mode |= VMXNET3_RXM_MCAST;
> +				rxConf->mfTableLen = netdev->mc_count *
> +						     ETH_ALEN;
> +				rxConf->mfTablePA = virt_to_phys(new_table);
> +			} else {
> +				printk(KERN_INFO "%s: failed to copy mcast list"
> +				       ", setting ALL_MULTI\n", netdev->name);
> +				new_mode |= VMXNET3_RXM_ALL_MULTI;
> +			}
> +		}
> +
> +
> +	if (!(new_mode & VMXNET3_RXM_MCAST)) {
> +		rxConf->mfTableLen = 0;
> +		rxConf->mfTablePA = 0;
> +	}
> +
> +	if (new_mode != rxConf->rxMode) {
> +		rxConf->rxMode = new_mode;
> +		VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD,
> +				       VMXNET3_CMD_UPDATE_RX_MODE);
> +	}
> +
> +	VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD,
> +			       VMXNET3_CMD_UPDATE_MAC_FILTERS);
> +
> +	kfree(new_table);
> +}
> +
> +
> +/*
> + *   Set up driver_shared based on settings in adapter.
> + */
> +
> +static void
> +vmxnet3_setup_driver_shared(struct vmxnet3_adapter *adapter)
> +{
> +	struct Vmxnet3_DriverShared *shared = adapter->shared;
> +	struct Vmxnet3_DSDevRead *devRead = &shared->devRead;
> +	struct Vmxnet3_TxQueueConf *tqc;
> +	struct Vmxnet3_RxQueueConf *rqc;
> +	int i;
> +
> +	memset(shared, 0, sizeof(*shared));
> +
> +	/* driver settings */
> +	shared->magic = VMXNET3_REV1_MAGIC;
> +	devRead->misc.driverInfo.version = VMXNET3_DRIVER_VERSION_NUM;
> +	devRead->misc.driverInfo.gos.gosBits = (sizeof(void *) == 4 ?
> +				VMXNET3_GOS_BITS_32 : VMXNET3_GOS_BITS_64);
> +	devRead->misc.driverInfo.gos.gosType = VMXNET3_GOS_TYPE_LINUX;
> +	devRead->misc.driverInfo.vmxnet3RevSpt = 1;
> +	devRead->misc.driverInfo.uptVerSpt = 1;
> +
> +	devRead->misc.ddPA = virt_to_phys(adapter);
> +	devRead->misc.ddLen = sizeof(struct vmxnet3_adapter);
> +
> +	/* set up feature flags */
> +	if (adapter->rxcsum)
> +		devRead->misc.uptFeatures |= UPT1_F_RXCSUM;
> +
> +	if (adapter->lro) {
> +		devRead->misc.uptFeatures |= UPT1_F_LRO;
> +		devRead->misc.maxNumRxSG = 1 + MAX_SKB_FRAGS;
> +	}
> +	if ((adapter->netdev->features & NETIF_F_HW_VLAN_RX)
> +			&& adapter->vlan_grp) {
> +		devRead->misc.uptFeatures |= UPT1_F_RXVLAN;
> +	}
> +
> +	devRead->misc.mtu = adapter->netdev->mtu;
> +	devRead->misc.queueDescPA = adapter->queue_desc_pa;
> +	devRead->misc.queueDescLen = sizeof(struct Vmxnet3_TxQueueDesc) +
> +				     sizeof(struct Vmxnet3_RxQueueDesc);
> +
> +	/* tx queue settings */
> +	BUG_ON(adapter->tx_queue.tx_ring.base == NULL);
> +
> +	devRead->misc.numTxQueues = 1;
> +	tqc = &adapter->tqd_start->conf;
> +	tqc->txRingBasePA   = adapter->tx_queue.tx_ring.basePA;
> +	tqc->dataRingBasePA = adapter->tx_queue.data_ring.basePA;
> +	tqc->compRingBasePA = adapter->tx_queue.comp_ring.basePA;
> +	tqc->ddPA           = virt_to_phys(adapter->tx_queue.buf_info);
> +	tqc->txRingSize     = adapter->tx_queue.tx_ring.size;
> +	tqc->dataRingSize   = adapter->tx_queue.data_ring.size;
> +	tqc->compRingSize   = adapter->tx_queue.comp_ring.size;
> +	tqc->ddLen          = sizeof(struct vmxnet3_tx_buf_info) *
> +			      tqc->txRingSize;
> +	tqc->intrIdx        = adapter->tx_queue.comp_ring.intr_idx;
> +
> +	/* rx queue settings */
> +	devRead->misc.numRxQueues = 1;
> +	rqc = &adapter->rqd_start->conf;
> +	rqc->rxRingBasePA[0] = adapter->rx_queue.rx_ring[0].basePA;
> +	rqc->rxRingBasePA[1] = adapter->rx_queue.rx_ring[1].basePA;
> +	rqc->compRingBasePA  = adapter->rx_queue.comp_ring.basePA;
> +	rqc->ddPA            = virt_to_phys(adapter->rx_queue.buf_info);
> +	rqc->rxRingSize[0]   = adapter->rx_queue.rx_ring[0].size;
> +	rqc->rxRingSize[1]   = adapter->rx_queue.rx_ring[1].size;
> +	rqc->compRingSize    = adapter->rx_queue.comp_ring.size;
> +	rqc->ddLen           = sizeof(struct vmxnet3_rx_buf_info) *
> +			       (rqc->rxRingSize[0] + rqc->rxRingSize[1]);
> +	rqc->intrIdx         = adapter->rx_queue.comp_ring.intr_idx;
> +
> +	/* intr settings */
> +	devRead->intrConf.autoMask = adapter->intr.mask_mode ==
> +				     VMXNET3_IMM_AUTO;
> +	devRead->intrConf.numIntrs = adapter->intr.num_intrs;
> +	for (i = 0; i < adapter->intr.num_intrs; i++)
> +		devRead->intrConf.modLevels[i] = adapter->intr.mod_levels[i];
> +
> +	devRead->intrConf.eventIntrIdx = adapter->intr.event_intr_idx;
> +
> +	/* rx filter settings */
> +	devRead->rxFilterConf.rxMode = 0;
> +	vmxnet3_restore_vlan(adapter);
> +	/* the rest are already zeroed */
> +}
> +
> +
> +int
> +vmxnet3_activate_dev(struct vmxnet3_adapter *adapter)
> +{
> +	int err;
> +	u32 ret;
> +
> +	dprintk(KERN_ERR "%s: skb_buf_size %d, rx_buf_per_pkt %d, ring sizes"
> +		" %u %u %u\n", adapter->netdev->name, adapter->skb_buf_size,
> +		adapter->rx_buf_per_pkt, adapter->tx_queue.tx_ring.size,
> +		adapter->rx_queue.rx_ring[0].size,
> +		adapter->rx_queue.rx_ring[1].size);
> +
> +	vmxnet3_tq_init(&adapter->tx_queue, adapter);
> +	err = vmxnet3_rq_init(&adapter->rx_queue, adapter);
> +	if (err) {
> +		printk(KERN_ERR "Failed to init rx queue for %s: error %d\n",
> +		       adapter->netdev->name, err);
> +		goto rq_err;
> +	}
> +
> +	err = vmxnet3_request_irqs(adapter);
> +	if (err) {
> +		printk(KERN_ERR "Failed to setup irq for %s: error %d\n",
> +		       adapter->netdev->name, err);
> +		goto irq_err;
> +	}
> +
> +	vmxnet3_setup_driver_shared(adapter);
> +
> +	VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_DSAL,
> +			       VMXNET3_GET_ADDR_LO(adapter->shared_pa));
> +	VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_DSAH,
> +			       VMXNET3_GET_ADDR_HI(adapter->shared_pa));
> +
> +	VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD,
> +			       VMXNET3_CMD_ACTIVATE_DEV);
> +	ret = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_CMD);
> +
> +	if (ret != 0) {
> +		printk(KERN_ERR "Failed to activate dev %s: error %u\n",
> +		       adapter->netdev->name, ret);
> +		err = -EINVAL;
> +		goto activate_err;
> +	}
> +	VMXNET3_WRITE_BAR0_REG(adapter, VMXNET3_REG_RXPROD,
> +			       adapter->rx_queue.rx_ring[0].next2fill);
> +	VMXNET3_WRITE_BAR0_REG(adapter, VMXNET3_REG_RXPROD2,
> +			       adapter->rx_queue.rx_ring[1].next2fill);
> +
> +	/* Apply the rx filter settins last. */
> +	vmxnet3_set_mc(adapter->netdev);
> +
> +	/*
> +	 * Check link state when first activating device. It will start the
> +	 * tx queue if the link is up.
> +	 */
> +	vmxnet3_check_link(adapter);
> +
> +	napi_enable(&adapter->napi);
> +	vmxnet3_enable_all_intrs(adapter);
> +	clear_bit(VMXNET3_STATE_BIT_QUIESCED, &adapter->state);
> +	return 0;
> +
> +activate_err:
> +	VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_DSAL, 0);
> +	VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_DSAH, 0);
> +	vmxnet3_free_irqs(adapter);
> +irq_err:
> +rq_err:
> +	/* free up buffers we allocated */
> +	vmxnet3_rq_cleanup(&adapter->rx_queue, adapter);
> +	return err;
> +}
> +
> +
> +void
> +vmxnet3_reset_dev(struct vmxnet3_adapter *adapter)
> +{
> +	VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, VMXNET3_CMD_RESET_DEV);
> +}
> +
> +
> +int
> +vmxnet3_quiesce_dev(struct vmxnet3_adapter *adapter)
> +{
> +	if (test_and_set_bit(VMXNET3_STATE_BIT_QUIESCED, &adapter->state))
> +		return 0;
> +
> +
> +	VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD,
> +			       VMXNET3_CMD_QUIESCE_DEV);
> +	vmxnet3_disable_all_intrs(adapter);
> +
> +	napi_disable(&adapter->napi);
> +	netif_tx_disable(adapter->netdev);
> +	adapter->link_speed = 0;
> +	netif_carrier_off(adapter->netdev);
> +
> +	vmxnet3_tq_cleanup(&adapter->tx_queue, adapter);
> +	vmxnet3_rq_cleanup(&adapter->rx_queue, adapter);
> +	vmxnet3_free_irqs(adapter);
> +	return 0;
> +}
> +
> +
> +static void
> +vmxnet3_write_mac_addr(struct vmxnet3_adapter *adapter, u8 *mac)
> +{
> +	u32 tmp;
> +
> +	tmp = *(u32 *)mac;
> +	VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_MACL, tmp);
> +
> +	tmp = (mac[5] << 8) | mac[4];
> +	VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_MACH, tmp);
> +}
> +
> +
> +static int
> +vmxnet3_set_mac_addr(struct net_device *netdev, void *p)
> +{
> +	struct sockaddr *addr = p;
> +	struct vmxnet3_adapter *adapter = netdev_priv(netdev);
> +
> +	memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len);
> +	vmxnet3_write_mac_addr(adapter, addr->sa_data);
> +
> +	return 0;
> +}
> +
> +
> +/* ==================== initialization and cleanup routines ============ */
> +
> +static int
> +vmxnet3_alloc_pci_resources(struct vmxnet3_adapter *adapter, bool *dma64)
> +{
> +	int err;
> +	unsigned long mmio_start, mmio_len;
> +	struct pci_dev *pdev = adapter->pdev;
> +
> +	err = pci_enable_device(pdev);
> +	if (err) {
> +		printk(KERN_ERR "Failed to enable adapter %s: error %d\n",
> +		       pci_name(pdev), err);
> +		return err;
> +	}
> +
> +	if (pci_set_dma_mask(pdev, DMA_BIT_MASK(64)) == 0) {
> +		if (pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)) != 0) {
> +			printk(KERN_ERR "pci_set_consistent_dma_mask failed "
> +			       "for adapter %s\n", pci_name(pdev));
> +			err = -EIO;
> +			goto err_set_mask;
> +		}
> +		*dma64 = true;
> +	} else {
> +		if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) != 0) {
> +			printk(KERN_ERR "pci_set_dma_mask failed for adapter "
> +			       "%s\n",	pci_name(pdev));
> +			err = -EIO;
> +			goto err_set_mask;
> +		}
> +		*dma64 = false;
> +	}
> +
> +	err = pci_request_selected_regions(pdev, (1 << 2) - 1,
> +					   vmxnet3_driver_name);
> +	if (err) {
> +		printk(KERN_ERR "Failed to request region for adapter %s: "
> +		       "error %d\n", pci_name(pdev), err);
> +		goto err_set_mask;
> +	}
> +
> +	pci_set_master(pdev);
> +
> +	mmio_start = pci_resource_start(pdev, 0);
> +	mmio_len = pci_resource_len(pdev, 0);
> +	adapter->hw_addr0 = ioremap(mmio_start, mmio_len);
> +	if (!adapter->hw_addr0) {
> +		printk(KERN_ERR "Failed to map bar0 for adapter %s\n",
> +		       pci_name(pdev));
> +		err = -EIO;
> +		goto err_ioremap;
> +	}
> +
> +	mmio_start = pci_resource_start(pdev, 1);
> +	mmio_len = pci_resource_len(pdev, 1);
> +	adapter->hw_addr1 = ioremap(mmio_start, mmio_len);
> +	if (!adapter->hw_addr1) {
> +		printk(KERN_ERR "Failed to map bar1 for adapter %s\n",
> +		       pci_name(pdev));
> +		err = -EIO;
> +		goto err_bar1;
> +	}
> +	return 0;
> +
> +err_bar1:
> +	iounmap(adapter->hw_addr0);
> +err_ioremap:
> +	pci_release_selected_regions(pdev, (1 << 2) - 1);
> +err_set_mask:
> +	pci_disable_device(pdev);
> +	return err;
> +}
> +
> +
> +static void
> +vmxnet3_free_pci_resources(struct vmxnet3_adapter *adapter)
> +{
> +	BUG_ON(!adapter->pdev);
> +
> +	iounmap(adapter->hw_addr0);
> +	iounmap(adapter->hw_addr1);
> +	pci_release_selected_regions(adapter->pdev, (1 << 2) - 1);
> +	pci_disable_device(adapter->pdev);
> +}
> +
> +
> +static void
> +vmxnet3_adjust_rx_ring_size(struct vmxnet3_adapter *adapter)
> +{
> +	size_t sz;
> +
> +	if (adapter->netdev->mtu <= VMXNET3_MAX_SKB_BUF_SIZE -
> +				    VMXNET3_MAX_ETH_HDR_SIZE) {
> +		adapter->skb_buf_size = adapter->netdev->mtu +
> +					VMXNET3_MAX_ETH_HDR_SIZE;
> +		if (adapter->skb_buf_size < VMXNET3_MIN_T0_BUF_SIZE)
> +			adapter->skb_buf_size = VMXNET3_MIN_T0_BUF_SIZE;
> +
> +		adapter->rx_buf_per_pkt = 1;
> +	} else {
> +		adapter->skb_buf_size = VMXNET3_MAX_SKB_BUF_SIZE;
> +		sz = adapter->netdev->mtu - VMXNET3_MAX_SKB_BUF_SIZE +
> +					    VMXNET3_MAX_ETH_HDR_SIZE;
> +		adapter->rx_buf_per_pkt = 1 + (sz + PAGE_SIZE - 1) / PAGE_SIZE;
> +	}
> +
> +	/*
> +	 * for simplicity, force the ring0 size to be a multiple of
> +	 * rx_buf_per_pkt * VMXNET3_RING_SIZE_ALIGN
> +	 */
> +	sz = adapter->rx_buf_per_pkt * VMXNET3_RING_SIZE_ALIGN;
> +	adapter->rx_queue.rx_ring[0].size = (adapter->rx_queue.rx_ring[0].size +
> +					     sz - 1) / sz * sz;
> +	adapter->rx_queue.rx_ring[0].size = min_t(u32,
> +					    adapter->rx_queue.rx_ring[0].size,
> +					    VMXNET3_RX_RING_MAX_SIZE / sz * sz);
> +}
> +
> +
> +int
> +vmxnet3_create_queues(struct vmxnet3_adapter *adapter, u32 tx_ring_size,
> +		      u32 rx_ring_size, u32 rx_ring2_size)
> +{
> +	int err;
> +
> +	adapter->tx_queue.tx_ring.size   = tx_ring_size;
> +	adapter->tx_queue.data_ring.size = tx_ring_size;
> +	adapter->tx_queue.comp_ring.size = tx_ring_size;
> +	adapter->tx_queue.shared = &adapter->tqd_start->ctrl;
> +	adapter->tx_queue.stopped = true;
> +	err = vmxnet3_tq_create(&adapter->tx_queue, adapter);
> +	if (err)
> +		return err;
> +
> +	adapter->rx_queue.rx_ring[0].size = rx_ring_size;
> +	adapter->rx_queue.rx_ring[1].size = rx_ring2_size;
> +	vmxnet3_adjust_rx_ring_size(adapter);
> +	adapter->rx_queue.comp_ring.size  = adapter->rx_queue.rx_ring[0].size +
> +					    adapter->rx_queue.rx_ring[1].size;
> +	adapter->rx_queue.qid  = 0;
> +	adapter->rx_queue.qid2 = 1;
> +	adapter->rx_queue.shared = &adapter->rqd_start->ctrl;
> +	err = vmxnet3_rq_create(&adapter->rx_queue, adapter);
> +	if (err)
> +		vmxnet3_tq_destroy(&adapter->tx_queue, adapter);
> +
> +	return err;
> +}
> +
> +static int
> +vmxnet3_open(struct net_device *netdev)
> +{
> +	struct vmxnet3_adapter *adapter;
> +	int err;
> +
> +	adapter = netdev_priv(netdev);
> +
> +	spin_lock_init(&adapter->tx_queue.tx_lock);
> +
> +	err = vmxnet3_create_queues(adapter, VMXNET3_DEF_TX_RING_SIZE,
> +				    VMXNET3_DEF_RX_RING_SIZE,
> +				    VMXNET3_DEF_RX_RING_SIZE);
> +	if (err)
> +		goto queue_err;
> +
> +	err = vmxnet3_activate_dev(adapter);
> +	if (err)
> +		goto activate_err;
> +
> +	return 0;
> +
> +activate_err:
> +	vmxnet3_rq_destroy(&adapter->rx_queue, adapter);
> +	vmxnet3_tq_destroy(&adapter->tx_queue, adapter);
> +queue_err:
> +	return err;
> +}
> +
> +
> +static int
> +vmxnet3_close(struct net_device *netdev)
> +{
> +	struct vmxnet3_adapter *adapter = netdev_priv(netdev);
> +
> +	/*
> +	 * Reset_work may be in the middle of resetting the device, wait for its
> +	 * completion.
> +	 */
> +	while (test_and_set_bit(VMXNET3_STATE_BIT_RESETTING, &adapter->state))
> +		msleep(1);
> +
> +	vmxnet3_quiesce_dev(adapter);
> +
> +	vmxnet3_rq_destroy(&adapter->rx_queue, adapter);
> +	vmxnet3_tq_destroy(&adapter->tx_queue, adapter);
> +
> +	clear_bit(VMXNET3_STATE_BIT_RESETTING, &adapter->state);
> +
> +
> +	return 0;
> +}
> +
> +
> +void
> +vmxnet3_force_close(struct vmxnet3_adapter *adapter)
> +{
> +	/*
> +	 * we must clear VMXNET3_STATE_BIT_RESETTING, otherwise
> +	 * vmxnet3_close() will deadlock.
> +	 */
> +	BUG_ON(test_bit(VMXNET3_STATE_BIT_RESETTING, &adapter->state));
> +
> +	/* we need to enable NAPI, otherwise dev_close will deadlock */
> +	napi_enable(&adapter->napi);
> +	dev_close(adapter->netdev);
> +}
> +
> +
> +static int
> +vmxnet3_change_mtu(struct net_device *netdev, int new_mtu)
> +{
> +	struct vmxnet3_adapter *adapter = netdev_priv(netdev);
> +	int err = 0;
> +
> +	if (new_mtu < VMXNET3_MIN_MTU || new_mtu > VMXNET3_MAX_MTU)
> +		return -EINVAL;
> +
> +	if (new_mtu > 1500 && !adapter->jumbo_frame)
> +		return -EINVAL;
> +
> +	netdev->mtu = new_mtu;
> +
> +	/*
> +	 * Reset_work may be in the middle of resetting the device, wait for its
> +	 * completion.
> +	 */
> +	while (test_and_set_bit(VMXNET3_STATE_BIT_RESETTING, &adapter->state))
> +		msleep(1);
> +
> +	if (netif_running(netdev)) {
> +		vmxnet3_quiesce_dev(adapter);
> +		vmxnet3_reset_dev(adapter);
> +
> +		/* we need to re-create the rx queue based on the new mtu */
> +		vmxnet3_rq_destroy(&adapter->rx_queue, adapter);
> +		vmxnet3_adjust_rx_ring_size(adapter);
> +		adapter->rx_queue.comp_ring.size  =
> +					adapter->rx_queue.rx_ring[0].size +
> +					adapter->rx_queue.rx_ring[1].size;
> +		err = vmxnet3_rq_create(&adapter->rx_queue, adapter);
> +		if (err) {
> +			printk(KERN_ERR "%s: failed to re-create rx queue,"
> +				" error %d. Closing it.\n", netdev->name, err);
> +			goto out;
> +		}
> +
> +		err = vmxnet3_activate_dev(adapter);
> +		if (err) {
> +			printk(KERN_ERR "%s: failed to re-activate, error %d. "
> +				"Closing it\n", netdev->name, err);
> +			goto out;
> +		}
> +	}
> +
> +out:
> +	clear_bit(VMXNET3_STATE_BIT_RESETTING, &adapter->state);
> +	if (err)
> +		vmxnet3_force_close(adapter);
> +
> +	return err;
> +}
> +
> +
> +static void
> +vmxnet3_declare_features(struct vmxnet3_adapter *adapter, bool dma64)
> +{
> +	struct net_device *netdev = adapter->netdev;
> +
> +	netdev->features = NETIF_F_SG |
> +		NETIF_F_HW_CSUM |
> +		NETIF_F_HW_VLAN_TX |
> +		NETIF_F_HW_VLAN_RX |
> +		NETIF_F_HW_VLAN_FILTER |
> +		NETIF_F_TSO |
> +		NETIF_F_TSO6 |
> +		NETIF_F_LRO;
> +
> +	printk(KERN_INFO "features: sg csum vlan jf tso tsoIPv6 lro");
> +
> +	adapter->rxcsum = true;
> +	adapter->jumbo_frame = true;
> +	adapter->lro = true;
> +
> +	if (dma64) {
> +		netdev->features |= NETIF_F_HIGHDMA;
> +		printk(" highDMA");
> +	}
> +
> +	netdev->vlan_features = netdev->features;
> +	printk("\n");
> +}
> +
> +
> +static void
> +vmxnet3_read_mac_addr(struct vmxnet3_adapter *adapter, u8 *mac)
> +{
> +	u32 tmp;
> +
> +	tmp = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_MACL);
> +	*(u32 *)mac = tmp;
> +
> +	tmp = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_MACH);
> +	mac[4] = tmp & 0xff;
> +	mac[5] = (tmp >> 8) & 0xff;
> +}
> +
> +
> +static void
> +vmxnet3_alloc_intr_resources(struct vmxnet3_adapter *adapter)
> +{
> +	u32 cfg;
> +
> +	/* intr settings */
> +	VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD,
> +			       VMXNET3_CMD_GET_CONF_INTR);
> +	cfg = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_CMD);
> +	adapter->intr.type = cfg & 0x3;
> +	adapter->intr.mask_mode = (cfg >> 2) & 0x3;
> +
> +	if (adapter->intr.type == VMXNET3_IT_AUTO) {
> +		int err;
> +
> +		adapter->intr.msix_entries[0].entry = 0;
> +		err = pci_enable_msix(adapter->pdev, adapter->intr.msix_entries,
> +				      VMXNET3_LINUX_MAX_MSIX_VECT);
> +		if (!err) {
> +			adapter->intr.num_intrs = 1;
> +			adapter->intr.type = VMXNET3_IT_MSIX;
> +			return;
> +		}
> +
> +		err = pci_enable_msi(adapter->pdev);
> +		if (!err) {
> +			adapter->intr.num_intrs = 1;
> +			adapter->intr.type = VMXNET3_IT_MSI;
> +			return;
> +		}
> +	}
> +
> +	adapter->intr.type = VMXNET3_IT_INTX;
> +
> +	/* INT-X related setting */
> +	adapter->intr.num_intrs = 1;
> +}
> +
> +
> +static void
> +vmxnet3_free_intr_resources(struct vmxnet3_adapter *adapter)
> +{
> +	if (adapter->intr.type == VMXNET3_IT_MSIX)
> +		pci_disable_msix(adapter->pdev);
> +	else if (adapter->intr.type == VMXNET3_IT_MSI)
> +		pci_disable_msi(adapter->pdev);
> +	else
> +		BUG_ON(adapter->intr.type != VMXNET3_IT_INTX);
> +}
> +
> +
> +static void
> +vmxnet3_tx_timeout(struct net_device *netdev)
> +{
> +	struct vmxnet3_adapter *adapter = netdev_priv(netdev);
> +	adapter->tx_timeout_count++;
> +
> +	printk(KERN_ERR "%s: tx hang\n", adapter->netdev->name);
> +	schedule_work(&adapter->work);
> +}
> +
> +
> +static void
> +vmxnet3_reset_work(struct work_struct *data)
> +{
> +	struct vmxnet3_adapter *adapter;
> +
> +	adapter = container_of(data, struct vmxnet3_adapter, work);
> +
> +	/* if another thread is resetting the device, no need to proceed */
> +	if (test_and_set_bit(VMXNET3_STATE_BIT_RESETTING, &adapter->state))
> +		return;
> +
> +	/* if the device is closed, we must leave it alone */
> +	if (netif_running(adapter->netdev)) {
> +		printk(KERN_INFO "%s: resetting\n", adapter->netdev->name);
> +		vmxnet3_quiesce_dev(adapter);
> +		vmxnet3_reset_dev(adapter);
> +		vmxnet3_activate_dev(adapter);
> +	} else {
> +		printk(KERN_INFO "%s: already closed\n", adapter->netdev->name);
> +	}
> +
> +	clear_bit(VMXNET3_STATE_BIT_RESETTING, &adapter->state);
> +}
> +
> +
> +static int __devinit
> +vmxnet3_probe_device(struct pci_dev *pdev,
> +		     const struct pci_device_id *id)
> +{
> +	static const struct net_device_ops vmxnet3_netdev_ops = {
> +		.ndo_open = vmxnet3_open,
> +		.ndo_stop = vmxnet3_close,
> +		.ndo_start_xmit = vmxnet3_xmit_frame,
> +		.ndo_set_mac_address = vmxnet3_set_mac_addr,
> +		.ndo_change_mtu = vmxnet3_change_mtu,
> +		.ndo_get_stats = vmxnet3_get_stats,
> +		.ndo_tx_timeout = vmxnet3_tx_timeout,
> +		.ndo_set_multicast_list = vmxnet3_set_mc,
> +		.ndo_vlan_rx_register = vmxnet3_vlan_rx_register,
> +		.ndo_vlan_rx_add_vid = vmxnet3_vlan_rx_add_vid,
> +		.ndo_vlan_rx_kill_vid = vmxnet3_vlan_rx_kill_vid,
> +#ifdef CONFIG_NET_POLL_CONTROLLER
> +		.ndo_poll_controller = vmxnet3_netpoll,
> +#endif
> +	};
> +	int err;
> +	bool dma64 = false; /* stupid gcc */
> +	u32 ver;
> +	struct net_device *netdev;
> +	struct vmxnet3_adapter *adapter;
> +	u8 mac[ETH_ALEN];
> +
> +	netdev = alloc_etherdev(sizeof(struct vmxnet3_adapter));
> +	if (!netdev) {
> +		printk(KERN_ERR "Failed to alloc ethernet device for adapter "
> +			"%s\n",	pci_name(pdev));
> +		return -ENOMEM;
> +	}
> +
> +	pci_set_drvdata(pdev, netdev);
> +	adapter = netdev_priv(netdev);
> +	adapter->netdev = netdev;
> +	adapter->pdev = pdev;
> +
> +	adapter->shared = pci_alloc_consistent(adapter->pdev,
> +			  sizeof(struct Vmxnet3_DriverShared),
> +			  &adapter->shared_pa);
> +	if (!adapter->shared) {
> +		printk(KERN_ERR "Failed to allocate memory for %s\n",
> +			pci_name(pdev));
> +		err = -ENOMEM;
> +		goto err_alloc_shared;
> +	}
> +
> +	adapter->tqd_start = pci_alloc_consistent(adapter->pdev,
> +			     sizeof(struct Vmxnet3_TxQueueDesc) +
> +			     sizeof(struct Vmxnet3_RxQueueDesc),
> +			     &adapter->queue_desc_pa);
> +
> +	if (!adapter->tqd_start) {
> +		printk(KERN_ERR "Failed to allocate memory for %s\n",
> +			pci_name(pdev));
> +		err = -ENOMEM;
> +		goto err_alloc_queue_desc;
> +	}
> +	adapter->rqd_start = (struct Vmxnet3_RxQueueDesc *)(adapter->tqd_start
> +							    + 1);
> +
> +	adapter->pm_conf = kmalloc(sizeof(struct Vmxnet3_PMConf), GFP_KERNEL);
> +	if (adapter->pm_conf == NULL) {
> +		printk(KERN_ERR "Failed to allocate memory for %s\n",
> +			pci_name(pdev));
> +		err = -ENOMEM;
> +		goto err_alloc_pm;
> +	}
> +
> +	err = vmxnet3_alloc_pci_resources(adapter, &dma64);
> +	if (err < 0)
> +		goto err_alloc_pci;
> +
> +	ver = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_VRRS);
> +	if (ver & 1) {
> +		VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_VRRS, 1);
> +	} else {
> +		printk(KERN_ERR "Incompatible h/w version (0x%x) for adapter"
> +		       " %s\n",	ver, pci_name(pdev));
> +		err = -EBUSY;
> +		goto err_ver;
> +	}
> +
> +	ver = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_UVRS);
> +	if (ver & 1) {
> +		VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_UVRS, 1);
> +	} else {
> +		printk(KERN_ERR "Incompatible upt version (0x%x) for "
> +		       "adapter %s\n", ver, pci_name(pdev));
> +		err = -EBUSY;
> +		goto err_ver;
> +	}
> +
> +	vmxnet3_declare_features(adapter, dma64);
> +
> +	adapter->dev_number = atomic_read(&devices_found);
> +	vmxnet3_alloc_intr_resources(adapter);
> +
> +	vmxnet3_read_mac_addr(adapter, mac);
> +	memcpy(netdev->dev_addr,  mac, netdev->addr_len);
> +
> +	netdev->netdev_ops = &vmxnet3_netdev_ops;
> +	netdev->watchdog_timeo = 5 * HZ;
> +	vmxnet3_set_ethtool_ops(netdev);
> +
> +	INIT_WORK(&adapter->work, vmxnet3_reset_work);
> +
> +	netif_napi_add(netdev, &adapter->napi, vmxnet3_poll, 64);
> +	SET_NETDEV_DEV(netdev, &pdev->dev);
> +	err = register_netdev(netdev);
> +
> +	if (err) {
> +		printk(KERN_ERR "Failed to register adapter %s\n",
> +			pci_name(pdev));
> +		goto err_register;
> +	}
> +
> +	set_bit(VMXNET3_STATE_BIT_QUIESCED, &adapter->state);
> +	atomic_inc(&devices_found);
> +	return 0;
> +
> +err_register:
> +	vmxnet3_free_intr_resources(adapter);
> +err_ver:
> +	vmxnet3_free_pci_resources(adapter);
> +err_alloc_pci:
> +	kfree(adapter->pm_conf);
> +err_alloc_pm:
> +	pci_free_consistent(adapter->pdev, sizeof(struct Vmxnet3_TxQueueDesc) +
> +			    sizeof(struct Vmxnet3_RxQueueDesc),
> +			    adapter->tqd_start, adapter->queue_desc_pa);
> +err_alloc_queue_desc:
> +	pci_free_consistent(adapter->pdev, sizeof(struct Vmxnet3_DriverShared),
> +			    adapter->shared, adapter->shared_pa);
> +err_alloc_shared:
> +	pci_set_drvdata(pdev, NULL);
> +	free_netdev(netdev);
> +	return err;
> +}
> +
> +
> +static void __devexit
> +vmxnet3_remove_device(struct pci_dev *pdev)
> +{
> +	struct net_device *netdev = pci_get_drvdata(pdev);
> +	struct vmxnet3_adapter *adapter = netdev_priv(netdev);
> +
> +	flush_scheduled_work();
> +
> +	unregister_netdev(netdev);
> +
> +	vmxnet3_free_intr_resources(adapter);
> +	vmxnet3_free_pci_resources(adapter);
> +	kfree(adapter->pm_conf);
> +	pci_free_consistent(adapter->pdev, sizeof(struct Vmxnet3_TxQueueDesc) +
> +			    sizeof(struct Vmxnet3_RxQueueDesc),
> +			    adapter->tqd_start, adapter->queue_desc_pa);
> +	pci_free_consistent(adapter->pdev, sizeof(struct Vmxnet3_DriverShared),
> +			    adapter->shared, adapter->shared_pa);
> +	free_netdev(netdev);
> +}
> +
> +
> +#ifdef CONFIG_PM
> +
> +static int
> +vmxnet3_suspend(struct device *device)
> +{
> +	struct pci_dev *pdev = to_pci_dev(device);
> +	struct net_device *netdev = pci_get_drvdata(pdev);
> +	struct vmxnet3_adapter *adapter = netdev_priv(netdev);
> +	struct Vmxnet3_PMConf *pmConf;
> +	struct ethhdr *ehdr;
> +	struct arphdr *ahdr;
> +	u8 *arpreq;
> +	struct in_device *in_dev;
> +	struct in_ifaddr *ifa;
> +	int i = 0;
> +
> +	if (!netif_running(netdev))
> +		return 0;
> +
> +	vmxnet3_disable_all_intrs(adapter);
> +
> +	netif_device_detach(netdev);
> +	netif_stop_queue(netdev);
> +
> +	/* Create wake-up filters. */
> +	pmConf = adapter->pm_conf;
> +	memset(pmConf, 0, sizeof(*pmConf));
> +
> +	if (adapter->wol & WAKE_UCAST) {
> +		pmConf->filters[i].patternSize = ETH_ALEN;
> +		pmConf->filters[i].maskSize = 1;
> +		memcpy(pmConf->filters[i].pattern, netdev->dev_addr, ETH_ALEN);
> +		pmConf->filters[i].mask[0] = 0x3F; /* LSB ETH_ALEN bits */
> +
> +		pmConf->wakeUpEvents |= VMXNET3_PM_WAKEUP_FILTER;
> +		i++;
> +	}
> +
> +	if (adapter->wol & WAKE_ARP) {
> +		in_dev = in_dev_get(netdev);
> +		if (!in_dev)
> +			goto skip_arp;
> +
> +		ifa = (struct in_ifaddr *)in_dev->ifa_list;
> +		if (!ifa)
> +			goto skip_arp;
> +
> +		pmConf->filters[i].patternSize = ETH_HLEN + /* Ethernet header*/
> +			sizeof(struct arphdr) +		/* ARP header */
> +			2 * ETH_ALEN +		/* 2 Ethernet addresses*/
> +			2 * sizeof(u32);	/*2 IPv4 addresses */
> +		pmConf->filters[i].maskSize =
> +			(pmConf->filters[i].patternSize - 1) / 8 + 1;
> +
> +		/* ETH_P_ARP in Ethernet header. */
> +		ehdr = (struct ethhdr *)pmConf->filters[i].pattern;
> +		ehdr->h_proto = htons(ETH_P_ARP);
> +
> +		/* ARPOP_REQUEST in ARP header. */
> +		ahdr = (struct arphdr *)&pmConf->filters[i].pattern[ETH_HLEN];
> +		ahdr->ar_op = htons(ARPOP_REQUEST);
> +		arpreq = (u8 *)(ahdr + 1);
> +
> +		/* The Unicast IPv4 address in 'tip' field. */
> +		arpreq += 2 * ETH_ALEN + sizeof(u32);
> +		*(u32 *)arpreq = ifa->ifa_address;
> +
> +		/* The mask for the relevant bits. */
> +		pmConf->filters[i].mask[0] = 0x00;
> +		pmConf->filters[i].mask[1] = 0x30; /* ETH_P_ARP */
> +		pmConf->filters[i].mask[2] = 0x30; /* ARPOP_REQUEST */
> +		pmConf->filters[i].mask[3] = 0x00;
> +		pmConf->filters[i].mask[4] = 0xC0; /* IPv4 TIP */
> +		pmConf->filters[i].mask[5] = 0x03; /* IPv4 TIP */
> +		in_dev_put(in_dev);
> +
> +		pmConf->wakeUpEvents |= VMXNET3_PM_WAKEUP_FILTER;
> +		i++;
> +	}
> +
> +skip_arp:
> +	if (adapter->wol & WAKE_MAGIC)
> +		pmConf->wakeUpEvents |= VMXNET3_PM_WAKEUP_MAGIC;
> +
> +	pmConf->numFilters = i;
> +
> +	adapter->shared->devRead.pmConfDesc.confVer = 1;
> +	adapter->shared->devRead.pmConfDesc.confLen = sizeof(*pmConf);
> +	adapter->shared->devRead.pmConfDesc.confPA = virt_to_phys(pmConf);
> +
> +	VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD,
> +			       VMXNET3_CMD_UPDATE_PMCFG);
> +
> +	pci_save_state(pdev);
> +	pci_enable_wake(pdev, pci_choose_state(pdev, PMSG_SUSPEND),
> +			adapter->wol);
> +	pci_disable_device(pdev);
> +	pci_set_power_state(pdev, pci_choose_state(pdev, PMSG_SUSPEND));
> +
> +	return 0;
> +}
> +
> +
> +static int
> +vmxnet3_resume(struct device *device)
> +{
> +	int err;
> +	struct pci_dev *pdev = to_pci_dev(device);
> +	struct net_device *netdev = pci_get_drvdata(pdev);
> +	struct vmxnet3_adapter *adapter = netdev_priv(netdev);
> +	struct Vmxnet3_PMConf *pmConf;
> +
> +	if (!netif_running(netdev))
> +		return 0;
> +
> +	/* Destroy wake-up filters. */
> +	pmConf = adapter->pm_conf;
> +	memset(pmConf, 0, sizeof(*pmConf));
> +
> +	adapter->shared->devRead.pmConfDesc.confVer = 1;
> +	adapter->shared->devRead.pmConfDesc.confLen = sizeof(*pmConf);
> +	adapter->shared->devRead.pmConfDesc.confPA = virt_to_phys(pmConf);
> +
> +	netif_device_attach(netdev);
> +	pci_set_power_state(pdev, PCI_D0);
> +	pci_restore_state(pdev);
> +	err = pci_enable_device_mem(pdev);
> +	if (err != 0)
> +		return err;
> +
> +	pci_enable_wake(pdev, PCI_D0, 0);
> +
> +	VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD,
> +			       VMXNET3_CMD_UPDATE_PMCFG);
> +	vmxnet3_enable_all_intrs(adapter);
> +
> +	return 0;
> +}
> +
> +static struct dev_pm_ops vmxnet3_pm_ops = {
> +	.suspend = vmxnet3_suspend,
> +	.resume = vmxnet3_resume,
> +};
> +#endif
> +
> +static struct pci_driver vmxnet3_driver = {
> +	.name		= vmxnet3_driver_name,
> +	.id_table	= vmxnet3_pciid_table,
> +	.probe		= vmxnet3_probe_device,
> +	.remove		= __devexit_p(vmxnet3_remove_device),
> +#ifdef CONFIG_PM
> +	.driver.pm	= &vmxnet3_pm_ops,
> +#endif
> +};
> +
> +
> +static int __init
> +vmxnet3_init_module(void)
> +{
> +	printk(KERN_INFO "%s - version %s\n", VMXNET3_DRIVER_DESC,
> +		VMXNET3_DRIVER_VERSION_REPORT);
> +	return pci_register_driver(&vmxnet3_driver);
> +}
> +
> +module_init(vmxnet3_init_module);
> +
> +
> +static void
> +vmxnet3_exit_module(void)
> +{
> +	pci_unregister_driver(&vmxnet3_driver);
> +}
> +
> +module_exit(vmxnet3_exit_module);
> +
> +MODULE_AUTHOR("VMware, Inc.");
> +MODULE_DESCRIPTION(VMXNET3_DRIVER_DESC);
> +MODULE_LICENSE("GPL v2");
> +MODULE_VERSION(VMXNET3_DRIVER_VERSION_STRING);
> diff --git a/drivers/net/vmxnet3/vmxnet3_ethtool.c b/drivers/net/vmxnet3/vmxnet3_ethtool.c
> new file mode 100644
> index 0000000..c2c15e4
> --- /dev/null
> +++ b/drivers/net/vmxnet3/vmxnet3_ethtool.c
> @@ -0,0 +1,566 @@
> +/*
> + * Linux driver for VMware's vmxnet3 ethernet NIC.
> + *
> + * Copyright (C) 2008-2009, VMware, Inc. All Rights Reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License and no later version.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
> + * NON INFRINGEMENT.  See the GNU General Public License for more
> + * details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
> + *
> + * The full GNU General Public License is included in this distribution in
> + * the file called "COPYING".
> + *
> + * Maintained by: Shreyas Bhatewara <pv-drivers@vmware.com>
> + *
> + */
> +
> +
> +#include "vmxnet3_int.h"
> +
> +struct vmxnet3_stat_desc {
> +	char desc[ETH_GSTRING_LEN];
> +	int  offset;
> +};
> +
> +
> +static u32
> +vmxnet3_get_rx_csum(struct net_device *netdev)
> +{
> +	struct vmxnet3_adapter *adapter = netdev_priv(netdev);
> +	return adapter->rxcsum;
> +}
> +
> +
> +static int
> +vmxnet3_set_rx_csum(struct net_device *netdev, u32 val)
> +{
> +	struct vmxnet3_adapter *adapter = netdev_priv(netdev);
> +
> +	if (adapter->rxcsum != val) {
> +		adapter->rxcsum = val;
> +		if (netif_running(netdev)) {
> +			if (val)
> +				adapter->shared->devRead.misc.uptFeatures |=
> +								UPT1_F_RXCSUM;
> +			else
> +				adapter->shared->devRead.misc.uptFeatures &=
> +								~UPT1_F_RXCSUM;
> +
> +			VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD,
> +					       VMXNET3_CMD_UPDATE_FEATURE);
> +		}
> +	}
> +	return 0;
> +}
> +
> +
> +/* per tq stats maintained by the device */
> +static const struct vmxnet3_stat_desc
> +vmxnet3_tq_dev_stats[] = {
> +	/* description,         offset */
> +	{ "TSO pkts tx",        offsetof(struct UPT1_TxStats, TSOPktsTxOK) },
> +	{ "TSO bytes tx",       offsetof(struct UPT1_TxStats, TSOBytesTxOK) },
> +	{ "ucast pkts tx",      offsetof(struct UPT1_TxStats, ucastPktsTxOK) },
> +	{ "ucast bytes tx",     offsetof(struct UPT1_TxStats, ucastBytesTxOK) },
> +	{ "mcast pkts tx",      offsetof(struct UPT1_TxStats, mcastPktsTxOK) },
> +	{ "mcast bytes tx",     offsetof(struct UPT1_TxStats, mcastBytesTxOK) },
> +	{ "bcast pkts tx",      offsetof(struct UPT1_TxStats, bcastPktsTxOK) },
> +	{ "bcast bytes tx",     offsetof(struct UPT1_TxStats, bcastBytesTxOK) },
> +	{ "pkts tx err",        offsetof(struct UPT1_TxStats, pktsTxError) },
> +	{ "pkts tx discard",    offsetof(struct UPT1_TxStats, pktsTxDiscard) },
> +};
> +
> +/* per tq stats maintained by the driver */
> +static const struct vmxnet3_stat_desc
> +vmxnet3_tq_driver_stats[] = {
> +	/* description,         offset */
> +	{"drv dropped tx total", offsetof(struct vmxnet3_tq_driver_stats,
> +					drop_total) },
> +	{ "   too many frags",  offsetof(struct vmxnet3_tq_driver_stats,
> +					drop_too_many_frags) },
> +	{ "   giant hdr",       offsetof(struct vmxnet3_tq_driver_stats,
> +					drop_oversized_hdr) },
> +	{ "   hdr err",         offsetof(struct vmxnet3_tq_driver_stats,
> +					drop_hdr_inspect_err) },
> +	{ "   tso",             offsetof(struct vmxnet3_tq_driver_stats,
> +					drop_tso) },
> +	{ "ring full",          offsetof(struct vmxnet3_tq_driver_stats,
> +					tx_ring_full) },
> +	{ "pkts linearized",    offsetof(struct vmxnet3_tq_driver_stats,
> +					linearized) },
> +	{ "hdr cloned",         offsetof(struct vmxnet3_tq_driver_stats,
> +					copy_skb_header) },
> +	{ "giant hdr",          offsetof(struct vmxnet3_tq_driver_stats,
> +					oversized_hdr) },
> +};
> +
> +/* per rq stats maintained by the device */
> +static const struct vmxnet3_stat_desc
> +vmxnet3_rq_dev_stats[] = {
> +	{ "LRO pkts rx",        offsetof(struct UPT1_RxStats, LROPktsRxOK) },
> +	{ "LRO byte rx",        offsetof(struct UPT1_RxStats, LROBytesRxOK) },
> +	{ "ucast pkts rx",      offsetof(struct UPT1_RxStats, ucastPktsRxOK) },
> +	{ "ucast bytes rx",     offsetof(struct UPT1_RxStats, ucastBytesRxOK) },
> +	{ "mcast pkts rx",      offsetof(struct UPT1_RxStats, mcastPktsRxOK) },
> +	{ "mcast bytes rx",     offsetof(struct UPT1_RxStats, mcastBytesRxOK) },
> +	{ "bcast pkts rx",      offsetof(struct UPT1_RxStats, bcastPktsRxOK) },
> +	{ "bcast bytes rx",     offsetof(struct UPT1_RxStats, bcastBytesRxOK) },
> +	{ "pkts rx out of buf", offsetof(struct UPT1_RxStats, pktsRxOutOfBuf) },
> +	{ "pkts rx err",        offsetof(struct UPT1_RxStats, pktsRxError) },
> +};
> +
> +/* per rq stats maintained by the driver */
> +static const struct vmxnet3_stat_desc
> +vmxnet3_rq_driver_stats[] = {
> +	/* description,         offset */
> +	{ "drv dropped rx total", offsetof(struct vmxnet3_rq_driver_stats,
> +					   drop_total) },
> +	{ "   err",            offsetof(struct vmxnet3_rq_driver_stats,
> +					drop_err) },
> +	{ "   fcs",            offsetof(struct vmxnet3_rq_driver_stats,
> +					drop_fcs) },
> +	{ "rx buf alloc fail", offsetof(struct vmxnet3_rq_driver_stats,
> +					rx_buf_alloc_failure) },
> +};
> +
> +/* gloabl stats maintained by the driver */
> +static const struct vmxnet3_stat_desc
> +vmxnet3_global_stats[] = {
> +	/* description,         offset */
> +	{ "tx timeout count",   offsetof(struct vmxnet3_adapter,
> +					 tx_timeout_count) }
> +};
> +
> +
> +struct net_device_stats *
> +vmxnet3_get_stats(struct net_device *netdev)
> +{
> +	struct vmxnet3_adapter *adapter;
> +	struct vmxnet3_tq_driver_stats *drvTxStats;
> +	struct vmxnet3_rq_driver_stats *drvRxStats;
> +	struct UPT1_TxStats *devTxStats;
> +	struct UPT1_RxStats *devRxStats;
> +	struct net_device_stats *net_stats = &netdev->stats;
> +
> +	adapter = netdev_priv(netdev);
> +
> +	/* Collect the dev stats into the shared area */
> +	VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, VMXNET3_CMD_GET_STATS);
> +
> +	/* Assuming that we have a single queue device */
> +	devTxStats = &adapter->tqd_start->stats;
> +	devRxStats = &adapter->rqd_start->stats;
> +
> +	/* Get access to the driver stats per queue */
> +	drvTxStats = &adapter->tx_queue.stats;
> +	drvRxStats = &adapter->rx_queue.stats;
> +
> +	memset(net_stats, 0, sizeof(*net_stats));
> +
> +	net_stats->rx_packets = devRxStats->ucastPktsRxOK +
> +				devRxStats->mcastPktsRxOK +
> +				devRxStats->bcastPktsRxOK;
> +
> +	net_stats->tx_packets = devTxStats->ucastPktsTxOK +
> +				devTxStats->mcastPktsTxOK +
> +				devTxStats->bcastPktsTxOK;
> +
> +	net_stats->rx_bytes = devRxStats->ucastBytesRxOK +
> +			      devRxStats->mcastBytesRxOK +
> +			      devRxStats->bcastBytesRxOK;
> +
> +	net_stats->tx_bytes = devTxStats->ucastBytesTxOK +
> +			      devTxStats->mcastBytesTxOK +
> +			      devTxStats->bcastBytesTxOK;
> +
> +	net_stats->rx_errors = devRxStats->pktsRxError;
> +	net_stats->tx_errors = devTxStats->pktsTxError;
> +	net_stats->rx_dropped = drvRxStats->drop_total;
> +	net_stats->tx_dropped = drvTxStats->drop_total;
> +	net_stats->multicast =  devRxStats->mcastPktsRxOK;
> +
> +	return net_stats;
> +}
> +
> +static int
> +vmxnet3_get_sset_count(struct net_device *netdev, int sset)
> +{
> +	switch (sset) {
> +	case ETH_SS_STATS:
> +		return ARRAY_SIZE(vmxnet3_tq_dev_stats) +
> +			ARRAY_SIZE(vmxnet3_tq_driver_stats) +
> +			ARRAY_SIZE(vmxnet3_rq_dev_stats) +
> +			ARRAY_SIZE(vmxnet3_rq_driver_stats) +
> +			ARRAY_SIZE(vmxnet3_global_stats);
> +	default:
> +		return -EOPNOTSUPP;
> +	}
> +}
> +
> +
> +static int
> +vmxnet3_get_regs_len(struct net_device *netdev)
> +{
> +	return 20 * sizeof(u32);
> +}
> +
> +
> +static void
> +vmxnet3_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo)
> +{
> +	struct vmxnet3_adapter *adapter = netdev_priv(netdev);
> +
> +	strlcpy(drvinfo->driver, vmxnet3_driver_name, sizeof(drvinfo->driver));
> +	drvinfo->driver[sizeof(drvinfo->driver) - 1] = '\0';
> +
> +	strlcpy(drvinfo->version, VMXNET3_DRIVER_VERSION_REPORT,
> +		sizeof(drvinfo->version));
> +	drvinfo->driver[sizeof(drvinfo->version) - 1] = '\0';
> +
> +	strlcpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version));
> +	drvinfo->fw_version[sizeof(drvinfo->fw_version) - 1] = '\0';
> +
> +	strlcpy(drvinfo->bus_info, pci_name(adapter->pdev),
> +		ETHTOOL_BUSINFO_LEN);
> +	drvinfo->n_stats = vmxnet3_get_sset_count(netdev, ETH_SS_STATS);
> +	drvinfo->testinfo_len = 0;
> +	drvinfo->eedump_len   = 0;
> +	drvinfo->regdump_len  = vmxnet3_get_regs_len(netdev);
> +}
> +
> +
> +static void
> +vmxnet3_get_strings(struct net_device *netdev, u32 stringset, u8 *buf)
> +{
> +	if (stringset == ETH_SS_STATS) {
> +		int i;
> +
> +		for (i = 0; i < ARRAY_SIZE(vmxnet3_tq_dev_stats); i++) {
> +			memcpy(buf, vmxnet3_tq_dev_stats[i].desc,
> +			       ETH_GSTRING_LEN);
> +			buf += ETH_GSTRING_LEN;
> +		}
> +		for (i = 0; i < ARRAY_SIZE(vmxnet3_tq_driver_stats); i++) {
> +			memcpy(buf, vmxnet3_tq_driver_stats[i].desc,
> +			       ETH_GSTRING_LEN);
> +			buf += ETH_GSTRING_LEN;
> +		}
> +		for (i = 0; i < ARRAY_SIZE(vmxnet3_rq_dev_stats); i++) {
> +			memcpy(buf, vmxnet3_rq_dev_stats[i].desc,
> +			       ETH_GSTRING_LEN);
> +			buf += ETH_GSTRING_LEN;
> +		}
> +		for (i = 0; i < ARRAY_SIZE(vmxnet3_rq_driver_stats); i++) {
> +			memcpy(buf, vmxnet3_rq_driver_stats[i].desc,
> +			       ETH_GSTRING_LEN);
> +			buf += ETH_GSTRING_LEN;
> +		}
> +		for (i = 0; i < ARRAY_SIZE(vmxnet3_global_stats); i++) {
> +			memcpy(buf, vmxnet3_global_stats[i].desc,
> +				ETH_GSTRING_LEN);
> +			buf += ETH_GSTRING_LEN;
> +		}
> +	}
> +}
> +
> +static u32
> +vmxnet3_get_flags(struct net_device *netdev) {
> +	return netdev->features;
> +}
> +
> +static int
> +vmxnet3_set_flags(struct net_device *netdev, u32 data) {
> +	struct vmxnet3_adapter *adapter = netdev_priv(netdev);
> +	u8 lro_requested = (data & ETH_FLAG_LRO) == 0 ? 0 : 1;
> +	u8 lro_present = (netdev->features & NETIF_F_LRO) == 0 ? 0 : 1;
> +
> +	if (lro_requested ^ lro_present) {
> +		/* toggle the LRO feature*/
> +		netdev->features ^= NETIF_F_LRO;
> +
> +		/* update harware LRO capability accordingly */
> +		if (lro_requested)
> +			adapter->shared->devRead.misc.uptFeatures &= UPT1_F_LRO;
> +		else
> +			adapter->shared->devRead.misc.uptFeatures &=
> +								~UPT1_F_LRO;
> +		VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD,
> +				       VMXNET3_CMD_UPDATE_FEATURE);
> +	}
> +	return 0;
> +}
> +
> +static void
> +vmxnet3_get_ethtool_stats(struct net_device *netdev,
> +			  struct ethtool_stats *stats, u64  *buf)
> +{
> +	struct vmxnet3_adapter *adapter = netdev_priv(netdev);
> +	u8 *base;
> +	int i;
> +
> +	VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, VMXNET3_CMD_GET_STATS);
> +
> +	/* this does assume each counter is 64-bit wide */
> +
> +	base = (u8 *)&adapter->tqd_start->stats;
> +	for (i = 0; i < ARRAY_SIZE(vmxnet3_tq_dev_stats); i++)
> +		*buf++ = *(u64 *)(base + vmxnet3_tq_dev_stats[i].offset);
> +
> +	base = (u8 *)&adapter->tx_queue.stats;
> +	for (i = 0; i < ARRAY_SIZE(vmxnet3_tq_driver_stats); i++)
> +		*buf++ = *(u64 *)(base + vmxnet3_tq_driver_stats[i].offset);
> +
> +	base = (u8 *)&adapter->rqd_start->stats;
> +	for (i = 0; i < ARRAY_SIZE(vmxnet3_rq_dev_stats); i++)
> +		*buf++ = *(u64 *)(base + vmxnet3_rq_dev_stats[i].offset);
> +
> +	base = (u8 *)&adapter->rx_queue.stats;
> +	for (i = 0; i < ARRAY_SIZE(vmxnet3_rq_driver_stats); i++)
> +		*buf++ = *(u64 *)(base + vmxnet3_rq_driver_stats[i].offset);
> +
> +	base = (u8 *)adapter;
> +	for (i = 0; i < ARRAY_SIZE(vmxnet3_global_stats); i++)
> +		*buf++ = *(u64 *)(base + vmxnet3_global_stats[i].offset);
> +}
> +
> +
> +static void
> +vmxnet3_get_regs(struct net_device *netdev, struct ethtool_regs *regs, void *p)
> +{
> +	struct vmxnet3_adapter *adapter = netdev_priv(netdev);
> +	u32 *buf = p;
> +
> +	memset(p, 0, vmxnet3_get_regs_len(netdev));
> +
> +	regs->version = 1;
> +
> +	/* Update vmxnet3_get_regs_len if we want to dump more registers */
> +
> +	/* make each ring use multiple of 16 bytes */
> +	buf[0] = adapter->tx_queue.tx_ring.next2fill;
> +	buf[1] = adapter->tx_queue.tx_ring.next2comp;
> +	buf[2] = adapter->tx_queue.tx_ring.gen;
> +	buf[3] = 0;
> +
> +	buf[4] = adapter->tx_queue.comp_ring.next2proc;
> +	buf[5] = adapter->tx_queue.comp_ring.gen;
> +	buf[6] = adapter->tx_queue.stopped;
> +	buf[7] = 0;
> +
> +	buf[8] = adapter->rx_queue.rx_ring[0].next2fill;
> +	buf[9] = adapter->rx_queue.rx_ring[0].next2comp;
> +	buf[10] = adapter->rx_queue.rx_ring[0].gen;
> +	buf[11] = 0;
> +
> +	buf[12] = adapter->rx_queue.rx_ring[1].next2fill;
> +	buf[13] = adapter->rx_queue.rx_ring[1].next2comp;
> +	buf[14] = adapter->rx_queue.rx_ring[1].gen;
> +	buf[15] = 0;
> +
> +	buf[16] = adapter->rx_queue.comp_ring.next2proc;
> +	buf[17] = adapter->rx_queue.comp_ring.gen;
> +	buf[18] = 0;
> +	buf[19] = 0;
> +}
> +
> +
> +static void
> +vmxnet3_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
> +{
> +	struct vmxnet3_adapter *adapter = netdev_priv(netdev);
> +
> +	wol->supported = WAKE_UCAST | WAKE_ARP | WAKE_MAGIC;
> +	wol->wolopts = adapter->wol;
> +}
> +
> +
> +static int
> +vmxnet3_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
> +{
> +	struct vmxnet3_adapter *adapter = netdev_priv(netdev);
> +
> +	if (wol->wolopts & (WAKE_PHY | WAKE_MCAST | WAKE_BCAST |
> +			    WAKE_MAGICSECURE)) {
> +		return -EOPNOTSUPP;
> +	}
> +
> +	adapter->wol = wol->wolopts;
> +
> +	device_set_wakeup_enable(&adapter->pdev->dev, adapter->wol);
> +
> +	return 0;
> +}
> +
> +
> +static int
> +vmxnet3_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
> +{
> +	struct vmxnet3_adapter *adapter = netdev_priv(netdev);
> +
> +	ecmd->supported = SUPPORTED_10000baseT_Full | SUPPORTED_1000baseT_Full |
> +			  SUPPORTED_TP;
> +	ecmd->advertising = ADVERTISED_TP;
> +	ecmd->port = PORT_TP;
> +	ecmd->transceiver = XCVR_INTERNAL;
> +
> +	if (adapter->link_speed) {
> +		ecmd->speed = adapter->link_speed;
> +		ecmd->duplex = DUPLEX_FULL;
> +	} else {
> +		ecmd->speed = -1;
> +		ecmd->duplex = -1;
> +	}
> +	return 0;
> +}
> +
> +
> +static void
> +vmxnet3_get_ringparam(struct net_device *netdev,
> +		      struct ethtool_ringparam *param)
> +{
> +	struct vmxnet3_adapter *adapter = netdev_priv(netdev);
> +
> +	param->rx_max_pending = VMXNET3_RX_RING_MAX_SIZE;
> +	param->tx_max_pending = VMXNET3_TX_RING_MAX_SIZE;
> +	param->rx_mini_max_pending = 0;
> +	param->rx_jumbo_max_pending = 0;
> +
> +	param->rx_pending = adapter->rx_queue.rx_ring[0].size;
> +	param->tx_pending = adapter->tx_queue.tx_ring.size;
> +	param->rx_mini_pending = 0;
> +	param->rx_jumbo_pending = 0;
> +}
> +
> +
> +static int
> +vmxnet3_set_ringparam(struct net_device *netdev,
> +		      struct ethtool_ringparam *param)
> +{
> +	struct vmxnet3_adapter *adapter = netdev_priv(netdev);
> +	u32 new_tx_ring_size, new_rx_ring_size;
> +	u32 sz;
> +	int err = 0;
> +
> +	if (param->tx_pending == 0 || param->tx_pending >
> +						VMXNET3_TX_RING_MAX_SIZE)
> +		return -EINVAL;
> +
> +	if (param->rx_pending == 0 || param->rx_pending >
> +						VMXNET3_RX_RING_MAX_SIZE)
> +		return -EINVAL;
> +
> +
> +	/* round it up to a multiple of VMXNET3_RING_SIZE_ALIGN */
> +	new_tx_ring_size = (param->tx_pending + VMXNET3_RING_SIZE_MASK) &
> +							~VMXNET3_RING_SIZE_MASK;
> +	new_tx_ring_size = min_t(u32, new_tx_ring_size,
> +				 VMXNET3_TX_RING_MAX_SIZE);
> +	if (new_tx_ring_size > VMXNET3_TX_RING_MAX_SIZE || (new_tx_ring_size %
> +						VMXNET3_RING_SIZE_ALIGN) != 0)
> +		return -EINVAL;
> +
> +	/* ring0 has to be a multiple of
> +	 * rx_buf_per_pkt * VMXNET3_RING_SIZE_ALIGN
> +	 */
> +	sz = adapter->rx_buf_per_pkt * VMXNET3_RING_SIZE_ALIGN;
> +	new_rx_ring_size = (param->rx_pending + sz - 1) / sz * sz;
> +	new_rx_ring_size = min_t(u32, new_rx_ring_size,
> +				 VMXNET3_RX_RING_MAX_SIZE / sz * sz);
> +	if (new_rx_ring_size > VMXNET3_RX_RING_MAX_SIZE || (new_rx_ring_size %
> +							   sz) != 0)
> +		return -EINVAL;
> +
> +	if (new_tx_ring_size == adapter->tx_queue.tx_ring.size &&
> +			new_rx_ring_size == adapter->rx_queue.rx_ring[0].size) {
> +		return 0;
> +	}
> +
> +	/*
> +	 * Reset_work may be in the middle of resetting the device, wait for its
> +	 * completion.
> +	 */
> +	while (test_and_set_bit(VMXNET3_STATE_BIT_RESETTING, &adapter->state))
> +		msleep(1);
> +
> +	if (netif_running(netdev)) {
> +		vmxnet3_quiesce_dev(adapter);
> +		vmxnet3_reset_dev(adapter);
> +
> +		/* recreate the rx queue and the tx queue based on the
> +		 * new sizes */
> +		vmxnet3_tq_destroy(&adapter->tx_queue, adapter);
> +		vmxnet3_rq_destroy(&adapter->rx_queue, adapter);
> +
> +		err = vmxnet3_create_queues(adapter, new_tx_ring_size,
> +			new_rx_ring_size, VMXNET3_DEF_RX_RING_SIZE);
> +		if (err) {
> +			/* failed, most likely because of OOM, try default
> +			 * size */
> +			printk(KERN_ERR "%s: failed to apply new sizes, try the"
> +				" default ones\n", netdev->name);
> +			err = vmxnet3_create_queues(adapter,
> +						    VMXNET3_DEF_TX_RING_SIZE,
> +						    VMXNET3_DEF_RX_RING_SIZE,
> +						    VMXNET3_DEF_RX_RING_SIZE);
> +			if (err) {
> +				printk(KERN_ERR "%s: failed to create queues "
> +					"with default sizes. Closing it\n",
> +					netdev->name);
> +				goto out;
> +			}
> +		}
> +
> +		err = vmxnet3_activate_dev(adapter);
> +		if (err)
> +			printk(KERN_ERR "%s: failed to re-activate, error %d."
> +				" Closing it\n", netdev->name, err);
> +	}
> +
> +out:
> +	clear_bit(VMXNET3_STATE_BIT_RESETTING, &adapter->state);
> +	if (err)
> +		vmxnet3_force_close(adapter);
> +
> +	return err;
> +}
> +
> +
> +static struct ethtool_ops vmxnet3_ethtool_ops = {
> +	.get_settings      = vmxnet3_get_settings,
> +	.get_drvinfo       = vmxnet3_get_drvinfo,
> +	.get_regs_len      = vmxnet3_get_regs_len,
> +	.get_regs          = vmxnet3_get_regs,
> +	.get_wol           = vmxnet3_get_wol,
> +	.set_wol           = vmxnet3_set_wol,
> +	.get_link          = ethtool_op_get_link,
> +	.get_rx_csum       = vmxnet3_get_rx_csum,
> +	.set_rx_csum       = vmxnet3_set_rx_csum,
> +	.get_tx_csum       = ethtool_op_get_tx_csum,
> +	.set_tx_csum       = ethtool_op_set_tx_hw_csum,
> +	.get_sg            = ethtool_op_get_sg,
> +	.set_sg            = ethtool_op_set_sg,
> +	.get_tso           = ethtool_op_get_tso,
> +	.set_tso           = ethtool_op_set_tso,
> +	.get_strings       = vmxnet3_get_strings,
> +	.get_flags	   = vmxnet3_get_flags,
> +	.set_flags	   = vmxnet3_set_flags,
> +	.get_sset_count	   = vmxnet3_get_sset_count,
> +	.get_ethtool_stats = vmxnet3_get_ethtool_stats,
> +	.get_ringparam     = vmxnet3_get_ringparam,
> +	.set_ringparam     = vmxnet3_set_ringparam,
> +};
> +
> +void vmxnet3_set_ethtool_ops(struct net_device *netdev)
> +{
> +	SET_ETHTOOL_OPS(netdev, &vmxnet3_ethtool_ops);
> +}
> diff --git a/drivers/net/vmxnet3/vmxnet3_int.h b/drivers/net/vmxnet3/vmxnet3_int.h
> new file mode 100644
> index 0000000..cf4b08d
> --- /dev/null
> +++ b/drivers/net/vmxnet3/vmxnet3_int.h
> @@ -0,0 +1,389 @@
> +/*
> + * Linux driver for VMware's vmxnet3 ethernet NIC.
> + *
> + * Copyright (C) 2008-2009, VMware, Inc. All Rights Reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; version 2 of the License and no later version.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
> + * NON INFRINGEMENT.  See the GNU General Public License for more
> + * details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
> + *
> + * The full GNU General Public License is included in this distribution in
> + * the file called "COPYING".
> + *
> + * Maintained by: Shreyas Bhatewara <pv-drivers@vmware.com>
> + *
> + */
> +
> +#ifndef _VMXNET3_INT_H
> +#define _VMXNET3_INT_H
> +
> +#include <linux/types.h>
> +#include <linux/ethtool.h>
> +#include <linux/delay.h>
> +#include <linux/netdevice.h>
> +#include <linux/pci.h>
> +#include <linux/ethtool.h>
> +#include <linux/compiler.h>
> +#include <linux/module.h>
> +#include <linux/moduleparam.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +#include <linux/ioport.h>
> +#include <linux/highmem.h>
> +#include <linux/init.h>
> +#include <linux/timer.h>
> +#include <linux/skbuff.h>
> +#include <linux/interrupt.h>
> +#include <linux/workqueue.h>
> +#include <linux/uaccess.h>
> +#include <asm/dma.h>
> +#include <asm/page.h>
> +
> +#include <linux/tcp.h>
> +#include <linux/udp.h>
> +#include <linux/ip.h>
> +#include <linux/ipv6.h>
> +#include <linux/in.h>
> +#include <linux/etherdevice.h>
> +#include <asm/checksum.h>
> +#include <linux/if_vlan.h>
> +#include <linux/if_arp.h>
> +#include <linux/inetdevice.h>
> +#include <linux/dst.h>
> +
> +#include "vmxnet3_defs.h"
> +
> +#ifdef DEBUG
> +# define VMXNET3_DRIVER_VERSION_REPORT VMXNET3_DRIVER_VERSION_STRING"-NAPI(debug)"
> +#else
> +# define VMXNET3_DRIVER_VERSION_REPORT VMXNET3_DRIVER_VERSION_STRING"-NAPI"
> +#endif
> +
> +
> +/*
> + * Version numbers
> + */
> +#define VMXNET3_DRIVER_VERSION_STRING   "1.0.4.0-k"
> +
> +/* a 32-bit int, each byte encode a verion number in VMXNET3_DRIVER_VERSION */
> +#define VMXNET3_DRIVER_VERSION_NUM      0x01000400
> +
> +
> +/*
> + * Capabilities
> + */
> +
> +enum {
> +	VMNET_CAP_SG	        = 0x0001, /* Can do scatter-gather transmits. */
> +	VMNET_CAP_IP4_CSUM      = 0x0002, /* Can checksum only TCP/UDP over
> +					   * IPv4 */
> +	VMNET_CAP_HW_CSUM       = 0x0004, /* Can checksum all packets. */
> +	VMNET_CAP_HIGH_DMA      = 0x0008, /* Can DMA to high memory. */
> +	VMNET_CAP_TOE	        = 0x0010, /* Supports TCP/IP offload. */
> +	VMNET_CAP_TSO	        = 0x0020, /* Supports TCP Segmentation
> +					   * offload */
> +	VMNET_CAP_SW_TSO        = 0x0040, /* Supports SW TCP Segmentation */
> +	VMNET_CAP_VMXNET_APROM  = 0x0080, /* Vmxnet APROM support */
> +	VMNET_CAP_HW_TX_VLAN    = 0x0100, /* Can we do VLAN tagging in HW */
> +	VMNET_CAP_HW_RX_VLAN    = 0x0200, /* Can we do VLAN untagging in HW */
> +	VMNET_CAP_SW_VLAN       = 0x0400, /* VLAN tagging/untagging in SW */
> +	VMNET_CAP_WAKE_PCKT_RCV = 0x0800, /* Can wake on network packet recv? */
> +	VMNET_CAP_ENABLE_INT_INLINE = 0x1000,  /* Enable Interrupt Inline */
> +	VMNET_CAP_ENABLE_HEADER_COPY = 0x2000,  /* copy header for vmkernel */
> +	VMNET_CAP_TX_CHAIN      = 0x4000, /* Guest can use multiple tx entries
> +					  * for a pkt */
> +	VMNET_CAP_RX_CHAIN      = 0x8000, /* pkt can span multiple rx entries */
> +	VMNET_CAP_LPD           = 0x10000, /* large pkt delivery */
> +	VMNET_CAP_BPF           = 0x20000, /* BPF Support in VMXNET Virtual HW*/
> +	VMNET_CAP_SG_SPAN_PAGES = 0x40000, /* Scatter-gather can span multiple*/
> +					   /* pages transmits */
> +	VMNET_CAP_IP6_CSUM      = 0x80000, /* Can do IPv6 csum offload. */
> +	VMNET_CAP_TSO6         = 0x100000, /* TSO seg. offload for IPv6 pkts. */
> +	VMNET_CAP_TSO256k      = 0x200000, /* Can do TSO seg offload for */
> +					   /* pkts up to 256kB. */
> +	VMNET_CAP_UPT          = 0x400000  /* Support UPT */
> +};
> +
> +/*
> + * PCI vendor and device IDs.
> + */
> +#define PCI_VENDOR_ID_VMWARE            0x15AD
> +#define PCI_DEVICE_ID_VMWARE_VMXNET3    0x07B0
> +#define MAX_ETHERNET_CARDS		10
> +#define MAX_PCI_PASSTHRU_DEVICE		6
> +
> +struct vmxnet3_cmd_ring {
> +	union Vmxnet3_GenericDesc *base;
> +	u32		size;
> +	u32		next2fill;
> +	u32		next2comp;
> +	u8		gen;
> +	dma_addr_t	basePA;
> +};
> +
> +static inline void
> +vmxnet3_cmd_ring_adv_next2fill(struct vmxnet3_cmd_ring *ring)
> +{
> +	ring->next2fill++;
> +	if (unlikely(ring->next2fill == ring->size)) {
> +		ring->next2fill = 0;
> +		VMXNET3_FLIP_RING_GEN(ring->gen);
> +	}
> +}
> +
> +static inline void
> +vmxnet3_cmd_ring_adv_next2comp(struct vmxnet3_cmd_ring *ring)
> +{
> +	VMXNET3_INC_RING_IDX_ONLY(ring->next2comp, ring->size);
> +}
> +
> +static inline int
> +vmxnet3_cmd_ring_desc_avail(struct vmxnet3_cmd_ring *ring)
> +{
> +	return (ring->next2comp > ring->next2fill ? 0 : ring->size) +
> +		ring->next2comp - ring->next2fill - 1;
> +}
> +
> +struct vmxnet3_comp_ring {
> +	union Vmxnet3_GenericDesc *base;
> +	u32               size;
> +	u32               next2proc;
> +	u8                gen;
> +	u8                intr_idx;
> +	dma_addr_t           basePA;
> +};
> +
> +static inline void
> +vmxnet3_comp_ring_adv_next2proc(struct vmxnet3_comp_ring *ring)
> +{
> +	ring->next2proc++;
> +	if (unlikely(ring->next2proc == ring->size)) {
> +		ring->next2proc = 0;
> +		VMXNET3_FLIP_RING_GEN(ring->gen);
> +	}
> +}
> +
> +struct vmxnet3_tx_data_ring {
> +	struct Vmxnet3_TxDataDesc *base;
> +	u32              size;
> +	dma_addr_t          basePA;
> +};
> +
> +enum vmxnet3_buf_map_type {
> +	VMXNET3_MAP_INVALID = 0,
> +	VMXNET3_MAP_NONE,
> +	VMXNET3_MAP_SINGLE,
> +	VMXNET3_MAP_PAGE,
> +};
> +
> +struct vmxnet3_tx_buf_info {
> +	u32      map_type;
> +	u16      len;
> +	u16      sop_idx;
> +	dma_addr_t  dma_addr;
> +	struct sk_buff *skb;
> +};
> +
> +struct vmxnet3_tq_driver_stats {
> +	u64 drop_total;     /* # of pkts dropped by the driver, the
> +				* counters below track droppings due to
> +				* different reasons
> +				*/
> +	u64 drop_too_many_frags;
> +	u64 drop_oversized_hdr;
> +	u64 drop_hdr_inspect_err;
> +	u64 drop_tso;
> +
> +	u64 tx_ring_full;
> +	u64 linearized;         /* # of pkts linearized */
> +	u64 copy_skb_header;    /* # of times we have to copy skb header */
> +	u64 oversized_hdr;
> +};
> +
> +struct vmxnet3_tx_ctx {
> +	bool   ipv4;
> +	u16 mss;
> +	u32 eth_ip_hdr_size; /* only valid for pkts requesting tso or csum
> +				 * offloading
> +				 */
> +	u32 l4_hdr_size;     /* only valid if mss != 0 */
> +	u32 copy_size;       /* # of bytes copied into the data ring */
> +	union Vmxnet3_GenericDesc *sop_txd;
> +	union Vmxnet3_GenericDesc *eop_txd;
> +};
> +
> +struct vmxnet3_tx_queue {
> +	spinlock_t                      tx_lock;
> +	struct vmxnet3_cmd_ring         tx_ring;
> +	struct vmxnet3_tx_buf_info     *buf_info;
> +	struct vmxnet3_tx_data_ring     data_ring;
> +	struct vmxnet3_comp_ring        comp_ring;
> +	struct Vmxnet3_TxQueueCtrl            *shared;
> +	struct vmxnet3_tq_driver_stats  stats;
> +	bool                            stopped;
> +	int                             num_stop;  /* # of times the queue is
> +						    * stopped */
> +} __attribute__((__aligned__(SMP_CACHE_BYTES)));
> +
> +enum vmxnet3_rx_buf_type {
> +	VMXNET3_RX_BUF_NONE = 0,
> +	VMXNET3_RX_BUF_SKB = 1,
> +	VMXNET3_RX_BUF_PAGE = 2
> +};
> +
> +struct vmxnet3_rx_buf_info {
> +	enum vmxnet3_rx_buf_type buf_type;
> +	u16     len;
> +	union {
> +		struct sk_buff *skb;
> +		struct page    *page;
> +	};
> +	dma_addr_t dma_addr;
> +};
> +
> +struct vmxnet3_rx_ctx {
> +	struct sk_buff *skb;
> +	u32 sop_idx;
> +};
> +
> +struct vmxnet3_rq_driver_stats {
> +	u64 drop_total;
> +	u64 drop_err;
> +	u64 drop_fcs;
> +	u64 rx_buf_alloc_failure;
> +};
> +
> +struct vmxnet3_rx_queue {
> +	struct vmxnet3_cmd_ring   rx_ring[2];
> +	struct vmxnet3_comp_ring  comp_ring;
> +	struct vmxnet3_rx_ctx     rx_ctx;
> +	u32 qid;            /* rqID in RCD for buffer from 1st ring */
> +	u32 qid2;           /* rqID in RCD for buffer from 2nd ring */
> +	u32 uncommitted[2]; /* # of buffers allocated since last RXPROD
> +				* update */
> +	struct vmxnet3_rx_buf_info     *buf_info[2];
> +	struct Vmxnet3_RxQueueCtrl            *shared;
> +	struct vmxnet3_rq_driver_stats  stats;
> +} __attribute__((__aligned__(SMP_CACHE_BYTES)));
> +
> +#define VMXNET3_LINUX_MAX_MSIX_VECT     1
> +
> +struct vmxnet3_intr {
> +	enum vmxnet3_intr_mask_mode  mask_mode;
> +	enum vmxnet3_intr_type       type;	/* MSI-X, MSI, or INTx? */
> +	u8  num_intrs;			/* # of intr vectors */
> +	u8  event_intr_idx;		/* idx of the intr vector for event */
> +	u8  mod_levels[VMXNET3_LINUX_MAX_MSIX_VECT]; /* moderation level */
> +#ifdef CONFIG_PCI_MSI
> +	struct msix_entry msix_entries[VMXNET3_LINUX_MAX_MSIX_VECT];
> +#endif
> +};
> +
> +#define VMXNET3_STATE_BIT_RESETTING   0
> +#define VMXNET3_STATE_BIT_QUIESCED    1
> +struct vmxnet3_adapter {
> +	struct vmxnet3_tx_queue         tx_queue;
> +	struct vmxnet3_rx_queue         rx_queue;
> +	struct napi_struct              napi;
> +	struct vlan_group              *vlan_grp;
> +
> +	struct vmxnet3_intr             intr;
> +
> +	struct Vmxnet3_DriverShared    *shared;
> +	struct Vmxnet3_PMConf          *pm_conf;
> +	struct Vmxnet3_TxQueueDesc     *tqd_start;     /* first tx queue desc */
> +	struct Vmxnet3_RxQueueDesc     *rqd_start;     /* first rx queue desc */
> +	struct net_device              *netdev;
> +	struct pci_dev                 *pdev;
> +
> +	u8				*hw_addr0; /* for BAR 0 */
> +	u8				*hw_addr1; /* for BAR 1 */
> +
> +	/* feature control */
> +	bool				rxcsum;
> +	bool				lro;
> +	bool				jumbo_frame;
> +
> +	/* rx buffer related */
> +	unsigned			skb_buf_size;
> +	int		rx_buf_per_pkt;  /* only apply to the 1st ring */
> +	dma_addr_t			shared_pa;
> +	dma_addr_t queue_desc_pa;
> +
> +	/* Wake-on-LAN */
> +	u32     wol;
> +
> +	/* Link speed */
> +	u32     link_speed; /* in mbps */
> +
> +	u64     tx_timeout_count;
> +	struct work_struct work;
> +
> +	unsigned long  state;    /* VMXNET3_STATE_BIT_xxx */
> +
> +	int dev_number;
> +};
> +
> +#define VMXNET3_WRITE_BAR0_REG(adapter, reg, val)  \
> +	writel((val), (adapter)->hw_addr0 + (reg))
> +#define VMXNET3_READ_BAR0_REG(adapter, reg)        \
> +	readl((adapter)->hw_addr0 + (reg))
> +
> +#define VMXNET3_WRITE_BAR1_REG(adapter, reg, val)  \
> +	writel((val), (adapter)->hw_addr1 + (reg))
> +#define VMXNET3_READ_BAR1_REG(adapter, reg)        \
> +	readl((adapter)->hw_addr1 + (reg))
> +
> +#define VMXNET3_WAKE_QUEUE_THRESHOLD(tq)  (5)
> +#define VMXNET3_RX_ALLOC_THRESHOLD(rq, ring_idx, adapter) \
> +	((rq)->rx_ring[ring_idx].size >> 3)
> +
> +#define VMXNET3_GET_ADDR_LO(dma)   ((u32)(dma))
> +#define VMXNET3_GET_ADDR_HI(dma)   ((u32)(((u64)(dma)) >> 32))
> +
> +/* must be a multiple of VMXNET3_RING_SIZE_ALIGN */
> +#define VMXNET3_DEF_TX_RING_SIZE    512
> +#define VMXNET3_DEF_RX_RING_SIZE    256
> +
> +#define VMXNET3_MAX_ETH_HDR_SIZE    22
> +#define VMXNET3_MAX_SKB_BUF_SIZE    (3*1024)
> +
> +int
> +vmxnet3_quiesce_dev(struct vmxnet3_adapter *adapter);
> +
> +int
> +vmxnet3_activate_dev(struct vmxnet3_adapter *adapter);
> +
> +void
> +vmxnet3_force_close(struct vmxnet3_adapter *adapter);
> +
> +void
> +vmxnet3_reset_dev(struct vmxnet3_adapter *adapter);
> +
> +void
> +vmxnet3_tq_destroy(struct vmxnet3_tx_queue *tq,
> +		   struct vmxnet3_adapter *adapter);
> +
> +void
> +vmxnet3_rq_destroy(struct vmxnet3_rx_queue *rq,
> +		   struct vmxnet3_adapter *adapter);
> +
> +int
> +vmxnet3_create_queues(struct vmxnet3_adapter *adapter,
> +		      u32 tx_ring_size, u32 rx_ring_size, u32 rx_ring2_size);
> +
> +extern void vmxnet3_set_ethtool_ops(struct net_device *netdev);
> +extern struct net_device_stats *vmxnet3_get_stats(struct net_device *netdev);
> +
> +extern char vmxnet3_driver_name[];
> +#endif
> 

^ permalink raw reply

* Re: [PATCH 2/2] [RFC] Add c/r support for connected INET sockets
From: John Dykstra @ 2009-10-08 18:10 UTC (permalink / raw)
  To: Dan Smith
  Cc: containers-qjLDD68F18O7TbgM5vRIOg, netdev-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <8763apg57w.fsf-FLMGYpZoEPULwtHQx/6qkW3U47Q5hpJU@public.gmane.org>

On Thu, 2009-10-08 at 10:34 -0700, Dan Smith wrote:
> JD> Yep.  It sort of messes up your separation between layers, though.
> 
> In what way?  It's just this:

I just meant that you're peeking into the checkpointed sk data before
you create the new sk.  In so much of your code, you manage to restore
structs as a whole, without peeking first.

> Which seems like a why-didn't-I-do-that-in-the-first-place sort of
> thing...   Am I missing something?

Not anything that I see.  

  --  John

^ permalink raw reply

* Re: [PATCH 2/2] [RFC] Add c/r support for connected INET sockets
From: Dan Smith @ 2009-10-08 18:11 UTC (permalink / raw)
  To: John Dykstra
  Cc: containers-qjLDD68F18O7TbgM5vRIOg, netdev-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1255025408.8033.25.camel@Maple>

JD> I just meant that you're peeking into the checkpointed sk data
JD> before you create the new sk.  In so much of your code, you manage
JD> to restore structs as a whole, without peeking first.

Ah, okay, gotcha.

Thanks!

-- 
Dan Smith
IBM Linux Technology Center
email: danms-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org

^ permalink raw reply

* [PATCH v4 net-next-2.6] cxgb3: Added private MAC address and provisioning packet handler for iSCSI
From: kxie @ 2009-10-08 19:11 UTC (permalink / raw)
  To: davem
  Cc: swise, divy, rakesh, kxie, James.Bottomley, michaelc,
	linux-kernel, netdev

From: Karen Xie <kxie@chelsio.com>

[PATCH v4 net-next-2.6] cxgb3: Added private MAC address and provisioning packet handler for iSCSI

This patch added support of private MAC address per port and provisioning
packet handler for iSCSI traffic only.

The above changes are isolated to the cxgb3 driver, independent of any scsi or iscsi driver changes.

Acked-by: Karen Xie <kxie@chelsio.com>
Acked-by: Divy Le Ray <divy@chelsio.com>
Signed-off-by: Rakesh Ranjan <rakesh@chelsio.com>
---

 drivers/net/cxgb3/adapter.h    |   16 ++++++++++++++++
 drivers/net/cxgb3/cxgb3_main.c |   22 ++++++++++++++++++----
 drivers/net/cxgb3/sge.c        |   28 +++++++++++++++++++---------
 3 files changed, 53 insertions(+), 13 deletions(-)


diff --git a/drivers/net/cxgb3/adapter.h b/drivers/net/cxgb3/adapter.h
index 2b1aea6..463633e 100644
--- a/drivers/net/cxgb3/adapter.h
+++ b/drivers/net/cxgb3/adapter.h
@@ -48,12 +48,27 @@
 struct vlan_group;
 struct adapter;
 struct sge_qset;
+struct port_info;
 
 enum {			/* rx_offload flags */
 	T3_RX_CSUM	= 1 << 0,
 	T3_LRO		= 1 << 1,
 };
 
+enum mac_idx_types {
+	LAN_MAC_IDX	= 0,
+	SAN_MAC_IDX,
+
+	MAX_MAC_IDX
+};
+
+struct iscsi_config {
+	__u8	mac_addr[ETH_ALEN];
+	__u32	flags;
+	int (*send)(struct port_info *pi, struct sk_buff **skb);
+	int (*recv)(struct port_info *pi, struct sk_buff *skb);
+};
+
 struct port_info {
 	struct adapter *adapter;
 	struct vlan_group *vlan_grp;
@@ -68,6 +83,7 @@ struct port_info {
 	struct net_device_stats netstats;
 	int activity;
 	__be32 iscsi_ipv4addr;
+	struct iscsi_config iscsic;
 
 	int link_fault; /* link fault was detected */
 };
diff --git a/drivers/net/cxgb3/cxgb3_main.c b/drivers/net/cxgb3/cxgb3_main.c
index 34e776c..c9113d3 100644
--- a/drivers/net/cxgb3/cxgb3_main.c
+++ b/drivers/net/cxgb3/cxgb3_main.c
@@ -344,8 +344,10 @@ static void link_start(struct net_device *dev)
 
 	init_rx_mode(&rm, dev, dev->mc_list);
 	t3_mac_reset(mac);
+	t3_mac_set_num_ucast(mac, MAX_MAC_IDX);
 	t3_mac_set_mtu(mac, dev->mtu);
-	t3_mac_set_address(mac, 0, dev->dev_addr);
+	t3_mac_set_address(mac, LAN_MAC_IDX, dev->dev_addr);
+	t3_mac_set_address(mac, SAN_MAC_IDX, pi->iscsic.mac_addr);
 	t3_mac_set_rx_mode(mac, &rm);
 	t3_link_start(&pi->phy, mac, &pi->link_config);
 	t3_mac_enable(mac, MAC_DIRECTION_RX | MAC_DIRECTION_TX);
@@ -903,6 +905,7 @@ static inline int offload_tx(struct t3cdev *tdev, struct sk_buff *skb)
 static int write_smt_entry(struct adapter *adapter, int idx)
 {
 	struct cpl_smt_write_req *req;
+	struct port_info *pi = netdev_priv(adapter->port[idx]);
 	struct sk_buff *skb = alloc_skb(sizeof(*req), GFP_KERNEL);
 
 	if (!skb)
@@ -913,8 +916,8 @@ static int write_smt_entry(struct adapter *adapter, int idx)
 	OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_SMT_WRITE_REQ, idx));
 	req->mtu_idx = NMTUS - 1;	/* should be 0 but there's a T3 bug */
 	req->iff = idx;
-	memset(req->src_mac1, 0, sizeof(req->src_mac1));
 	memcpy(req->src_mac0, adapter->port[idx]->dev_addr, ETH_ALEN);
+	memcpy(req->src_mac1, pi->iscsic.mac_addr, ETH_ALEN);
 	skb->priority = 1;
 	offload_tx(&adapter->tdev, skb);
 	return 0;
@@ -2516,7 +2519,7 @@ static int cxgb_set_mac_addr(struct net_device *dev, void *p)
 		return -EINVAL;
 
 	memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
-	t3_mac_set_address(&pi->mac, 0, dev->dev_addr);
+	t3_mac_set_address(&pi->mac, LAN_MAC_IDX, dev->dev_addr);
 	if (offload_running(adapter))
 		write_smt_entry(adapter, pi->port_id);
 	return 0;
@@ -2654,7 +2657,7 @@ static void check_t3b2_mac(struct adapter *adapter)
 			struct cmac *mac = &p->mac;
 
 			t3_mac_set_mtu(mac, dev->mtu);
-			t3_mac_set_address(mac, 0, dev->dev_addr);
+			t3_mac_set_address(mac, LAN_MAC_IDX, dev->dev_addr);
 			cxgb_set_rxmode(dev);
 			t3_link_start(&p->phy, mac, &p->link_config);
 			t3_mac_enable(mac, MAC_DIRECTION_RX | MAC_DIRECTION_TX);
@@ -3112,6 +3115,14 @@ static const struct net_device_ops cxgb_netdev_ops = {
 #endif
 };
 
+static void __devinit cxgb3_init_iscsi_mac(struct net_device *dev)
+{
+	struct port_info *pi = netdev_priv(dev);
+
+	memcpy(pi->iscsic.mac_addr, dev->dev_addr, ETH_ALEN);
+	pi->iscsic.mac_addr[3] |= 0x80;
+}
+
 static int __devinit init_one(struct pci_dev *pdev,
 			      const struct pci_device_id *ent)
 {
@@ -3270,6 +3281,9 @@ static int __devinit init_one(struct pci_dev *pdev,
 		goto out_free_dev;
 	}
 
+	for_each_port(adapter, i)
+		cxgb3_init_iscsi_mac(adapter->port[i]);
+
 	/* Driver's ready. Reflect it on LEDs */
 	t3_led_ready(adapter);
 
diff --git a/drivers/net/cxgb3/sge.c b/drivers/net/cxgb3/sge.c
index f866128..b7f4ee4 100644
--- a/drivers/net/cxgb3/sge.c
+++ b/drivers/net/cxgb3/sge.c
@@ -1946,10 +1946,9 @@ static void restart_tx(struct sge_qset *qs)
  *	Check if the ARP request is probing the private IP address
  *	dedicated to iSCSI, generate an ARP reply if so.
  */
-static void cxgb3_arp_process(struct adapter *adapter, struct sk_buff *skb)
+static void cxgb3_arp_process(struct port_info *pi, struct sk_buff *skb)
 {
 	struct net_device *dev = skb->dev;
-	struct port_info *pi;
 	struct arphdr *arp;
 	unsigned char *arp_ptr;
 	unsigned char *sha;
@@ -1972,12 +1971,11 @@ static void cxgb3_arp_process(struct adapter *adapter, struct sk_buff *skb)
 	arp_ptr += dev->addr_len;
 	memcpy(&tip, arp_ptr, sizeof(tip));
 
-	pi = netdev_priv(dev);
 	if (tip != pi->iscsi_ipv4addr)
 		return;
 
 	arp_send(ARPOP_REPLY, ETH_P_ARP, sip, dev, tip, sha,
-		 dev->dev_addr, sha);
+		 pi->iscsic.mac_addr, sha);
 
 }
 
@@ -1986,6 +1984,19 @@ static inline int is_arp(struct sk_buff *skb)
 	return skb->protocol == htons(ETH_P_ARP);
 }
 
+static void cxgb3_process_iscsi_prov_pack(struct port_info *pi,
+					struct sk_buff *skb)
+{
+	if (is_arp(skb)) {
+		cxgb3_arp_process(pi, skb);
+		return;
+	}
+
+	if (pi->iscsic.recv)
+		pi->iscsic.recv(pi, skb);
+
+}
+
 /**
  *	rx_eth - process an ingress ethernet packet
  *	@adap: the adapter
@@ -2024,13 +2035,12 @@ static void rx_eth(struct adapter *adap, struct sge_rspq *rq,
 				vlan_gro_receive(&qs->napi, grp,
 						 ntohs(p->vlan), skb);
 			else {
-				if (unlikely(pi->iscsi_ipv4addr &&
-				    is_arp(skb))) {
+				if (unlikely(pi->iscsic.flags)) {
 					unsigned short vtag = ntohs(p->vlan) &
 								VLAN_VID_MASK;
 					skb->dev = vlan_group_get_device(grp,
 									 vtag);
-					cxgb3_arp_process(adap, skb);
+					cxgb3_process_iscsi_prov_pack(pi, skb);
 				}
 				__vlan_hwaccel_rx(skb, grp, ntohs(p->vlan),
 					  	  rq->polling);
@@ -2041,8 +2051,8 @@ static void rx_eth(struct adapter *adap, struct sge_rspq *rq,
 		if (lro)
 			napi_gro_receive(&qs->napi, skb);
 		else {
-			if (unlikely(pi->iscsi_ipv4addr && is_arp(skb)))
-				cxgb3_arp_process(adap, skb);
+			if (unlikely(pi->iscsic.flags))
+				cxgb3_process_iscsi_prov_pack(pi, skb);
 			netif_receive_skb(skb);
 		}
 	} else

^ permalink raw reply related

* Re: [PATCH] TI DaVinci EMAC: Clear statistics register properly.
From: Kevin Hilman @ 2009-10-08 19:11 UTC (permalink / raw)
  To: Sriramakrishnan
  Cc: netdev-u79uwXL29TY76Z2rM5mHXA,
	davinci-linux-open-source-VycZQUHpC/PFrsHnngEfi1aTQe2KTcn/
In-Reply-To: <1254919470-20595-1-git-send-email-srk-l0cyMroinI0@public.gmane.org>

Sriramakrishnan <srk-l0cyMroinI0@public.gmane.org> writes:

> The mechanism to clear the statistics register is dependent
> on the status of GMIIEN bit in MAC control register. If the
> GMIIEN bit is set, the stats registers are write to decrement.
> If the GMIIEN bit is cleared, the stats registers are plain
> read/write registers. The stats register clearing operation
> must take into account the current state of GMIIEN as it
> can be cleared when the interface is brought down.
>
> With existing implementation logic, querying for interface stats
> when the interface is down, can corrupt the statistics counters.
> This patch examines the GMIIEN bit status in MAC_CONTROL
> register before choosing an appropriate mask for clearing stats
> registers.
>
> Signed-off-by: Sriramakrishnan <srk-l0cyMroinI0@public.gmane.org>
> Acked-by: Chaithrika U S <chaithrika-l0cyMroinI0@public.gmane.org>
> ---
> This patch is generated against the tip of net-next-2.6.

OK, will also add to linux-davinci while waiting for it reach
mainline.

Kevin

>  drivers/net/davinci_emac.c |   36 ++++++++++++++++++++++++------------
>  1 files changed, 24 insertions(+), 12 deletions(-)
>
> diff --git a/drivers/net/davinci_emac.c b/drivers/net/davinci_emac.c
> index a421ec0..a876dce 100644
> --- a/drivers/net/davinci_emac.c
> +++ b/drivers/net/davinci_emac.c
> @@ -330,6 +330,9 @@ static const char emac_version_string[] = "TI DaVinci EMAC Linux v6.1";
>  #define EMAC_DM646X_MAC_EOI_C0_RXEN	(0x01)
>  #define EMAC_DM646X_MAC_EOI_C0_TXEN	(0x02)
>  
> +/* EMAC Stats Clear Mask */
> +#define EMAC_STATS_CLR_MASK    (0xFFFFFFFF)
> +
>  /** net_buf_obj: EMAC network bufferdata structure
>   *
>   * EMAC network buffer data structure
> @@ -2544,40 +2547,49 @@ static int emac_dev_stop(struct net_device *ndev)
>  static struct net_device_stats *emac_dev_getnetstats(struct net_device *ndev)
>  {
>  	struct emac_priv *priv = netdev_priv(ndev);
> +	u32 mac_control;
> +	u32 stats_clear_mask;
>  
>  	/* update emac hardware stats and reset the registers*/
>  
> +	mac_control = emac_read(EMAC_MACCONTROL);
> +
> +	if (mac_control & EMAC_MACCONTROL_GMIIEN)
> +		stats_clear_mask = EMAC_STATS_CLR_MASK;
> +	else
> +		stats_clear_mask = 0;
> +
>  	priv->net_dev_stats.multicast += emac_read(EMAC_RXMCASTFRAMES);
> -	emac_write(EMAC_RXMCASTFRAMES, EMAC_ALL_MULTI_REG_VALUE);
> +	emac_write(EMAC_RXMCASTFRAMES, stats_clear_mask);
>  
>  	priv->net_dev_stats.collisions += (emac_read(EMAC_TXCOLLISION) +
>  					   emac_read(EMAC_TXSINGLECOLL) +
>  					   emac_read(EMAC_TXMULTICOLL));
> -	emac_write(EMAC_TXCOLLISION, EMAC_ALL_MULTI_REG_VALUE);
> -	emac_write(EMAC_TXSINGLECOLL, EMAC_ALL_MULTI_REG_VALUE);
> -	emac_write(EMAC_TXMULTICOLL, EMAC_ALL_MULTI_REG_VALUE);
> +	emac_write(EMAC_TXCOLLISION, stats_clear_mask);
> +	emac_write(EMAC_TXSINGLECOLL, stats_clear_mask);
> +	emac_write(EMAC_TXMULTICOLL, stats_clear_mask);
>  
>  	priv->net_dev_stats.rx_length_errors += (emac_read(EMAC_RXOVERSIZED) +
>  						emac_read(EMAC_RXJABBER) +
>  						emac_read(EMAC_RXUNDERSIZED));
> -	emac_write(EMAC_RXOVERSIZED, EMAC_ALL_MULTI_REG_VALUE);
> -	emac_write(EMAC_RXJABBER, EMAC_ALL_MULTI_REG_VALUE);
> -	emac_write(EMAC_RXUNDERSIZED, EMAC_ALL_MULTI_REG_VALUE);
> +	emac_write(EMAC_RXOVERSIZED, stats_clear_mask);
> +	emac_write(EMAC_RXJABBER, stats_clear_mask);
> +	emac_write(EMAC_RXUNDERSIZED, stats_clear_mask);
>  
>  	priv->net_dev_stats.rx_over_errors += (emac_read(EMAC_RXSOFOVERRUNS) +
>  					       emac_read(EMAC_RXMOFOVERRUNS));
> -	emac_write(EMAC_RXSOFOVERRUNS, EMAC_ALL_MULTI_REG_VALUE);
> -	emac_write(EMAC_RXMOFOVERRUNS, EMAC_ALL_MULTI_REG_VALUE);
> +	emac_write(EMAC_RXSOFOVERRUNS, stats_clear_mask);
> +	emac_write(EMAC_RXMOFOVERRUNS, stats_clear_mask);
>  
>  	priv->net_dev_stats.rx_fifo_errors += emac_read(EMAC_RXDMAOVERRUNS);
> -	emac_write(EMAC_RXDMAOVERRUNS, EMAC_ALL_MULTI_REG_VALUE);
> +	emac_write(EMAC_RXDMAOVERRUNS, stats_clear_mask);
>  
>  	priv->net_dev_stats.tx_carrier_errors +=
>  		emac_read(EMAC_TXCARRIERSENSE);
> -	emac_write(EMAC_TXCARRIERSENSE, EMAC_ALL_MULTI_REG_VALUE);
> +	emac_write(EMAC_TXCARRIERSENSE, stats_clear_mask);
>  
>  	priv->net_dev_stats.tx_fifo_errors = emac_read(EMAC_TXUNDERRUN);
> -	emac_write(EMAC_TXUNDERRUN, EMAC_ALL_MULTI_REG_VALUE);
> +	emac_write(EMAC_TXUNDERRUN, stats_clear_mask);
>  
>  	return &priv->net_dev_stats;
>  }
> -- 
> 1.6.2.4
>
> _______________________________________________
> Davinci-linux-open-source mailing list
> Davinci-linux-open-source-VycZQUHpC/PFrsHnngEfi1aTQe2KTcn/@public.gmane.org
> http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source

^ permalink raw reply

* Re: using huge numbers of queues
From: Mark Smith @ 2009-10-08 19:56 UTC (permalink / raw)
  To: David Miller; +Cc: andy.grover, herbert, netdev
In-Reply-To: <20091007.153811.139592035.davem@davemloft.net>

On Wed, 07 Oct 2009 15:38:11 -0700 (PDT)
David Miller <davem@davemloft.net> wrote:

> From: Andrew Grover <andy.grover@gmail.com>
> Date: Wed, 7 Oct 2009 14:59:49 -0700
> 
> > At NetConf, you made a passing remark about wanting lots of queues,
> > even 1-per-socket. Have you thought further about how we would use so
> > many?
> 
> Classification.
> 
> Lots and lots of virtual queues, which map to a smaller number
> of physical queues for delivery.
> 
> The virtual queue matched serves as a index and a classification
> hint to things like GRO receive, etc.

Is that similar to what is described in 

Trading Packet Headers for Packet Processing
http://www.sigcomm.org/sigcomm95/papers/chandranmenon.ps

?

My understanding of that paper is that when a packet enters the host it
is classified using various attributes e.g. Ip src/dest/etc, and then
assigned an unique identifier. Subsequent processing of the packet is
indexed by this identifier, rather than each processing stage
performing it's own packet classification and selection.

For packets that are forwarded, the packet is then tagged/labled with
an ID, so that subsequent hosts don't have to perform classification
either.

(This paper is pretty much the origins of MPLS, which is where my
original interest came from)


> --
> 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


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox