Netdev List
 help / color / mirror / Atom feed
* Re: [RFC/PATCH 4/4] UDP memory usage accounting (take 4): memory limitation
From: Stephen Hemminger @ 2007-10-05 15:52 UTC (permalink / raw)
  To: Satoshi OSHIMA
  Cc: Andi Kleen, David Miller, Evgeniy Polyakov, Herbert Xu, netdev,
	?? ??, Yumiko SUGITA, ??@RedHat
In-Reply-To: <47065217.5050004@hitachi.com>

On Sat, 06 Oct 2007 00:02:47 +0900
Satoshi OSHIMA <satoshi.oshima.fk@hitachi.com> wrote:

> This patch introduces memory limitation for UDP.
> 
> 
> signed-off-by: Satoshi Oshima <satoshi.oshima.fk@hitachi.com>
> signed-off-by: Hideo Aoki <haoki@redhat.com>

> Index: 2.6.23-rc9-udp_limit/net/ipv4/sysctl_net_ipv4.c
> ===================================================================
> --- 2.6.23-rc9-udp_limit.orig/net/ipv4/sysctl_net_ipv4.c
> +++ 2.6.23-rc9-udp_limit/net/ipv4/sysctl_net_ipv4.c
> @@ -17,6 +17,7 @@
>  #include <net/ip.h>
>  #include <net/route.h>
>  #include <net/tcp.h>
> +#include <net/udp.h>
>  #include <net/cipso_ipv4.h>
>  
>  /* From af_inet.c */
> @@ -599,6 +600,14 @@ ctl_table ipv4_table[] = {
>  		.proc_handler	= &proc_dointvec
>  	},
>  	{
> +		.ctl_name	= NET_UDP_MEM,
> +		.procname	= "udp_mem",
> +		.data		= &sysctl_udp_mem,
> +		.maxlen		= sizeof(sysctl_udp_mem),
> +		.mode		= 0644,
> +		.proc_handler	= &proc_dointvec
> +	},
> +	{
>  		.ctl_name	= NET_TCP_APP_WIN,
>  		.procname	= "tcp_app_win",
>  		.data		= &sysctl_tcp_app_win,

if you use &proc_dointvec_minmax, then you could inforce min/max
values for udp_mem for the sysctl
-- 
Stephen Hemminger <shemminger@linux-foundation.org>

^ permalink raw reply

* Re: [PATCH]: Third (final?) release of Sun Neptune driver
From: Stephen Hemminger @ 2007-10-05 15:43 UTC (permalink / raw)
  To: David Miller
  Cc: netdev, Ariel.Hendel, greg.onufer, jeff, Ashley.Saulsbury,
	Matheos.Worku
In-Reply-To: <20071005.031209.57156822.davem@davemloft.net>

> I just checked the following into net-2.6.24, besides minor
> tweaks and bug fixes, this is probably what is going to Linus.
> 
> I verified it builds no just on sparc64 but also even on 32-bit
> platforms such as x86. :-)
> 
> Thanks!

Sparse warnings from new driver.
 CHECK   drivers/net/niu.c
drivers/net/niu.c:619:7: warning: symbol 'err' shadows an earlier one
drivers/net/niu.c:574:6: originally declared here
drivers/net/niu.c:4673:7: warning: symbol 'err' shadows an earlier one
drivers/net/niu.c:4668:12: originally declared here
drivers/net/niu.c:203:2: error: bad constant expression
drivers/net/niu.c:203:2: error: cannot size expression
drivers/net/niu.c:4214:2: error: bad constant expression
drivers/net/niu.c:4214:2: error: cannot size expression

^ permalink raw reply

* Re: [PATCH]: Third (final?) release of Sun Neptune driver
From: Stephen Hemminger @ 2007-10-05 15:40 UTC (permalink / raw)
  To: David Miller
  Cc: netdev, Ariel.Hendel, greg.onufer, jeff, Ashley.Saulsbury,
	Matheos.Worku
In-Reply-To: <20071005.031209.57156822.davem@davemloft.net>

> I just checked the following into net-2.6.24, besides minor
> tweaks and bug fixes, this is probably what is going to Linus.
> 
> I verified it builds no just on sparc64 but also even on 32-bit
> platforms such as x86. :-)
> 
> Thanks!

Output of checkpatch.pl for your patch:
CHECK: Use #include <linux/io.h> instead of <asm/io.h>
#114: FILE: drivers/net/niu.c:26:
+#include <asm/io.h>

ERROR: trailing whitespace
#632: FILE: drivers/net/niu.c:544:
+^I$

WARNING: braces {} are not necessary for single statement blocks
#691: FILE: drivers/net/niu.c:603:
+	if (lp->loopback_mode == LOOPBACK_PHY) {
+		test_cfg_val |= ((ENET_TEST_MD_PAD_LOOPBACK <<
+				  ENET_SERDES_TEST_MD_0_SHIFT) |
+				 (ENET_TEST_MD_PAD_LOOPBACK <<
+				  ENET_SERDES_TEST_MD_1_SHIFT) |
+				 (ENET_TEST_MD_PAD_LOOPBACK <<
+				  ENET_SERDES_TEST_MD_2_SHIFT) |
+				 (ENET_TEST_MD_PAD_LOOPBACK <<
+				  ENET_SERDES_TEST_MD_3_SHIFT));
+	}

CHECK: if this code is redundant consider removing it
#1109: FILE: drivers/net/niu.c:1021:
+#if 0

CHECK: if this code is redundant consider removing it
#1665: FILE: drivers/net/niu.c:1577:
+#if 0

CHECK: if this code is redundant consider removing it
#1703: FILE: drivers/net/niu.c:1615:
+#if 0

CHECK: if this code is redundant consider removing it
#1773: FILE: drivers/net/niu.c:1685:
+#if 0

CHECK: if this code is redundant consider removing it
#1816: FILE: drivers/net/niu.c:1728:
+#if 0

CHECK: if this code is redundant consider removing it
#1887: FILE: drivers/net/niu.c:1799:
+#if 0

ERROR: trailing whitespace
#1924: FILE: drivers/net/niu.c:1836:
+^I	       $

WARNING: multiple assignments should be avoided
#2442: FILE: drivers/net/niu.c:2354:
+	tmp = pkt_cnt = (cs & TX_CS_PKT_CNT) >> TX_CS_PKT_CNT_SHIFT;

CHECK: memory barrier without comment
#2457: FILE: drivers/net/niu.c:2369:
+	smp_mb();

WARNING: multiple assignments should be avoided
#2489: FILE: drivers/net/niu.c:2401:
+	rcr_done = work_done = 0;

WARNING: multiple assignments should be avoided
#3254: FILE: drivers/net/niu.c:3166:
+	first_rx_channel = first_tx_channel = 0;

ERROR: trailing whitespace
#4241: FILE: drivers/net/niu.c:4153:
+^Ival &= ~(XMAC_MIN_TX_MIN_PKT_SIZE | $

WARNING: braces {} are not necessary for single statement blocks
#4649: FILE: drivers/net/niu.c:4561:
+	if (limit < 0 &&
+	    (rd != 0 && wr != 1)) {
+		printk(KERN_ERR PFX "%s: IPP would not quiesce, "
+		       "rd_ptr[%llx] wr_ptr[%llx]\n",
+		       np->dev->name,
+		       (unsigned long long) nr64_ipp(IPP_DFIFO_RD_PTR),
+		       (unsigned long long) nr64_ipp(IPP_DFIFO_WR_PTR));
+	}

WARNING: multiple assignments should be avoided
#4950: FILE: drivers/net/niu.c:4862:
+	pkts = dropped = errors = bytes = 0;

WARNING: multiple assignments should be avoided
#4970: FILE: drivers/net/niu.c:4882:
+	pkts = errors = bytes = 0;

WARNING: multiple assignments should be avoided
#5207: FILE: drivers/net/niu.c:5119:
+	ipv6 = ihl = 0;

WARNING: multiple assignments should be avoided
#5219: FILE: drivers/net/niu.c:5131:
+		ip_proto = ihl = 0;

WARNING: multiple assignments should be avoided
#5342: FILE: drivers/net/niu.c:5254:
+		mrk = nfg = 0;

ERROR: use tabs not spaces
#5458: FILE: drivers/net/niu.c:5370:
+  ^Istruct niu *np = netdev_priv(dev);$

WARNING: multiple assignments should be avoided
#5512: FILE: drivers/net/niu.c:5424:
+		len = eeprom->len = np->eeprom_len - offset;

WARNING: line over 80 characters
#5786: FILE: drivers/net/niu.c:5698:
+		 * the firmware, and we're not supposed to change them.  Validate

WARNING: line over 80 characters
#5791: FILE: drivers/net/niu.c:5703:
+			printk(KERN_ERR PFX "Port %u, mis-matched LDG assignment "

WARNING: braces {} are not necessary for single statement blocks
#5890: FILE: drivers/net/niu.c:5802:
+	if (err < 0) {
+		return err;
+	}

WARNING: braces {} are not necessary for single statement blocks
#5895: FILE: drivers/net/niu.c:5807:
+	if (err < 0) {
+		return err;
+	}

ERROR: trailing whitespace
#5969: FILE: drivers/net/niu.c:5881:
+^I	$

WARNING: line over 80 characters
#6129: FILE: drivers/net/niu.c:6041:
+static int __devinit niu_phy_type_prop_decode(struct niu *np, const char *phy_prop)

WARNING: printk() should include KERN_ facility level
#6297: FILE: drivers/net/niu.c:6209:
+			printk("%02x ", dev->perm_addr[i]);

WARNING: multiple assignments should be avoided
#6481: FILE: drivers/net/niu.c:6393:
+		rx_chans_per_10g = rx_chans_per_1g =

WARNING: multiple assignments should be avoided
#6483: FILE: drivers/net/niu.c:6395:
+		tx_chans_per_10g = tx_chans_per_1g =

WARNING: multiple assignments should be avoided
#6497: FILE: drivers/net/niu.c:6409:
+	tot_rx = tot_tx = 0;

WARNING: braces {} are not necessary for single statement blocks
#6531: FILE: drivers/net/niu.c:6443:
+	if (tot_rx < NIU_NUM_RXCHAN || tot_tx < NIU_NUM_TXCHAN) {
+		printk(KERN_WARNING PFX "niu%d: Driver bug, wasted channels, "
+		       "RX[%d] TX[%d]\n",
+		       parent->index, tot_rx, tot_tx);
+	}

WARNING: multiple assignments should be avoided
#6777: FILE: drivers/net/niu.c:6689:
+	lp->speed = lp->active_speed = SPEED_INVALID;

WARNING: multiple assignments should be avoided
#6778: FILE: drivers/net/niu.c:6690:
+	lp->duplex = lp->active_duplex = DUPLEX_INVALID;

CHECK: if this code is redundant consider removing it
#6779: FILE: drivers/net/niu.c:6691:
+#if 0

WARNING: line over 80 characters
#7048: FILE: drivers/net/niu.c:6960:
+		printk(KERN_ERR PFX "%s: OF node lacks local-mac-address property\n",

WARNING: braces {} are not necessary for single statement blocks
#7052: FILE: drivers/net/niu.c:6964:
+	if (prop_len != dev->addr_len) {
+		printk(KERN_ERR PFX "%s: OF MAC address prop len (%d) is wrong.\n",
+		       dp->full_name, prop_len);
+	}

WARNING: line over 80 characters
#7053: FILE: drivers/net/niu.c:6965:
+		printk(KERN_ERR PFX "%s: OF MAC address prop len (%d) is wrong.\n",

WARNING: printk() should include KERN_ facility level
#7065: FILE: drivers/net/niu.c:6977:
+			printk("%02x ", dev->perm_addr[i]);

WARNING: braces {} are not necessary for single statement blocks
#7209: FILE: drivers/net/niu.c:7121:
+	for (i = 0; i < p->num_ports; i++) {
+		buf += sprintf(buf,
+			       (i == 0) ? "%d" : " %d",
+			       arr[i]);
+	}

ERROR: "foo * bar" should be "foo *bar"
#7249: FILE: drivers/net/niu.c:7161:
+static struct niu_parent * __devinit niu_new_parent(struct niu *np,

ERROR: "foo * bar" should be "foo *bar"
#7316: FILE: drivers/net/niu.c:7228:
+static struct niu_parent * __devinit niu_get_parent(struct niu *np,

ERROR: do not initialise statics to 0 or NULL
#7438: FILE: drivers/net/niu.c:7350:
+	static int niu_version_printed = 0;

ERROR: "foo * bar" should be "foo *bar"
#7444: FILE: drivers/net/niu.c:7356:
+static struct net_device * __devinit niu_alloc_and_init(struct device *gen_dev,

WARNING: line over 80 characters
#7447: FILE: drivers/net/niu.c:7359:
+							const struct niu_ops *ops,

WARNING: line over 80 characters
#7819: FILE: drivers/net/niu.c:7731:
+static int __devinit niu_of_probe(struct of_device *op, const struct of_device_id *match)

ERROR: trailing whitespace
#8318: FILE: drivers/net/niu.h:284:
+#define XMAC_ADDR2^I		0x000b0UL $

ERROR: need space after that ',' (ctx:VxV)
#9610: FILE: drivers/net/niu.h:1576:
+#define RDC_TBL(TBL,SLOT)		(FZC_ZCP + 0x10000UL + \
                    ^

WARNING: line over 80 characters
#10002: FILE: drivers/net/niu.h:1968:
+#define  RXDMA_CFIG1_MBADDR_H		0x0000000000000fffULL /* mboxaddr 43:32 */

WARNING: line over 80 characters
#10005: FILE: drivers/net/niu.h:1971:
+#define  RXDMA_CFIG2_MBADDR_L		0x00000000ffffffc0ULL /* mboxaddr 31:6 */

CHECK: spinlock_t definition without comment
#11039: FILE: drivers/net/niu.h:3005:
+	spinlock_t		lock;

WARNING: line over 80 characters
#11203: FILE: drivers/net/niu.h:3169:
+#define NIU_FLAGS_MCAST			0x00200000 /* multicast filter enabled */

Your patch has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.

^ permalink raw reply

* Re: [PATCH]: Third (final?) release of Sun Neptune driver
From: Stephen Hemminger @ 2007-10-05 15:39 UTC (permalink / raw)
  To: David Miller
  Cc: netdev, Ariel.Hendel, greg.onufer, jeff, Ashley.Saulsbury,
	Matheos.Worku
In-Reply-To: <20071005.031209.57156822.davem@davemloft.net>

> I just checked the following into net-2.6.24, besides minor
> tweaks and bug fixes, this is probably what is going to Linus.
> 
> I verified it builds no just on sparc64 but also even on 32-bit
> platforms such as x86. :-)
> 
> Thanks!

Build failed on x86_64

ERROR: "__bad_udelay" [drivers/net/niu.ko] undefined!

^ permalink raw reply

* Re: [PATCH]: Third (final?) release of Sun Neptune driver
From: Stephen Hemminger @ 2007-10-05 15:35 UTC (permalink / raw)
  To: David Miller
  Cc: netdev, Ariel.Hendel, greg.onufer, jeff, Ashley.Saulsbury,
	Matheos.Worku
In-Reply-To: <20071005.031209.57156822.davem@davemloft.net>

On Fri, 05 Oct 2007 03:12:09 -0700 (PDT)
David Miller <davem@davemloft.net> wrote:

> 
> I just checked the following into net-2.6.24, besides minor
> tweaks and bug fixes, this is probably what is going to Linus.
> 
> I verified it builds no just on sparc64 but also even on 32-bit
> platforms such as x86. :-)
> 
> Thanks!

Looks good, minor stuff...
Surprisingly low number of comments. Which is okay by me, but others
might find it harder. Perhaps some explanation of the queue stuff and
other hardware surprises would be useful.
 
> commit 147fd2f057af893f63df5bad497bb60ac3abbaab
> Author: David S. Miller <davem@sunset.davemloft.net>
> Date:   Fri Oct 5 02:48:00 2007 -0700
> 
>     [NIU]: Add Sun Neptune ethernet driver.
>     
>     Signed-off-by: David S. Miller <davem@davemloft.net>
> 
> diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
> index 0e48b29..9c635a2 100644
> --- a/drivers/net/Kconfig
> +++ b/drivers/net/Kconfig
> @@ -2594,6 +2594,13 @@ config NETXEN_NIC
>  	help
>  	  This enables the support for NetXen's Gigabit Ethernet card.
>  
> +config NIU
> +	tristate "Sun Neptune 10Gbit Ethernet support"
> +	depends on PCI
> +	help
> +	  This enables support for cards based upon Sun's
> +	  Neptune chipset.
> +
>  config PASEMI_MAC
>  	tristate "PA Semi 1/10Gbit MAC"
>  	depends on PPC64 && PCI
> diff --git a/drivers/net/Makefile b/drivers/net/Makefile
> index eb5c655..d2e0f35 100644
> --- a/drivers/net/Makefile
> +++ b/drivers/net/Makefile
> @@ -241,3 +241,4 @@ obj-$(CONFIG_NETCONSOLE) += netconsole.o
>  obj-$(CONFIG_FS_ENET) += fs_enet/
>  
>  obj-$(CONFIG_NETXEN_NIC) += netxen/
> +obj-$(CONFIG_NIU) += niu.o
> diff --git a/drivers/net/niu.c b/drivers/net/niu.c
> new file mode 100644
> index 0000000..0dfabbe
> --- /dev/null
> +++ b/drivers/net/niu.c
> @@ -0,0 +1,7940 @@
> +/* niu.c: Neptune ethernet driver.
> + *
> + * Copyright (C) 2007 David S. Miller (davem@davemloft.net)
> + */
> +
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/pci.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/netdevice.h>
> +#include <linux/ethtool.h>
> +#include <linux/etherdevice.h>
> +#include <linux/platform_device.h>
> +#include <linux/delay.h>
> +#include <linux/bitops.h>
> +#include <linux/mii.h>
> +#include <linux/if_ether.h>
> +#include <linux/if_vlan.h>
> +#include <linux/ip.h>
> +#include <linux/in.h>
> +#include <linux/ipv6.h>
> +#include <linux/log2.h>
> +#include <linux/jiffies.h>
> +#include <linux/crc32.h>
> +
> +#include <asm/io.h>
> +
> +#ifdef CONFIG_SPARC64
> +#include <linux/of_device.h>
> +#endif
> +
> +#include "niu.h"
> +
> +#define DRV_MODULE_NAME		"niu"
> +#define PFX DRV_MODULE_NAME	": "
> +#define DRV_MODULE_VERSION	"0.5"
> +#define DRV_MODULE_RELDATE	"October 5, 2007"
> +
> +static char version[] __devinitdata =
> +	DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";
> +
> +MODULE_AUTHOR("David S. Miller (davem@davemloft.net)");
> +MODULE_DESCRIPTION("NIU ethernet driver");
> +MODULE_LICENSE("GPL");
> +MODULE_VERSION(DRV_MODULE_VERSION);
> +
> +#ifndef DMA_44BIT_MASK
> +#define DMA_44BIT_MASK	0x00000fffffffffffULL
> +#endif
> +
> +#ifndef readq
> +static u64 readq(void __iomem *reg)
> +{
> +	return (((u64)readl(reg + 0x4UL) << 32) |
> +		(u64)readl(reg));
> +}
> +
> +static void writeq(u64 val, void __iomem *reg)
> +{
> +	writel(val & 0xffffffff, reg);
> +	writel(val >> 32, reg + 0x4UL);
> +}
> +#endif
> +
> +static struct pci_device_id niu_pci_tbl[] = {
> +	{PCI_DEVICE(PCI_VENDOR_ID_SUN, 0xabcd)},
> +	{}
> +};
> +
> +MODULE_DEVICE_TABLE(pci, niu_pci_tbl);
> +
> +#define NIU_TX_TIMEOUT			(5 * HZ)
> +
> +#define nr64(reg)		readq(np->regs + (reg))
> +#define nw64(reg, val)		writeq((val), np->regs + (reg))
> +
> +#define nr64_mac(reg)		readq(np->mac_regs + (reg))
> +#define nw64_mac(reg, val)	writeq((val), np->mac_regs + (reg))
> +
> +#define nr64_ipp(reg)		readq(np->regs + np->ipp_off + (reg))
> +#define nw64_ipp(reg, val)	writeq((val), np->regs + np->ipp_off + (reg))
> +
> +#define nr64_pcs(reg)		readq(np->regs + np->pcs_off + (reg))
> +#define nw64_pcs(reg, val)	writeq((val), np->regs + np->pcs_off + (reg))
> +
> +#define nr64_xpcs(reg)		readq(np->regs + np->xpcs_off + (reg))
> +#define nw64_xpcs(reg, val)	writeq((val), np->regs + np->xpcs_off + (reg))
> +
> +static unsigned int niu_debug;
> +#define NIU_DEBUG_INTERRUPT	0x00000001
> +#define NIU_DEBUG_TX_WORK	0x00000002
> +#define NIU_DEBUG_RX_WORK	0x00000004
> +#define NIU_DEBUG_POLL		0x00000008
> +#define NIU_DEBUG_PROBE		0x00010000
> +#define NIU_DEBUG_MDIO		0x00020000
> +#define NIU_DEBUG_MII		0x00040000
> +#define NIU_DEBUG_INIT_HW	0x00080000
> +#define NIU_DEBUG_STOP_HW	0x00100000

These match with existing netif_msg_XXX and your
driver already has msglvl ethtool hook.

> +
> +module_param(niu_debug, int, 0);
> +MODULE_PARM_DESC(niu_debug,
> +"NIU bitmapped debugging message enable value:\n"
> +"	0x00000001	Log interrupt events\n"
> +"	0x00000002	Log TX work\n"
> +"	0x00000004	Log RX work\n"
> +"	0x00000008	Log NAPI poll\n"
> +"	0x00010000	Log device probe events\n"
> +"	0x00020000	Log MDIO reads and writes\n"
> +"	0x00040000	Log MII reads and writes\n"
> +"	0x00080000	Log HW initialization\n"
> +"	0x00100000	Log HW shutdown\n"
> +);
> +
> +#define niudbg(TYPE, f, a...) \
> +do {	if (niu_debug & NIU_DEBUG_##TYPE) \
> +		printk(KERN_ERR PFX f, ## a); \
Do you really want to print all these at KERN_ERR level?

> +} while (0)
> +
> +#define niu_lock_parent(np, flags) \
> +	spin_lock_irqsave(&np->parent->lock, flags)
> +#define niu_unlock_parent(np, flags) \
> +	spin_unlock_irqrestore(&np->parent->lock, flags)
> +
> +static int __niu_wait_bits_clear_mac(struct niu *np, unsigned long reg,
> +				     u64 bits, int limit, int delay)
> +{
> +	while (--limit >= 0) {
> +		u64 val = nr64_mac(reg);
> +
> +		if (!(val & bits))
> +			break;
> +		udelay(delay);
> +	}
> +	if (limit < 0)
> +		return -ENODEV;
> +	return 0;
> +}
> +
> +static int __niu_set_and_wait_clear_mac(struct niu *np, unsigned long reg,
> +					u64 bits, int limit, int delay,
> +					const char *reg_name)
> +{
> +	int err;
> +
> +	nw64_mac(reg, bits);
> +	err = __niu_wait_bits_clear_mac(np, reg, bits, limit, delay);
> +	if (err)
> +		printk(KERN_ERR PFX "%s: bits (%llx) of register %s "
> +		       "would not clear, val[%llx]\n",
> +		       np->dev->name, (unsigned long long) bits, reg_name,
> +		       (unsigned long long) nr64_mac(reg));
> +	return err;
> +}
> +
> +#define niu_set_and_wait_clear_mac(NP, REG, BITS, LIMIT, DELAY, REG_NAME) \
> +({	BUILD_BUG_ON(LIMIT <= 0 || DELAY < 0); \
> +	__niu_set_and_wait_clear_mac(NP, REG, BITS, LIMIT, DELAY, REG_NAME); \
> +})
> +
> +static int __niu_wait_bits_clear_ipp(struct niu *np, unsigned long reg,
> +				     u64 bits, int limit, int delay)
> +{
> +	while (--limit >= 0) {
> +		u64 val = nr64_ipp(reg);
> +
> +		if (!(val & bits))
> +			break;
> +		udelay(delay);
> +	}
> +	if (limit < 0)
> +		return -ENODEV;
> +	return 0;
> +}
> +
> +static int __niu_set_and_wait_clear_ipp(struct niu *np, unsigned long reg,
> +					u64 bits, int limit, int delay,
> +					const char *reg_name)
> +{
> +	int err;
> +	u64 val;
> +
> +	val = nr64_ipp(reg);
> +	val |= bits;
> +	nw64_ipp(reg, val);
> +
> +	err = __niu_wait_bits_clear_ipp(np, reg, bits, limit, delay);
> +	if (err)
> +		printk(KERN_ERR PFX "%s: bits (%llx) of register %s "
> +		       "would not clear, val[%llx]\n",
> +		       np->dev->name, (unsigned long long) bits, reg_name,
> +		       (unsigned long long) nr64_ipp(reg));
> +	return err;
> +}
> +
> +#define niu_set_and_wait_clear_ipp(NP, REG, BITS, LIMIT, DELAY, REG_NAME) \
> +({	BUILD_BUG_ON(LIMIT <= 0 || DELAY < 0); \
> +	__niu_set_and_wait_clear_ipp(NP, REG, BITS, LIMIT, DELAY, REG_NAME); \
> +})
> +
> +static int __niu_wait_bits_clear(struct niu *np, unsigned long reg,
> +				 u64 bits, int limit, int delay)
> +{
> +	BUILD_BUG_ON(limit <= 0 || delay < 0);

redundant you already checked

> +	while (--limit >= 0) {
> +		u64 val = nr64(reg);
> +
> +		if (!(val & bits))
> +			break;
> +		udelay(delay);
> +	}
> +	if (limit < 0)
> +		return -ENODEV;
> +	return 0;
> +}
> +
> +#define niu_wait_bits_clear(NP, REG, BITS, LIMIT, DELAY) \
> +({	BUILD_BUG_ON(LIMIT <= 0 || DELAY < 0); \
> +	__niu_wait_bits_clear(NP, REG, BITS, LIMIT, DELAY); \
> +})
> +
> +static int __niu_set_and_wait_clear(struct niu *np, unsigned long reg,
> +				    u64 bits, int limit, int delay,
> +				    const char *reg_name)
> +{
> +	int err;
> +
> +	nw64(reg, bits);
> +	err = __niu_wait_bits_clear(np, reg, bits, limit, delay);
> +	if (err)
> +		printk(KERN_ERR PFX "%s: bits (%llx) of register %s "
> +		       "would not clear, val[%llx]\n",
> +		       np->dev->name, (unsigned long long) bits, reg_name,
> +		       (unsigned long long) nr64(reg));
> +	return err;
> +}
> +
> +#define niu_set_and_wait_clear(NP, REG, BITS, LIMIT, DELAY, REG_NAME) \
> +({	BUILD_BUG_ON(LIMIT <= 0 || DELAY < 0); \
> +	__niu_set_and_wait_clear(NP, REG, BITS, LIMIT, DELAY, REG_NAME); \
> +})
> +
> +static void niu_ldg_rearm(struct niu *np, struct niu_ldg *lp, int on)
> +{
> +	u64 val = (u64) lp->timer;
> +
> +	if (on)
> +		val |= LDG_IMGMT_ARM;
> +
> +	nw64(LDG_IMGMT(lp->ldg_num), val);
> +}
> +
> +static int niu_ldn_irq_enable(struct niu *np, int ldn, int on)
> +{
> +	unsigned long mask_reg, bits;
> +	u64 val;
> +
> +	if (ldn < 0 || ldn > LDN_MAX)
> +		return -EINVAL;
> +
> +	if (ldn < 64) {
> +		mask_reg = LD_IM0(ldn);
> +		bits = LD_IM0_MASK;
> +	} else {
> +		mask_reg = LD_IM1(ldn - 64);
> +		bits = LD_IM1_MASK;
> +	}
> +
> +	val = nr64(mask_reg);
> +	if (on)
> +		val &= ~bits;
> +	else
> +		val |= bits;
> +	nw64(mask_reg, val);
> +
> +	return 0;
> +}
> +
> +static int niu_enable_ldn_in_ldg(struct niu *np, struct niu_ldg *lp, int on)
> +{
> +	struct niu_parent *parent = np->parent;
> +	int i;
> +
> +	for (i = 0; i <= LDN_MAX; i++) {
> +		int err;
> +
> +		if (parent->ldg_map[i] != lp->ldg_num)
> +			continue;
> +
> +		err = niu_ldn_irq_enable(np, i, on);
> +		if (err)
> +			return err;
> +	}
> +	return 0;
> +}
> +
> +static int niu_enable_interrupts(struct niu *np, int on)
> +{
> +	int i;
> +
> +	for (i = 0; i < np->num_ldg; i++) {
> +		struct niu_ldg *lp = &np->ldg[i];
> +		int err;
> +
> +		err = niu_enable_ldn_in_ldg(np, lp, on);
> +		if (err)
> +			return err;
> +	}
> +	for (i = 0; i < np->num_ldg; i++)
> +		niu_ldg_rearm(np, &np->ldg[i], on);
> +
> +	return 0;
> +}
> +
> +static u32 phy_encode(u32 type, int port)
> +{
> +	return (type << (port * 2));
> +}
> +
> +static u32 phy_decode(u32 val, int port)
> +{
> +	return (val >> (port * 2)) & PORT_TYPE_MASK;
> +}
> +
> +static int mdio_wait(struct niu *np)
> +{
> +	int limit = 1000;
> +	u64 val;
> +
> +	while (--limit > 0) {
> +		val = nr64(MIF_FRAME_OUTPUT);
> +		if ((val >> MIF_FRAME_OUTPUT_TA_SHIFT) & 0x1)
> +			return val & MIF_FRAME_OUTPUT_DATA;
> +
> +		udelay(10);
> +	}
> +
> +	return -ENODEV;
> +}
> +
> +static int mdio_read(struct niu *np, int port, int dev, int reg)
> +{
> +	int err;
> +
> +	nw64(MIF_FRAME_OUTPUT, MDIO_ADDR_OP(port, dev, reg));
> +	err = mdio_wait(np);
> +	if (err < 0)
> +		return err;
> +
> +	nw64(MIF_FRAME_OUTPUT, MDIO_READ_OP(port, dev));
> +	return mdio_wait(np);
> +}
> +
> +static int mdio_write(struct niu *np, int port, int dev, int reg, int data)
> +{
> +	int err;
> +
> +	nw64(MIF_FRAME_OUTPUT, MDIO_ADDR_OP(port, dev, reg));
> +	err = mdio_wait(np);
> +	if (err < 0)
> +		return err;
> +
> +	nw64(MIF_FRAME_OUTPUT, MDIO_WRITE_OP(port, dev, data));
> +	err = mdio_wait(np);
> +	if (err < 0)
> +		return err;
> +
> +	return 0;
> +}
> +
> +static int mii_read(struct niu *np, int port, int reg)
> +{
> +	nw64(MIF_FRAME_OUTPUT, MII_READ_OP(port, reg));
> +	return mdio_wait(np);
> +}
> +
> +static int mii_write(struct niu *np, int port, int reg, int data)
> +{
> +	int err;
> +
> +	nw64(MIF_FRAME_OUTPUT, MII_WRITE_OP(port, reg, data));
> +	err = mdio_wait(np);
> +	if (err < 0)
> +		return err;
> +
> +	return 0;
> +}
> +
> +static int esr2_set_tx_cfg(struct niu *np, unsigned long channel, u32 val)
> +{
> +	int err;
> +
> +	err = mdio_write(np, np->port, NIU_ESR2_DEV_ADDR,
> +			 ESR2_TI_PLL_TX_CFG_L(channel),
> +			 val & 0xffff);
> +	if (!err)
> +		err = mdio_write(np, np->port, NIU_ESR2_DEV_ADDR,
> +				 ESR2_TI_PLL_TX_CFG_H(channel),
> +				 val >> 16);
> +	return err;
> +}
> +
> +static int esr2_set_rx_cfg(struct niu *np, unsigned long channel, u32 val)
> +{
> +	int err;
> +
> +	err = mdio_write(np, np->port, NIU_ESR2_DEV_ADDR,
> +			 ESR2_TI_PLL_RX_CFG_L(channel),
> +			 val & 0xffff);
> +	if (!err)
> +		err = mdio_write(np, np->port, NIU_ESR2_DEV_ADDR,
> +				 ESR2_TI_PLL_RX_CFG_H(channel),
> +				 val >> 16);
> +	return err;
> +}
> +
> +/* Mode is always 10G fiber.  */
> +static int serdes_init_niu(struct niu *np)
> +{
> +	struct niu_link_config *lp = &np->link_config;
> +	u32 tx_cfg, rx_cfg;
> +	unsigned long i;
> +
> +	tx_cfg = (PLL_TX_CFG_ENTX | PLL_TX_CFG_SWING_1375MV);
> +	rx_cfg = (PLL_RX_CFG_ENRX | PLL_RX_CFG_TERM_0P8VDDT |
> +		  PLL_RX_CFG_ALIGN_ENA | PLL_RX_CFG_LOS_LTHRESH |
> +		  PLL_RX_CFG_EQ_LP_ADAPTIVE);
> +
> +	if (lp->loopback_mode == LOOPBACK_PHY) {
> +		u16 test_cfg = PLL_TEST_CFG_LOOPBACK_CML_DIS;
> +
> +		mdio_write(np, np->port, NIU_ESR2_DEV_ADDR,
> +			   ESR2_TI_PLL_TEST_CFG_L, test_cfg);
> +
> +		tx_cfg |= PLL_TX_CFG_ENTEST;
> +		rx_cfg |= PLL_RX_CFG_ENTEST;
> +	}
> +
> +	for (i = 0; i < 4; i++) {
> +		int err = esr2_set_tx_cfg(np, i, tx_cfg);
> +		if (err)
> +			return err;
> +	}
> +
> +	for (i = 0; i < 4; i++) {
> +		int err = esr2_set_rx_cfg(np, i, rx_cfg);
> +		if (err)
> +			return err;
> +	}
> +
> +	return 0;
> +}
> +
> +static int esr_read_rxtx_ctrl(struct niu *np, unsigned long chan, u32 *val)
> +{
> +	int err;
> +
> +	err = mdio_read(np, np->port, NIU_ESR_DEV_ADDR, ESR_RXTX_CTRL_L(chan));
> +	if (err >= 0) {
> +		*val = (err & 0xffff);
> +		err = mdio_read(np, np->port, NIU_ESR_DEV_ADDR,
> +				ESR_RXTX_CTRL_H(chan));
> +		if (err >= 0)
> +			*val |= ((err & 0xffff) << 16);
> +		err = 0;
> +	}
> +	return err;
> +}
> +
> +static int esr_read_glue0(struct niu *np, unsigned long chan, u32 *val)
> +{
> +	int err;
> +
> +	err = mdio_read(np, np->port, NIU_ESR_DEV_ADDR,
> +			ESR_GLUE_CTRL0_L(chan));
> +	if (err >= 0) {
> +		*val = (err & 0xffff);
> +		err = mdio_read(np, np->port, NIU_ESR_DEV_ADDR,
> +				ESR_GLUE_CTRL0_H(chan));
> +		if (err >= 0) {
> +			*val |= ((err & 0xffff) << 16);
> +			err = 0;
> +		}
> +	}
> +	return err;
> +}
> +
> +static int esr_read_reset(struct niu *np, u32 *val)
> +{
> +	int err;
> +
> +	err = mdio_read(np, np->port, NIU_ESR_DEV_ADDR,
> +			ESR_RXTX_RESET_CTRL_L);
> +	if (err >= 0) {
> +		*val = (err & 0xffff);
> +		err = mdio_read(np, np->port, NIU_ESR_DEV_ADDR,
> +				ESR_RXTX_RESET_CTRL_H);
> +		if (err >= 0) {
> +			*val |= ((err & 0xffff) << 16);
> +			err = 0;
> +		}
> +	}
> +	return err;
> +}
> +
> +static int esr_write_rxtx_ctrl(struct niu *np, unsigned long chan, u32 val)
> +{
> +	int err;
> +
> +	err = mdio_write(np, np->port, NIU_ESR_DEV_ADDR,
> +			 ESR_RXTX_CTRL_L(chan), val & 0xffff);
> +	if (!err)
> +		err = mdio_write(np, np->port, NIU_ESR_DEV_ADDR,
> +				 ESR_RXTX_CTRL_H(chan), (val >> 16));
> +	return err;
> +}
> +
> +static int esr_write_glue0(struct niu *np, unsigned long chan, u32 val)
> +{
> +	int err;
> +
> +	err = mdio_write(np, np->port, NIU_ESR_DEV_ADDR,
> +			ESR_GLUE_CTRL0_L(chan), val & 0xffff);
> +	if (!err)
> +		err = mdio_write(np, np->port, NIU_ESR_DEV_ADDR,
> +				 ESR_GLUE_CTRL0_H(chan), (val >> 16));
> +	return err;
> +}
> +
> +static int esr_reset(struct niu *np)
> +{
> +	u32 reset;
> +	int err;
> +
> +	err = mdio_write(np, np->port, NIU_ESR_DEV_ADDR,
> +			 ESR_RXTX_RESET_CTRL_L, 0x0000);
> +	if (err)
> +		return err;
> +	err = mdio_write(np, np->port, NIU_ESR_DEV_ADDR,
> +			 ESR_RXTX_RESET_CTRL_H, 0xffff);
> +	if (err)
> +		return err;
> +	udelay(200);
> +
> +	
> +	err = mdio_write(np, np->port, NIU_ESR_DEV_ADDR,
> +			 ESR_RXTX_RESET_CTRL_L, 0xffff);
> +	if (err)
> +		return err;
> +	udelay(200);
> +
> +	err = mdio_write(np, np->port, NIU_ESR_DEV_ADDR,
> +			 ESR_RXTX_RESET_CTRL_H, 0x0000);
> +	if (err)
> +		return err;
> +	udelay(200);
> +
> +	err = esr_read_reset(np, &reset);
> +	if (err)
> +		return err;
> +	if (reset != 0) {
> +		printk(KERN_ERR PFX "Port %u ESR_RESET did not clear [%08x]\n",
> +		       np->port, reset);
> +		return -ENODEV;
> +	}
> +
> +	return 0;
> +}
> +
> +static int serdes_init_10g(struct niu *np)
> +{
> +	struct niu_link_config *lp = &np->link_config;
> +	unsigned long ctrl_reg, test_cfg_reg, i;
> +	u64 ctrl_val, test_cfg_val, sig, mask, val;
> +	int err;
> +
> +	switch (np->port) {
> +	case 0:
> +		ctrl_reg = ENET_SERDES_0_CTRL_CFG;
> +		test_cfg_reg = ENET_SERDES_0_TEST_CFG;
> +		break;
> +	case 1:
> +		ctrl_reg = ENET_SERDES_1_CTRL_CFG;
> +		test_cfg_reg = ENET_SERDES_1_TEST_CFG;
> +		break;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +	ctrl_val = (ENET_SERDES_CTRL_SDET_0 |
> +		    ENET_SERDES_CTRL_SDET_1 |
> +		    ENET_SERDES_CTRL_SDET_2 |
> +		    ENET_SERDES_CTRL_SDET_3 |
> +		    (0x5 << ENET_SERDES_CTRL_EMPH_0_SHIFT) |
> +		    (0x5 << ENET_SERDES_CTRL_EMPH_1_SHIFT) |
> +		    (0x5 << ENET_SERDES_CTRL_EMPH_2_SHIFT) |
> +		    (0x5 << ENET_SERDES_CTRL_EMPH_3_SHIFT) |
> +		    (0x1 << ENET_SERDES_CTRL_LADJ_0_SHIFT) |
> +		    (0x1 << ENET_SERDES_CTRL_LADJ_1_SHIFT) |
> +		    (0x1 << ENET_SERDES_CTRL_LADJ_2_SHIFT) |
> +		    (0x1 << ENET_SERDES_CTRL_LADJ_3_SHIFT));
> +	test_cfg_val = 0;
> +
> +	if (lp->loopback_mode == LOOPBACK_PHY) {
> +		test_cfg_val |= ((ENET_TEST_MD_PAD_LOOPBACK <<
> +				  ENET_SERDES_TEST_MD_0_SHIFT) |
> +				 (ENET_TEST_MD_PAD_LOOPBACK <<
> +				  ENET_SERDES_TEST_MD_1_SHIFT) |
> +				 (ENET_TEST_MD_PAD_LOOPBACK <<
> +				  ENET_SERDES_TEST_MD_2_SHIFT) |
> +				 (ENET_TEST_MD_PAD_LOOPBACK <<
> +				  ENET_SERDES_TEST_MD_3_SHIFT));
> +	}
> +
> +	nw64(ctrl_reg, ctrl_val);
> +	nw64(test_cfg_reg, test_cfg_val);
> +
> +	for (i = 0; i < 4; i++) {
> +		u32 rxtx_ctrl, glue0;
> +		int err;
> +
> +		err = esr_read_rxtx_ctrl(np, i, &rxtx_ctrl);
> +		if (err)
> +			return err;
> +		err = esr_read_glue0(np, i, &glue0);
> +		if (err)
> +			return err;
> +
> +		rxtx_ctrl &= ~(ESR_RXTX_CTRL_VMUXLO);
> +		rxtx_ctrl |= (ESR_RXTX_CTRL_ENSTRETCH |
> +			      (2 << ESR_RXTX_CTRL_VMUXLO_SHIFT));
> +
> +		glue0 &= ~(ESR_GLUE_CTRL0_SRATE |
> +			   ESR_GLUE_CTRL0_THCNT |
> +			   ESR_GLUE_CTRL0_BLTIME);
> +		glue0 |= (ESR_GLUE_CTRL0_RXLOSENAB |
> +			  (0xf << ESR_GLUE_CTRL0_SRATE_SHIFT) |
> +			  (0xff << ESR_GLUE_CTRL0_THCNT_SHIFT) |
> +			  (BLTIME_300_CYCLES <<
> +			   ESR_GLUE_CTRL0_BLTIME_SHIFT));
> +
> +		err = esr_write_rxtx_ctrl(np, i, rxtx_ctrl);
> +		if (err)
> +			return err;
> +		err = esr_write_glue0(np, i, glue0);
> +		if (err)
> +			return err;
> +	}
> +
> +	err = esr_reset(np);
> +	if (err)
> +		return err;
> +
> +	sig = nr64(ESR_INT_SIGNALS);
> +	switch (np->port) {
> +	case 0:
> +		mask = ESR_INT_SIGNALS_P0_BITS;
> +		val = (ESR_INT_SRDY0_P0 |
> +		       ESR_INT_DET0_P0 |
> +		       ESR_INT_XSRDY_P0 |
> +		       ESR_INT_XDP_P0_CH3 |
> +		       ESR_INT_XDP_P0_CH2 |
> +		       ESR_INT_XDP_P0_CH1 |
> +		       ESR_INT_XDP_P0_CH0);
> +		break;
> +
> +	case 1:
> +		mask = ESR_INT_SIGNALS_P1_BITS;
> +		val = (ESR_INT_SRDY0_P1 |
> +		       ESR_INT_DET0_P1 |
> +		       ESR_INT_XSRDY_P1 |
> +		       ESR_INT_XDP_P1_CH3 |
> +		       ESR_INT_XDP_P1_CH2 |
> +		       ESR_INT_XDP_P1_CH1 |
> +		       ESR_INT_XDP_P1_CH0);
> +		break;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	if ((sig & mask) != val) {
> +		printk(KERN_ERR PFX "Port %u signal bits [%08x] are not "
> +		       "[%08x]\n", np->port, (int) (sig & mask), (int) val);
> +		return -ENODEV;
> +	}
> +
> +	return 0;
> +}
> +
> +static int serdes_init_1g(struct niu *np)
> +{
> +	u64 val;
> +
> +	val = nr64(ENET_SERDES_1_PLL_CFG);
> +	val &= ~ENET_SERDES_PLL_FBDIV2;
> +	switch (np->port) {
> +	case 0:
> +		val |= ENET_SERDES_PLL_HRATE0;
> +		break;
> +	case 1:
> +		val |= ENET_SERDES_PLL_HRATE1;
> +		break;
> +	case 2:
> +		val |= ENET_SERDES_PLL_HRATE2;
> +		break;
> +	case 3:
> +		val |= ENET_SERDES_PLL_HRATE3;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +	nw64(ENET_SERDES_1_PLL_CFG, val);
> +
> +	return 0;
> +}
> +
> +static int bcm8704_reset(struct niu *np)
> +{
> +	int err, limit;
> +
> +	err = mdio_read(np, np->phy_addr,
> +			BCM8704_PHYXS_DEV_ADDR, MII_BMCR);
> +	if (err < 0)
> +		return err;
> +	err |= BMCR_RESET;
> +	err = mdio_write(np, np->phy_addr, BCM8704_PHYXS_DEV_ADDR,
> +			 MII_BMCR, err);
> +	if (err)
> +		return err;
> +
> +	limit = 1000;
> +	while (--limit >= 0) {
> +		err = mdio_read(np, np->phy_addr,
> +				BCM8704_PHYXS_DEV_ADDR, MII_BMCR);
> +		if (err < 0)
> +			return err;
> +		if (!(err & BMCR_RESET))
> +			break;
> +	}
> +	if (limit < 0) {
> +		printk(KERN_ERR PFX "Port %u PHY will not reset "
> +		       "(bmcr=%04x)\n", np->port, (err & 0xffff));
> +		return -ENODEV;
> +	}
> +	return 0;
> +}
> +
> +/* When written, certain PHY registers need to be read back twice
> + * in order for the bits to settle properly.
> + */
> +static int bcm8704_user_dev3_readback(struct niu *np, int reg)
> +{
> +	int err = mdio_read(np, np->phy_addr, BCM8704_USER_DEV3_ADDR, reg);
> +	if (err < 0)
> +		return err;
> +	err = mdio_read(np, np->phy_addr, BCM8704_USER_DEV3_ADDR, reg);
> +	if (err < 0)
> +		return err;
> +	return 0;
> +}
> +
> +static int bcm8704_init_user_dev3(struct niu *np)
> +{
> +	int err;
> +
> +	err = mdio_write(np, np->phy_addr,
> +			 BCM8704_USER_DEV3_ADDR, BCM8704_USER_CONTROL,
> +			 (USER_CONTROL_OPTXRST_LVL |
> +			  USER_CONTROL_OPBIASFLT_LVL |
> +			  USER_CONTROL_OBTMPFLT_LVL |
> +			  USER_CONTROL_OPPRFLT_LVL |
> +			  USER_CONTROL_OPTXFLT_LVL |
> +			  USER_CONTROL_OPRXLOS_LVL |
> +			  USER_CONTROL_OPRXFLT_LVL |
> +			  USER_CONTROL_OPTXON_LVL |
> +			  (0x3f << USER_CONTROL_RES1_SHIFT)));
> +	if (err)
> +		return err;
> +
> +	err = mdio_write(np, np->phy_addr,
> +			 BCM8704_USER_DEV3_ADDR, BCM8704_USER_PMD_TX_CONTROL,
> +			 (USER_PMD_TX_CTL_XFP_CLKEN |
> +			  (1 << USER_PMD_TX_CTL_TX_DAC_TXD_SH) |
> +			  (2 << USER_PMD_TX_CTL_TX_DAC_TXCK_SH) |
> +			  USER_PMD_TX_CTL_TSCK_LPWREN));
> +	if (err)
> +		return err;
> +
> +	err = bcm8704_user_dev3_readback(np, BCM8704_USER_CONTROL);
> +	if (err)
> +		return err;
> +	err = bcm8704_user_dev3_readback(np, BCM8704_USER_PMD_TX_CONTROL);
> +	if (err)
> +		return err;
> +
> +	err = mdio_read(np, np->phy_addr, BCM8704_USER_DEV3_ADDR,
> +			BCM8704_USER_OPT_DIGITAL_CTRL);
> +	if (err < 0)
> +		return err;
> +	err &= ~USER_ODIG_CTRL_GPIOS;
> +	err |= (0x3 << USER_ODIG_CTRL_GPIOS_SHIFT);
> +	err = mdio_write(np, np->phy_addr, BCM8704_USER_DEV3_ADDR,
> +			 BCM8704_USER_OPT_DIGITAL_CTRL, err);
> +	if (err)
> +		return err;
> +
> +	udelay(1000000);
> +
> +	return 0;
> +}
> +
> +static int xcvr_init_10g(struct niu *np)
> +{
> +	struct niu_link_config *lp = &np->link_config;
> +	u16 analog_stat0, tx_alarm_status;
> +	int err;
> +	u64 val;
> +
> +	val = nr64_mac(XMAC_CONFIG);
> +	val &= ~XMAC_CONFIG_LED_POLARITY;
> +	val |= XMAC_CONFIG_FORCE_LED_ON;
> +	nw64_mac(XMAC_CONFIG, val);
> +
> +	/* XXX shared resource, lock parent XXX */
> +	val = nr64(MIF_CONFIG);
> +	val |= MIF_CONFIG_INDIRECT_MODE;
> +	nw64(MIF_CONFIG, val);
> +
> +	err = bcm8704_reset(np);
> +	if (err)
> +		return err;
> +
> +	err = bcm8704_init_user_dev3(np);
> +	if (err)
> +		return err;
> +
> +	err = mdio_read(np, np->phy_addr, BCM8704_PCS_DEV_ADDR,
> +			MII_BMCR);
> +	if (err < 0)
> +		return err;
> +	err &= ~BMCR_LOOPBACK;
> +
> +	if (lp->loopback_mode == LOOPBACK_MAC)
> +		err |= BMCR_LOOPBACK;
> +
> +	err = mdio_write(np, np->phy_addr, BCM8704_PCS_DEV_ADDR,
> +			 MII_BMCR, err);
> +	if (err)
> +		return err;
> +
> +#if 1
> +	err = mdio_read(np, np->phy_addr, BCM8704_PMA_PMD_DEV_ADDR,
> +			MII_STAT1000);
> +	if (err < 0)
> +		return err;
> +	printk(KERN_INFO PFX "Port %u PMA_PMD(MII_STAT1000) [%04x]\n",
> +	       np->port, err);

pr_info() ?
> +
> +	err = mdio_read(np, np->phy_addr, BCM8704_USER_DEV3_ADDR, 0x20);
> +	if (err < 0)
> +		return err;
> +	printk(KERN_INFO PFX "Port %u USER_DEV3(0x20) [%04x]\n",
> +	       np->port, err);

pr_info() ?
> +
> +	err = mdio_read(np, np->phy_addr, BCM8704_PHYXS_DEV_ADDR,
> +			MII_NWAYTEST);
> +	if (err < 0)
> +		return err;
> +	printk(KERN_INFO PFX "Port %u PHYXS(MII_NWAYTEST) [%04x]\n",
> +	       np->port, err);
> +#endif
> +
> +	/* XXX dig this out it might not be so useful XXX */
> +	err = mdio_read(np, np->phy_addr, BCM8704_USER_DEV3_ADDR,
> +			BCM8704_USER_ANALOG_STATUS0);
> +	if (err < 0)
> +		return err;
> +	err = mdio_read(np, np->phy_addr, BCM8704_USER_DEV3_ADDR,
> +			BCM8704_USER_ANALOG_STATUS0);
> +	if (err < 0)
> +		return err;
> +	analog_stat0 = err;
> +
> +	err = mdio_read(np, np->phy_addr, BCM8704_USER_DEV3_ADDR,
> +			BCM8704_USER_TX_ALARM_STATUS);
> +	if (err < 0)
> +		return err;
> +	err = mdio_read(np, np->phy_addr, BCM8704_USER_DEV3_ADDR,
> +			BCM8704_USER_TX_ALARM_STATUS);
> +	if (err < 0)
> +		return err;
> +	tx_alarm_status = err;
> +
> +	if (analog_stat0 != 0x03fc) {
> +		if ((analog_stat0 == 0x43bc) && (tx_alarm_status != 0)) {
> +			printk(KERN_INFO PFX "Port %u cable not connected "
> +			       "or bad cable.\n", np->port);

pr_info() ?
> +		} else if (analog_stat0 == 0x639c) {
> +			printk(KERN_INFO PFX "Port %u optical module is bad "
> +			       "or missing.\n", np->port);

pr_info() ?
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int mii_reset(struct niu *np)
> +{
> +	int limit, err;
> +
> +	err = mii_write(np, np->phy_addr, MII_BMCR, BMCR_RESET);
> +
> +	limit = 1000;
> +	while (--limit >= 0) {
> +		err = mii_read(np, np->phy_addr, MII_BMCR);
> +		if (err < 0)
> +			return err;
> +		if (!(err & BMCR_RESET))
> +			break;
> +	}
> +	if (limit < 0) {
> +		printk(KERN_ERR PFX "Port %u MII would not reset, "
> +		       "bmcr[%04x]\n", np->port, err);
> +		return -ENODEV;
> +	}
> +
> +	err = mii_write(np, np->phy_addr, MII_BMCR, 0);
> +	if (err)
> +		return err;
> +
> +	return 0;
> +}
> +
> +static int mii_init_common(struct niu *np)
> +{
> +	struct niu_link_config *lp = &np->link_config;
> +	u16 bmcr, bmsr, adv, estat;
> +	int err;
> +
> +	err = mii_reset(np);
> +	if (err)
> +		return err;
> +
> +	err = mii_read(np, np->phy_addr, MII_BMSR);
> +	if (err < 0)
> +		return err;
> +	bmsr = err;
> +
> +	estat = 0;
> +	if (bmsr & BMSR_ESTATEN) {
> +		err = mii_read(np, np->phy_addr, MII_ESTATUS);
> +		if (err < 0)
> +			return err;
> +		estat = err;
> +	}
> +
> +	bmcr = 0;
> +
> +	if (lp->loopback_mode == LOOPBACK_MAC) {
> +		bmcr |= BMCR_LOOPBACK;
> +		bmcr &= ~BMCR_ANENABLE;
> +		if (lp->active_speed == SPEED_1000)
> +			bmcr |= BMCR_SPEED1000;
> +		if (lp->active_duplex == DUPLEX_FULL)
> +			bmcr |= BMCR_FULLDPLX;
> +	} else {
> +		bmcr &= ~BMCR_LOOPBACK;
> +	}
> +	if (lp->loopback_mode == LOOPBACK_PHY) {
> +		u16 aux;
> +
> +		aux = (BCM5464R_AUX_CTL_EXT_LB |
> +		       BCM5464R_AUX_CTL_WRITE_1);
> +		err = mii_write(np, np->phy_addr, BCM5464R_AUX_CTL, aux);
> +		if (err)
> +			return err;
> +	}
> +
> +	/* XXX configurable XXX */
> +	adv = 0;
> +	if (bmsr & BMSR_10HALF)
> +		adv |= ADVERTISE_10HALF;
> +	if (bmsr & BMSR_10FULL)
> +		adv |= ADVERTISE_10FULL;
> +	if (bmsr & BMSR_100HALF)
> +		adv |= ADVERTISE_100HALF;
> +	if (bmsr & BMSR_100FULL)
> +		adv |= ADVERTISE_100FULL;
> +	if (bmsr & BMSR_100BASE4)
> +		adv |= ADVERTISE_100BASE4;
> +	adv |= (ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM);
> +	err = mii_write(np, np->phy_addr, MII_ADVERTISE, adv);
> +	if (err)
> +		return err;
> +
> +	if (bmsr & BMSR_ESTATEN) {
> +		u16 ctrl1000;
> +
> +		ctrl1000 = (BCM5464R_CTRL1000_AS_MASTER |
> +			    BCM5464R_CTRL1000_ENABLE_AS_MASTER);
> +		if (estat & ESTATUS_1000_TFULL)
> +			ctrl1000 |= ADVERTISE_1000FULL;
> +		if (estat & ESTATUS_1000_THALF)
> +			ctrl1000 |= ADVERTISE_1000HALF;
> +		err = mii_write(np, np->phy_addr, MII_CTRL1000, ctrl1000);
> +		if (err)
> +			return err;
> +	}
> +	bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART);
> +
> +	err = mii_write(np, np->phy_addr, MII_BMCR, bmcr);
> +	if (err)
> +		return err;
> +
> +	err = mii_read(np, np->phy_addr, MII_BMCR);
> +	if (err < 0)
> +		return err;
> +	err = mii_read(np, np->phy_addr, MII_BMSR);
> +	if (err < 0)
> +		return err;
> +#if 0
> +	printk(KERN_ERR PFX "Port %u after MII init bmcr[%04x] bmsr[%04x]\n",
> +	       np->port, bmcr, bmsr);
> +#endif
> +
> +	return 0;
> +}
> +
> +static int xcvr_init_1g(struct niu *np)
> +{
> +	u64 val;
> +
> +	/* XXX shared resource, lock parent XXX */
> +	val = nr64(MIF_CONFIG);
> +	val &= ~MIF_CONFIG_INDIRECT_MODE;
> +	nw64(MIF_CONFIG, val);
> +
> +	return mii_init_common(np);
> +}
> +
> +static int niu_xcvr_init(struct niu *np)
> +{
> +	const struct niu_phy_ops *ops = np->phy_ops;
> +	int err;
> +
> +	err = 0;
> +	if (ops->xcvr_init)
> +		err = ops->xcvr_init(np);
> +
> +	return err;
> +}
> +
> +static int niu_serdes_init(struct niu *np)
> +{
> +	const struct niu_phy_ops *ops = np->phy_ops;
> +	int err;
> +
> +	err = 0;
> +	if (ops->serdes_init)
> +		err = ops->serdes_init(np);
> +
> +	return err;
> +}
> +
> +static void niu_init_xif(struct niu *);
> +
> +static int niu_link_status_common(struct niu *np, int link_up)
> +{
> +	struct niu_link_config *lp = &np->link_config;
> +	struct net_device *dev = np->dev;
> +	unsigned long flags;
> +
> +	if (!netif_carrier_ok(dev) && link_up) {
> +		printk(KERN_INFO PFX "%s: Link is up at %s, %s duplex\n",
> +		       dev->name,
> +		       (lp->active_speed == SPEED_10000 ?
> +			"10Gb/sec" :
> +			(lp->active_speed == SPEED_1000 ?
> +			 "1Gb/sec" :
> +			 (lp->active_speed == SPEED_100 ?
> +			  "100Mbit/sec" : "10Mbit/sec"))),
> +		       (lp->active_duplex == DUPLEX_FULL ?
> +			"full" : "half"));

pr_info() ?
> +
> +		spin_lock_irqsave(&np->lock, flags);
> +		niu_init_xif(np);
> +		spin_unlock_irqrestore(&np->lock, flags);
> +
> +		netif_carrier_on(dev);
> +	} else if (netif_carrier_ok(dev) && !link_up) {
> +		printk(KERN_WARNING PFX "%s: Link is down\n", dev->name);
> +		netif_carrier_off(dev);
> +	}
> +
> +	return 0;
> +}
> +
> +static int link_status_10g(struct niu *np, int *link_up_p)
> +{
> +	unsigned long flags;
> +	int err, link_up;
> +
> +	if (np->link_config.loopback_mode != LOOPBACK_DISABLED)
> +		return -EINVAL;
> +
> +	link_up = 0;
> +
> +	spin_lock_irqsave(&np->lock, flags);
> +
> +	err = mdio_read(np, np->phy_addr, BCM8704_PMA_PMD_DEV_ADDR,
> +			BCM8704_PMD_RCV_SIGDET);
> +	if (err < 0)
> +		return err;
> +	if (!(err & PMD_RCV_SIGDET_GLOBAL))
> +		goto out;
> +
> +	err = mdio_read(np, np->phy_addr, BCM8704_PCS_DEV_ADDR,
> +			BCM8704_PCS_10G_R_STATUS);
> +	if (err < 0)
> +		return err;
> +	if (!(err & PCS_10G_R_STATUS_BLK_LOCK))
> +		goto out;
> +
> +	err = mdio_read(np, np->phy_addr, BCM8704_PHYXS_DEV_ADDR,
> +			BCM8704_PHYXS_XGXS_LANE_STAT);
> +	if (err < 0)
> +		return err;
> +
> +	if (err != (PHYXS_XGXS_LANE_STAT_ALINGED |
> +		    PHYXS_XGXS_LANE_STAT_MAGIC |
> +		    PHYXS_XGXS_LANE_STAT_LANE3 |
> +		    PHYXS_XGXS_LANE_STAT_LANE2 |
> +		    PHYXS_XGXS_LANE_STAT_LANE1 |
> +		    PHYXS_XGXS_LANE_STAT_LANE0))
> +		goto out;
> +
> +	link_up = 1;
> +	np->link_config.active_speed = SPEED_10000;
> +	np->link_config.active_duplex = DUPLEX_FULL;
> +
> +out:
> +	spin_unlock_irqrestore(&np->lock, flags);
> +
> +	*link_up_p = link_up;
> +	return 0;
> +}
> +
> +static int link_status_1g(struct niu *np, int *link_up_p)
> +{
> +	u16 current_speed, bmsr;
> +	unsigned long flags;
> +	u8 current_duplex;
> +	int err, link_up;
> +
> +	if (np->link_config.loopback_mode != LOOPBACK_DISABLED)
> +		return -EINVAL;
> +
> +	link_up = 0;
> +	current_speed = SPEED_INVALID;
> +	current_duplex = DUPLEX_INVALID;
> +
> +	spin_lock_irqsave(&np->lock, flags);
> +
> +	err = mii_read(np, np->phy_addr, MII_BMSR);
> +	if (err < 0)
> +		goto out;
> +
> +	bmsr = err;
> +	if (bmsr & BMSR_LSTATUS) {
> +		u16 adv, lpa, common, estat;
> +
> +		err = mii_read(np, np->phy_addr, MII_ADVERTISE);
> +		if (err < 0)
> +			goto out;
> +		adv = err;
> +
> +		err = mii_read(np, np->phy_addr, MII_LPA);
> +		if (err < 0)
> +			goto out;
> +		lpa = err;
> +
> +		common = adv & lpa;
> +
> +		err = mii_read(np, np->phy_addr, MII_ESTATUS);
> +		if (err < 0)
> +			goto out;
> +		estat = err;
> +
> +		if (estat & (ESTATUS_1000_TFULL | ESTATUS_1000_THALF)) {
> +			current_speed = SPEED_1000;
> +			if (estat == ESTATUS_1000_TFULL)
> +				current_duplex = DUPLEX_FULL;
> +			else
> +				current_duplex = DUPLEX_HALF;
> +		} else {
> +			if (common & ADVERTISE_100BASE4) {
> +				current_speed = SPEED_100;
> +				current_duplex = DUPLEX_HALF;
> +			} else if (common & ADVERTISE_100FULL) {
> +				current_speed = SPEED_100;
> +				current_duplex = DUPLEX_FULL;
> +			} else if (common & ADVERTISE_100HALF) {
> +				current_speed = SPEED_100;
> +				current_duplex = DUPLEX_HALF;
> +			} else if (common & ADVERTISE_10FULL) {
> +				current_speed = SPEED_10;
> +				current_duplex = DUPLEX_FULL;
> +			} else if (common & ADVERTISE_10HALF) {
> +				current_speed = SPEED_10;
> +				current_duplex = DUPLEX_HALF;
> +			} else
> +				goto out;
> +		}
> +		link_up = 1;
> +	}
> +
> +out:
> +	spin_unlock_irqrestore(&np->lock, flags);
> +
> +	*link_up_p = link_up;
> +	return 0;
> +}
> +
> +static int niu_link_status(struct niu *np, int *link_up_p)
> +{
> +	const struct niu_phy_ops *ops = np->phy_ops;
> +	int err;
> +
> +	err = 0;
> +	if (ops->link_status)
> +		err = ops->link_status(np, link_up_p);
> +
> +	return err;
> +}
> +
> +static void niu_timer(unsigned long __opaque)
> +{
> +	struct niu *np = (struct niu *) __opaque;
> +	unsigned long off;
> +	int err, link_up;
> +
> +	err = niu_link_status(np, &link_up);
> +	if (!err)
> +		niu_link_status_common(np, link_up);
> +
> +	if (netif_carrier_ok(np->dev))
> +		off = 5 * HZ;
> +	else
> +		off = 1 * HZ;
> +	np->timer.expires = jiffies + off;
> +
> +	add_timer(&np->timer);
> +}
> +
> +static const struct niu_phy_ops phy_ops_10g_fiber_niu = {
> +	.serdes_init		= serdes_init_niu,
> +	.xcvr_init		= xcvr_init_10g,
> +	.link_status		= link_status_10g,
> +};
> +
> +static const struct niu_phy_ops phy_ops_10g_fiber = {
> +	.serdes_init		= serdes_init_10g,
> +	.xcvr_init		= xcvr_init_10g,
> +	.link_status		= link_status_10g,
> +};
> +
> +static const struct niu_phy_ops phy_ops_10g_copper = {
> +	.serdes_init		= serdes_init_10g,
> +	.link_status		= link_status_10g, /* XXX */
> +};
> +
> +static const struct niu_phy_ops phy_ops_1g_fiber = {
> +	.serdes_init		= serdes_init_1g,
> +	.xcvr_init		= xcvr_init_1g,
> +	.link_status		= link_status_1g,
> +};
> +
> +static const struct niu_phy_ops phy_ops_1g_copper = {
> +	.xcvr_init		= xcvr_init_1g,
> +	.link_status		= link_status_1g,
> +};
> +
> +struct niu_phy_template {
> +	const struct niu_phy_ops	*ops;
> +	u32				phy_addr_base;
> +};
> +
> +static const struct niu_phy_template phy_template_niu = {
> +	.ops		= &phy_ops_10g_fiber_niu,
> +	.phy_addr_base	= 16,
> +};
> +
> +static const struct niu_phy_template phy_template_10g_fiber = {
> +	.ops		= &phy_ops_10g_fiber,
> +	.phy_addr_base	= 8,
> +};
> +
> +static const struct niu_phy_template phy_template_10g_copper = {
> +	.ops		= &phy_ops_10g_copper,
> +	.phy_addr_base	= 10,
> +};
> +
> +static const struct niu_phy_template phy_template_1g_fiber = {
> +	.ops		= &phy_ops_1g_fiber,
> +	.phy_addr_base	= 0,
> +};
> +
> +static const struct niu_phy_template phy_template_1g_copper = {
> +	.ops		= &phy_ops_1g_copper,
> +	.phy_addr_base	= 0,
> +};
> +
> +static int niu_determine_phy_disposition(struct niu *np)
> +{
> +	struct niu_parent *parent = np->parent;
> +	u8 plat_type = parent->plat_type;
> +	const struct niu_phy_template *tp;
> +	u32 phy_addr_off = 0;
> +
> +	if (plat_type == PLAT_TYPE_NIU) {
> +		tp = &phy_template_niu;
> +		phy_addr_off += np->port;
> +	} else {
> +		switch (np->flags & (NIU_FLAGS_10G | NIU_FLAGS_FIBER)) {
> +		case 0:
> +			/* 1G copper */
> +			tp = &phy_template_1g_copper;
> +			if (plat_type == PLAT_TYPE_VF_P0)
> +				phy_addr_off = 10;
> +			else if (plat_type == PLAT_TYPE_VF_P1)
> +				phy_addr_off = 26;
> +
> +			phy_addr_off += (np->port ^ 0x3);
> +			break;
> +
> +		case NIU_FLAGS_10G:
> +			/* 10G copper */
> +			tp = &phy_template_1g_copper;
> +			break;
> +
> +		case NIU_FLAGS_FIBER:
> +			/* 1G fiber */
> +			tp = &phy_template_1g_fiber;
> +			break;
> +
> +		case NIU_FLAGS_10G | NIU_FLAGS_FIBER:
> +			/* 10G fiber */
> +			tp = &phy_template_10g_fiber;
> +			if (plat_type == PLAT_TYPE_VF_P0 ||
> +			    plat_type == PLAT_TYPE_VF_P1)
> +				phy_addr_off = 8;
> +			phy_addr_off += np->port;
> +			break;
> +
> +		default:
> +			return -EINVAL;
> +		}
> +	}
> +
> +	np->phy_ops = tp->ops;
> +	np->phy_addr = tp->phy_addr_base + phy_addr_off;
> +
> +	return 0;
> +}
> +
> +static int niu_init_link(struct niu *np)
> +{
> +	struct niu_parent *parent = np->parent;
> +	int err, ignore;
> +
> +	if (parent->plat_type == PLAT_TYPE_NIU) {
> +		err = niu_xcvr_init(np);
> +		if (err)
> +			return err;
> +		msleep(200);
> +	}
> +	err = niu_serdes_init(np);
> +	if (err)
> +		return err;
> +	msleep(200);
> +	err = niu_xcvr_init(np);
> +	if (!err)
> +		niu_link_status(np, &ignore);
> +	return 0;
> +}
> +
> +static void niu_set_primary_mac(struct niu *np, unsigned char *addr)
> +{
> +	u16 reg0 = addr[4] << 8 | addr[5];
> +	u16 reg1 = addr[2] << 8 | addr[3];
> +	u16 reg2 = addr[0] << 8 | addr[1];
> +
> +	if (np->flags & NIU_FLAGS_XMAC) {
> +		nw64_mac(XMAC_ADDR0, reg0);
> +		nw64_mac(XMAC_ADDR1, reg1);
> +		nw64_mac(XMAC_ADDR2, reg2);
> +	} else {
> +		nw64_mac(BMAC_ADDR0, reg0);
> +		nw64_mac(BMAC_ADDR1, reg1);
> +		nw64_mac(BMAC_ADDR2, reg2);
> +	}
> +}
> +
> +static int niu_num_alt_addr(struct niu *np)
> +{
> +	if (np->flags & NIU_FLAGS_XMAC)
> +		return XMAC_NUM_ALT_ADDR;
> +	else
> +		return BMAC_NUM_ALT_ADDR;
> +}
> +
> +static int niu_set_alt_mac(struct niu *np, int index, unsigned char *addr)
> +{
> +	u16 reg0 = addr[4] << 8 | addr[5];
> +	u16 reg1 = addr[2] << 8 | addr[3];
> +	u16 reg2 = addr[0] << 8 | addr[1];
> +
> +	if (index >= niu_num_alt_addr(np))
> +		return -EINVAL;
> +
> +	if (np->flags & NIU_FLAGS_XMAC) {
> +		nw64_mac(XMAC_ALT_ADDR0(index), reg0);
> +		nw64_mac(XMAC_ALT_ADDR1(index), reg1);
> +		nw64_mac(XMAC_ALT_ADDR2(index), reg2);
> +	} else {
> +		nw64_mac(BMAC_ALT_ADDR0(index), reg0);
> +		nw64_mac(BMAC_ALT_ADDR1(index), reg1);
> +		nw64_mac(BMAC_ALT_ADDR2(index), reg2);
> +	}
> +
> +	return 0;
> +}
> +
> +static int niu_enable_alt_mac(struct niu *np, int index, int on)
> +{
> +	unsigned long reg;
> +	u64 val, mask;
> +
> +	if (index >= niu_num_alt_addr(np))
> +		return -EINVAL;
> +
> +	if (np->flags & NIU_FLAGS_XMAC)
> +		reg = XMAC_ADDR_CMPEN;
> +	else
> +		reg = BMAC_ADDR_CMPEN;
> +
> +	mask = 1 << index;
> +
> +	val = nr64_mac(reg);
> +	if (on)
> +		val |= mask;
> +	else
> +		val &= ~mask;
> +	nw64_mac(reg, val);
> +
> +	return 0;
> +}
> +
> +static void __set_rdc_table_num_hw(struct niu *np, unsigned long reg,
> +				   int num, int mac_pref)
> +{
> +	u64 val = nr64_mac(reg);
> +	val &= ~(HOST_INFO_MACRDCTBLN | HOST_INFO_MPR);
> +	val |= num;
> +	if (mac_pref)
> +		val |= HOST_INFO_MPR;
> +	nw64_mac(reg, val);
> +}
> +
> +static int __set_rdc_table_num(struct niu *np,
> +			       int xmac_index, int bmac_index,
> +			       int rdc_table_num, int mac_pref)
> +{
> +	unsigned long reg;
> +
> +	if (rdc_table_num & ~HOST_INFO_MACRDCTBLN)
> +		return -EINVAL;
> +	if (np->flags & NIU_FLAGS_XMAC)
> +		reg = XMAC_HOST_INFO(xmac_index);
> +	else
> +		reg = BMAC_HOST_INFO(bmac_index);
> +	__set_rdc_table_num_hw(np, reg, rdc_table_num, mac_pref);
> +	return 0;
> +}
> +
> +static int niu_set_primary_mac_rdc_table(struct niu *np, int table_num,
> +					 int mac_pref)
> +{
> +	return __set_rdc_table_num(np, 17, 0, table_num, mac_pref);
> +}
> +
> +static int niu_set_multicast_mac_rdc_table(struct niu *np, int table_num,
> +					   int mac_pref)
> +{
> +	return __set_rdc_table_num(np, 16, 8, table_num, mac_pref);
> +}
> +
> +static int niu_set_alt_mac_rdc_table(struct niu *np, int idx,
> +				     int table_num, int mac_pref)
> +{
> +	if (idx >= niu_num_alt_addr(np))
> +		return -EINVAL;
> +	return __set_rdc_table_num(np, idx, idx + 1, table_num, mac_pref);
> +}
> +
> +static u64 vlan_entry_set_parity(u64 reg_val)
> +{
> +	u64 port01_mask;
> +	u64 port23_mask;
> +
> +	port01_mask = 0x00ff;
> +	port23_mask = 0xff00;
> +
> +	if (hweight64(reg_val & port01_mask) & 1)
> +		reg_val |= ENET_VLAN_TBL_PARITY0;
> +	else
> +		reg_val &= ~ENET_VLAN_TBL_PARITY0;
> +
> +	if (hweight64(reg_val & port23_mask) & 1)
> +		reg_val |= ENET_VLAN_TBL_PARITY1;
> +	else
> +		reg_val &= ~ENET_VLAN_TBL_PARITY1;
> +
> +	return reg_val;
> +}
> +
> +static void vlan_tbl_write(struct niu *np, unsigned long index,
> +			   int port, int vpr, int rdc_table)
> +{
> +	u64 reg_val = nr64(ENET_VLAN_TBL(index));
> +
> +	reg_val &= ~((ENET_VLAN_TBL_VPR |
> +		      ENET_VLAN_TBL_VLANRDCTBLN) <<
> +		     ENET_VLAN_TBL_SHIFT(port));
> +	if (vpr)
> +		reg_val |= (ENET_VLAN_TBL_VPR <<
> +			    ENET_VLAN_TBL_SHIFT(port));
> +	reg_val |= (rdc_table << ENET_VLAN_TBL_SHIFT(port));
> +
> +	reg_val = vlan_entry_set_parity(reg_val);
> +
> +	nw64(ENET_VLAN_TBL(index), reg_val);
> +}
> +
> +static void vlan_tbl_clear(struct niu *np)
> +{
> +	int i;
> +
> +	for (i = 0; i < ENET_VLAN_TBL_NUM_ENTRIES; i++)
> +		nw64(ENET_VLAN_TBL(i), 0);
> +}
> +
> +static int tcam_wait_bit(struct niu *np, u64 bit)
> +{
> +	int limit = 1000;
> +
> +	while (--limit > 0) {
> +		if (nr64(TCAM_CTL) & bit)
> +			break;
> +		udelay(1);
> +	}
> +	if (limit < 0)
> +		return -ENODEV;
> +
> +	return 0;
> +}
> +
> +static int tcam_flush(struct niu *np, int index)
> +{
> +	nw64(TCAM_KEY_0, 0x00);
> +	nw64(TCAM_KEY_MASK_0, 0xff);
> +	nw64(TCAM_CTL, (TCAM_CTL_RWC_TCAM_WRITE | index));
> +
> +	return tcam_wait_bit(np, TCAM_CTL_STAT);
> +}
> +
> +#if 0
> +static int tcam_read(struct niu *np, int index,
> +		     u64 *key, u64 *mask)
> +{
> +	int err;
> +
> +	nw64(TCAM_CTL, (TCAM_CTL_RWC_TCAM_READ | index));
> +	err = tcam_wait_bit(np, TCAM_CTL_STAT);
> +	if (!err) {
> +		key[0] = nr64(TCAM_KEY_0);
> +		key[1] = nr64(TCAM_KEY_1);
> +		key[2] = nr64(TCAM_KEY_2);
> +		key[3] = nr64(TCAM_KEY_3);
> +		mask[0] = nr64(TCAM_KEY_MASK_0);
> +		mask[1] = nr64(TCAM_KEY_MASK_1);
> +		mask[2] = nr64(TCAM_KEY_MASK_2);
> +		mask[3] = nr64(TCAM_KEY_MASK_3);
> +	}
> +	return err;
> +}
> +#endif
> +
> +static int tcam_write(struct niu *np, int index,
> +		      u64 *key, u64 *mask)
> +{
> +	nw64(TCAM_KEY_0, key[0]);
> +	nw64(TCAM_KEY_1, key[1]);
> +	nw64(TCAM_KEY_2, key[2]);
> +	nw64(TCAM_KEY_3, key[3]);
> +	nw64(TCAM_KEY_MASK_0, mask[0]);
> +	nw64(TCAM_KEY_MASK_1, mask[1]);
> +	nw64(TCAM_KEY_MASK_2, mask[2]);
> +	nw64(TCAM_KEY_MASK_3, mask[3]);
> +	nw64(TCAM_CTL, (TCAM_CTL_RWC_TCAM_WRITE | index));
> +
> +	return tcam_wait_bit(np, TCAM_CTL_STAT);
> +}
> +
> +#if 0
> +static int tcam_assoc_read(struct niu *np, int index, u64 *data)
> +{
> +	int err;
> +
> +	nw64(TCAM_CTL, (TCAM_CTL_RWC_RAM_READ | index));
> +	err = tcam_wait_bit(np, TCAM_CTL_STAT);
> +	if (!err)
> +		*data = nr64(TCAM_KEY_1);
> +
> +	return err;
> +}
> +#endif
> +
> +static int tcam_assoc_write(struct niu *np, int index, u64 assoc_data)
> +{
> +	nw64(TCAM_KEY_1, assoc_data);
> +	nw64(TCAM_CTL, (TCAM_CTL_RWC_RAM_WRITE | index));
> +
> +	return tcam_wait_bit(np, TCAM_CTL_STAT);
> +}
> +
> +static void tcam_enable(struct niu *np, int on)
> +{
> +	u64 val = nr64(FFLP_CFG_1);
> +
> +	if (on)
> +		val &= ~FFLP_CFG_1_TCAM_DIS;
> +	else
> +		val |= FFLP_CFG_1_TCAM_DIS;
> +	nw64(FFLP_CFG_1, val);
> +}
> +
> +static void tcam_set_lat_and_ratio(struct niu *np, u64 latency, u64 ratio)
> +{
> +	u64 val = nr64(FFLP_CFG_1);
> +
> +	val &= ~(FFLP_CFG_1_FFLPINITDONE |
> +		 FFLP_CFG_1_CAMLAT |
> +		 FFLP_CFG_1_CAMRATIO);
> +	val |= (latency << FFLP_CFG_1_CAMLAT_SHIFT);
> +	val |= (ratio << FFLP_CFG_1_CAMRATIO_SHIFT);
> +	nw64(FFLP_CFG_1, val);
> +
> +	val = nr64(FFLP_CFG_1);
> +	val |= FFLP_CFG_1_FFLPINITDONE;
> +	nw64(FFLP_CFG_1, val);
> +}
> +
> +static int tcam_user_eth_class_enable(struct niu *np, unsigned long class,
> +				      int on)
> +{
> +	unsigned long reg;
> +	u64 val;
> +
> +	if (class < CLASS_CODE_ETHERTYPE1 ||
> +	    class > CLASS_CODE_ETHERTYPE2)
> +		return -EINVAL;
> +
> +	reg = L2_CLS(class - CLASS_CODE_ETHERTYPE1);
> +	val = nr64(reg);
> +	if (on)
> +		val |= L2_CLS_VLD;
> +	else
> +		val &= ~L2_CLS_VLD;
> +	nw64(reg, val);
> +
> +	return 0;
> +}
> +
> +#if 0
> +static int tcam_user_eth_class_set(struct niu *np, unsigned long class,
> +				   u64 ether_type)
> +{
> +	unsigned long reg;
> +	u64 val;
> +
> +	if (class < CLASS_CODE_ETHERTYPE1 ||
> +	    class > CLASS_CODE_ETHERTYPE2 ||
> +	    (ether_type & ~(u64)0xffff) != 0)
> +		return -EINVAL;
> +
> +	reg = L2_CLS(class - CLASS_CODE_ETHERTYPE1);
> +	val = nr64(reg);
> +	val &= ~L2_CLS_ETYPE;
> +	val |= (ether_type << L2_CLS_ETYPE_SHIFT);
> +	nw64(reg, val);
> +
> +	return 0;
> +}
> +#endif
> +
> +static int tcam_user_ip_class_enable(struct niu *np, unsigned long class,
> +				     int on)
> +{
> +	unsigned long reg;
> +	u64 val;
> +
> +	if (class < CLASS_CODE_USER_PROG1 ||
> +	    class > CLASS_CODE_USER_PROG4)
> +		return -EINVAL;
> +
> +	reg = L3_CLS(class - CLASS_CODE_USER_PROG1);
> +	val = nr64(reg);
> +	if (on)
> +		val |= L3_CLS_VALID;
> +	else
> +		val &= ~L3_CLS_VALID;
> +	nw64(reg, val);
> +
> +	return 0;
> +}
> +
> +#if 0
> +static int tcam_user_ip_class_set(struct niu *np, unsigned long class,
> +				  int ipv6, u64 protocol_id,
> +				  u64 tos_mask, u64 tos_val)
> +{
> +	unsigned long reg;
> +	u64 val;
> +
> +	if (class < CLASS_CODE_USER_PROG1 ||
> +	    class > CLASS_CODE_USER_PROG4 ||
> +	    (protocol_id & ~(u64)0xff) != 0 ||
> +	    (tos_mask & ~(u64)0xff) != 0 ||
> +	    (tos_val & ~(u64)0xff) != 0)
> +		return -EINVAL;
> +
> +	reg = L3_CLS(class - CLASS_CODE_USER_PROG1);
> +	val = nr64(reg);
> +	val &= ~(L3_CLS_IPVER | L3_CLS_PID |
> +		 L3_CLS_TOSMASK | L3_CLS_TOS);
> +	if (ipv6)
> +		val |= L3_CLS_IPVER;
> +	val |= (protocol_id << L3_CLS_PID_SHIFT);
> +	val |= (tos_mask << L3_CLS_TOSMASK_SHIFT);
> +	val |= (tos_val << L3_CLS_TOS_SHIFT);
> +	nw64(reg, val);
> +
> +	return 0;
> +}
> +#endif
> +
> +static int tcam_early_init(struct niu *np)
> +{
> +	unsigned long i;
> +	int err;
> +
> +	tcam_enable(np, 0);
> +	tcam_set_lat_and_ratio(np,
> +			       DEFAULT_TCAM_LATENCY,
> +			       DEFAULT_TCAM_ACCESS_RATIO);
> +	for (i = CLASS_CODE_ETHERTYPE1; i <= CLASS_CODE_ETHERTYPE2; i++) {
> +		err = tcam_user_eth_class_enable(np, i, 0);
> +		if (err)
> +			return err;
> +	}
> +	for (i = CLASS_CODE_USER_PROG1; i <= CLASS_CODE_USER_PROG4; i++) {
> +		err = tcam_user_ip_class_enable(np, i, 0);
> +		if (err)
> +			return err;
> +	}
> +
> +	return 0;
> +}
> +
> +static int tcam_flush_all(struct niu *np)
> +{
> +	unsigned long i;
> +
> +	for (i = 0; i < np->parent->tcam_num_entries; i++) {
> +		int err = tcam_flush(np, i);
> +		if (err)
> +			return err;
> +	}
> +	return 0;
> +}
> +
> +static u64 hash_addr_regval(unsigned long index, unsigned long num_entries)
> +{
> +	return ((u64)index | (num_entries == 1 ?
> +			      HASH_TBL_ADDR_AUTOINC : 0));
> +}
> +
> +#if 0
> +static int hash_read(struct niu *np, unsigned long partition,
> +		     unsigned long index, unsigned long num_entries,
> +		     u64 *data)
> +{
> +	u64 val = hash_addr_regval(index, num_entries);
> +	unsigned long i;
> +
> +	if (partition >= FCRAM_NUM_PARTITIONS ||
> +	    index + num_entries > FCRAM_SIZE)
> +		return -EINVAL;
> +
> +	nw64(HASH_TBL_ADDR(partition), val);
> +	for (i = 0; i < num_entries; i++)
> +		data[i] = nr64(HASH_TBL_DATA(partition));
> +
> +	return 0;
> +}
> +#endif
> +
> +static int hash_write(struct niu *np, unsigned long partition,
> +		      unsigned long index, unsigned long num_entries,
> +		      u64 *data)
> +{
> +	u64 val = hash_addr_regval(index, num_entries);
> +	unsigned long i;
> +
> +	if (partition >= FCRAM_NUM_PARTITIONS ||
> +	    index + (num_entries * 8) > FCRAM_SIZE)
> +		return -EINVAL;
> +
> +	nw64(HASH_TBL_ADDR(partition), val);
> +	for (i = 0; i < num_entries; i++)
> +		nw64(HASH_TBL_DATA(partition), data[i]);
> +
> +	return 0;
> +}
> +		       
> +static void fflp_reset(struct niu *np)
> +{
> +	u64 val;
> +
> +	nw64(FFLP_CFG_1, FFLP_CFG_1_PIO_FIO_RST);
> +	udelay(10);
> +	nw64(FFLP_CFG_1, 0);
> +
> +	val = FFLP_CFG_1_FCRAMOUTDR_NORMAL | FFLP_CFG_1_FFLPINITDONE;
> +	nw64(FFLP_CFG_1, val);
> +}
> +
> +static void fflp_set_timings(struct niu *np)
> +{
> +	u64 val = nr64(FFLP_CFG_1);
> +
> +	val &= ~FFLP_CFG_1_FFLPINITDONE;
> +	val |= (DEFAULT_FCRAMRATIO << FFLP_CFG_1_FCRAMRATIO_SHIFT);
> +	nw64(FFLP_CFG_1, val);
> +
> +	val = nr64(FFLP_CFG_1);
> +	val |= FFLP_CFG_1_FFLPINITDONE;
> +	nw64(FFLP_CFG_1, val);
> +
> +	val = nr64(FCRAM_REF_TMR);
> +	val &= ~(FCRAM_REF_TMR_MAX | FCRAM_REF_TMR_MIN);
> +	val |= (DEFAULT_FCRAM_REFRESH_MAX << FCRAM_REF_TMR_MAX_SHIFT);
> +	val |= (DEFAULT_FCRAM_REFRESH_MIN << FCRAM_REF_TMR_MIN_SHIFT);
> +	nw64(FCRAM_REF_TMR, val);
> +}
> +
> +static int fflp_set_partition(struct niu *np, u64 partition,
> +			      u64 mask, u64 base, int enable)
> +{
> +	unsigned long reg;
> +	u64 val;
> +
> +	if (partition >= FCRAM_NUM_PARTITIONS ||
> +	    (mask & ~(u64)0x1f) != 0 ||
> +	    (base & ~(u64)0x1f) != 0)
> +		return -EINVAL;
> +
> +	reg = FLW_PRT_SEL(partition);
> +
> +	val = nr64(reg);
> +	val &= ~(FLW_PRT_SEL_EXT | FLW_PRT_SEL_MASK | FLW_PRT_SEL_BASE);
> +	val |= (mask << FLW_PRT_SEL_MASK_SHIFT);
> +	val |= (base << FLW_PRT_SEL_BASE_SHIFT);
> +	if (enable)
> +		val |= FLW_PRT_SEL_EXT;
> +	nw64(reg, val);
> +
> +	return 0;
> +}
> +
> +static int fflp_disable_all_partitions(struct niu *np)
> +{
> +	unsigned long i;
> +
> +	for (i = 0; i < FCRAM_NUM_PARTITIONS; i++) {
> +		int err = fflp_set_partition(np, 0, 0, 0, 0);
> +		if (err)
> +			return err;
> +	}
> +	return 0;
> +}
> +
> +static void fflp_llcsnap_enable(struct niu *np, int on)
> +{
> +	u64 val = nr64(FFLP_CFG_1);
> +
> +	if (on)
> +		val |= FFLP_CFG_1_LLCSNAP;
> +	else
> +		val &= ~FFLP_CFG_1_LLCSNAP;
> +	nw64(FFLP_CFG_1, val);
> +}
> +
> +static void fflp_errors_enable(struct niu *np, int on)
> +{
> +	u64 val = nr64(FFLP_CFG_1);
> +
> +	if (on)
> +		val &= ~FFLP_CFG_1_ERRORDIS;
> +	else
> +		val |= FFLP_CFG_1_ERRORDIS;
> +	nw64(FFLP_CFG_1, val);
> +}
> +
> +static int fflp_hash_clear(struct niu *np)
> +{
> +	struct fcram_hash_ipv4 ent;
> +	unsigned long i;
> +
> +	/* IPV4 hash entry with valid bit clear, rest is don't care.  */
> +	memset(&ent, 0, sizeof(ent));
> +	ent.header = HASH_HEADER_EXT;
> +
> +	for (i = 0; i < FCRAM_SIZE; i += sizeof(ent)) {
> +		int err = hash_write(np, 0, i, 1, (u64 *) &ent);
> +		if (err)
> +			return err;
> +	}
> +	return 0;
> +}
> +
> +static int fflp_early_init(struct niu *np)
> +{
> +	struct niu_parent *parent;
> +	unsigned long flags;
> +	int err;
> +
> +	niu_lock_parent(np, flags);
> +
> +	parent = np->parent;
> +	err = 0;
> +	if (!(parent->flags & PARENT_FLGS_CLS_HWINIT)) {
> +		niudbg(PROBE, "fflp_early_init: Initting hw on port %u\n",
> +		       np->port);
> +		if (np->parent->plat_type != PLAT_TYPE_NIU) {
> +			fflp_reset(np);
> +			fflp_set_timings(np);
> +			err = fflp_disable_all_partitions(np);
> +			if (err) {
> +				niudbg(PROBE, "fflp_disable_all_partitions "
> +				       "failed, err=%d\n", err);
> +				goto out;
> +			}
> +		}
> +
> +		err = tcam_early_init(np);
> +		if (err) {
> +			niudbg(PROBE, "tcam_early_init failed, err=%d\n",
> +			       err);
> +			goto out;
> +		}
> +		fflp_llcsnap_enable(np, 1);
> +		fflp_errors_enable(np, 0);
> +		nw64(H1POLY, 0);
> +		nw64(H2POLY, 0);
> +
> +		err = tcam_flush_all(np);
> +		if (err) {
> +			niudbg(PROBE, "tcam_flush_all failed, err=%d\n",
> +			       err);
> +			goto out;
> +		}
> +		if (np->parent->plat_type != PLAT_TYPE_NIU) {
> +			err = fflp_hash_clear(np);
> +			if (err) {
> +				niudbg(PROBE, "fflp_hash_clear failed, "
> +				       "err=%d\n", err);
> +				goto out;
> +			}
> +		}
> +
> +		vlan_tbl_clear(np);
> +
> +		niudbg(PROBE, "fflp_early_init: Success\n");
> +		parent->flags |= PARENT_FLGS_CLS_HWINIT;
> +	}
> +out:
> +	niu_unlock_parent(np, flags);
> +	return err;
> +}
> +
> +static int niu_set_flow_key(struct niu *np, unsigned long class_code, u64 key)
> +{
> +	if (class_code < CLASS_CODE_USER_PROG1 ||
> +	    class_code > CLASS_CODE_SCTP_IPV6)
> +		return -EINVAL;
> +
> +	nw64(FLOW_KEY(class_code - CLASS_CODE_USER_PROG1), key);
> +	return 0;
> +}
> +
> +static int niu_set_tcam_key(struct niu *np, unsigned long class_code, u64 key)
> +{
> +	if (class_code < CLASS_CODE_USER_PROG1 ||
> +	    class_code > CLASS_CODE_SCTP_IPV6)
> +		return -EINVAL;
> +
> +	nw64(TCAM_KEY(class_code - CLASS_CODE_USER_PROG1), key);
> +	return 0;
> +}
> +
> +static void niu_rx_skb_append(struct sk_buff *skb, struct page *page,
> +			      u32 offset, u32 size)
> +{
> +	int i = skb_shinfo(skb)->nr_frags;
> +	skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
> +
> +	frag->page = page;
> +	frag->page_offset = offset;
> +	frag->size = size;
> +
> +	skb->len += size;
> +	skb->data_len += size;
> +	skb->truesize += size;
> +
> +	skb_shinfo(skb)->nr_frags = i + 1;
> +}
> +
> +static unsigned int niu_hash_rxaddr(struct rx_ring_info *rp, u64 a)
> +{
> +	a >>= PAGE_SHIFT;
> +	a ^= (a >> ilog2(MAX_RBR_RING_SIZE));
> +
> +	return (a & (MAX_RBR_RING_SIZE - 1));
> +}
> +
> +static struct page *niu_find_rxpage(struct rx_ring_info *rp, u64 addr,
> +				    struct page ***link)
> +{
> +	unsigned int h = niu_hash_rxaddr(rp, addr);
> +	struct page *p, **pp;
> +
> +	addr &= PAGE_MASK;
> +	pp = &rp->rxhash[h];
> +	for (; (p = *pp) != NULL; pp = (struct page **) &p->mapping) {
> +		if (p->index == addr) {
> +			*link = pp;
> +			break;
> +		}
> +	}
> +
> +	return p;
> +}
> +
> +static void niu_hash_page(struct rx_ring_info *rp, struct page *page, u64 base)
> +{
> +	unsigned int h = niu_hash_rxaddr(rp, base);
> +
> +	page->index = base;
> +	page->mapping = (struct address_space *) rp->rxhash[h];
> +	rp->rxhash[h] = page;
> +}
> +
> +static int niu_rbr_add_page(struct niu *np, struct rx_ring_info *rp,
> +			    gfp_t mask, int start_index)
> +{
> +	struct page *page;
> +	u64 addr;
> +	int i;
> +
> +	page = alloc_page(mask);
> +	if (!page)
> +		return -ENOMEM;
> +
> +	addr = np->ops->map_page(np->device, page, 0,
> +				 PAGE_SIZE, DMA_FROM_DEVICE);
> +
> +	niu_hash_page(rp, page, addr);
> +	if (rp->rbr_blocks_per_page > 1)
> +		atomic_add(rp->rbr_blocks_per_page - 1,
> +			   &compound_head(page)->_count);
> +
> +	for (i = 0; i < rp->rbr_blocks_per_page; i++) {
> +		__le32 *rbr = &rp->rbr[start_index + i];
> +
> +		*rbr = cpu_to_le32(addr >> RBR_DESCR_ADDR_SHIFT);
> +		addr += rp->rbr_block_size;
> +	}
> +
> +	return 0;
> +}
> +
> +static void niu_rbr_refill(struct niu *np, struct rx_ring_info *rp, gfp_t mask)
> +{
> +	int index = rp->rbr_index;
> +
> +	rp->rbr_pending++;
> +	if ((rp->rbr_pending % rp->rbr_blocks_per_page) == 0) {
> +		int err = niu_rbr_add_page(np, rp, mask, index);
> +
> +		if (unlikely(err)) {
> +			rp->rbr_pending--;
> +			return;
> +		}
> +
> +		rp->rbr_index += rp->rbr_blocks_per_page;
> +		BUG_ON(rp->rbr_index > rp->rbr_table_size);
> +		if (rp->rbr_index == rp->rbr_table_size)
> +			rp->rbr_index = 0;
> +
> +		if (rp->rbr_pending >= rp->rbr_kick_thresh) {
> +			nw64(RBR_KICK(rp->rx_channel), rp->rbr_pending);
> +			rp->rbr_pending = 0;
> +		}
> +	}
> +}
> +
> +static int niu_rx_pkt_ignore(struct niu *np, struct rx_ring_info *rp)
> +{
> +	unsigned int index = rp->rcr_index;
> +	int num_rcr = 0;
> +
> +	rp->rx_dropped++;
> +	while (1) {
> +		struct page *page, **link;
> +		u64 addr, val;
> +		u32 rcr_size;
> +
> +		num_rcr++;
> +
> +		val = le64_to_cpup(&rp->rcr[index]);
> +		addr = (val & RCR_ENTRY_PKT_BUF_ADDR) <<
> +			RCR_ENTRY_PKT_BUF_ADDR_SHIFT;
> +		page = niu_find_rxpage(rp, addr, &link);
> +		BUG_ON(!page);

BUG_ON checks for NULL are useless if the code following is
just going to dereference the pointer anyway. 

> +
> +		rcr_size = rp->rbr_sizes[(val & RCR_ENTRY_PKTBUFSZ) >>
> +					 RCR_ENTRY_PKTBUFSZ_SHIFT];
> +		if ((page->index + PAGE_SIZE) - rcr_size == addr) {
> +			*link = (struct page *) page->mapping;
> +			np->ops->unmap_page(np->device, page->index,
> +					    PAGE_SIZE, DMA_FROM_DEVICE);
> +			page->index = 0;
> +			page->mapping = NULL;
> +			__free_page(page);
> +			rp->rbr_refill_pending++;
> +		}
> +
> +		index = NEXT_RCR(rp, index);
> +		if (!(val & RCR_ENTRY_MULTI))
> +			break;
> +
> +	}
> +	rp->rcr_index = index;
> +
> +	return num_rcr;
> +}
> +
> +static int niu_process_rx_pkt(struct niu *np, struct rx_ring_info *rp)
> +{
> +	unsigned int index = rp->rcr_index;
> +	struct sk_buff *skb;
> +	int len, num_rcr;
> +
> +	skb = netdev_alloc_skb(np->dev, RX_SKB_ALLOC_SIZE);
> +	if (unlikely(!skb))
> +		return niu_rx_pkt_ignore(np, rp);
> +
> +	num_rcr = 0;
> +	while (1) {
> +		struct page *page, **link;
> +		u32 rcr_size, append_size;
> +		u64 addr, val, off;
> +
> +		num_rcr++;
> +
> +		val = le64_to_cpup(&rp->rcr[index]);
> +
> +		len = (val & RCR_ENTRY_L2_LEN) >>
> +			RCR_ENTRY_L2_LEN_SHIFT;
> +		len -= ETH_FCS_LEN;
> +
> +		addr = (val & RCR_ENTRY_PKT_BUF_ADDR) <<
> +			RCR_ENTRY_PKT_BUF_ADDR_SHIFT;
> +		page = niu_find_rxpage(rp, addr, &link);
> +		BUG_ON(!page);
> +
> +		rcr_size = rp->rbr_sizes[(val & RCR_ENTRY_PKTBUFSZ) >>
> +					 RCR_ENTRY_PKTBUFSZ_SHIFT];
> +
> +		off = addr & ~PAGE_MASK;
> +		append_size = rcr_size;
> +		if (num_rcr == 1) {
> +			int ptype;
> +
> +			off += 2;
> +			append_size -= 2;
> +
> +			ptype = (val >> RCR_ENTRY_PKT_TYPE_SHIFT);
> +			if ((ptype == RCR_PKT_TYPE_TCP ||
> +			     ptype == RCR_PKT_TYPE_UDP) &&
> +			    !(val & (RCR_ENTRY_NOPORT |
> +				     RCR_ENTRY_ERROR)))
> +				skb->ip_summed = CHECKSUM_UNNECESSARY;
> +			else
> +				skb->ip_summed = CHECKSUM_NONE;
> +		}
> +		if (!(val & RCR_ENTRY_MULTI))
> +			append_size = len - skb->len;
> +
> +		niu_rx_skb_append(skb, page, off, append_size);
> +		if ((page->index + rp->rbr_block_size) - rcr_size == addr) {
> +			*link = (struct page *) page->mapping;
> +			np->ops->unmap_page(np->device, page->index,
> +					    PAGE_SIZE, DMA_FROM_DEVICE);
> +			page->index = 0;
> +			page->mapping = NULL;
> +			rp->rbr_refill_pending++;
> +		} else
> +			get_page(page);
> +
> +		index = NEXT_RCR(rp, index);
> +		if (!(val & RCR_ENTRY_MULTI))
> +			break;
> +
> +	}
> +	rp->rcr_index = index;
> +
> +	skb_reserve(skb, NET_IP_ALIGN);
> +	__pskb_pull_tail(skb, min(len, NIU_RXPULL_MAX));
> +
> +	rp->rx_packets++;
> +	rp->rx_bytes += skb->len;
> +
> +	skb->protocol = eth_type_trans(skb, np->dev);
> +	netif_receive_skb(skb);
> +
> +	return num_rcr;
> +}
> +
> +static int niu_rbr_fill(struct niu *np, struct rx_ring_info *rp, gfp_t mask)
> +{
> +	int blocks_per_page = rp->rbr_blocks_per_page;
> +	int err, index = rp->rbr_index;
> +
> +	err = 0;
> +	while (index < (rp->rbr_table_size - blocks_per_page)) {
> +		err = niu_rbr_add_page(np, rp, mask, index);
> +		if (err)
> +			break;
> +
> +		index += blocks_per_page;
> +	}
> +
> +	rp->rbr_index = index;
> +	return err;
> +}
> +
> +static void niu_rbr_free(struct niu *np, struct rx_ring_info *rp)
> +{
> +	int i;
> +
> +	for (i = 0; i < MAX_RBR_RING_SIZE; i++) {
> +		struct page *page;
> +
> +		page = rp->rxhash[i];
> +		while (page) {
> +			struct page *next = (struct page *) page->mapping;
> +			u64 base = page->index;
> +
> +			np->ops->unmap_page(np->device, base, PAGE_SIZE,
> +					    DMA_FROM_DEVICE);
> +			page->index = 0;
> +			page->mapping = NULL;
> +
> +			__free_page(page);
> +
> +			page = next;
> +		}
> +	}
> +
> +	for (i = 0; i < rp->rbr_table_size; i++)
> +		rp->rbr[i] = cpu_to_le32(0);
> +	rp->rbr_index = 0;
> +}
> +
> +static int release_tx_packet(struct niu *np, struct tx_ring_info *rp, int idx)
> +{
> +	struct tx_buff_info *tb = &rp->tx_buffs[idx];
> +	struct sk_buff *skb = tb->skb;
> +	struct tx_pkt_hdr *tp;
> +	u64 tx_flags;
> +	int i, len;
> +
> +	BUG_ON(!skb);

BUG_ON checks for NULL are useless if the code following is
just going to dereference the pointer anyway. 

> +
> +	tp = (struct tx_pkt_hdr *) skb->data;
> +	tx_flags = le64_to_cpup(&tp->flags);
> +
> +	rp->tx_packets++;
> +	rp->tx_bytes += (((tx_flags & TXHDR_LEN) >> TXHDR_LEN_SHIFT) -
> +			 ((tx_flags & TXHDR_PAD) / 2));
> +
> +	len = skb_headlen(skb);
> +	np->ops->unmap_single(np->device, tb->mapping,
> +			      len, DMA_TO_DEVICE);
> +
> +	if (le64_to_cpu(rp->descr[idx]) & TX_DESC_MARK)
> +		rp->mark_pending--;
> +
> +	tb->skb = NULL;
> +	do {
> +		idx = NEXT_TX(rp, idx);
> +		len -= MAX_TX_DESC_LEN;
> +	} while (len > 0);
> +
> +	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
> +		tb = &rp->tx_buffs[idx];
> +		BUG_ON(tb->skb != NULL);

> +		np->ops->unmap_page(np->device, tb->mapping,
> +				    skb_shinfo(skb)->frags[i].size,
> +				    DMA_TO_DEVICE);

Shouldn't this be skb_shinfo(tb->skb)->frags...

> +		idx = NEXT_TX(rp, idx);
> +	}
> +
> +	dev_kfree_skb(skb);

Should be dev_kfree_skb_any() if you plan on supporting
netconsole.

> +
> +	return idx;
> +}
> +
> +#define NIU_TX_WAKEUP_THRESH(rp)		((rp)->pending / 4)
> +
> +static void niu_tx_work(struct niu *np, struct tx_ring_info *rp)
> +{
> +	u16 pkt_cnt, tmp;
> +	int cons;
> +	u64 cs;
> +
> +	cs = rp->tx_cs;
> +	if (unlikely(!(cs & (TX_CS_MK | TX_CS_MMK))))
> +		goto out;
> +
> +	tmp = pkt_cnt = (cs & TX_CS_PKT_CNT) >> TX_CS_PKT_CNT_SHIFT;
> +	pkt_cnt = (pkt_cnt - rp->last_pkt_cnt) &
> +		(TX_CS_PKT_CNT >> TX_CS_PKT_CNT_SHIFT);
> +
> +	rp->last_pkt_cnt = tmp;
> +
> +	cons = rp->cons;
> +
> +	niudbg(TX_WORK, "%s: niu_tx_work() pkt_cnt[%u] cons[%d]\n",
> +	       np->dev->name, pkt_cnt, cons);
> +
> +	while (pkt_cnt--)
> +		cons = release_tx_packet(np, rp, cons);
> +
> +	rp->cons = cons;
> +	smp_mb();
> +
> +out:
> +	if (unlikely(netif_queue_stopped(np->dev) &&
> +		     (niu_tx_avail(rp) > NIU_TX_WAKEUP_THRESH(rp)))) {
> +		netif_tx_lock(np->dev);
> +		if (netif_queue_stopped(np->dev) &&
> +		    (niu_tx_avail(rp) > NIU_TX_WAKEUP_THRESH(rp)))
> +			netif_wake_queue(np->dev);
> +		netif_tx_unlock(np->dev);
> +	}
> +}
> +
> +static int niu_rx_work(struct niu *np, struct rx_ring_info *rp, int budget)
> +{
> +	int qlen, rcr_done = 0, work_done = 0;
> +	struct rxdma_mailbox *mbox = rp->mbox;
> +	u64 stat;
> +
> +#if 1
> +	stat = nr64(RX_DMA_CTL_STAT(rp->rx_channel));
> +	qlen = nr64(RCRSTAT_A(rp->rx_channel)) & RCRSTAT_A_QLEN;
> +#else
> +	stat = le64_to_cpup(&mbox->rx_dma_ctl_stat);
> +	qlen = (le64_to_cpup(&mbox->rcrstat_a) & RCRSTAT_A_QLEN);
> +#endif
> +	mbox->rx_dma_ctl_stat = 0;
> +	mbox->rcrstat_a = 0;
> +
> +	niudbg(RX_WORK, "%s: niu_rx_work(chan[%d]), stat[%llx] qlen=%d\n",
> +	       np->dev->name, rp->rx_channel, (unsigned long long) stat, qlen);
> +
> +	rcr_done = work_done = 0;
> +	qlen = min(qlen, budget);
> +	while (work_done < qlen) {
> +		rcr_done += niu_process_rx_pkt(np, rp);
> +		work_done++;
> +	}
> +
> +	if (rp->rbr_refill_pending >= rp->rbr_kick_thresh) {
> +		unsigned int i;
> +
> +		for (i = 0; i < rp->rbr_refill_pending; i++)
> +			niu_rbr_refill(np, rp, GFP_ATOMIC);
> +		rp->rbr_refill_pending = 0;
> +	}
> +
> +	stat = (RX_DMA_CTL_STAT_MEX |
> +		((u64)work_done << RX_DMA_CTL_STAT_PKTREAD_SHIFT) |
> +		((u64)rcr_done << RX_DMA_CTL_STAT_PTRREAD_SHIFT));
> +
> +	nw64(RX_DMA_CTL_STAT(rp->rx_channel), stat);
> +
> +	return work_done;
> +}
> +
> +static int niu_poll_core(struct niu *np, struct niu_ldg *lp, int budget)
> +{
> +	u64 v0 = lp->v0;
> +	u32 tx_vec = (v0 >> 32);
> +	u32 rx_vec = (v0 & 0xffffffff);
> +	int i, work_done = 0;
> +
> +	niudbg(POLL, "%s: niu_poll_core() v0[%016llx]\n",
> +	       np->dev->name, (unsigned long long) v0);
> +
> +	for (i = 0; i < np->num_tx_rings; i++) {
> +		struct tx_ring_info *rp = &np->tx_rings[i];
> +		if (tx_vec & (1 << rp->tx_channel))
> +			niu_tx_work(np, rp);
> +		nw64(LD_IM0(LDN_TXDMA(rp->tx_channel)), 0);
> +	}
> +
> +	for (i = 0; i < np->num_rx_rings; i++) {
> +		struct rx_ring_info *rp = &np->rx_rings[i];
> +
> +		if (rx_vec & (1 << rp->rx_channel)) {
> +			int this_work_done;
> +
> +			this_work_done = niu_rx_work(np, rp,
> +						     budget);
> +
> +			budget -= this_work_done;
> +			work_done += this_work_done;
> +		}
> +		nw64(LD_IM0(LDN_RXDMA(rp->rx_channel)), 0);
> +	}
> +
> +	return work_done;
> +}
> +
> +static int niu_poll(struct napi_struct *napi, int budget)
> +{
> +	struct niu_ldg *lp = container_of(napi, struct niu_ldg, napi);
> +	struct niu *np = lp->np;
> +	int work_done;
> +
> +	work_done = niu_poll_core(np, lp, budget);
> +
> +	if (work_done < budget) {
> +		netif_rx_complete(np->dev, napi);
> +		niu_ldg_rearm(np, lp, 1);
> +	}
> +	return work_done;
> +}
> +
> +static void niu_log_rxchan_errors(struct niu *np, struct rx_ring_info *rp,
> +				  u64 stat)
> +{
> +	printk(KERN_ERR PFX "%s: RX channel %u errors ( ",
> +	       np->dev->name, rp->rx_channel);
dev_err??
> +
> +	if (stat & RX_DMA_CTL_STAT_RBR_TMOUT)
> +		printk("RBR_TMOUT ");
> +	if (stat & RX_DMA_CTL_STAT_RSP_CNT_ERR)
> +		printk("RSP_CNT ");
> +	if (stat & RX_DMA_CTL_STAT_BYTE_EN_BUS)
> +		printk("BYTE_EN_BUS ");
> +	if (stat & RX_DMA_CTL_STAT_RSP_DAT_ERR)
> +		printk("RSP_DAT ");
> +	if (stat & RX_DMA_CTL_STAT_RCR_ACK_ERR)
> +		printk("RCR_ACK ");
> +	if (stat & RX_DMA_CTL_STAT_RCR_SHA_PAR)
> +		printk("RCR_SHA_PAR ");
> +	if (stat & RX_DMA_CTL_STAT_RBR_PRE_PAR)
> +		printk("RBR_PRE_PAR ");
> +	if (stat & RX_DMA_CTL_STAT_CONFIG_ERR)
> +		printk("CONFIG ");
> +	if (stat & RX_DMA_CTL_STAT_RCRINCON)
> +		printk("RCRINCON ");
> +	if (stat & RX_DMA_CTL_STAT_RCRFULL)
> +		printk("RCRFULL ");
> +	if (stat & RX_DMA_CTL_STAT_RBRFULL)
> +		printk("RBRFULL ");
> +	if (stat & RX_DMA_CTL_STAT_RBRLOGPAGE)
> +		printk("RBRLOGPAGE ");
> +	if (stat & RX_DMA_CTL_STAT_CFIGLOGPAGE)
> +		printk("CFIGLOGPAGE ");
> +	if (stat & RX_DMA_CTL_STAT_DC_FIFO_ERR)
> +		printk("DC_FIDO ");
> +
> +	printk(")\n");
> +}
> +
> +static int niu_rx_error(struct niu *np, struct rx_ring_info *rp)
> +{
> +	u64 stat = nr64(RX_DMA_CTL_STAT(rp->rx_channel));
> +	int err = 0;
> +
> +	printk(KERN_ERR PFX "%s: RX channel %u error, stat[%llx]\n",
> +	       np->dev->name, rp->rx_channel, (unsigned long long) stat);
> +
> +	niu_log_rxchan_errors(np, rp, stat);
> +
> +	if (stat & (RX_DMA_CTL_STAT_CHAN_FATAL |
> +		    RX_DMA_CTL_STAT_PORT_FATAL))
> +		err = -EINVAL;
> +
> +	nw64(RX_DMA_CTL_STAT(rp->rx_channel),
> +	     stat & RX_DMA_CTL_WRITE_CLEAR_ERRS);
> +
> +	return err;
> +}
> +
> +static void niu_log_txchan_errors(struct niu *np, struct tx_ring_info *rp,
> +				  u64 cs)
> +{
> +	printk(KERN_ERR PFX "%s: TX channel %u errors ( ",
> +	       np->dev->name, rp->tx_channel);
> +
> +	if (cs & TX_CS_MBOX_ERR)
> +		printk("MBOX ");
> +	if (cs & TX_CS_PKT_SIZE_ERR)
> +		printk("PKT_SIZE ");
> +	if (cs & TX_CS_TX_RING_OFLOW)
> +		printk("TX_RING_OFLOW ");
> +	if (cs & TX_CS_PREF_BUF_PAR_ERR)
> +		printk("PREF_BUF_PAR ");
> +	if (cs & TX_CS_NACK_PREF)
> +		printk("NACK_PREF ");
> +	if (cs & TX_CS_NACK_PKT_RD)
> +		printk("NACK_PKT_RD ");
> +	if (cs & TX_CS_CONF_PART_ERR)
> +		printk("CONF_PART ");
> +	if (cs & TX_CS_PKT_PRT_ERR)
> +		printk("PKT_PTR ");
> +
> +	printk(")\n");
> +}
> +
> +static int niu_tx_error(struct niu *np, struct tx_ring_info *rp)
> +{
> +	u64 cs, logh, logl;
> +
> +	cs = nr64(TX_CS(rp->tx_channel));
> +	logh = nr64(TX_RNG_ERR_LOGH(rp->tx_channel));
> +	logl = nr64(TX_RNG_ERR_LOGL(rp->tx_channel));
> +
> +	printk(KERN_ERR PFX "%s: TX channel %u error, "
> +	       "cs[%llx] logh[%llx] logl[%llx]\n",
> +	       np->dev->name, rp->tx_channel,
> +	       (unsigned long long) cs,
> +	       (unsigned long long) logh,
> +	       (unsigned long long) logl);
> +
> +	niu_log_txchan_errors(np, rp, cs);
> +
> +	return -ENODEV;
> +}
> +
> +static int niu_mif_interrupt(struct niu *np)
> +{
> +	u64 mif_status = nr64(MIF_STATUS);
> +	int phy_mdint = 0;
> +
> +	if (np->flags & NIU_FLAGS_XMAC) {
> +		u64 xrxmac_stat = nr64_mac(XRXMAC_STATUS);
> +
> +		if (xrxmac_stat & XRXMAC_STATUS_PHY_MDINT)
> +			phy_mdint = 1;
> +	}
> +
> +	printk(KERN_ERR PFX "%s: MIF interrupt, "
> +	       "stat[%llx] phy_mdint(%d)\n",
> +	       np->dev->name, (unsigned long long) mif_status, phy_mdint);
> +
> +	return -ENODEV;
> +}
> +
> +static void niu_xmac_interrupt(struct niu *np)
> +{
> +	struct niu_xmac_stats *mp = &np->mac_stats.xmac;
> +	u64 val;
> +
> +	val = nr64_mac(XTXMAC_STATUS);
> +	if (val & XTXMAC_STATUS_FRAME_CNT_EXP)
> +		mp->tx_frames += TXMAC_FRM_CNT_COUNT;
> +	if (val & XTXMAC_STATUS_BYTE_CNT_EXP)
> +		mp->tx_bytes += TXMAC_BYTE_CNT_COUNT;
> +	if (val & XTXMAC_STATUS_TXFIFO_XFR_ERR)
> +		mp->tx_fifo_errors++;
> +	if (val & XTXMAC_STATUS_TXMAC_OFLOW)
> +		mp->tx_overflow_errors++;
> +	if (val & XTXMAC_STATUS_MAX_PSIZE_ERR)
> +		mp->tx_max_pkt_size_errors++;
> +	if (val & XTXMAC_STATUS_TXMAC_UFLOW)
> +		mp->tx_underflow_errors++;
> +
> +	val = nr64_mac(XRXMAC_STATUS);
> +	if (val & XRXMAC_STATUS_LCL_FLT_STATUS)
> +		mp->rx_local_faults++;
> +	if (val & XRXMAC_STATUS_RFLT_DET)
> +		mp->rx_remote_faults++;
> +	if (val & XRXMAC_STATUS_LFLT_CNT_EXP)
> +		mp->rx_link_faults += LINK_FAULT_CNT_COUNT;
> +	if (val & XRXMAC_STATUS_ALIGNERR_CNT_EXP)
> +		mp->rx_align_errors += RXMAC_ALIGN_ERR_CNT_COUNT;
> +	if (val & XRXMAC_STATUS_RXFRAG_CNT_EXP)
> +		mp->rx_frags += RXMAC_FRAG_CNT_COUNT;
> +	if (val & XRXMAC_STATUS_RXMULTF_CNT_EXP)
> +		mp->rx_mcasts += RXMAC_MC_FRM_CNT_COUNT;
> +	if (val & XRXMAC_STATUS_RXBCAST_CNT_EXP)
> +		mp->rx_bcasts += RXMAC_BC_FRM_CNT_COUNT;
> +	if (val & XRXMAC_STATUS_RXBCAST_CNT_EXP)
> +		mp->rx_bcasts += RXMAC_BC_FRM_CNT_COUNT;
> +	if (val & XRXMAC_STATUS_RXHIST1_CNT_EXP)
> +		mp->rx_hist_cnt1 += RXMAC_HIST_CNT1_COUNT;
> +	if (val & XRXMAC_STATUS_RXHIST2_CNT_EXP)
> +		mp->rx_hist_cnt2 += RXMAC_HIST_CNT2_COUNT;
> +	if (val & XRXMAC_STATUS_RXHIST3_CNT_EXP)
> +		mp->rx_hist_cnt3 += RXMAC_HIST_CNT3_COUNT;
> +	if (val & XRXMAC_STATUS_RXHIST4_CNT_EXP)
> +		mp->rx_hist_cnt4 += RXMAC_HIST_CNT4_COUNT;
> +	if (val & XRXMAC_STATUS_RXHIST5_CNT_EXP)
> +		mp->rx_hist_cnt5 += RXMAC_HIST_CNT5_COUNT;
> +	if (val & XRXMAC_STATUS_RXHIST6_CNT_EXP)
> +		mp->rx_hist_cnt6 += RXMAC_HIST_CNT6_COUNT;
> +	if (val & XRXMAC_STATUS_RXHIST7_CNT_EXP)
> +		mp->rx_hist_cnt7 += RXMAC_HIST_CNT7_COUNT;
> +	if (val & XRXMAC_STAT_MSK_RXOCTET_CNT_EXP)
> +		mp->rx_octets += RXMAC_BT_CNT_COUNT;
> +	if (val & XRXMAC_STATUS_CVIOLERR_CNT_EXP)
> +		mp->rx_code_violations += RXMAC_CD_VIO_CNT_COUNT;
> +	if (val & XRXMAC_STATUS_LENERR_CNT_EXP)
> +		mp->rx_len_errors += RXMAC_MPSZER_CNT_COUNT;
> +	if (val & XRXMAC_STATUS_CRCERR_CNT_EXP)
> +		mp->rx_crc_errors += RXMAC_CRC_ER_CNT_COUNT;
> +	if (val & XRXMAC_STATUS_RXUFLOW)
> +		mp->rx_underflows++;
> +	if (val & XRXMAC_STATUS_RXOFLOW)
> +		mp->rx_overflows++;
> +
> +	val = nr64_mac(XMAC_FC_STAT);
> +	if (val & XMAC_FC_STAT_TX_MAC_NPAUSE)
> +		mp->pause_off_state++;
> +	if (val & XMAC_FC_STAT_TX_MAC_PAUSE)
> +		mp->pause_on_state++;
> +	if (val & XMAC_FC_STAT_RX_MAC_RPAUSE)
> +		mp->pause_received++;
> +}
> +
> +static void niu_bmac_interrupt(struct niu *np)
> +{
> +	struct niu_bmac_stats *mp = &np->mac_stats.bmac;
> +	u64 val;
> +
> +	val = nr64_mac(BTXMAC_STATUS);
> +	if (val & BTXMAC_STATUS_UNDERRUN)
> +		mp->tx_underflow_errors++;
> +	if (val & BTXMAC_STATUS_MAX_PKT_ERR)
> +		mp->tx_max_pkt_size_errors++;
> +	if (val & BTXMAC_STATUS_BYTE_CNT_EXP)
> +		mp->tx_bytes += BTXMAC_BYTE_CNT_COUNT;
> +	if (val & BTXMAC_STATUS_FRAME_CNT_EXP)
> +		mp->tx_frames += BTXMAC_FRM_CNT_COUNT;
> +
> +	val = nr64_mac(BRXMAC_STATUS);
> +	if (val & BRXMAC_STATUS_OVERFLOW)
> +		mp->rx_overflows++;
> +	if (val & BRXMAC_STATUS_FRAME_CNT_EXP)
> +		mp->rx_frames += BRXMAC_FRAME_CNT_COUNT;
> +	if (val & BRXMAC_STATUS_ALIGN_ERR_EXP)
> +		mp->rx_align_errors += BRXMAC_ALIGN_ERR_CNT_COUNT;
> +	if (val & BRXMAC_STATUS_CRC_ERR_EXP)
> +		mp->rx_crc_errors += BRXMAC_ALIGN_ERR_CNT_COUNT;
> +	if (val & BRXMAC_STATUS_LEN_ERR_EXP)
> +		mp->rx_len_errors += BRXMAC_CODE_VIOL_ERR_CNT_COUNT;
> +
> +	val = nr64_mac(BMAC_CTRL_STATUS);
> +	if (val & BMAC_CTRL_STATUS_NOPAUSE)
> +		mp->pause_off_state++;
> +	if (val & BMAC_CTRL_STATUS_PAUSE)
> +		mp->pause_on_state++;
> +	if (val & BMAC_CTRL_STATUS_PAUSE_RECV)
> +		mp->pause_received++;
> +}
> +
> +static int niu_mac_interrupt(struct niu *np)
> +{
> +	if (np->flags & NIU_FLAGS_XMAC)
> +		niu_xmac_interrupt(np);
> +	else
> +		niu_bmac_interrupt(np);
> +
> +	return 0;
> +}
> +
> +static void niu_log_device_error(struct niu *np, u64 stat)
> +{
> +	printk(KERN_ERR PFX "%s: Core device errors ( ",
> +	       np->dev->name);
> +
> +	if (stat & SYS_ERR_MASK_META2)
> +		printk("META2 ");
> +	if (stat & SYS_ERR_MASK_META1)
> +		printk("META1 ");
> +	if (stat & SYS_ERR_MASK_PEU)
> +		printk("PEU ");
> +	if (stat & SYS_ERR_MASK_TXC)
> +		printk("TXC ");
> +	if (stat & SYS_ERR_MASK_RDMC)
> +		printk("RDMC ");
> +	if (stat & SYS_ERR_MASK_TDMC)
> +		printk("TDMC ");
> +	if (stat & SYS_ERR_MASK_ZCP)
> +		printk("ZCP ");
> +	if (stat & SYS_ERR_MASK_FFLP)
> +		printk("FFLP ");
> +	if (stat & SYS_ERR_MASK_IPP)
> +		printk("IPP ");
> +	if (stat & SYS_ERR_MASK_MAC)
> +		printk("MAC ");
> +	if (stat & SYS_ERR_MASK_SMX)
> +		printk("SMX ");
> +
> +	printk(")\n");
> +}
> +
> +static int niu_device_error(struct niu *np)
> +{
> +	u64 stat = nr64(SYS_ERR_STAT);
> +
> +	printk(KERN_ERR PFX "%s: Core device error, stat[%llx]\n",
> +	       np->dev->name, (unsigned long long) stat);
> +
> +	niu_log_device_error(np, stat);
> +
> +	return -ENODEV;
> +}
> +
> +static int niu_slowpath_interrupt(struct niu *np, struct niu_ldg *lp)
> +{
> +	u64 v0 = lp->v0;
> +	u64 v1 = lp->v1;
> +	u64 v2 = lp->v2;
> +	int i, err = 0;
> +
> +	if (v1 & 0x00000000ffffffffULL) {
> +		u32 rx_vec = (v1 & 0xffffffff);
> +
> +		for (i = 0; i < np->num_rx_rings; i++) {
> +			struct rx_ring_info *rp = &np->rx_rings[i];
> +
> +			if (rx_vec & (1 << rp->rx_channel)) {
> +				int r = niu_rx_error(np, rp);
> +				if (r)
> +					err = r;
> +			}
> +		}
> +	}
> +	if (v1 & 0x7fffffff00000000ULL) {
> +		u32 tx_vec = (v1 >> 32) & 0x7fffffff;
> +
> +		for (i = 0; i < np->num_tx_rings; i++) {
> +			struct tx_ring_info *rp = &np->tx_rings[i];
> +
> +			if (tx_vec & (1 << rp->tx_channel)) {
> +				int r = niu_tx_error(np, rp);
> +				if (r)
> +					err = r;
> +			}
> +		}
> +	}
> +	if ((v0 | v1) & 0x8000000000000000ULL) {
> +		int r = niu_mif_interrupt(np);
> +		if (r)
> +			err = r;
> +	}
> +	if (v2) {
> +		if (v2 & 0x01ef) {
> +			int r = niu_mac_interrupt(np);
> +			if (r)
> +				err = r;
> +		}
> +		if (v2 & 0x0210) {
> +			int r = niu_device_error(np);
> +			if (r)
> +				err = r;
> +		}
> +	}
> +
> +	if (err)
> +		niu_enable_interrupts(np, 0);
> +
> +	return -EINVAL;
> +}
> +
> +static void niu_rxchan_intr(struct niu *np, struct rx_ring_info *rp,
> +			    int ldn)
> +{
> +	struct rxdma_mailbox *mbox = rp->mbox;
> +	u64 stat_write, stat = le64_to_cpup(&mbox->rx_dma_ctl_stat);
> +
> +	stat_write = (RX_DMA_CTL_STAT_RCRTHRES |
> +		      RX_DMA_CTL_STAT_RCRTO);
> +	nw64(RX_DMA_CTL_STAT(rp->rx_channel), stat_write);
> +
> +	niudbg(INTERRUPT, "%s: rxchan_intr stat[%llx]\n",
> +	       np->dev->name, (unsigned long long) stat);
> +}
> +
> +static void niu_txchan_intr(struct niu *np, struct tx_ring_info *rp,
> +			    int ldn)
> +{
> +	rp->tx_cs = nr64(TX_CS(rp->tx_channel));
> +
> +	niudbg(INTERRUPT, "%s: txchan_intr cs[%llx]\n",
> +	       np->dev->name, (unsigned long long) rp->tx_cs);
> +}
> +
> +static void __niu_fastpath_interrupt(struct niu *np, int ldg, u64 v0)
> +{
> +	struct niu_parent *parent = np->parent;
> +	u32 rx_vec, tx_vec;
> +	int i;
> +
> +	tx_vec = (v0 >> 32);
> +	rx_vec = (v0 & 0xffffffff);
> +
> +	for (i = 0; i < np->num_rx_rings; i++) {
> +		struct rx_ring_info *rp = &np->rx_rings[i];
> +		int ldn = LDN_RXDMA(rp->rx_channel);
> +
> +		if (parent->ldg_map[ldn] != ldg)
> +			continue;
> +
> +		nw64(LD_IM0(ldn), LD_IM0_MASK);
> +		if (rx_vec & (1 << rp->rx_channel))
> +			niu_rxchan_intr(np, rp, ldn);
> +	}
> +
> +	for (i = 0; i < np->num_tx_rings; i++) {
> +		struct tx_ring_info *rp = &np->tx_rings[i];
> +		int ldn = LDN_TXDMA(rp->tx_channel);
> +
> +		if (parent->ldg_map[ldn] != ldg)
> +			continue;
> +
> +		nw64(LD_IM0(ldn), LD_IM0_MASK);
> +		if (tx_vec & (1 << rp->tx_channel))
> +			niu_txchan_intr(np, rp, ldn);
> +	}
> +}
> +
> +static void niu_schedule_napi(struct niu *np, struct niu_ldg *lp,
> +			      u64 v0, u64 v1, u64 v2)
> +{
> +	if (likely(netif_rx_schedule_prep(np->dev, &lp->napi))) {
> +		lp->v0 = v0;
> +		lp->v1 = v1;
> +		lp->v2 = v2;
> +		__niu_fastpath_interrupt(np, lp->ldg_num, v0);
> +		__netif_rx_schedule(np->dev, &lp->napi);
> +	}
> +}
> +
> +static irqreturn_t niu_interrupt(int irq, void *dev_id)
> +{
> +	struct niu_ldg *lp = dev_id;
> +	struct niu *np = lp->np;
> +	int ldg = lp->ldg_num;
> +	unsigned long flags;
> +	u64 v0, v1, v2;
> +
> +	if (niu_debug & NIU_DEBUG_INTERRUPT)
> +		printk(KERN_ERR PFX "niu_interrupt() ldg[%p](%d) ",
> +		       lp, ldg);

Why not netif_msg_intr()?

> +
> +	spin_lock_irqsave(&np->lock, flags);
> +
> +	v0 = nr64(LDSV0(ldg));
> +	v1 = nr64(LDSV1(ldg));
> +	v2 = nr64(LDSV2(ldg));
> +
> +	if (niu_debug & NIU_DEBUG_INTERRUPT)
> +		printk("v0[%llx] v1[%llx] v2[%llx]\n",
> +		       (unsigned long long) v0,
> +		       (unsigned long long) v1,
> +		       (unsigned long long) v2);
> +
> +	if (unlikely(!v0 && !v1 && !v2)) {
> +		spin_unlock_irqrestore(&np->lock, flags);
> +		return IRQ_NONE;
> +	}
> +
> +	if (unlikely((v0 & ((u64)1 << LDN_MIF)) || v1 || v2)) {
> +		int err = niu_slowpath_interrupt(np, lp);
> +		if (err)
> +			goto out;
> +	}
> +	if (likely(v0 & ~((u64)1 << LDN_MIF)))
> +		niu_schedule_napi(np, lp, v0, v1, v2);
> +	else
> +		niu_ldg_rearm(np, lp, 1);
> +out:
> +	spin_unlock_irqrestore(&np->lock, flags);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static void niu_free_rx_ring_info(struct niu *np, struct rx_ring_info *rp)
> +{
> +	if (rp->mbox) {
> +		np->ops->free_coherent(np->device,
> +				       sizeof(struct rxdma_mailbox),
> +				       rp->mbox, rp->mbox_dma);
> +		rp->mbox = NULL;
> +	}
> +	if (rp->rcr) {
> +		np->ops->free_coherent(np->device,
> +				       MAX_RCR_RING_SIZE * sizeof(__le64),
> +				       rp->rcr, rp->rcr_dma);
> +		rp->rcr = NULL;
> +		rp->rcr_table_size = 0;
> +		rp->rcr_index = 0;
> +	}
> +	if (rp->rbr) {
> +		niu_rbr_free(np, rp);
> +
> +		np->ops->free_coherent(np->device,
> +				       MAX_RBR_RING_SIZE * sizeof(__le32),
> +				       rp->rbr, rp->rbr_dma);
> +		rp->rbr = NULL;
> +		rp->rbr_table_size = 0;
> +		rp->rbr_index = 0;
> +	}
> +	kfree(rp->rxhash);
> +	rp->rxhash = NULL;
> +}
> +
> +static void niu_free_tx_ring_info(struct niu *np, struct tx_ring_info *rp)
> +{
> +	if (rp->mbox) {
> +		np->ops->free_coherent(np->device,
> +				       sizeof(struct txdma_mailbox),
> +				       rp->mbox, rp->mbox_dma);
> +		rp->mbox = NULL;
> +	}
> +	if (rp->descr) {
> +		int i;
> +
> +		for (i = 0; i < MAX_TX_RING_SIZE; i++) {
> +			if (rp->tx_buffs[i].skb)
> +				(void) release_tx_packet(np, rp, i);
> +		}
> +
> +		np->ops->free_coherent(np->device,
> +				       MAX_TX_RING_SIZE * sizeof(__le64),
> +				       rp->descr, rp->descr_dma);
> +		rp->descr = NULL;
> +		rp->pending = 0;
> +		rp->prod = 0;
> +		rp->cons = 0;
> +		rp->wrap_bit = 0;
> +	}
> +}
> +
> +static void niu_free_channels(struct niu *np)
> +{
> +	int i;
> +
> +	if (np->rx_rings) {
> +		for (i = 0; i < np->num_rx_rings; i++) {
> +			struct rx_ring_info *rp = &np->rx_rings[i];
> +
> +			niu_free_rx_ring_info(np, rp);
> +		}
> +		kfree(np->rx_rings);
> +		np->rx_rings = NULL;
> +		np->num_rx_rings = 0;
> +	}
> +
> +	if (np->tx_rings) {
> +		for (i = 0; i < np->num_tx_rings; i++) {
> +			struct tx_ring_info *rp = &np->tx_rings[i];
> +
> +			niu_free_tx_ring_info(np, rp);
> +		}
> +		kfree(np->tx_rings);
> +		np->tx_rings = NULL;
> +		np->num_tx_rings = 0;
> +	}
> +}
> +
> +static int niu_alloc_rx_ring_info(struct niu *np,
> +				  struct rx_ring_info *rp)
> +{
> +	BUILD_BUG_ON(sizeof(struct rxdma_mailbox) != 64);
> +
> +	rp->rxhash = kzalloc(MAX_RBR_RING_SIZE * sizeof(struct page *),
> +			     GFP_KERNEL);
> +	if (!rp->rxhash)
> +		return -ENOMEM;
> +
> +	rp->mbox = np->ops->alloc_coherent(np->device,
> +					   sizeof(struct rxdma_mailbox),
> +					   &rp->mbox_dma, GFP_KERNEL);
> +	if (!rp->mbox)
> +		return -ENOMEM;
> +	if ((unsigned long)rp->mbox & (64UL - 1)) {
> +		printk(KERN_ERR PFX "%s: Coherent alloc gives misaligned "
> +		       "RXDMA mailbox %p\n", np->dev->name, rp->mbox);
> +		return -EINVAL;
> +	}
> +
> +	rp->rcr = np->ops->alloc_coherent(np->device,
> +					  MAX_RCR_RING_SIZE * sizeof(__le64),
> +					  &rp->rcr_dma, GFP_KERNEL);
> +	if (!rp->rcr)
> +		return -ENOMEM;
> +	if ((unsigned long)rp->rcr & (64UL - 1)) {
> +		printk(KERN_ERR PFX "%s: Coherent alloc gives misaligned "
> +		       "RXDMA RCR table %p\n", np->dev->name, rp->rcr);
> +		return -EINVAL;
> +	}
> +	rp->rcr_table_size = MAX_RCR_RING_SIZE;
> +	rp->rcr_index = 0;
> +
> +	rp->rbr = np->ops->alloc_coherent(np->device,
> +					  MAX_RBR_RING_SIZE * sizeof(__le32),
> +					  &rp->rbr_dma, GFP_KERNEL);
> +	if (!rp->rbr)
> +		return -ENOMEM;
> +	if ((unsigned long)rp->rbr & (64UL - 1)) {
> +		printk(KERN_ERR PFX "%s: Coherent alloc gives misaligned "
> +		       "RXDMA RBR table %p\n", np->dev->name, rp->rbr);
> +		return -EINVAL;
> +	}
> +	rp->rbr_table_size = MAX_RBR_RING_SIZE;
> +	rp->rbr_index = 0;
> +	rp->rbr_pending = 0;
> +
> +	return 0;
> +}
> +
> +static void niu_set_max_burst(struct niu *np, struct tx_ring_info *rp)
> +{
> +	int mtu = np->dev->mtu;
> +
> +	rp->max_burst = mtu + 32;
> +	if (rp->max_burst > 4096)
> +		rp->max_burst = 4096;
> +}
> +
> +static int niu_alloc_tx_ring_info(struct niu *np,
> +				  struct tx_ring_info *rp)
> +{
> +	BUILD_BUG_ON(sizeof(struct txdma_mailbox) != 64);
> +
> +	rp->mbox = np->ops->alloc_coherent(np->device,
> +					   sizeof(struct txdma_mailbox),
> +					   &rp->mbox_dma, GFP_KERNEL);
> +	if (!rp->mbox)
> +		return -ENOMEM;
> +	if ((unsigned long)rp->mbox & (64UL - 1)) {
> +		printk(KERN_ERR PFX "%s: Coherent alloc gives misaligned "
> +		       "TXDMA mailbox %p\n", np->dev->name, rp->mbox);
> +		return -EINVAL;
> +	}
> +
> +	rp->descr = np->ops->alloc_coherent(np->device,
> +					    MAX_TX_RING_SIZE * sizeof(__le64),
> +					    &rp->descr_dma, GFP_KERNEL);
> +	if (!rp->descr)
> +		return -ENOMEM;
> +	if ((unsigned long)rp->descr & (64UL - 1)) {
> +		printk(KERN_ERR PFX "%s: Coherent alloc gives misaligned "
> +		       "TXDMA descr table %p\n", np->dev->name, rp->descr);
> +		return -EINVAL;
> +	}
> +
> +	rp->pending = MAX_TX_RING_SIZE;
> +	rp->prod = 0;
> +	rp->cons = 0;
> +	rp->wrap_bit = 0;
> +
> +	/* XXX make these configurable... XXX */
> +	rp->mark_freq = rp->pending / 4;
> +
> +	niu_set_max_burst(np, rp);
> +
> +	return 0;
> +}
> +
> +static void niu_size_rbr(struct niu *np, struct rx_ring_info *rp)
> +{
> +	u16 bs;
> +
> +	switch (PAGE_SIZE) {
> +	case 4 * 1024:
> +	case 8 * 1024:
> +	case 16 * 1024:
> +	case 32 * 1024:
> +		rp->rbr_block_size = PAGE_SIZE;
> +		rp->rbr_blocks_per_page = 1;
> +		break;
> +
> +	default:
> +		if (PAGE_SIZE % (32 * 1024) == 0)
> +			bs = 32 * 1024;
> +		else if (PAGE_SIZE % (16 * 1024) == 0)
> +			bs = 16 * 1024;
> +		else if (PAGE_SIZE % (8 * 1024) == 0)
> +			bs = 8 * 1024;
> +		else if (PAGE_SIZE % (4 * 1024) == 0)
> +			bs = 4 * 1024;
> +		else
> +			BUG();
> +		rp->rbr_block_size = bs;
> +		rp->rbr_blocks_per_page = PAGE_SIZE / bs;
> +	}
> +
> +	rp->rbr_sizes[0] = 256;
> +	rp->rbr_sizes[1] = 1024;
> +	if (np->dev->mtu > ETH_DATA_LEN) {
> +		switch (PAGE_SIZE) {
> +		case 4 * 1024:
> +			rp->rbr_sizes[2] = 4096;
> +			break;
> +
> +		default:
> +			rp->rbr_sizes[2] = 8192;
> +			break;
> +		}
> +	} else {
> +		rp->rbr_sizes[2] = 2048;
> +	}
> +	rp->rbr_sizes[3] = rp->rbr_block_size;
> +}
> +
> +static int niu_alloc_channels(struct niu *np)
> +{
> +	struct niu_parent *parent = np->parent;
> +	int first_rx_channel, first_tx_channel;
> +	int i, port, err;
> +
> +	port = np->port;
> +	first_rx_channel = first_tx_channel = 0;
> +	for (i = 0; i < port; i++) {
> +		first_rx_channel += parent->rxchan_per_port[i];
> +		first_tx_channel += parent->txchan_per_port[i];
> +	}
> +
> +	np->num_rx_rings = parent->rxchan_per_port[port];
> +	np->num_tx_rings = parent->txchan_per_port[port];
> +
> +	np->rx_rings = kzalloc(np->num_rx_rings * sizeof(struct rx_ring_info),
> +			       GFP_KERNEL);
> +	err = -ENOMEM;
> +	if (!np->rx_rings)
> +		goto out_err;
> +
> +	for (i = 0; i < np->num_rx_rings; i++) {
> +		struct rx_ring_info *rp = &np->rx_rings[i];
> +
> +		rp->np = np;
> +		rp->rx_channel = first_rx_channel + i;
> +
> +		err = niu_alloc_rx_ring_info(np, rp);
> +		if (err)
> +			goto out_err;
> +
> +		niu_size_rbr(np, rp);
> +
> +		/* XXX better defaults, configurable, etc... XXX */
> +		rp->nonsyn_window = 64;
> +		rp->nonsyn_threshold = rp->rcr_table_size - 64;
> +		rp->syn_window = 64;
> +		rp->syn_threshold = rp->rcr_table_size - 64;
> +		rp->rcr_pkt_threshold = 16;
> +		rp->rcr_timeout = 8;
> +		rp->rbr_kick_thresh = RBR_REFILL_MIN;
> +		if (rp->rbr_kick_thresh < rp->rbr_blocks_per_page)
> +			rp->rbr_kick_thresh = rp->rbr_blocks_per_page;
> +
> +		err = niu_rbr_fill(np, rp, GFP_KERNEL);
> +		if (err)
> +			return err;
> +	}
> +
> +	np->tx_rings = kzalloc(np->num_tx_rings * sizeof(struct tx_ring_info),
> +			       GFP_KERNEL);
> +	err = -ENOMEM;
> +	if (!np->tx_rings)
> +		goto out_err;
> +
> +	for (i = 0; i < np->num_tx_rings; i++) {
> +		struct tx_ring_info *rp = &np->tx_rings[i];
> +
> +		rp->np = np;
> +		rp->tx_channel = first_tx_channel + i;
> +
> +		err = niu_alloc_tx_ring_info(np, rp);
> +		if (err)
> +			goto out_err;
> +	}
> +
> +	return 0;
> +
> +out_err:
> +	niu_free_channels(np);
> +	return err;
> +}
> +
> +static int niu_tx_cs_sng_poll(struct niu *np, int channel)
> +{
> +	int limit = 1000;
> +
> +	while (--limit > 0) {
> +		u64 val = nr64(TX_CS(channel));
> +		if (val & TX_CS_SNG_STATE)
> +			return 0;
> +	}
> +	return -ENODEV;
> +}
> +
> +static int niu_tx_channel_stop(struct niu *np, int channel)
> +{
> +	u64 val = nr64(TX_CS(channel));
> +
> +	val |= TX_CS_STOP_N_GO;
> +	nw64(TX_CS(channel), val);
> +
> +	return niu_tx_cs_sng_poll(np, channel);
> +}
> +
> +static int niu_tx_cs_reset_poll(struct niu *np, int channel)
> +{
> +	int limit = 1000;
> +
> +	while (--limit > 0) {
> +		u64 val = nr64(TX_CS(channel));
> +		if (!(val & TX_CS_RST))
> +			return 0;
> +	}
> +	return -ENODEV;
> +}
> +
> +static int niu_tx_channel_reset(struct niu *np, int channel)
> +{
> +	u64 val = nr64(TX_CS(channel));
> +	int err;
> +
> +	val |= TX_CS_RST;
> +	nw64(TX_CS(channel), val);
> +
> +	err = niu_tx_cs_reset_poll(np, channel);
> +	if (!err)
> +		nw64(TX_RING_KICK(channel), 0);
> +
> +	return err;
> +}
> +
> +static int niu_tx_channel_lpage_init(struct niu *np, int channel)
> +{
> +	u64 val;
> +
> +	nw64(TX_LOG_MASK1(channel), 0);
> +	nw64(TX_LOG_VAL1(channel), 0);
> +	nw64(TX_LOG_MASK2(channel), 0);
> +	nw64(TX_LOG_VAL2(channel), 0);
> +	nw64(TX_LOG_PAGE_RELO1(channel), 0);
> +	nw64(TX_LOG_PAGE_RELO2(channel), 0);
> +	nw64(TX_LOG_PAGE_HDL(channel), 0);
> +
> +	val  = (u64)np->port << TX_LOG_PAGE_VLD_FUNC_SHIFT;
> +	val |= (TX_LOG_PAGE_VLD_PAGE0 | TX_LOG_PAGE_VLD_PAGE1);
> +	nw64(TX_LOG_PAGE_VLD(channel), val);
> +
> +	/* XXX TXDMA 32bit mode? XXX */
> +
> +	return 0;
> +}
> +
> +static void niu_txc_enable_port(struct niu *np, int on)
> +{
> +	unsigned long flags;
> +	u64 val, mask;
> +
> +	niu_lock_parent(np, flags);
> +	val = nr64(TXC_CONTROL);
> +	mask = (u64)1 << np->port;
> +	if (on) {
> +		val |= TXC_CONTROL_ENABLE | mask;
> +	} else {
> +		val &= ~mask;
> +		if ((val & ~TXC_CONTROL_ENABLE) == 0)
> +			val &= ~TXC_CONTROL_ENABLE;
> +	}
> +	nw64(TXC_CONTROL, val);
> +	niu_unlock_parent(np, flags);
> +}
> +
> +static void niu_txc_set_imask(struct niu *np, u64 imask)
> +{
> +	unsigned long flags;
> +	u64 val;
> +
> +	niu_lock_parent(np, flags);
> +	val = nr64(TXC_INT_MASK);
> +	val &= ~TXC_INT_MASK_VAL(np->port);
> +	val |= (imask << TXC_INT_MASK_VAL_SHIFT(np->port));
> +	niu_unlock_parent(np, flags);
> +}
> +
> +static void niu_txc_port_dma_enable(struct niu *np, int on)
> +{
> +	u64 val = 0;
> +
> +	if (on) {
> +		int i;
> +
> +		for (i = 0; i < np->num_tx_rings; i++)
> +			val |= (1 << np->tx_rings[i].tx_channel);
> +	}
> +	nw64(TXC_PORT_DMA(np->port), val);
> +}
> +
> +static int niu_init_one_tx_channel(struct niu *np, struct tx_ring_info *rp)
> +{
> +	int err, channel = rp->tx_channel;
> +	u64 val, ring_len;
> +
> +	err = niu_tx_channel_stop(np, channel);
> +	if (err)
> +		return err;
> +
> +	err = niu_tx_channel_reset(np, channel);
> +	if (err)
> +		return err;
> +
> +	err = niu_tx_channel_lpage_init(np, channel);
> +	if (err)
> +		return err;
> +
> +	nw64(TXC_DMA_MAX(channel), rp->max_burst);
> +	nw64(TX_ENT_MSK(channel), 0);
> +
> +	if (rp->descr_dma & ~(TX_RNG_CFIG_STADDR_BASE |
> +			      TX_RNG_CFIG_STADDR)) {
> +		printk(KERN_ERR PFX "%s: TX ring channel %d "
> +		       "DMA addr (%llx) is not aligned.\n",
> +		       np->dev->name, channel,
> +		       (unsigned long long) rp->descr_dma);
> +		return -EINVAL;
> +	}
> +
> +	/* The length field in TX_RNG_CFIG is measured in 64-byte
> +	 * blocks.  rp->pending is the number of TX descriptors in
> +	 * our ring, 8 bytes each, thus we divide by 8 bytes more
> +	 * to get the proper value the chip wants.
> +	 */
> +	ring_len = (rp->pending / 8);
> +
> +	val = ((ring_len << TX_RNG_CFIG_LEN_SHIFT) |
> +	       rp->descr_dma);
> +	nw64(TX_RNG_CFIG(channel), val);
> +
> +	if (((rp->mbox_dma >> 32) & ~TXDMA_MBH_MBADDR) ||
> +	    ((u32)rp->mbox_dma & ~TXDMA_MBL_MBADDR)) {
> +		printk(KERN_ERR PFX "%s: TX ring channel %d "
> +		       "MBOX addr (%llx) is has illegal bits.\n",
> +		       np->dev->name, channel,
> +		       (unsigned long long) rp->mbox_dma);
> +		return -EINVAL;
> +	}
> +	nw64(TXDMA_MBH(channel), rp->mbox_dma >> 32);
> +	nw64(TXDMA_MBL(channel), rp->mbox_dma & TXDMA_MBL_MBADDR);
> +
> +	nw64(TX_CS(channel), 0);
> +
> +	rp->last_pkt_cnt = 0;
> +
> +	return 0;
> +}
> +
> +static void niu_init_rdc_groups(struct niu *np)
> +{
> +	struct niu_rdc_tables *tp = &np->parent->rdc_group_cfg[np->port];
> +	int i, first_table_num = tp->first_table_num;
> +
> +	for (i = 0; i < tp->num_tables; i++) {
> +		struct rdc_table *tbl = &tp->tables[i];
> +		int this_table = first_table_num + i;
> +		int slot;
> +
> +		for (slot = 0; slot < NIU_RDC_TABLE_SLOTS; slot++)
> +			nw64(RDC_TBL(this_table, slot),
> +			     tbl->rxdma_channel[slot]);
> +	}
> +
> +	nw64(DEF_RDC(np->port), np->parent->rdc_default[np->port]);
> +}
> +
> +static void niu_init_drr_weight(struct niu *np)
> +{
> +	int type = phy_decode(np->parent->port_phy, np->port);
> +	u64 val;
> +
> +	switch (type) {
> +	case PORT_TYPE_10G:
> +		val = PT_DRR_WEIGHT_DEFAULT_10G;
> +		break;
> +
> +	case PORT_TYPE_1G:
> +	default:
> +		val = PT_DRR_WEIGHT_DEFAULT_1G;
> +		break;
> +	}
> +	nw64(PT_DRR_WT(np->port), val);
> +}
> +
> +static int niu_init_hostinfo(struct niu *np)
> +{
> +	struct niu_parent *parent = np->parent;
> +	struct niu_rdc_tables *tp = &parent->rdc_group_cfg[np->port];
> +	int i, err, num_alt = niu_num_alt_addr(np);
> +	int first_rdc_table = tp->first_table_num;
> +
> +	err = niu_set_primary_mac_rdc_table(np, first_rdc_table, 1);
> +	if (err)
> +		return err;
> +
> +	err = niu_set_multicast_mac_rdc_table(np, first_rdc_table, 1);
> +	if (err)
> +		return err;
> +
> +	for (i = 0; i < num_alt; i++) {
> +		err = niu_set_alt_mac_rdc_table(np, i, first_rdc_table, 1);
> +		if (err)
> +			return err;
> +	}
> +
> +	return 0;
> +}
> +
> +static int niu_rx_channel_reset(struct niu *np, int channel)
> +{
> +	return niu_set_and_wait_clear(np, RXDMA_CFIG1(channel),
> +				      RXDMA_CFIG1_RST, 1000, 10,
> +				      "RXDMA_CFIG1");
> +}
> +
> +static int niu_rx_channel_lpage_init(struct niu *np, int channel)
> +{
> +	u64 val;
> +
> +	nw64(RX_LOG_MASK1(channel), 0);
> +	nw64(RX_LOG_VAL1(channel), 0);
> +	nw64(RX_LOG_MASK2(channel), 0);
> +	nw64(RX_LOG_VAL2(channel), 0);
> +	nw64(RX_LOG_PAGE_RELO1(channel), 0);
> +	nw64(RX_LOG_PAGE_RELO2(channel), 0);
> +	nw64(RX_LOG_PAGE_HDL(channel), 0);
> +
> +	val  = (u64)np->port << RX_LOG_PAGE_VLD_FUNC_SHIFT;
> +	val |= (RX_LOG_PAGE_VLD_PAGE0 | RX_LOG_PAGE_VLD_PAGE1);
> +	nw64(RX_LOG_PAGE_VLD(channel), val);
> +
> +	return 0;
> +}
> +
> +static void niu_rx_channel_wred_init(struct niu *np, struct rx_ring_info *rp)
> +{
> +	u64 val;
> +
> +	val = (((u64)rp->nonsyn_window << RDC_RED_PARA_WIN_SHIFT) |
> +	       ((u64)rp->nonsyn_threshold << RDC_RED_PARA_THRE_SHIFT) |
> +	       ((u64)rp->syn_window << RDC_RED_PARA_WIN_SYN_SHIFT) |
> +	       ((u64)rp->syn_threshold << RDC_RED_PARA_THRE_SYN_SHIFT));
> +	nw64(RDC_RED_PARA(rp->rx_channel), val);
> +}
> +
> +static int niu_compute_rbr_cfig_b(struct rx_ring_info *rp, u64 *ret)
> +{
> +	u64 val = 0;
> +
> +	switch (rp->rbr_block_size) {
> +	case 4 * 1024:
> +		val |= (RBR_BLKSIZE_4K << RBR_CFIG_B_BLKSIZE_SHIFT);
> +		break;
> +	case 8 * 1024:
> +		val |= (RBR_BLKSIZE_8K << RBR_CFIG_B_BLKSIZE_SHIFT);
> +		break;
> +	case 16 * 1024:
> +		val |= (RBR_BLKSIZE_16K << RBR_CFIG_B_BLKSIZE_SHIFT);
> +		break;
> +	case 32 * 1024:
> +		val |= (RBR_BLKSIZE_32K << RBR_CFIG_B_BLKSIZE_SHIFT);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +	val |= RBR_CFIG_B_VLD2;
> +	switch (rp->rbr_sizes[2]) {
> +	case 2 * 1024:
> +		val |= (RBR_BUFSZ2_2K << RBR_CFIG_B_BUFSZ2_SHIFT);
> +		break;
> +	case 4 * 1024:
> +		val |= (RBR_BUFSZ2_4K << RBR_CFIG_B_BUFSZ2_SHIFT);
> +		break;
> +	case 8 * 1024:
> +		val |= (RBR_BUFSZ2_8K << RBR_CFIG_B_BUFSZ2_SHIFT);
> +		break;
> +	case 16 * 1024:
> +		val |= (RBR_BUFSZ2_16K << RBR_CFIG_B_BUFSZ2_SHIFT);
> +		break;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +	val |= RBR_CFIG_B_VLD1;
> +	switch (rp->rbr_sizes[1]) {
> +	case 1 * 1024:
> +		val |= (RBR_BUFSZ1_1K << RBR_CFIG_B_BUFSZ1_SHIFT);
> +		break;
> +	case 2 * 1024:
> +		val |= (RBR_BUFSZ1_2K << RBR_CFIG_B_BUFSZ1_SHIFT);
> +		break;
> +	case 4 * 1024:
> +		val |= (RBR_BUFSZ1_4K << RBR_CFIG_B_BUFSZ1_SHIFT);
> +		break;
> +	case 8 * 1024:
> +		val |= (RBR_BUFSZ1_8K << RBR_CFIG_B_BUFSZ1_SHIFT);
> +		break;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +	val |= RBR_CFIG_B_VLD0;
> +	switch (rp->rbr_sizes[0]) {
> +	case 256:
> +		val |= (RBR_BUFSZ0_256 << RBR_CFIG_B_BUFSZ0_SHIFT);
> +		break;
> +	case 512:
> +		val |= (RBR_BUFSZ0_512 << RBR_CFIG_B_BUFSZ0_SHIFT);
> +		break;
> +	case 1 * 1024:
> +		val |= (RBR_BUFSZ0_1K << RBR_CFIG_B_BUFSZ0_SHIFT);
> +		break;
> +	case 2 * 1024:
> +		val |= (RBR_BUFSZ0_2K << RBR_CFIG_B_BUFSZ0_SHIFT);
> +		break;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	*ret = val;
> +	return 0;
> +}
> +
> +static int niu_enable_rx_channel(struct niu *np, int channel, int on)
> +{
> +	u64 val = nr64(RXDMA_CFIG1(channel));
> +	int limit;
> +
> +	if (on)
> +		val |= RXDMA_CFIG1_EN;
> +	else
> +		val &= ~RXDMA_CFIG1_EN;
> +	nw64(RXDMA_CFIG1(channel), val);
> +
> +	limit = 1000;
> +	while (--limit > 0) {
> +		if (nr64(RXDMA_CFIG1(channel)) & RXDMA_CFIG1_QST)
> +			break;
> +		udelay(10);
> +	}
> +	if (limit <= 0)
> +		return -ENODEV;
> +	return 0;
> +}
> +
> +static int niu_init_one_rx_channel(struct niu *np, struct rx_ring_info *rp)
> +{
> +	int err, channel = rp->rx_channel;
> +	u64 val;
> +
> +	err = niu_rx_channel_reset(np, channel);
> +	if (err)
> +		return err;
> +
> +	err = niu_rx_channel_lpage_init(np, channel);
> +	if (err)
> +		return err;
> +
> +	niu_rx_channel_wred_init(np, rp);
> +
> +	nw64(RX_DMA_ENT_MSK(channel), RX_DMA_ENT_MSK_RBR_EMPTY);
> +	nw64(RX_DMA_CTL_STAT(channel),
> +	     (RX_DMA_CTL_STAT_MEX |
> +	      RX_DMA_CTL_STAT_RCRTHRES |
> +	      RX_DMA_CTL_STAT_RCRTO |
> +	      RX_DMA_CTL_STAT_RBR_EMPTY));
> +	nw64(RXDMA_CFIG1(channel), rp->mbox_dma >> 32);
> +	nw64(RXDMA_CFIG2(channel), (rp->mbox_dma & 0x00000000ffffffc0));
> +	nw64(RBR_CFIG_A(channel),
> +	     ((u64)rp->rbr_table_size << RBR_CFIG_A_LEN_SHIFT) |
> +	     (rp->rbr_dma & (RBR_CFIG_A_STADDR_BASE | RBR_CFIG_A_STADDR)));
> +	err = niu_compute_rbr_cfig_b(rp, &val);
> +	if (err)
> +		return err;
> +	nw64(RBR_CFIG_B(channel), val);
> +	nw64(RCRCFIG_A(channel),
> +	     ((u64)rp->rcr_table_size << RCRCFIG_A_LEN_SHIFT) |
> +	     (rp->rcr_dma & (RCRCFIG_A_STADDR_BASE | RCRCFIG_A_STADDR)));
> +	nw64(RCRCFIG_B(channel),
> +	     ((u64)rp->rcr_pkt_threshold << RCRCFIG_B_PTHRES_SHIFT) |
> +	     RCRCFIG_B_ENTOUT |
> +	     ((u64)rp->rcr_timeout << RCRCFIG_B_TIMEOUT_SHIFT));
> +
> +	err = niu_enable_rx_channel(np, channel, 1);
> +	if (err)
> +		return err;
> +
> +	nw64(RBR_KICK(channel), rp->rbr_index);
> +
> +	val = nr64(RX_DMA_CTL_STAT(channel));
> +	val |= RX_DMA_CTL_STAT_RBR_EMPTY;
> +	nw64(RX_DMA_CTL_STAT(channel), val);
> +
> +	return 0;
> +}
> +
> +static int niu_init_rx_channels(struct niu *np)
> +{
> +	unsigned long flags;
> +	u64 seed = jiffies_64;
> +	int err, i;
> +
> +	niu_lock_parent(np, flags);
> +	nw64(RX_DMA_CK_DIV, np->parent->rxdma_clock_divider);
> +	nw64(RED_RAN_INIT, RED_RAN_INIT_OPMODE | (seed & RED_RAN_INIT_VAL));
> +	niu_unlock_parent(np, flags);
> +
> +	/* XXX RXDMA 32bit mode? XXX */
> +
> +	niu_init_rdc_groups(np);
> +	niu_init_drr_weight(np);
> +
> +	err = niu_init_hostinfo(np);
> +	if (err)
> +		return err;
> +
> +	for (i = 0; i < np->num_rx_rings; i++) {
> +		struct rx_ring_info *rp = &np->rx_rings[i];
> +
> +		err = niu_init_one_rx_channel(np, rp);
> +		if (err)
> +			return err;
> +	}
> +
> +	return 0;
> +}
> +
> +static int niu_set_ip_frag_rule(struct niu *np)
> +{
> +	struct niu_parent *parent = np->parent;
> +	struct niu_classifier *cp = &np->clas;
> +	struct niu_tcam_entry *tp;
> +	int index, err;
> +
> +	/* XXX fix this allocation scheme XXX */
> +	index = cp->tcam_index;
> +	tp = &parent->tcam[index];
> +
> +	/* Note that the noport bit is the same in both ipv4 and
> +	 * ipv6 format TCAM entries.
> +	 */
> +	memset(tp, 0, sizeof(*tp));
> +	tp->key[1] = TCAM_V4KEY1_NOPORT;
> +	tp->key_mask[1] = TCAM_V4KEY1_NOPORT;
> +	tp->assoc_data = (TCAM_ASSOCDATA_TRES_USE_OFFSET |
> +			  ((u64)0 << TCAM_ASSOCDATA_OFFSET_SHIFT));
> +	err = tcam_write(np, index, tp->key, tp->key_mask);
> +	if (err)
> +		return err;
> +	err = tcam_assoc_write(np, index, tp->assoc_data);
> +	if (err)
> +		return err;
> +
> +	return 0;
> +}
> +
> +static int niu_init_classifier_hw(struct niu *np)
> +{
> +	struct niu_parent *parent = np->parent;
> +	struct niu_classifier *cp = &np->clas;
> +	int i, err;
> +
> +	nw64(H1POLY, cp->h1_init);
> +	nw64(H2POLY, cp->h2_init);
> +
> +	err = niu_init_hostinfo(np);
> +	if (err)
> +		return err;
> +
> +	for (i = 0; i < ENET_VLAN_TBL_NUM_ENTRIES; i++) {
> +		struct niu_vlan_rdc *vp = &cp->vlan_mappings[i];
> +
> +		vlan_tbl_write(np, i, np->port,
> +			       vp->vlan_pref, vp->rdc_num);
> +	}
> +
> +	for (i = 0; i < cp->num_alt_mac_mappings; i++) {
> +		struct niu_altmac_rdc *ap = &cp->alt_mac_mappings[i];
> +
> +		err = niu_set_alt_mac_rdc_table(np, ap->alt_mac_num,
> +						ap->rdc_num, ap->mac_pref);
> +		if (err)
> +			return err;
> +	}
> +
> +	for (i = CLASS_CODE_USER_PROG1; i <= CLASS_CODE_SCTP_IPV6; i++) {
> +		int index = i - CLASS_CODE_USER_PROG1;
> +
> +		err = niu_set_tcam_key(np, i, parent->tcam_key[index]);
> +		if (err)
> +			return err;
> +		err = niu_set_flow_key(np, i, parent->flow_key[index]);
> +		if (err)
> +			return err;
> +	}
> +
> +	err = niu_set_ip_frag_rule(np);
> +	if (err)
> +		return err;
> +
> +	tcam_enable(np, 1);
> +
> +	return 0;
> +}
> +
> +static int niu_zcp_write(struct niu *np, int index, u64 *data)
> +{
> +	nw64(ZCP_RAM_DATA0, data[0]);
> +	nw64(ZCP_RAM_DATA1, data[1]);
> +	nw64(ZCP_RAM_DATA2, data[2]);
> +	nw64(ZCP_RAM_DATA3, data[3]);
> +	nw64(ZCP_RAM_DATA4, data[4]);
> +	nw64(ZCP_RAM_BE, ZCP_RAM_BE_VAL);
> +	nw64(ZCP_RAM_ACC,
> +	     (ZCP_RAM_ACC_WRITE |
> +	      (0 << ZCP_RAM_ACC_ZFCID_SHIFT) |
> +	      (ZCP_RAM_SEL_CFIFO(np->port) << ZCP_RAM_ACC_RAM_SEL_SHIFT)));
> +
> +	return niu_wait_bits_clear(np, ZCP_RAM_ACC, ZCP_RAM_ACC_BUSY,
> +				   1000, 100);
> +}
> +
> +static int niu_zcp_read(struct niu *np, int index, u64 *data)
> +{
> +	int err;
> +
> +	err = niu_wait_bits_clear(np, ZCP_RAM_ACC, ZCP_RAM_ACC_BUSY,
> +				  1000, 100);
> +	if (err) {
> +		printk(KERN_ERR PFX "%s: ZCP read busy won't clear, "
> +		       "ZCP_RAM_ACC[%llx]\n", np->dev->name,
> +		       (unsigned long long) nr64(ZCP_RAM_ACC));
> +		return err;
> +	}
> +
> +	nw64(ZCP_RAM_ACC,
> +	     (ZCP_RAM_ACC_READ |
> +	      (0 << ZCP_RAM_ACC_ZFCID_SHIFT) |
> +	      (ZCP_RAM_SEL_CFIFO(np->port) << ZCP_RAM_ACC_RAM_SEL_SHIFT)));
> +
> +	err = niu_wait_bits_clear(np, ZCP_RAM_ACC, ZCP_RAM_ACC_BUSY,
> +				  1000, 100);
> +	if (err) {
> +		printk(KERN_ERR PFX "%s: ZCP read busy2 won't clear, "
> +		       "ZCP_RAM_ACC[%llx]\n", np->dev->name,
> +		       (unsigned long long) nr64(ZCP_RAM_ACC));
> +		return err;
> +	}
> +
> +	data[0] = nr64(ZCP_RAM_DATA0);
> +	data[1] = nr64(ZCP_RAM_DATA1);
> +	data[2] = nr64(ZCP_RAM_DATA2);
> +	data[3] = nr64(ZCP_RAM_DATA3);
> +	data[4] = nr64(ZCP_RAM_DATA4);
> +
> +	return 0;
> +}
> +
> +static void niu_zcp_cfifo_reset(struct niu *np)
> +{
> +	u64 val = nr64(RESET_CFIFO);
> +
> +	val |= RESET_CFIFO_RST(np->port);
> +	nw64(RESET_CFIFO, val);
> +	udelay(10);
> +
> +	val &= ~RESET_CFIFO_RST(np->port);
> +	nw64(RESET_CFIFO, val);
> +}
> +
> +static int niu_init_zcp(struct niu *np)
> +{
> +	u64 data[5], rbuf[5];
> +	int i, max, err;
> +
> +	if (np->parent->plat_type != PLAT_TYPE_NIU) {
> +		if (np->port == 0 || np->port == 1)
> +			max = ATLAS_P0_P1_CFIFO_ENTRIES;
> +		else
> +			max = ATLAS_P2_P3_CFIFO_ENTRIES;
> +	} else
> +		max = NIU_CFIFO_ENTRIES;
> +
> +	data[0] = 0;
> +	data[1] = 0;
> +	data[2] = 0;
> +	data[3] = 0;
> +	data[4] = 0;
> +
> +	for (i = 0; i < max; i++) {
> +		err = niu_zcp_write(np, i, data);
> +		if (err)
> +			return err;
> +		err = niu_zcp_read(np, i, rbuf);
> +		if (err)
> +			return err;
> +	}
> +
> +	niu_zcp_cfifo_reset(np);
> +	nw64(CFIFO_ECC(np->port), 0);
> +	nw64(ZCP_INT_STAT, ZCP_INT_STAT_ALL);
> +	(void) nr64(ZCP_INT_STAT);
> +	nw64(ZCP_INT_MASK, ZCP_INT_MASK_ALL);
> +
> +	return 0;
> +}
> +
> +static void niu_ipp_write(struct niu *np, int index, u64 *data)
> +{
> +	u64 val = nr64_ipp(IPP_CFIG);
> +
> +	nw64_ipp(IPP_CFIG, val | IPP_CFIG_DFIFO_PIO_W);
> +	nw64_ipp(IPP_DFIFO_WR_PTR, index);
> +	nw64_ipp(IPP_DFIFO_WR0, data[0]);
> +	nw64_ipp(IPP_DFIFO_WR1, data[1]);
> +	nw64_ipp(IPP_DFIFO_WR2, data[2]);
> +	nw64_ipp(IPP_DFIFO_WR3, data[3]);
> +	nw64_ipp(IPP_DFIFO_WR4, data[4]);
> +	nw64_ipp(IPP_CFIG, val & ~IPP_CFIG_DFIFO_PIO_W);
> +}
> +
> +static void niu_ipp_read(struct niu *np, int index, u64 *data)
> +{
> +	nw64_ipp(IPP_DFIFO_RD_PTR, index);
> +	data[0] = nr64_ipp(IPP_DFIFO_RD0);
> +	data[1] = nr64_ipp(IPP_DFIFO_RD1);
> +	data[2] = nr64_ipp(IPP_DFIFO_RD2);
> +	data[3] = nr64_ipp(IPP_DFIFO_RD3);
> +	data[4] = nr64_ipp(IPP_DFIFO_RD4);
> +}
> +
> +static int niu_ipp_reset(struct niu *np)
> +{
> +	return niu_set_and_wait_clear_ipp(np, IPP_CFIG, IPP_CFIG_SOFT_RST,
> +					  1000, 100, "IPP_CFIG");
> +}
> +
> +static int niu_init_ipp(struct niu *np)
> +{
> +	u64 data[5], rbuf[5], val;
> +	int i, max, err;
> +
> +	if (np->parent->plat_type != PLAT_TYPE_NIU) {
> +		if (np->port == 0 || np->port == 1)
> +			max = ATLAS_P0_P1_DFIFO_ENTRIES;
> +		else
> +			max = ATLAS_P2_P3_DFIFO_ENTRIES;
> +	} else
> +		max = NIU_DFIFO_ENTRIES;
> +
> +	data[0] = 0;
> +	data[1] = 0;
> +	data[2] = 0;
> +	data[3] = 0;
> +	data[4] = 0;
> +
> +	for (i = 0; i < max; i++) {
> +		niu_ipp_write(np, i, data);
> +		niu_ipp_read(np, i, rbuf);
> +	}
> +
> +	(void) nr64_ipp(IPP_INT_STAT);
> +	(void) nr64_ipp(IPP_INT_STAT);
> +
> +	err = niu_ipp_reset(np);
> +	if (err)
> +		return err;
> +
> +	(void) nr64_ipp(IPP_PKT_DIS);
> +	(void) nr64_ipp(IPP_BAD_CS_CNT);
> +	(void) nr64_ipp(IPP_ECC);
> +
> +	(void) nr64_ipp(IPP_INT_STAT);
> +
> +	nw64_ipp(IPP_MSK, ~IPP_MSK_ALL);
> +
> +	val = nr64_ipp(IPP_CFIG);
> +	val &= ~IPP_CFIG_IP_MAX_PKT;
> +	val |= (IPP_CFIG_IPP_ENABLE |
> +		IPP_CFIG_DFIFO_ECC_EN |
> +		IPP_CFIG_DROP_BAD_CRC |
> +		IPP_CFIG_CKSUM_EN |
> +		(0x1ffff << IPP_CFIG_IP_MAX_PKT_SHIFT));
> +	nw64_ipp(IPP_CFIG, val);
> +
> +	return 0;
> +}
> +
> +static void niu_init_xif_xmac(struct niu *np)
> +{
> +	struct niu_link_config *lp = &np->link_config;
> +	u64 val;
> +
> +	val = nr64_mac(XMAC_CONFIG);
> +
> +	if ((np->flags & NIU_FLAGS_10G) != 0 &&
> +	    (np->flags & NIU_FLAGS_FIBER) != 0) {
> +		if (netif_carrier_ok(np->dev)) {
> +			val |= XMAC_CONFIG_LED_POLARITY;
> +			val &= ~XMAC_CONFIG_FORCE_LED_ON;
> +		} else {
> +			val |= XMAC_CONFIG_FORCE_LED_ON;
> +			val &= ~XMAC_CONFIG_LED_POLARITY;
> +		}
> +	}
> +
> +	val &= ~XMAC_CONFIG_SEL_POR_CLK_SRC;
> +
> +	val |= XMAC_CONFIG_TX_OUTPUT_EN;
> +
> +	if (lp->loopback_mode == LOOPBACK_MAC) {
> +		val &= ~XMAC_CONFIG_SEL_POR_CLK_SRC;
> +		val |= XMAC_CONFIG_LOOPBACK;
> +	} else {
> +		val &= ~XMAC_CONFIG_LOOPBACK;
> +	}
> +
> +	if (np->flags & NIU_FLAGS_10G) {
> +		val &= ~XMAC_CONFIG_LFS_DISABLE;
> +	} else {
> +		val |= XMAC_CONFIG_LFS_DISABLE;
> +		if (!(np->flags & NIU_FLAGS_FIBER))
> +			val |= XMAC_CONFIG_1G_PCS_BYPASS;
> +		else
> +			val &= ~XMAC_CONFIG_1G_PCS_BYPASS;
> +	}
> +
> +	val &= ~XMAC_CONFIG_10G_XPCS_BYPASS;
> +
> +	if (lp->active_speed == SPEED_100)
> +		val |= XMAC_CONFIG_SEL_CLK_25MHZ;
> +	else
> +		val &= ~XMAC_CONFIG_SEL_CLK_25MHZ;
> +
> +	nw64_mac(XMAC_CONFIG, val);
> +
> +	val = nr64_mac(XMAC_CONFIG);
> +	val &= ~XMAC_CONFIG_MODE_MASK;
> +	if (np->flags & NIU_FLAGS_10G) {
> +		val |= XMAC_CONFIG_MODE_XGMII;
> +	} else {
> +		if (lp->active_speed == SPEED_100)
> +			val |= XMAC_CONFIG_MODE_MII;
> +		else
> +			val |= XMAC_CONFIG_MODE_GMII;
> +	}
> +
> +	nw64_mac(XMAC_CONFIG, val);
> +}
> +
> +static void niu_init_xif_bmac(struct niu *np)
> +{
> +	struct niu_link_config *lp = &np->link_config;
> +	u64 val;
> +
> +	val = BMAC_XIF_CONFIG_TX_OUTPUT_EN;
> +
> +	if (lp->loopback_mode == LOOPBACK_MAC)
> +		val |= BMAC_XIF_CONFIG_MII_LOOPBACK;
> +	else
> +		val &= ~BMAC_XIF_CONFIG_MII_LOOPBACK;
> +
> +	if (lp->active_speed == SPEED_1000)
> +		val |= BMAC_XIF_CONFIG_GMII_MODE;
> +	else
> +		val &= ~BMAC_XIF_CONFIG_GMII_MODE;
> +
> +	val &= ~(BMAC_XIF_CONFIG_LINK_LED |
> +		 BMAC_XIF_CONFIG_LED_POLARITY);
> +
> +	if (!(np->flags & NIU_FLAGS_10G) &&
> +	    !(np->flags & NIU_FLAGS_FIBER) &&
> +	    lp->active_speed == SPEED_100)
> +		val |= BMAC_XIF_CONFIG_25MHZ_CLOCK;
> +	else
> +		val &= ~BMAC_XIF_CONFIG_25MHZ_CLOCK;
> +
> +	nw64_mac(BMAC_XIF_CONFIG, val);
> +}
> +
> +static void niu_init_xif(struct niu *np)
> +{
> +	if (np->flags & NIU_FLAGS_XMAC)
> +		niu_init_xif_xmac(np);
> +	else
> +		niu_init_xif_bmac(np);
> +}
> +
> +static void niu_pcs_mii_reset(struct niu *np)
> +{
> +	u64 val = nr64_pcs(PCS_MII_CTL);
> +	val |= PCS_MII_CTL_RST;
> +	nw64_pcs(PCS_MII_CTL, val);
> +}
> +
> +static void niu_xpcs_reset(struct niu *np)
> +{
> +	u64 val = nr64_xpcs(XPCS_CONTROL1);
> +	val |= XPCS_CONTROL1_RESET;
> +	nw64_xpcs(XPCS_CONTROL1, val);
> +}
> +
> +static int niu_init_pcs(struct niu *np)
> +{
> +	struct niu_link_config *lp = &np->link_config;
> +	u64 val;
> +
> +	switch (np->flags & (NIU_FLAGS_10G | NIU_FLAGS_FIBER)) {
> +	case NIU_FLAGS_FIBER:
> +		/* 1G fiber */
> +		nw64_pcs(PCS_CONF, PCS_CONF_MASK | PCS_CONF_ENABLE);
> +		nw64_pcs(PCS_DPATH_MODE, 0);
> +		niu_pcs_mii_reset(np);
> +		break;
> +
> +	case NIU_FLAGS_10G:
> +	case NIU_FLAGS_10G | NIU_FLAGS_FIBER:
> +		if (!(np->flags & NIU_FLAGS_XMAC))
> +			return -EINVAL;
> +
> +		/* 10G copper or fiber */
> +		val = nr64_mac(XMAC_CONFIG);
> +		val &= ~XMAC_CONFIG_10G_XPCS_BYPASS;
> +		nw64_mac(XMAC_CONFIG, val);
> +
> +		niu_xpcs_reset(np);
> +
> +		val = nr64_xpcs(XPCS_CONTROL1);
> +		if (lp->loopback_mode == LOOPBACK_PHY)
> +			val |= XPCS_CONTROL1_LOOPBACK;
> +		else
> +			val &= ~XPCS_CONTROL1_LOOPBACK;
> +		nw64_xpcs(XPCS_CONTROL1, val);
> +
> +		nw64_xpcs(XPCS_DESKEW_ERR_CNT, 0);
> +		(void) nr64_xpcs(XPCS_SYMERR_CNT01);
> +		(void) nr64_xpcs(XPCS_SYMERR_CNT23);
> +		break;
> +
> +	case 0:
> +		/* 1G copper */
> +		nw64_pcs(PCS_DPATH_MODE, PCS_DPATH_MODE_MII);
> +		niu_pcs_mii_reset(np);
> +		break;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int niu_reset_tx_xmac(struct niu *np)
> +{
> +	return niu_set_and_wait_clear_mac(np, XTXMAC_SW_RST,
> +					  (XTXMAC_SW_RST_REG_RS |
> +					   XTXMAC_SW_RST_SOFT_RST),
> +					  1000, 100, "XTXMAC_SW_RST");
> +}
> +
> +static int niu_reset_tx_bmac(struct niu *np)
> +{
> +	int limit;
> +
> +	nw64_mac(BTXMAC_SW_RST, BTXMAC_SW_RST_RESET);
> +	limit = 1000;
> +	while (--limit >= 0) {
> +		if (!(nr64_mac(BTXMAC_SW_RST) & BTXMAC_SW_RST_RESET))
> +			break;
> +		udelay(100);
> +	}
> +	if (limit < 0) {
> +		printk(KERN_ERR PFX "Port %u TX BMAC would not reset, "
> +		       "BTXMAC_SW_RST[%llx]\n",
> +		       np->port,
> +		       (unsigned long long) nr64_mac(BTXMAC_SW_RST));
> +		return -ENODEV;
> +	}
> +
> +	return 0;
> +}
> +
> +static int niu_reset_tx_mac(struct niu *np)
> +{
> +	if (np->flags & NIU_FLAGS_XMAC)
> +		return niu_reset_tx_xmac(np);
> +	else
> +		return niu_reset_tx_bmac(np);
> +}
> +
> +static void niu_init_tx_xmac(struct niu *np, u64 min, u64 max)
> +{
> +	u64 val;
> +
> +	val = nr64_mac(XMAC_MIN);
> +	val &= ~(XMAC_MIN_TX_MIN_PKT_SIZE | 
> +		 XMAC_MIN_RX_MIN_PKT_SIZE);
> +	val |= (min << XMAC_MIN_RX_MIN_PKT_SIZE_SHFT);
> +	val |= (min << XMAC_MIN_TX_MIN_PKT_SIZE_SHFT);
> +	nw64_mac(XMAC_MIN, val);
> +
> +	nw64_mac(XMAC_MAX, max);
> +
> +	nw64_mac(XTXMAC_STAT_MSK, ~(u64)0);
> +
> +	val = nr64_mac(XMAC_IPG);
> +	if (np->flags & NIU_FLAGS_10G) {
> +		val &= ~XMAC_IPG_IPG_XGMII;
> +		val |= (IPG_12_15_XGMII << XMAC_IPG_IPG_XGMII_SHIFT);
> +	} else {
> +		val &= ~XMAC_IPG_IPG_MII_GMII;
> +		val |= (IPG_12_MII_GMII << XMAC_IPG_IPG_MII_GMII_SHIFT);
> +	}
> +	nw64_mac(XMAC_IPG, val);
> +
> +	val = nr64_mac(XMAC_CONFIG);
> +	val &= ~(XMAC_CONFIG_ALWAYS_NO_CRC |
> +		 XMAC_CONFIG_STRETCH_MODE |
> +		 XMAC_CONFIG_VAR_MIN_IPG_EN |
> +		 XMAC_CONFIG_TX_ENABLE);
> +	nw64_mac(XMAC_CONFIG, val);
> +
> +	nw64_mac(TXMAC_FRM_CNT, 0);
> +	nw64_mac(TXMAC_BYTE_CNT, 0);
> +}
> +
> +static void niu_init_tx_bmac(struct niu *np, u64 min, u64 max)
> +{
> +	u64 val;
> +
> +	nw64_mac(BMAC_MIN_FRAME, min);
> +	nw64_mac(BMAC_MAX_FRAME, max);
> +
> +	nw64_mac(BTXMAC_STATUS_MASK, ~(u64)0);
> +	nw64_mac(BMAC_CTRL_TYPE, 0x8808);
> +	nw64_mac(BMAC_PREAMBLE_SIZE, 7);
> +
> +	val = nr64_mac(BTXMAC_CONFIG);
> +	val &= ~(BTXMAC_CONFIG_FCS_DISABLE |
> +		 BTXMAC_CONFIG_ENABLE);
> +	nw64_mac(BTXMAC_CONFIG, val);
> +}
> +
> +static void niu_init_tx_mac(struct niu *np)
> +{
> +	u64 min, max;
> +
> +	min = 64;
> +	if (np->dev->mtu > ETH_DATA_LEN)
> +		max = 9216;
> +	else
> +		max = 1522;
> +
> +	/* The XMAC_MIN register only accepts values for TX min which
> +	 * have the low 3 bits cleared.
> +	 */
> +	BUILD_BUG_ON(min & 0x7);
> +
> +	if (np->flags & NIU_FLAGS_XMAC)
> +		niu_init_tx_xmac(np, min, max);
> +	else
> +		niu_init_tx_bmac(np, min, max);
> +}
> +
> +static int niu_reset_rx_xmac(struct niu *np)
> +{
> +	int limit;
> +
> +	nw64_mac(XRXMAC_SW_RST,
> +		 XRXMAC_SW_RST_REG_RS | XRXMAC_SW_RST_SOFT_RST);
> +	limit = 1000;
> +	while (--limit >= 0) {
> +		if (!(nr64_mac(XRXMAC_SW_RST) & (XRXMAC_SW_RST_REG_RS |
> +						 XRXMAC_SW_RST_SOFT_RST)))
> +		    break;
> +		udelay(100);
> +	}
> +	if (limit < 0) {
> +		printk(KERN_ERR PFX "Port %u RX XMAC would not reset, "
> +		       "XRXMAC_SW_RST[%llx]\n",
> +		       np->port,
> +		       (unsigned long long) nr64_mac(XRXMAC_SW_RST));
> +		return -ENODEV;
> +	}
> +
> +	return 0;
> +}
> +
> +static int niu_reset_rx_bmac(struct niu *np)
> +{
> +	int limit;
> +
> +	nw64_mac(BRXMAC_SW_RST, BRXMAC_SW_RST_RESET);
> +	limit = 1000;
> +	while (--limit >= 0) {
> +		if (!(nr64_mac(BRXMAC_SW_RST) & BRXMAC_SW_RST_RESET))
> +			break;
> +		udelay(100);
> +	}
> +	if (limit < 0) {
> +		printk(KERN_ERR PFX "Port %u RX BMAC would not reset, "
> +		       "BRXMAC_SW_RST[%llx]\n",
> +		       np->port,
> +		       (unsigned long long) nr64_mac(BRXMAC_SW_RST));
> +		return -ENODEV;
> +	}
> +
> +	return 0;
> +}
> +
> +static int niu_reset_rx_mac(struct niu *np)
> +{
> +	if (np->flags & NIU_FLAGS_XMAC)
> +		return niu_reset_rx_xmac(np);
> +	else
> +		return niu_reset_rx_bmac(np);
> +}
> +
> +static void niu_init_rx_xmac(struct niu *np)
> +{
> +	struct niu_parent *parent = np->parent;
> +	struct niu_rdc_tables *tp = &parent->rdc_group_cfg[np->port];
> +	int first_rdc_table = tp->first_table_num;
> +	unsigned long i;
> +	u64 val;
> +
> +	nw64_mac(XMAC_ADD_FILT0, 0);
> +	nw64_mac(XMAC_ADD_FILT1, 0);
> +	nw64_mac(XMAC_ADD_FILT2, 0);
> +	nw64_mac(XMAC_ADD_FILT12_MASK, 0);
> +	nw64_mac(XMAC_ADD_FILT00_MASK, 0);
> +	for (i = 0; i < MAC_NUM_HASH; i++)
> +		nw64_mac(XMAC_HASH_TBL(i), 0);
> +	nw64_mac(XRXMAC_STAT_MSK, ~(u64)0);
> +	niu_set_primary_mac_rdc_table(np, first_rdc_table, 1);
> +	niu_set_multicast_mac_rdc_table(np, first_rdc_table, 1);
> +
> +	val = nr64_mac(XMAC_CONFIG);
> +	val &= ~(XMAC_CONFIG_RX_MAC_ENABLE |
> +		 XMAC_CONFIG_PROMISCUOUS |
> +		 XMAC_CONFIG_PROMISC_GROUP |
> +		 XMAC_CONFIG_ERR_CHK_DIS |
> +		 XMAC_CONFIG_RX_CRC_CHK_DIS |
> +		 XMAC_CONFIG_RESERVED_MULTICAST |
> +		 XMAC_CONFIG_RX_CODEV_CHK_DIS |
> +		 XMAC_CONFIG_ADDR_FILTER_EN |
> +		 XMAC_CONFIG_RCV_PAUSE_ENABLE |
> +		 XMAC_CONFIG_STRIP_CRC |
> +		 XMAC_CONFIG_PASS_FLOW_CTRL |
> +		 XMAC_CONFIG_MAC2IPP_PKT_CNT_EN);
> +	val |= (XMAC_CONFIG_HASH_FILTER_EN);
> +	nw64_mac(XMAC_CONFIG, val);
> +
> +	nw64_mac(RXMAC_BT_CNT, 0);
> +	nw64_mac(RXMAC_BC_FRM_CNT, 0);
> +	nw64_mac(RXMAC_MC_FRM_CNT, 0);
> +	nw64_mac(RXMAC_FRAG_CNT, 0);
> +	nw64_mac(RXMAC_HIST_CNT1, 0);
> +	nw64_mac(RXMAC_HIST_CNT2, 0);
> +	nw64_mac(RXMAC_HIST_CNT3, 0);
> +	nw64_mac(RXMAC_HIST_CNT4, 0);
> +	nw64_mac(RXMAC_HIST_CNT5, 0);
> +	nw64_mac(RXMAC_HIST_CNT6, 0);
> +	nw64_mac(RXMAC_HIST_CNT7, 0);
> +	nw64_mac(RXMAC_MPSZER_CNT, 0);
> +	nw64_mac(RXMAC_CRC_ER_CNT, 0);
> +	nw64_mac(RXMAC_CD_VIO_CNT, 0);
> +	nw64_mac(LINK_FAULT_CNT, 0);
> +}
> +
> +static void niu_init_rx_bmac(struct niu *np)
> +{
> +	struct niu_parent *parent = np->parent;
> +	struct niu_rdc_tables *tp = &parent->rdc_group_cfg[np->port];
> +	int first_rdc_table = tp->first_table_num;
> +	unsigned long i;
> +	u64 val;
> +
> +	nw64_mac(BMAC_ADD_FILT0, 0);
> +	nw64_mac(BMAC_ADD_FILT1, 0);
> +	nw64_mac(BMAC_ADD_FILT2, 0);
> +	nw64_mac(BMAC_ADD_FILT12_MASK, 0);
> +	nw64_mac(BMAC_ADD_FILT00_MASK, 0);
> +	for (i = 0; i < MAC_NUM_HASH; i++)
> +		nw64_mac(BMAC_HASH_TBL(i), 0);
> +	niu_set_primary_mac_rdc_table(np, first_rdc_table, 1);
> +	niu_set_multicast_mac_rdc_table(np, first_rdc_table, 1);
> +	nw64_mac(BRXMAC_STATUS_MASK, ~(u64)0);
> +
> +	val = nr64_mac(BRXMAC_CONFIG);
> +	val &= ~(BRXMAC_CONFIG_ENABLE |
> +		 BRXMAC_CONFIG_STRIP_PAD |
> +		 BRXMAC_CONFIG_STRIP_FCS |
> +		 BRXMAC_CONFIG_PROMISC |
> +		 BRXMAC_CONFIG_PROMISC_GRP |
> +		 BRXMAC_CONFIG_ADDR_FILT_EN |
> +		 BRXMAC_CONFIG_DISCARD_DIS);
> +	val |= (BRXMAC_CONFIG_HASH_FILT_EN);
> +	nw64_mac(BRXMAC_CONFIG, val);
> +
> +	val = nr64_mac(BMAC_ADDR_CMPEN);
> +	val |= BMAC_ADDR_CMPEN_EN0;
> +	nw64_mac(BMAC_ADDR_CMPEN, val);
> +}
> +
> +static void niu_init_rx_mac(struct niu *np)
> +{
> +	niu_set_primary_mac(np, np->dev->dev_addr);
> +
> +	if (np->flags & NIU_FLAGS_XMAC)
> +		niu_init_rx_xmac(np);
> +	else
> +		niu_init_rx_bmac(np);
> +}
> +
> +static void niu_enable_tx_xmac(struct niu *np, int on)
> +{
> +	u64 val = nr64_mac(XMAC_CONFIG);
> +
> +	if (on)
> +		val |= XMAC_CONFIG_TX_ENABLE;
> +	else
> +		val &= ~XMAC_CONFIG_TX_ENABLE;
> +	nw64_mac(XMAC_CONFIG, val);
> +}
> +
> +static void niu_enable_tx_bmac(struct niu *np, int on)
> +{
> +	u64 val = nr64_mac(BTXMAC_CONFIG);
> +
> +	if (on)
> +		val |= BTXMAC_CONFIG_ENABLE;
> +	else
> +		val &= ~BTXMAC_CONFIG_ENABLE;
> +	nw64_mac(BTXMAC_CONFIG, val);
> +}
> +
> +static void niu_enable_tx_mac(struct niu *np, int on)
> +{
> +	if (np->flags & NIU_FLAGS_XMAC)
> +		niu_enable_tx_xmac(np, on);
> +	else
> +		niu_enable_tx_bmac(np, on);
> +}
> +
> +static void niu_enable_rx_xmac(struct niu *np, int on)
> +{
> +	u64 val = nr64_mac(XMAC_CONFIG);
> +
> +	val &= ~(XMAC_CONFIG_HASH_FILTER_EN |
> +		 XMAC_CONFIG_PROMISCUOUS);
> +
> +	if (np->flags & NIU_FLAGS_MCAST)
> +		val |= XMAC_CONFIG_HASH_FILTER_EN;
> +	if (np->flags & NIU_FLAGS_PROMISC)
> +		val |= XMAC_CONFIG_PROMISCUOUS;
> +
> +	if (on)
> +		val |= XMAC_CONFIG_RX_MAC_ENABLE;
> +	else
> +		val &= ~XMAC_CONFIG_RX_MAC_ENABLE;
> +	nw64_mac(XMAC_CONFIG, val);
> +}
> +
> +static void niu_enable_rx_bmac(struct niu *np, int on)
> +{
> +	u64 val = nr64_mac(BRXMAC_CONFIG);
> +
> +	val &= ~(BRXMAC_CONFIG_HASH_FILT_EN |
> +		 BRXMAC_CONFIG_PROMISC);
> +
> +	if (np->flags & NIU_FLAGS_MCAST)
> +		val |= BRXMAC_CONFIG_HASH_FILT_EN;
> +	if (np->flags & NIU_FLAGS_PROMISC)
> +		val |= BRXMAC_CONFIG_PROMISC;
> +
> +	if (on)
> +		val |= BRXMAC_CONFIG_ENABLE;
> +	else
> +		val &= ~BRXMAC_CONFIG_ENABLE;
> +	nw64_mac(BRXMAC_CONFIG, val);
> +}
> +
> +static void niu_enable_rx_mac(struct niu *np, int on)
> +{
> +	if (np->flags & NIU_FLAGS_XMAC)
> +		niu_enable_rx_xmac(np, on);
> +	else
> +		niu_enable_rx_bmac(np, on);
> +}
> +
> +static int niu_init_mac(struct niu *np)
> +{
> +	int err;
> +
> +	niu_init_xif(np);
> +	err = niu_init_pcs(np);
> +	if (err)
> +		return err;
> +
> +	err = niu_reset_tx_mac(np);
> +	if (err)
> +		return err;
> +	niu_init_tx_mac(np);
> +	err = niu_reset_rx_mac(np);
> +	if (err)
> +		return err;
> +	niu_init_rx_mac(np);
> +
> +	/* This looks hookey but the RX MAC reset we just did will
> +	 * undo some of the state we setup in niu_init_tx_mac() so we
> +	 * have to call it again.  In particular, the RX MAC reset will
> +	 * set the XMAC_MAX register back to it's default value.
> +	 */
> +	niu_init_tx_mac(np);
> +	niu_enable_tx_mac(np, 1);
> +
> +	niu_enable_rx_mac(np, 1);
> +
> +	return 0;
> +}
> +
> +static void niu_stop_one_tx_channel(struct niu *np, struct tx_ring_info *rp)
> +{
> +	(void) niu_tx_channel_stop(np, rp->tx_channel);
> +}
> +
> +static void niu_stop_tx_channels(struct niu *np)
> +{
> +	int i;
> +
> +	for (i = 0; i < np->num_tx_rings; i++) {
> +		struct tx_ring_info *rp = &np->tx_rings[i];
> +
> +		niu_stop_one_tx_channel(np, rp);
> +	}
> +}
> +
> +static void niu_reset_one_tx_channel(struct niu *np, struct tx_ring_info *rp)
> +{
> +	(void) niu_tx_channel_reset(np, rp->tx_channel);
> +}
> +
> +static void niu_reset_tx_channels(struct niu *np)
> +{
> +	int i;
> +
> +	for (i = 0; i < np->num_tx_rings; i++) {
> +		struct tx_ring_info *rp = &np->tx_rings[i];
> +
> +		niu_reset_one_tx_channel(np, rp);
> +	}
> +}
> +
> +static void niu_stop_one_rx_channel(struct niu *np, struct rx_ring_info *rp)
> +{
> +	(void) niu_enable_rx_channel(np, rp->rx_channel, 0);
> +}
> +
> +static void niu_stop_rx_channels(struct niu *np)
> +{
> +	int i;
> +
> +	for (i = 0; i < np->num_rx_rings; i++) {
> +		struct rx_ring_info *rp = &np->rx_rings[i];
> +
> +		niu_stop_one_rx_channel(np, rp);
> +	}
> +}
> +
> +static void niu_reset_one_rx_channel(struct niu *np, struct rx_ring_info *rp)
> +{
> +	int channel = rp->rx_channel;
> +
> +	(void) niu_rx_channel_reset(np, channel);
> +	nw64(RX_DMA_ENT_MSK(channel), RX_DMA_ENT_MSK_ALL);
> +	nw64(RX_DMA_CTL_STAT(channel), 0);
> +	(void) niu_enable_rx_channel(np, channel, 0);
> +}
> +
> +static void niu_reset_rx_channels(struct niu *np)
> +{
> +	int i;
> +
> +	for (i = 0; i < np->num_rx_rings; i++) {
> +		struct rx_ring_info *rp = &np->rx_rings[i];
> +
> +		niu_reset_one_rx_channel(np, rp);
> +	}
> +}
> +
> +static void niu_disable_ipp(struct niu *np)
> +{
> +	u64 rd, wr, val;
> +	int limit;
> +
> +	rd = nr64_ipp(IPP_DFIFO_RD_PTR);
> +	wr = nr64_ipp(IPP_DFIFO_WR_PTR);
> +	limit = 100;
> +	while (--limit >= 0 && (rd != wr)) {
> +		rd = nr64_ipp(IPP_DFIFO_RD_PTR);
> +		wr = nr64_ipp(IPP_DFIFO_WR_PTR);
> +	}
> +	if (limit < 0 &&
> +	    (rd != 0 && wr != 1)) {
> +		printk(KERN_ERR PFX "%s: IPP would not quiesce, "
> +		       "rd_ptr[%llx] wr_ptr[%llx]\n",
> +		       np->dev->name,
> +		       (unsigned long long) nr64_ipp(IPP_DFIFO_RD_PTR),
> +		       (unsigned long long) nr64_ipp(IPP_DFIFO_WR_PTR));
> +	}
> +
> +	val = nr64_ipp(IPP_CFIG);
> +	val &= ~(IPP_CFIG_IPP_ENABLE |
> +		 IPP_CFIG_DFIFO_ECC_EN |
> +		 IPP_CFIG_DROP_BAD_CRC |
> +		 IPP_CFIG_CKSUM_EN);
> +	nw64_ipp(IPP_CFIG, val);
> +
> +	(void) niu_ipp_reset(np);
> +}
> +
> +static int niu_init_hw(struct niu *np)
> +{
> +	int i, err;
> +
> +	niudbg(INIT_HW, "%s: Initialize TXC\n", np->dev->name);
> +	niu_txc_enable_port(np, 1);
> +	niu_txc_port_dma_enable(np, 1);
> +	niu_txc_set_imask(np, 0);
> +
> +	niudbg(INIT_HW, "%s: Initialize TX channels\n", np->dev->name);
> +	for (i = 0; i < np->num_tx_rings; i++) {
> +		struct tx_ring_info *rp = &np->tx_rings[i];
> +
> +		err = niu_init_one_tx_channel(np, rp);
> +		if (err)
> +			return err;
> +	}
> +
> +	niudbg(INIT_HW, "%s: Initialize RX channels\n", np->dev->name);
> +	err = niu_init_rx_channels(np);
> +	if (err)
> +		goto out_uninit_tx_channels;
> +
> +	niudbg(INIT_HW, "%s: Initialize classifier\n", np->dev->name);
> +	err = niu_init_classifier_hw(np);
> +	if (err)
> +		goto out_uninit_rx_channels;
> +
> +	niudbg(INIT_HW, "%s: Initialize ZCP\n", np->dev->name);
> +	err = niu_init_zcp(np);
> +	if (err)
> +		goto out_uninit_rx_channels;
> +
> +	niudbg(INIT_HW, "%s: Initialize IPP\n", np->dev->name);
> +	err = niu_init_ipp(np);
> +	if (err)
> +		goto out_uninit_rx_channels;
> +
> +	niudbg(INIT_HW, "%s: Initialize MAC\n", np->dev->name);
> +	err = niu_init_mac(np);
> +	if (err)
> +		goto out_uninit_ipp;
> +
> +	return 0;
> +
> +out_uninit_ipp:
> +	niudbg(INIT_HW, "%s: Uninit IPP\n", np->dev->name);
> +	niu_disable_ipp(np);
> +
> +out_uninit_rx_channels:
> +	niudbg(INIT_HW, "%s: Uninit RX channels\n", np->dev->name);
> +	niu_stop_rx_channels(np);
> +	niu_reset_rx_channels(np);
> +
> +out_uninit_tx_channels:
> +	niudbg(INIT_HW, "%s: Uninit TX channels\n", np->dev->name);
> +	niu_stop_tx_channels(np);
> +	niu_reset_tx_channels(np);
> +
> +	return err;
> +}
> +
> +static void niu_stop_hw(struct niu *np)
> +{
> +	niudbg(STOP_HW, "%s: Disable interrupts\n", np->dev->name);
> +	niu_enable_interrupts(np, 0);
> +
> +	niudbg(STOP_HW, "%s: Disable RX MAC\n", np->dev->name);
> +	niu_enable_rx_mac(np, 0);
> +
> +	niudbg(STOP_HW, "%s: Disable IPP\n", np->dev->name);
> +	niu_disable_ipp(np);
> +
> +	niudbg(STOP_HW, "%s: Stop TX channels\n", np->dev->name);
> +	niu_stop_tx_channels(np);
> +
> +	niudbg(STOP_HW, "%s: Stop RX channels\n", np->dev->name);
> +	niu_stop_rx_channels(np);
> +
> +	niudbg(STOP_HW, "%s: Reset TX channels\n", np->dev->name);
> +	niu_reset_tx_channels(np);
> +
> +	niudbg(STOP_HW, "%s: Reset RX channels\n", np->dev->name);
> +	niu_reset_rx_channels(np);
> +}
> +
> +static int niu_request_irq(struct niu *np)
> +{
> +	int i, j, err;
> +
> +	err = 0;
> +	for (i = 0; i < np->num_ldg; i++) {
> +		struct niu_ldg *lp = &np->ldg[i];
> +		int err;
> +
> +		err = request_irq(lp->irq, niu_interrupt,
> +				  IRQF_SHARED | IRQF_SAMPLE_RANDOM,
> +				  np->dev->name, lp);
> +		if (err)
> +			goto out_free_irqs;
> +
> +	}
> +
> +	return 0;
> +
> +out_free_irqs:
> +	for (j = 0; j < i; j++) {
> +		struct niu_ldg *lp = &np->ldg[j];
> +
> +		free_irq(lp->irq, lp);
> +	}
> +	return err;
> +}
> +
> +static void niu_free_irq(struct niu *np)
> +{
> +	int i;
> +
> +	for (i = 0; i < np->num_ldg; i++) {
> +		struct niu_ldg *lp = &np->ldg[i];
> +
> +		free_irq(lp->irq, lp);
> +	}
> +}
> +
> +static void niu_enable_napi(struct niu *np)
> +{
> +	int i;
> +
> +	for (i = 0; i < np->num_ldg; i++)
> +		napi_enable(&np->ldg[i].napi);
> +}
> +
> +static void niu_disable_napi(struct niu *np)
> +{
> +	int i;
> +
> +	for (i = 0; i < np->num_ldg; i++)
> +		napi_disable(&np->ldg[i].napi);
> +}
> +
> +static int niu_open(struct net_device *dev)
> +{
> +	struct niu *np = netdev_priv(dev);
> +	int err;
> +
> +	netif_carrier_off(dev);
> +
> +	err = niu_alloc_channels(np);
> +	if (err)
> +		goto out_err;
> +
> +	err = niu_enable_interrupts(np, 0);
> +	if (err)
> +		goto out_free_channels;
> +
> +	err = niu_request_irq(np);
> +	if (err)
> +		goto out_free_channels;
> +
> +	niu_enable_napi(np);
> +
> +	spin_lock_irq(&np->lock);
> +
> +	err = niu_init_hw(np);
> +	if (!err) {
> +		init_timer(&np->timer);
> +		np->timer.expires = jiffies + HZ;
> +		np->timer.data = (unsigned long) np;
> +		np->timer.function = niu_timer;
> +
> +		err = niu_enable_interrupts(np, 1);
> +		if (err)
> +			niu_stop_hw(np);
> +	}
> +
> +	spin_unlock_irq(&np->lock);
> +
> +	if (err) {
> +		niu_disable_napi(np);
> +		goto out_free_irq;
> +	}
> +
> +	netif_start_queue(dev);
> +
> +	if (np->link_config.loopback_mode != LOOPBACK_DISABLED)
> +		netif_carrier_on(dev);
> +
> +	add_timer(&np->timer);
> +
> +	return 0;
> +
> +out_free_irq:
> +	niu_free_irq(np);
> +
> +out_free_channels:
> +	niu_free_channels(np);
> +
> +out_err:
> +	return err;
> +}
> +
> +static void niu_full_shutdown(struct niu *np, struct net_device *dev)
> +{
> +	cancel_work_sync(&np->reset_task);
> +
> +	niu_disable_napi(np);
> +	netif_stop_queue(dev);
> +
> +	del_timer_sync(&np->timer);
> +
> +	spin_lock_irq(&np->lock);
> +
> +	niu_stop_hw(np);
> +
> +	spin_unlock_irq(&np->lock);
> +}
> +
> +static int niu_close(struct net_device *dev)
> +{
> +	struct niu *np = netdev_priv(dev);
> +
> +	niu_full_shutdown(np, dev);
> +
> +	niu_free_irq(np);
> +
> +	niu_free_channels(np);
> +
> +	return 0;
> +}
> +
> +static void niu_sync_xmac_stats(struct niu *np)
> +{
> +	struct niu_xmac_stats *mp = &np->mac_stats.xmac;
> +
> +	mp->tx_frames += nr64_mac(TXMAC_FRM_CNT);
> +	mp->tx_bytes += nr64_mac(TXMAC_BYTE_CNT);
> +
> +	mp->rx_link_faults += nr64_mac(LINK_FAULT_CNT);
> +	mp->rx_align_errors += nr64_mac(RXMAC_ALIGN_ERR_CNT);
> +	mp->rx_frags += nr64_mac(RXMAC_FRAG_CNT);
> +	mp->rx_mcasts += nr64_mac(RXMAC_MC_FRM_CNT);
> +	mp->rx_bcasts += nr64_mac(RXMAC_BC_FRM_CNT);
> +	mp->rx_hist_cnt1 += nr64_mac(RXMAC_HIST_CNT1);
> +	mp->rx_hist_cnt2 += nr64_mac(RXMAC_HIST_CNT2);
> +	mp->rx_hist_cnt3 += nr64_mac(RXMAC_HIST_CNT3);
> +	mp->rx_hist_cnt4 += nr64_mac(RXMAC_HIST_CNT4);
> +	mp->rx_hist_cnt5 += nr64_mac(RXMAC_HIST_CNT5);
> +	mp->rx_hist_cnt6 += nr64_mac(RXMAC_HIST_CNT6);
> +	mp->rx_hist_cnt7 += nr64_mac(RXMAC_HIST_CNT7);
> +	mp->rx_octets += nr64_mac(RXMAC_BT_CNT);
> +	mp->rx_code_violations += nr64_mac(RXMAC_CD_VIO_CNT);
> +	mp->rx_len_errors += nr64_mac(RXMAC_MPSZER_CNT);
> +	mp->rx_crc_errors += nr64_mac(RXMAC_CRC_ER_CNT);
> +}
> +
> +static void niu_sync_bmac_stats(struct niu *np)
> +{
> +	struct niu_bmac_stats *mp = &np->mac_stats.bmac;
> +
> +	mp->tx_bytes += nr64_mac(BTXMAC_BYTE_CNT);
> +	mp->tx_frames += nr64_mac(BTXMAC_FRM_CNT);
> +
> +	mp->rx_frames += nr64_mac(BRXMAC_FRAME_CNT);
> +	mp->rx_align_errors += nr64_mac(BRXMAC_ALIGN_ERR_CNT);
> +	mp->rx_crc_errors += nr64_mac(BRXMAC_ALIGN_ERR_CNT);
> +	mp->rx_len_errors += nr64_mac(BRXMAC_CODE_VIOL_ERR_CNT);
> +}
> +
> +static void niu_sync_mac_stats(struct niu *np)
> +{
> +	if (np->flags & NIU_FLAGS_XMAC)
> +		niu_sync_xmac_stats(np);
> +	else
> +		niu_sync_bmac_stats(np);
> +}
> +
> +static void niu_get_rx_stats(struct niu *np)
> +{
> +	unsigned long pkts, dropped, errors, bytes;
> +	int i;
> +
> +	pkts = dropped = errors = bytes = 0;
> +	for (i = 0; i < np->num_rx_rings; i++) {
> +		struct rx_ring_info *rp = &np->rx_rings[i];
> +
> +		pkts += rp->rx_packets;
> +		bytes += rp->rx_bytes;
> +		dropped += rp->rx_dropped;
> +		errors += rp->rx_errors;
> +	}
> +	np->net_stats.rx_packets = pkts;
> +	np->net_stats.rx_bytes = bytes;
> +	np->net_stats.rx_dropped = dropped;
> +	np->net_stats.rx_errors = errors;
> +}
> +
> +static void niu_get_tx_stats(struct niu *np)
> +{
> +	unsigned long pkts, errors, bytes;
> +	int i;
> +
> +	pkts = errors = bytes = 0;
> +	for (i = 0; i < np->num_tx_rings; i++) {
> +		struct tx_ring_info *rp = &np->tx_rings[i];
> +
> +		pkts += rp->tx_packets;
> +		bytes += rp->tx_bytes;
> +		errors += rp->tx_errors;
> +	}
> +	np->net_stats.tx_packets = pkts;
> +	np->net_stats.tx_bytes = bytes;
> +	np->net_stats.tx_errors = errors;
> +}
> +
> +static struct net_device_stats *niu_get_stats(struct net_device *dev)
> +{
> +	struct niu *np = netdev_priv(dev);
> +
> +	niu_get_rx_stats(np);
> +	niu_get_tx_stats(np);
> +
> +	return &np->net_stats;
> +}
> +
> +static void niu_load_hash_xmac(struct niu *np, u16 *hash)
> +{
> +	int i;
> +
> +	for (i = 0; i < 16; i++)
> +		nw64_mac(XMAC_HASH_TBL(i), hash[i]);
> +}
> +
> +static void niu_load_hash_bmac(struct niu *np, u16 *hash)
> +{
> +	int i;
> +
> +	for (i = 0; i < 16; i++)
> +		nw64_mac(BMAC_HASH_TBL(i), hash[i]);
> +}
> +
> +static void niu_load_hash(struct niu *np, u16 *hash)
> +{
> +	if (np->flags & NIU_FLAGS_XMAC)
> +		niu_load_hash_xmac(np, hash);
> +	else
> +		niu_load_hash_bmac(np, hash);
> +}
> +
> +static void niu_set_rx_mode(struct net_device *dev)
> +{
> +	struct niu *np = netdev_priv(dev);
> +	int i, alt_cnt, err;
> +	struct dev_addr_list *addr;
> +	unsigned long flags;
> +	u16 hash[16] = { 0, };
> +
> +	spin_lock_irqsave(&np->lock, flags);
> +	niu_enable_rx_mac(np, 0);
> +
> +	np->flags &= ~(NIU_FLAGS_MCAST | NIU_FLAGS_PROMISC);
> +	if (dev->flags & IFF_PROMISC)
> +		np->flags |= NIU_FLAGS_PROMISC;
> +	if ((dev->flags & IFF_ALLMULTI) || (dev->mc_count > 0))
> +		np->flags |= NIU_FLAGS_MCAST;
> +
> +	alt_cnt = dev->uc_count;
> +	if (alt_cnt > niu_num_alt_addr(np)) {
> +		alt_cnt = 0;
> +		np->flags |= NIU_FLAGS_PROMISC;
> +	}
> +
> +	if (alt_cnt) {
> +		int index = 0;
> +
> +		for (addr = dev->uc_list; addr; addr = addr->next) {
> +			err = niu_set_alt_mac(np, index,
> +					      addr->da_addr);
> +			if (err)
> +				printk(KERN_WARNING PFX "%s: Error %d "
> +				       "adding alt mac %d\n",
> +				       dev->name, err, index);
> +			err = niu_enable_alt_mac(np, index, 1);
> +			if (err)
> +				printk(KERN_WARNING PFX "%s: Error %d "
> +				       "enabling alt mac %d\n",
> +				       dev->name, err, index);
> +
> +			index++;
> +		}
> +	} else {
> +		for (i = 0; i < niu_num_alt_addr(np); i++) {
> +			err = niu_enable_alt_mac(np, i, 0);
> +			if (err)
> +				printk(KERN_WARNING PFX "%s: Error %d "
> +				       "disabling alt mac %d\n",
> +				       dev->name, err, i);
> +		}
> +	}
> +	if (dev->flags & IFF_ALLMULTI) {
> +		for (i = 0; i < 16; i++)
> +			hash[i] = 0xffff;
> +	} else if (dev->mc_count > 0) {
> +		for (addr = dev->mc_list; addr; addr = addr->next) {
> +			u32 crc = ether_crc_le(ETH_ALEN, addr->da_addr);
> +
> +			crc >>= 24;
> +			hash[crc >> 4] |= (1 << (15 - (crc & 0xf)));
> +		}
> +	}
> +
> +	if (np->flags & NIU_FLAGS_MCAST)
> +		niu_load_hash(np, hash);
> +
> +	niu_enable_rx_mac(np, 1);
> +	spin_unlock_irqrestore(&np->lock, flags);
> +}
> +
> +static int niu_set_mac_addr(struct net_device *dev, void *p)
> +{
> +	struct niu *np = netdev_priv(dev);
> +	struct sockaddr *addr = p;
> +	unsigned long flags;
> +
> +	if (!is_valid_ether_addr(addr->sa_data))
> +		return -EINVAL;
> +
> +	memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN);
> +
> +	if (!netif_running(dev))
> +		return 0;
> +
> +	spin_lock_irqsave(&np->lock, flags);
> +	niu_enable_rx_mac(np, 0);
> +	niu_set_primary_mac(np, dev->dev_addr);
> +	niu_enable_rx_mac(np, 1);
> +	spin_unlock_irqrestore(&np->lock, flags);
> +
> +	return 0;
> +}
> +
> +static int niu_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
> +{
> +	return -EOPNOTSUPP;
> +}
> +
> +static void niu_netif_stop(struct niu *np)
> +{
> +	np->dev->trans_start = jiffies;	/* prevent tx timeout */
> +
> +	niu_disable_napi(np);
> +
> +	netif_tx_disable(np->dev);
> +}
> +
> +static void niu_netif_start(struct niu *np)
> +{
> +	/* NOTE: unconditional netif_wake_queue is only appropriate
> +	 * so long as all callers are assured to have free tx slots
> +	 * (such as after niu_init_hw).
> +	 */
> +	netif_wake_queue(np->dev);
> +
> +	niu_enable_napi(np);
> +
> +	niu_enable_interrupts(np, 1);
> +}
> +
> +static void niu_reset_task(struct work_struct *work)
> +{
> +	struct niu *np = container_of(work, struct niu, reset_task);
> +	unsigned long flags;
> +	int err;
> +
> +	spin_lock_irqsave(&np->lock, flags);
> +	if (!netif_running(np->dev)) {
> +		spin_unlock_irqrestore(&np->lock, flags);
> +		return;
> +	}
> +
> +	spin_unlock_irqrestore(&np->lock, flags);
> +
> +	del_timer_sync(&np->timer);
> +
> +	niu_netif_stop(np);
> +
> +	spin_lock_irqsave(&np->lock, flags);
> +
> +	niu_stop_hw(np);
> +
> +	err = niu_init_hw(np);
> +	if (!err) {
> +		np->timer.expires = jiffies + HZ;
> +		add_timer(&np->timer);
> +		niu_netif_start(np);
> +	}
> +
> +	spin_unlock_irqrestore(&np->lock, flags);
> +}
> +
> +static void niu_tx_timeout(struct net_device *dev)
> +{
> +	struct niu *np = netdev_priv(dev);
> +
> +	printk(KERN_ERR PFX "%s: Transmit timed out, resetting\n",
> +	       dev->name);
> +
> +	schedule_work(&np->reset_task);
> +}
> +
> +static void niu_set_txd(struct tx_ring_info *rp, int index,
> +			u64 mapping, u64 len, u64 mark,
> +			u64 n_frags)
> +{
> +	__le64 *desc = &rp->descr[index];
> +
> +	*desc = cpu_to_le64(mark |
> +			    (n_frags << TX_DESC_NUM_PTR_SHIFT) |
> +			    (len << TX_DESC_TR_LEN_SHIFT) |
> +			    (mapping & TX_DESC_SAD));
> +}
> +
> +static u64 niu_compute_tx_flags(struct sk_buff *skb, struct ethhdr *ehdr,
> +				u64 pad_bytes, u64 len)
> +{
> +	u16 eth_proto, eth_proto_inner;
> +	u64 csum_bits, l3off, ihl, ret;
> +	u8 ip_proto;
> +	int ipv6;
> +
> +	eth_proto = be16_to_cpu(ehdr->h_proto);
> +	eth_proto_inner = eth_proto;
> +	if (eth_proto == ETH_P_8021Q) {
> +		struct vlan_ethhdr *vp = (struct vlan_ethhdr *) ehdr;
> +		__be16 val = vp->h_vlan_encapsulated_proto;
> +
> +		eth_proto_inner = be16_to_cpu(val);
> +	}
> +
> +	ipv6 = ihl = 0;
> +	switch (skb->protocol) {
> +	case __constant_htons(ETH_P_IP):
> +		ip_proto = ip_hdr(skb)->protocol;
> +		ihl = ip_hdr(skb)->ihl;
> +		break;
> +	case __constant_htons(ETH_P_IPV6):
> +		ip_proto = ipv6_hdr(skb)->nexthdr;
> +		ihl = (40 >> 2);
> +		ipv6 = 1;
> +		break;
> +	default:
> +		ip_proto = ihl = 0;
> +		break;
> +	}
> +
> +	csum_bits = TXHDR_CSUM_NONE;
> +	if (skb->ip_summed == CHECKSUM_PARTIAL) {
> +		u64 start, stuff;
> +
> +		csum_bits = (ip_proto == IPPROTO_TCP ?
> +			     TXHDR_CSUM_TCP :
> +			     (ip_proto == IPPROTO_UDP ?
> +			      TXHDR_CSUM_UDP : TXHDR_CSUM_SCTP));
> +
> +		start = skb_transport_offset(skb) -
> +			(pad_bytes + sizeof(struct tx_pkt_hdr));
> +		stuff = start + skb->csum_offset;
> +
> +		csum_bits |= (start / 2) << TXHDR_L4START_SHIFT;
> +		csum_bits |= (stuff / 2) << TXHDR_L4STUFF_SHIFT;
> +	}
> +
> +	l3off = skb_network_offset(skb) -
> +		(pad_bytes + sizeof(struct tx_pkt_hdr));
> +
> +	ret = (((pad_bytes / 2) << TXHDR_PAD_SHIFT) |
> +	       (len << TXHDR_LEN_SHIFT) |
> +	       ((l3off / 2) << TXHDR_L3START_SHIFT) |
> +	       (ihl << TXHDR_IHL_SHIFT) |
> +	       ((eth_proto_inner < 1536) ? TXHDR_LLC : 0) |
> +	       ((eth_proto == ETH_P_8021Q) ? TXHDR_VLAN : 0) |
> +	       (ipv6 ? TXHDR_IP_VER : 0) |
> +	       csum_bits);
> +
> +	return ret;
> +}
> +
> +static struct tx_ring_info *tx_ring_select(struct niu *np, struct sk_buff *skb)
> +{
> +	return &np->tx_rings[0];
> +}
> +
> +static int niu_start_xmit(struct sk_buff *skb, struct net_device *dev)
> +{
> +	struct niu *np = netdev_priv(dev);
> +	unsigned long align, headroom;
> +	struct tx_ring_info *rp;
> +	struct tx_pkt_hdr *tp;
> +	unsigned int len, nfg;
> +	struct ethhdr *ehdr;
> +	int prod, i, tlen;
> +	u64 mapping, mrk;
> +
> +	rp = tx_ring_select(np, skb);
> +
> +	if (niu_tx_avail(rp) <= (skb_shinfo(skb)->nr_frags + 1)) {
> +		netif_stop_queue(dev);
> +		printk(KERN_ERR PFX "%s: BUG! Tx ring full when "
> +		       "queue awake!\n", dev->name);
> +		rp->tx_errors++;
> +		return NETDEV_TX_BUSY;
> +	}
> +
> +	if (skb->len < ETH_ZLEN) {
> +		unsigned int pad_bytes = ETH_ZLEN - skb->len;
> +
> +		if (skb_pad(skb, pad_bytes))
> +			goto out;
> +		skb_put(skb, pad_bytes);
> +	}
> +
> +	len = sizeof(struct tx_pkt_hdr) + 15;
> +	if (skb_headroom(skb) < len) {
> +		struct sk_buff *skb_new;
> +
> +		skb_new = skb_realloc_headroom(skb, len);
> +		if (!skb_new) {
> +			rp->tx_errors++;
> +			goto out_drop;
> +		}
> +		kfree_skb(skb);
> +		skb = skb_new;
> +	}
> +
> +	align = ((unsigned long) skb->data & (16 - 1));
> +	headroom = align + sizeof(struct tx_pkt_hdr);
> +
> +	ehdr = (struct ethhdr *) skb->data;
> +	tp = (struct tx_pkt_hdr *) skb_push(skb, headroom);
> +
> +	len = skb->len - sizeof(struct tx_pkt_hdr);
> +	tp->flags = cpu_to_le64(niu_compute_tx_flags(skb, ehdr, align, len));
> +	tp->resv = 0;
> +
> +	len = skb_headlen(skb);
> +	mapping = np->ops->map_single(np->device, skb->data,
> +				      len, DMA_TO_DEVICE);
> +
> +	prod = rp->prod;
> +
> +	rp->tx_buffs[prod].skb = skb;
> +	rp->tx_buffs[prod].mapping = mapping;
> +
> +	mrk = TX_DESC_SOP;
> +	if (++rp->mark_counter == rp->mark_freq) {
> +		rp->mark_counter = 0;
> +		mrk |= TX_DESC_MARK;
> +		rp->mark_pending++;
> +	}
> +
> +	tlen = len;
> +	nfg = skb_shinfo(skb)->nr_frags;
> +	while (tlen > 0) {
> +		tlen -= MAX_TX_DESC_LEN;
> +		nfg++;
> +	}
> +
> +	while (len > 0) {
> +		unsigned int this_len = len;
> +
> +		if (this_len > MAX_TX_DESC_LEN)
> +			this_len = MAX_TX_DESC_LEN;
> +
> +		niu_set_txd(rp, prod, mapping, this_len, mrk, nfg);
> +		mrk = nfg = 0;
> +
> +		prod = NEXT_TX(rp, prod);
> +		mapping += this_len;
> +		len -= this_len;
> +	}
> +
> +	for (i = 0; i <  skb_shinfo(skb)->nr_frags; i++) {
> +		skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
> +
> +		len = frag->size;
> +		mapping = np->ops->map_page(np->device, frag->page,
> +					    frag->page_offset, len,
> +					    DMA_TO_DEVICE);
> +
> +		rp->tx_buffs[prod].skb = NULL;
> +		rp->tx_buffs[prod].mapping = mapping;
> +
> +		niu_set_txd(rp, prod, mapping, len, 0, 0);
> +
> +		prod = NEXT_TX(rp, prod);
> +	}
> +
> +	if (prod < rp->prod)
> +		rp->wrap_bit ^= TX_RING_KICK_WRAP;
> +	rp->prod = prod;
> +
> +	nw64(TX_RING_KICK(rp->tx_channel), rp->wrap_bit | (prod << 3));
> +
> +	if (unlikely(niu_tx_avail(rp) <= (MAX_SKB_FRAGS + 1))) {
> +		netif_stop_queue(dev);
> +		if (niu_tx_avail(rp) > NIU_TX_WAKEUP_THRESH(rp))
> +			netif_wake_queue(dev);
> +	}
> +
> +	dev->trans_start = jiffies;
> +
> +out:
> +	return NETDEV_TX_OK;
> +
> +out_drop:
> +	rp->tx_errors++;
> +	kfree_skb(skb);
> +	goto out;
> +}
> +
> +static int niu_change_mtu(struct net_device *dev, int new_mtu)
> +{
> +	struct niu *np = netdev_priv(dev);
> +	int err, orig_jumbo, new_jumbo;
> +
> +	if (new_mtu < 68 || new_mtu > NIU_MAX_MTU)
> +		return -EINVAL;
> +
> +	orig_jumbo = (dev->mtu > ETH_DATA_LEN);
> +	new_jumbo = (new_mtu > ETH_DATA_LEN);
> +
> +	dev->mtu = new_mtu;
> +
> +	if (!netif_running(dev) ||
> +	    (orig_jumbo == new_jumbo))
> +		return 0;
> +
> +	niu_full_shutdown(np, dev);
> +
> +	niu_free_channels(np);
> +
> +	niu_enable_napi(np);
> +
> +	err = niu_alloc_channels(np);
> +	if (err)
> +		return err;
> +
> +	spin_lock_irq(&np->lock);
> +
> +	err = niu_init_hw(np);
> +	if (!err) {
> +		init_timer(&np->timer);
> +		np->timer.expires = jiffies + HZ;
> +		np->timer.data = (unsigned long) np;
> +		np->timer.function = niu_timer;
> +
> +		err = niu_enable_interrupts(np, 1);
> +		if (err)
> +			niu_stop_hw(np);
> +	}
> +
> +	spin_unlock_irq(&np->lock);
> +
> +	if (!err) {
> +		netif_start_queue(dev);
> +		if (np->link_config.loopback_mode != LOOPBACK_DISABLED)
> +			netif_carrier_on(dev);
> +
> +		add_timer(&np->timer);
> +	}
> +
> +	return err;
> +}
> +
> +static void niu_get_drvinfo(struct net_device *dev,
> +			    struct ethtool_drvinfo *info)
> +{
> +	struct niu *np = netdev_priv(dev);
> +	struct niu_vpd *vpd = &np->vpd;
> +
> +	strcpy(info->driver, DRV_MODULE_NAME);
> +	strcpy(info->version, DRV_MODULE_VERSION);
> +	sprintf(info->fw_version, "%d.%d",
> +		vpd->fcode_major, vpd->fcode_minor);
> +	if (np->parent->plat_type != PLAT_TYPE_NIU)
> +		strcpy(info->bus_info, pci_name(np->pdev));
> +}
> +
> +static int niu_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
> +{
> +  	struct niu *np = netdev_priv(dev);
> +	struct niu_link_config *lp;
> +
> +	lp = &np->link_config;
> +
> +	memset(cmd, 0, sizeof(*cmd));
> +	cmd->phy_address = np->phy_addr;
> +	cmd->supported = lp->supported;
> +	cmd->advertising = lp->advertising;
> +	cmd->autoneg = lp->autoneg;
> +	cmd->speed = lp->active_speed;
> +	cmd->duplex = lp->active_duplex;
> +
> +	return 0;
> +}
> +
> +static int niu_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
> +{
> +	return -EINVAL;
> +}

Why not leave function ptr null and get "-EOPNOTSUPP"

> +
> +static u32 niu_get_msglevel(struct net_device *dev)
> +{
> +	struct niu *np = netdev_priv(dev);
> +	return np->msg_enable;
> +}
> +
> +static void niu_set_msglevel(struct net_device *dev, u32 value)
> +{
> +	struct niu *np = netdev_priv(dev);
> +	np->msg_enable = value;
> +}
> +
> +static int niu_get_eeprom_len(struct net_device *dev)
> +{
> +	struct niu *np = netdev_priv(dev);
> +
> +	return np->eeprom_len;
> +}
> +
> +static int niu_get_eeprom(struct net_device *dev,
> +			  struct ethtool_eeprom *eeprom, u8 *data)
> +{
> +	struct niu *np = netdev_priv(dev);
> +	u32 offset, len, val;
> +
> +	offset = eeprom->offset;
> +	len = eeprom->len;
> +
> +	if (offset + len < offset)
> +		return -EINVAL;
> +	if (offset >= np->eeprom_len)
> +		return -EINVAL;
> +	if (offset + len > np->eeprom_len)
> +		len = eeprom->len = np->eeprom_len - offset;
> +
> +	if (offset & 3) {
> +		u32 b_offset, b_count;
> +
> +		b_offset = offset & 3;
> +		b_count = 4 - b_offset;
> +		if (b_count > len)
> +			b_count = len;
> +
> +		val = nr64(ESPC_NCR((offset - b_offset) / 4));
> +		memcpy(data, ((char *)&val) + b_offset, b_count);
> +		data += b_count;
> +		len -= b_count;
> +		offset += b_count;
> +	}
> +	while (len >= 4) {
> +		val = nr64(ESPC_NCR(offset / 4));
> +		memcpy(data, &val, 4);
> +		data += 4;
> +		len -= 4;
> +		offset += 4;
> +	}
> +	if (len) {
> +		val = nr64(ESPC_NCR(offset / 4));
> +		memcpy(data, &val, len);
> +	}
> +	return 0;
> +}
> +
> +static const struct {
> +	const char string[ETH_GSTRING_LEN];
> +} niu_xmac_stat_keys[] = {
> +	{ "tx_frames" },
> +	{ "tx_bytes" },
> +	{ "tx_fifo_errors" },
> +	{ "tx_overflow_errors" },
> +	{ "tx_max_pkt_size_errors" },
> +	{ "tx_underflow_errors" },
> +	{ "rx_local_faults" },
> +	{ "rx_remote_faults" },
> +	{ "rx_link_faults" },
> +	{ "rx_align_errors" },
> +	{ "rx_frags" },
> +	{ "rx_mcasts" },
> +	{ "rx_bcasts" },
> +	{ "rx_hist_cnt1" },
> +	{ "rx_hist_cnt2" },
> +	{ "rx_hist_cnt3" },
> +	{ "rx_hist_cnt4" },
> +	{ "rx_hist_cnt5" },
> +	{ "rx_hist_cnt6" },
> +	{ "rx_hist_cnt7" },
> +	{ "rx_octets" },
> +	{ "rx_code_violations" },
> +	{ "rx_len_errors" },
> +	{ "rx_crc_errors" },
> +	{ "rx_underflows" },
> +	{ "rx_overflows" },
> +	{ "pause_off_state" },
> +	{ "pause_on_state" },
> +	{ "pause_received" },
> +};
> +
> +#define NUM_XMAC_STAT_KEYS	ARRAY_SIZE(niu_xmac_stat_keys)
> +
> +static const struct {
> +	const char string[ETH_GSTRING_LEN];
> +} niu_bmac_stat_keys[] = {
> +	{ "tx_underflow_errors" },
> +	{ "tx_max_pkt_size_errors" },
> +	{ "tx_bytes" },
> +	{ "tx_frames" },
> +	{ "rx_overflows" },
> +	{ "rx_frames" },
> +	{ "rx_align_errors" },
> +	{ "rx_crc_errors" },
> +	{ "rx_len_errors" },
> +	{ "pause_off_state" },
> +	{ "pause_on_state" },
> +	{ "pause_received" },
> +};
> +
> +#define NUM_BMAC_STAT_KEYS	ARRAY_SIZE(niu_bmac_stat_keys)
> +
> +static const struct {
> +	const char string[ETH_GSTRING_LEN];
> +} niu_rxchan_stat_keys[] = {
> +	{ "rx_channel" },
> +	{ "rx_packets" },
> +	{ "rx_bytes" },
> +	{ "rx_dropped" },
> +	{ "rx_errors" },
> +};
> +
> +#define NUM_RXCHAN_STAT_KEYS	ARRAY_SIZE(niu_rxchan_stat_keys)
> +
> +static const struct {
> +	const char string[ETH_GSTRING_LEN];
> +} niu_txchan_stat_keys[] = {
> +	{ "tx_channel" },
> +	{ "tx_packets" },
> +	{ "tx_bytes" },
> +	{ "tx_errors" },
> +};
> +
> +#define NUM_TXCHAN_STAT_KEYS	ARRAY_SIZE(niu_txchan_stat_keys)
> +
> +static void niu_get_strings(struct net_device *dev, u32 stringset, u8 *data)
> +{
> +	struct niu *np = netdev_priv(dev);
> +	int i;
> +
> +	if (stringset != ETH_SS_STATS)
> +		return;
> +
> +	if (np->flags & NIU_FLAGS_XMAC) {
> +		memcpy(data, niu_xmac_stat_keys,
> +		       sizeof(niu_xmac_stat_keys));
> +		data += sizeof(niu_xmac_stat_keys);
> +	} else {
> +		memcpy(data, niu_bmac_stat_keys,
> +		       sizeof(niu_bmac_stat_keys));
> +		data += sizeof(niu_bmac_stat_keys);
> +	}
> +	for (i = 0; i < np->num_rx_rings; i++) {
> +		memcpy(data, niu_rxchan_stat_keys,
> +		       sizeof(niu_rxchan_stat_keys));
> +		data += sizeof(niu_rxchan_stat_keys);
> +	}
> +	for (i = 0; i < np->num_tx_rings; i++) {
> +		memcpy(data, niu_txchan_stat_keys,
> +		       sizeof(niu_txchan_stat_keys));
> +		data += sizeof(niu_txchan_stat_keys);
> +	}
> +}
> +
> +static int niu_get_stats_count(struct net_device *dev)
> +{
> +	struct niu *np = netdev_priv(dev);
> +
> +	return ((np->flags & NIU_FLAGS_XMAC ?
> +		 NUM_XMAC_STAT_KEYS :
> +		 NUM_BMAC_STAT_KEYS) +
> +		(np->num_rx_rings * NUM_RXCHAN_STAT_KEYS) +
> +		(np->num_tx_rings * NUM_TXCHAN_STAT_KEYS));
> +}
> +
> +static void niu_get_ethtool_stats(struct net_device *dev,
> +				  struct ethtool_stats *stats, u64 *data)
> +{
> +	struct niu *np = netdev_priv(dev);
> +	int i;
> +
> +	niu_sync_mac_stats(np);
> +	if (np->flags & NIU_FLAGS_XMAC) {
> +		memcpy(data, &np->mac_stats.xmac,
> +		       sizeof(struct niu_xmac_stats));
> +		data += (sizeof(struct niu_xmac_stats) / sizeof(u64));
> +	} else {
> +		memcpy(data, &np->mac_stats.bmac,
> +		       sizeof(struct niu_bmac_stats));
> +		data += (sizeof(struct niu_bmac_stats) / sizeof(u64));
> +	}
> +	for (i = 0; i < np->num_rx_rings; i++) {
> +		struct rx_ring_info *rp = &np->rx_rings[i];
> +
> +		data[0] = rp->rx_channel;
> +		data[1] = rp->rx_packets;
> +		data[2] = rp->rx_bytes;
> +		data[3] = rp->rx_dropped;
> +		data[4] = rp->rx_errors;
> +		data += 5;
> +	}
> +	for (i = 0; i < np->num_tx_rings; i++) {
> +		struct tx_ring_info *rp = &np->tx_rings[i];
> +
> +		data[0] = rp->tx_channel;
> +		data[1] = rp->tx_packets;
> +		data[2] = rp->tx_bytes;
> +		data[3] = rp->tx_errors;
> +		data += 4;
> +	}
> +}
> +
> +static u64 niu_led_state_save(struct niu *np)
> +{
> +	if (np->flags & NIU_FLAGS_XMAC)
> +		return nr64_mac(XMAC_CONFIG);
> +	else
> +		return nr64_mac(BMAC_XIF_CONFIG);
> +}
> +
> +static void niu_led_state_restore(struct niu *np, u64 val)
> +{
> +	if (np->flags & NIU_FLAGS_XMAC)
> +		nw64_mac(XMAC_CONFIG, val);
> +	else
> +		nw64_mac(BMAC_XIF_CONFIG, val);
> +}
> +
> +static void niu_force_led(struct niu *np, int on)
> +{
> +	u64 val, reg, bit;
> +
> +	if (np->flags & NIU_FLAGS_XMAC) {
> +		reg = XMAC_CONFIG;
> +		bit = XMAC_CONFIG_FORCE_LED_ON;
> +	} else {
> +		reg = BMAC_XIF_CONFIG;
> +		bit = BMAC_XIF_CONFIG_LINK_LED;
> +	}
> +
> +	val = nr64_mac(reg);
> +	if (on)
> +		val |= bit;
> +	else
> +		val &= ~bit;
> +	nw64_mac(reg, val);
> +}
> +
> +static int niu_phys_id(struct net_device *dev, u32 data)
> +{
> +	struct niu *np = netdev_priv(dev);
> +	u64 orig_led_state;
> +	int i;
> +
> +	if (!netif_running(dev))
> +		return -EAGAIN;
> +
> +	if (data == 0)
> +		data = 2;
> +
> +	orig_led_state = niu_led_state_save(np);
> +	for (i = 0; i < (data * 2); i++) {
> +		int on = ((i % 2) == 0);
> +
> +		niu_force_led(np, on);
> +
> +		if (msleep_interruptible(500))
> +			break;
> +	}
> +	niu_led_state_restore(np, orig_led_state);
> +
> +	return 0;
> +}
> +
> +static const struct ethtool_ops niu_ethtool_ops = {
> +	.get_drvinfo		= niu_get_drvinfo,
> +	.get_link		= ethtool_op_get_link,
> +	.get_msglevel		= niu_get_msglevel,
> +	.set_msglevel		= niu_set_msglevel,
> +	.get_eeprom_len		= niu_get_eeprom_len,
> +	.get_eeprom		= niu_get_eeprom,
> +	.get_settings		= niu_get_settings,
> +	.set_settings		= niu_set_settings,
> +	.get_strings		= niu_get_strings,
> +	.get_stats_count	= niu_get_stats_count,
> +	.get_ethtool_stats	= niu_get_ethtool_stats,
> +	.phys_id		= niu_phys_id,
> +};
> +
> +static int niu_ldg_assign_ldn(struct niu *np, struct niu_parent *parent,
> +			      int ldg, int ldn)
> +{
> +	if (ldg < NIU_LDG_MIN || ldg > NIU_LDG_MAX)
> +		return -EINVAL;
> +	if (ldn < 0 || ldn > LDN_MAX)
> +		return -EINVAL;
> +
> +	parent->ldg_map[ldn] = ldg;
> +
> +	if (np->parent->plat_type == PLAT_TYPE_NIU) {
> +		/* On N2 NIU, the ldn-->ldg assignments are setup and fixed by
> +		 * the firmware, and we're not supposed to change them.  Validate
> +		 * the mapping, because if it's wrong we probably won't get any
> +		 * interrupts and that's painful to debug.
> +		 */
> +		if (nr64(LDG_NUM(ldn)) != ldg) {
> +			printk(KERN_ERR PFX "Port %u, mis-matched LDG assignment "
> +			       "for ldn %d, should be %d is %llu\n",
> +			       np->port, ldn, ldg,
> +			       (unsigned long long) nr64(LDG_NUM(ldn)));
> +			return -EINVAL;
> +		}
> +	} else
> +		nw64(LDG_NUM(ldn), ldg);
> +
> +	return 0;
> +}
> +
> +static int niu_set_ldg_timer_res(struct niu *np, int res)
> +{
> +	if (res < 0 || res > LDG_TIMER_RES_VAL)
> +		return -EINVAL;
> +
> +
> +	nw64(LDG_TIMER_RES, res);
> +
> +	return 0;
> +}
> +
> +static int niu_set_ldg_sid(struct niu *np, int ldg, int func, int vector)
> +{
> +	if ((ldg < NIU_LDG_MIN || ldg > NIU_LDG_MAX) ||
> +	    (func < 0 || func > 3) ||
> +	    (vector < 0 || vector > 0x1f))
> +		return -EINVAL;
> +
> +	nw64(SID(ldg), (func << SID_FUNC_SHIFT) | vector);
> +
> +	return 0;
> +}
> +
> +static int __devinit niu_pci_eeprom_read(struct niu *np, u32 addr)
> +{
> +	u64 frame, frame_base = (ESPC_PIO_STAT_READ_START |
> +				 (addr << ESPC_PIO_STAT_ADDR_SHIFT));
> +	int limit;
> +
> +	if (addr > (ESPC_PIO_STAT_ADDR >> ESPC_PIO_STAT_ADDR_SHIFT))
> +		return -EINVAL;
> +
> +	frame = frame_base;
> +	nw64(ESPC_PIO_STAT, frame);
> +	limit = 64;
> +	do {
> +		udelay(5);
> +		frame = nr64(ESPC_PIO_STAT);
> +		if (frame & ESPC_PIO_STAT_READ_END)
> +			break;
> +	} while (limit--);
> +	if (!(frame & ESPC_PIO_STAT_READ_END)) {
> +		printk(KERN_ERR PFX "EEPROM read timeout frame[%llx]\n",
> +		       (unsigned long long) frame);
> +		return -ENODEV;
> +	}
> +
> +	frame = frame_base;
> +	nw64(ESPC_PIO_STAT, frame);
> +	limit = 64;
> +	do {
> +		udelay(5);
> +		frame = nr64(ESPC_PIO_STAT);
> +		if (frame & ESPC_PIO_STAT_READ_END)
> +			break;
> +	} while (limit--);
> +	if (!(frame & ESPC_PIO_STAT_READ_END)) {
> +		printk(KERN_ERR PFX "EEPROM read timeout frame[%llx]\n",
> +		       (unsigned long long) frame);
> +		return -ENODEV;
> +	}
> +
> +	frame = nr64(ESPC_PIO_STAT);
> +	return (frame & ESPC_PIO_STAT_DATA) >> ESPC_PIO_STAT_DATA_SHIFT;
> +}
> +
> +static int __devinit niu_pci_eeprom_read16(struct niu *np, u32 off)
> +{
> +	int err = niu_pci_eeprom_read(np, off);
> +	u16 val;
> +
> +	if (err < 0)
> +		return err;
> +	val = (err << 8);
> +	err = niu_pci_eeprom_read(np, off + 1);
> +	if (err < 0)
> +		return err;
> +	val |= (err & 0xff);
> +
> +	return val;
> +}
> +
> +static int __devinit niu_pci_eeprom_read16_swp(struct niu *np, u32 off)
> +{
> +	int err = niu_pci_eeprom_read(np, off);
> +	u16 val;
> +
> +	if (err < 0) {
> +		return err;
> +	}
> +	val = (err & 0xff);
> +	err = niu_pci_eeprom_read(np, off + 1);
> +	if (err < 0) {
> +		return err;
> +	}
> +	val |= (err & 0xff) << 8;
> +
> +	return val;
> +}
> +
> +static int __devinit niu_pci_vpd_get_propname(struct niu *np,
> +					      u32 off,
> +					      char *namebuf,
> +					      int namebuf_len)
> +{
> +	int i;
> +
> +	for (i = 0; i < namebuf_len; i++) {
> +		int err = niu_pci_eeprom_read(np, off + i);
> +		if (err < 0)
> +			return err;
> +		*namebuf++ = err;
> +		if (!err)
> +			break;
> +	}
> +	if (i >= namebuf_len)
> +		return -EINVAL;
> +
> +	return i + 1;
> +}
> +
> +static void __devinit niu_vpd_parse_version(struct niu *np)
> +{
> +	struct niu_vpd *vpd = &np->vpd;
> +	int len = strlen(vpd->version) + 1;
> +	const char *s = vpd->version;
> +	int i;
> +
> +	for (i = 0; i < len - 5; i++) {
> +		if (!strncmp(s + i, "FCode ", 5))
> +			break;
> +	}
> +	if (i >= len - 5)
> +		return;
> +
> +	s += i + 5;
> +	sscanf(s, "%d.%d", &vpd->fcode_major, &vpd->fcode_minor);
> +
> +	niudbg(PROBE, "VPD_SCAN: FCODE major(%d) minor(%d)\n",
> +	       vpd->fcode_major, vpd->fcode_minor);
> +	if (vpd->fcode_major > NIU_VPD_MIN_MAJOR ||
> +	    (vpd->fcode_major == NIU_VPD_MIN_MAJOR &&
> +	     vpd->fcode_minor >= NIU_VPD_MIN_MINOR))
> +		np->flags |= NIU_FLAGS_VPD_VALID;
> +}
> +
> +/* ESPC_PIO_EN_ENABLE must be set */
> +static int __devinit niu_pci_vpd_scan_props(struct niu *np,
> +					    u32 start, u32 end)
> +{
> +	unsigned int found_mask = 0;
> +#define FOUND_MASK_MODEL	0x00000001
> +#define FOUND_MASK_BMODEL	0x00000002
> +#define FOUND_MASK_VERS		0x00000004
> +#define FOUND_MASK_MAC		0x00000008
> +#define FOUND_MASK_NMAC		0x00000010
> +#define FOUND_MASK_PHY		0x00000020
> +#define FOUND_MASK_ALL		0x0000003f
> +
> +	niudbg(PROBE, "VPD_SCAN: start[%x] end[%x]\n",
> +	       start, end);
> +	while (start < end) {
> +		int len, err, instance, type, prop_len;
> +		char namebuf[64];
> +		u8 *prop_buf;
> +		int max_len;
> +		
> +		if (found_mask == FOUND_MASK_ALL) {
> +			niu_vpd_parse_version(np);
> +			return 1;
> +		}
> +
> +		err = niu_pci_eeprom_read(np, start + 2);
> +		if (err < 0)
> +			return err;
> +		len = err;
> +		start += 3;
> +
> +		instance = niu_pci_eeprom_read(np, start);
> +		type = niu_pci_eeprom_read(np, start + 3);
> +		prop_len = niu_pci_eeprom_read(np, start + 4);
> +		err = niu_pci_vpd_get_propname(np, start + 5, namebuf, 64);
> +		if (err < 0)
> +			return err;
> +
> +		prop_buf = NULL;
> +		max_len = 0;
> +		if (!strcmp(namebuf, "model")) {
> +			prop_buf = np->vpd.model;
> +			max_len = NIU_VPD_MODEL_MAX;
> +			found_mask |= FOUND_MASK_MODEL;
> +		} else if (!strcmp(namebuf, "board-model")) {
> +			prop_buf = np->vpd.board_model;
> +			max_len = NIU_VPD_BD_MODEL_MAX;
> +			found_mask |= FOUND_MASK_BMODEL;
> +		} else if (!strcmp(namebuf, "version")) {
> +			prop_buf = np->vpd.version;
> +			max_len = NIU_VPD_VERSION_MAX;
> +			found_mask |= FOUND_MASK_VERS;
> +		} else if (!strcmp(namebuf, "local-mac-address")) {
> +			prop_buf = np->vpd.local_mac;
> +			max_len = ETH_ALEN;
> +			found_mask |= FOUND_MASK_MAC;
> +		} else if (!strcmp(namebuf, "num-mac-addresses")) {
> +			prop_buf = &np->vpd.mac_num;
> +			max_len = 1;
> +			found_mask |= FOUND_MASK_NMAC;
> +		} else if (!strcmp(namebuf, "phy-type")) {
> +			prop_buf = np->vpd.phy_type;
> +			max_len = NIU_VPD_PHY_TYPE_MAX;
> +			found_mask |= FOUND_MASK_PHY;
> +		}
> +
> +		if (max_len && prop_len > max_len) {
> +			printk(KERN_ERR PFX "Property '%s' length (%d) is "
> +			       "too long.\n", namebuf, prop_len);
> +			return -EINVAL;
> +		}
> +
> +		if (prop_buf) {
> +			u32 off = start + 5 + err;
> +			int i;
> +
> +			niudbg(PROBE, "VPD_SCAN: Reading in property [%s] "
> +			       "len[%d]\n", namebuf, prop_len);
> +			for (i = 0; i < prop_len; i++)
> +				*prop_buf++ = niu_pci_eeprom_read(np, off + i);
> +		}
> +
> +		start += len;
> +	}
> +
> +	return 0;
> +}
> +
> +/* ESPC_PIO_EN_ENABLE must be set */
> +static void __devinit niu_pci_vpd_fetch(struct niu *np, u32 start)
> +{
> +	u32 offset;
> +	int err;
> +
> +	err = niu_pci_eeprom_read16_swp(np, start + 1);
> +	if (err < 0)
> +		return;
> +
> +	offset = err + 3;
> +
> +	while (start + offset < ESPC_EEPROM_SIZE) {
> +		u32 here = start + offset;
> +		u32 end;
> +
> +		err = niu_pci_eeprom_read(np, here);
> +		if (err != 0x90)
> +			return;
> +
> +		err = niu_pci_eeprom_read16_swp(np, here + 1);
> +		if (err < 0)
> +			return;
> +
> +		here = start + offset + 3;
> +		end = start + offset + err;
> +
> +		offset += err;
> +
> +		err = niu_pci_vpd_scan_props(np, here, end);
> +		if (err < 0 || err == 1)
> +			return;
> +	}
> +}
> +
> +/* ESPC_PIO_EN_ENABLE must be set */
> +static u32 __devinit niu_pci_vpd_offset(struct niu *np)
> +{
> +	u32 start = 0, end = ESPC_EEPROM_SIZE, ret;
> +	int err;
> +
> +	while (start < end) {
> +		ret = start;
> +
> +		/* ROM header signature?  */
> +		err = niu_pci_eeprom_read16(np, start +  0);
> +		if (err != 0x55aa)
> +			return 0;
> +
> +		/* Apply offset to PCI data structure.  */
> +		err = niu_pci_eeprom_read16(np, start + 23);
> +		if (err < 0)
> +			return 0;
> +		start += err;
> +
> +		/* Check for "PCIR" signature.  */
> +		err = niu_pci_eeprom_read16(np, start +  0);
> +		if (err != 0x5043)
> +			return 0;
> +		err = niu_pci_eeprom_read16(np, start +  2);
> +		if (err != 0x4952)
> +			return 0;
> +
> +		/* Check for OBP image type.  */
> +		err = niu_pci_eeprom_read(np, start + 20);
> +		if (err < 0)
> +			return 0;
> +		if (err != 0x01) {
> +			err = niu_pci_eeprom_read(np, ret + 2);
> +			if (err < 0)
> +				return 0;
> +
> +			start = ret + (err * 512);
> +			continue;
> +		}
> +
> +		err = niu_pci_eeprom_read16_swp(np, start + 8);
> +		if (err < 0)
> +			return err;
> +		ret += err;
> +
> +		err = niu_pci_eeprom_read(np, ret + 0);
> +		if (err != 0x82)
> +			return 0;
> +
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int __devinit niu_phy_type_prop_decode(struct niu *np, const char *phy_prop)
> +{
> +	if (!strcmp(phy_prop, "mif")) {
> +		/* 1G copper, MII */
> +		np->flags &= ~(NIU_FLAGS_FIBER |
> +			       NIU_FLAGS_10G);
> +		np->mac_xcvr = MAC_XCVR_MII;
> +	} else if (!strcmp(phy_prop, "xgf")) {
> +		/* 10G fiber, XPCS */
> +		np->flags |= (NIU_FLAGS_10G |
> +			      NIU_FLAGS_FIBER);
> +		np->mac_xcvr = MAC_XCVR_XPCS;
> +	} else if (!strcmp(phy_prop, "pcs")) {
> +		/* 1G fiber, PCS */
> +		np->flags &= ~NIU_FLAGS_10G;
> +		np->flags |= NIU_FLAGS_FIBER;
> +		np->mac_xcvr = MAC_XCVR_PCS;
> +	} else if (!strcmp(phy_prop, "xgc")) {
> +		/* 10G copper, XPCS */
> +		np->flags |= NIU_FLAGS_10G;
> +		np->flags &= ~NIU_FLAGS_FIBER;
> +		np->mac_xcvr = MAC_XCVR_XPCS;
> +	} else {
> +		return -EINVAL;
> +	}
> +	return 0;
> +}
> +
> +static void __devinit niu_pci_vpd_validate(struct niu *np)
> +{
> +	struct net_device *dev = np->dev;
> +	struct niu_vpd *vpd = &np->vpd;
> +	u8 val8;
> +
> +	if (!is_valid_ether_addr(&vpd->local_mac[0])) {
> +		printk(KERN_ERR PFX "VPD MAC invalid, "
> +		       "falling back to SPROM.\n");
> +
> +		np->flags &= ~NIU_FLAGS_VPD_VALID;
> +		return;
> +	}
> +
> +	if (niu_phy_type_prop_decode(np, np->vpd.phy_type)) {
> +		printk(KERN_ERR PFX "Illegal phy string [%s].\n",
> +		       np->vpd.phy_type);
> +		printk(KERN_ERR PFX "Falling back to SPROM.\n");
> +		np->flags &= ~NIU_FLAGS_VPD_VALID;
> +		return;
> +	}
> +
> +	memcpy(dev->perm_addr, vpd->local_mac, ETH_ALEN);
> +
> +	val8 = dev->perm_addr[5];
> +	dev->perm_addr[5] += np->port;
> +	if (dev->perm_addr[5] < val8)
> +		dev->perm_addr[4]++;
> +
> +	memcpy(dev->dev_addr, dev->perm_addr, dev->addr_len);
> +}
> +
> +static int __devinit niu_pci_probe_sprom(struct niu *np)
> +{
> +	struct net_device *dev = np->dev;
> +	int len, i;
> +	u64 val, sum;
> +	u8 val8;
> +
> +	val = (nr64(ESPC_VER_IMGSZ) & ESPC_VER_IMGSZ_IMGSZ);
> +	val >>= ESPC_VER_IMGSZ_IMGSZ_SHIFT;
> +	len = val / 4;
> +
> +	np->eeprom_len = len;
> +
> +	niudbg(PROBE, "SPROM: Image size %llu\n", (unsigned long long) val);
> +
> +	sum = 0;
> +	for (i = 0; i < len; i++) {
> +		val = nr64(ESPC_NCR(i));
> +		sum += (val >>  0) & 0xff;
> +		sum += (val >>  8) & 0xff;
> +		sum += (val >> 16) & 0xff;
> +		sum += (val >> 24) & 0xff;
> +	}
> +	niudbg(PROBE, "SPROM: Checksum %x\n", (int)(sum & 0xff));
> +	if ((sum & 0xff) != 0xab) {
> +		printk(KERN_ERR PFX "Bad SPROM checksum "
> +		       "(%x, should be 0xab)\n", (int) (sum & 0xff));
> +		return -EINVAL;
> +	}
> +
> +	val = nr64(ESPC_PHY_TYPE);
> +	switch (np->port) {
> +	case 0:
> +		val = (val & ESPC_PHY_TYPE_PORT0) >>
> +			ESPC_PHY_TYPE_PORT0_SHIFT;
> +		break;
> +	case 1:
> +		val = (val & ESPC_PHY_TYPE_PORT1) >>
> +			ESPC_PHY_TYPE_PORT1_SHIFT;
> +		break;
> +	case 2:
> +		val = (val & ESPC_PHY_TYPE_PORT2) >>
> +			ESPC_PHY_TYPE_PORT2_SHIFT;
> +		break;
> +	case 3:
> +		val = (val & ESPC_PHY_TYPE_PORT3) >>
> +			ESPC_PHY_TYPE_PORT3_SHIFT;
> +		break;
> +	default:
> +		printk(KERN_ERR PFX "Bogus port number %u\n",
> +		       np->port);
> +		return -EINVAL;
> +	}
> +	niudbg(PROBE, "SPROM: PHY type %llx\n", (unsigned long long) val);
> +
> +	switch (val) {
> +	case ESPC_PHY_TYPE_1G_COPPER:
> +		/* 1G copper, MII */
> +		np->flags &= ~(NIU_FLAGS_FIBER |
> +			       NIU_FLAGS_10G);
> +		np->mac_xcvr = MAC_XCVR_MII;
> +		break;
> +
> +	case ESPC_PHY_TYPE_1G_FIBER:
> +		/* 1G fiber, PCS */
> +		np->flags &= ~NIU_FLAGS_10G;
> +		np->flags |= NIU_FLAGS_FIBER;
> +		np->mac_xcvr = MAC_XCVR_PCS;
> +		break;
> +
> +	case ESPC_PHY_TYPE_10G_COPPER:
> +		/* 10G copper, XPCS */
> +		np->flags |= NIU_FLAGS_10G;
> +		np->flags &= ~NIU_FLAGS_FIBER;
> +		np->mac_xcvr = MAC_XCVR_XPCS;
> +		break;
> +
> +	case ESPC_PHY_TYPE_10G_FIBER:
> +		/* 10G fiber, XPCS */
> +		np->flags |= (NIU_FLAGS_10G |
> +			      NIU_FLAGS_FIBER);
> +		np->mac_xcvr = MAC_XCVR_XPCS;
> +		break;
> +
> +	default:
> +		printk(KERN_ERR PFX "Bogus SPROM phy type %llu\n",
> +		       (unsigned long long) val);
> +		return -EINVAL;
> +	}
> +
> +	val = nr64(ESPC_MAC_ADDR0);
> +	niudbg(PROBE, "SPROM: MAC_ADDR0[%08llx]\n",
> +	       (unsigned long long) val);
> +	dev->perm_addr[0] = (val >>  0) & 0xff;
> +	dev->perm_addr[1] = (val >>  8) & 0xff;
> +	dev->perm_addr[2] = (val >> 16) & 0xff;
> +	dev->perm_addr[3] = (val >> 24) & 0xff;
> +
> +	val = nr64(ESPC_MAC_ADDR1);
> +	niudbg(PROBE, "SPROM: MAC_ADDR1[%08llx]\n",
> +	       (unsigned long long) val);
> +	dev->perm_addr[4] = (val >>  0) & 0xff;
> +	dev->perm_addr[5] = (val >>  8) & 0xff;
> +
> +	if (!is_valid_ether_addr(&dev->perm_addr[0])) {
> +		printk(KERN_ERR PFX "SPROM MAC address invalid\n");
> +		printk(KERN_ERR PFX "[ \n");
> +		for (i = 0; i < 6; i++)
> +			printk("%02x ", dev->perm_addr[i]);
> +		printk("]\n");
> +		return -EINVAL;
> +	}
> +
> +	val8 = dev->perm_addr[5];
> +	dev->perm_addr[5] += np->port;
> +	if (dev->perm_addr[5] < val8)
> +		dev->perm_addr[4]++;
> +
> +	memcpy(dev->dev_addr, dev->perm_addr, dev->addr_len);
> +
> +	val = nr64(ESPC_MOD_STR_LEN);
> +	niudbg(PROBE, "SPROM: MOD_STR_LEN[%llu]\n",
> +	       (unsigned long long) val);
> +	if (val > 8 * 4)
> +		return -EINVAL;
> +
> +	for (i = 0; i < val; i += 4) {
> +		u64 tmp = nr64(ESPC_NCR(5 + (i / 4)));
> +
> +		np->vpd.model[i + 3] = (tmp >>  0) & 0xff;
> +		np->vpd.model[i + 2] = (tmp >>  8) & 0xff;
> +		np->vpd.model[i + 1] = (tmp >> 16) & 0xff;
> +		np->vpd.model[i + 0] = (tmp >> 24) & 0xff;
> +	}
> +	np->vpd.model[val] = '\0';
> +
> +	val = nr64(ESPC_BD_MOD_STR_LEN);
> +	niudbg(PROBE, "SPROM: BD_MOD_STR_LEN[%llu]\n",
> +	       (unsigned long long) val);
> +	if (val > 4 * 4)
> +		return -EINVAL;
> +
> +	for (i = 0; i < val; i += 4) {
> +		u64 tmp = nr64(ESPC_NCR(14 + (i / 4)));
> +
> +		np->vpd.board_model[i + 3] = (tmp >>  0) & 0xff;
> +		np->vpd.board_model[i + 2] = (tmp >>  8) & 0xff;
> +		np->vpd.board_model[i + 1] = (tmp >> 16) & 0xff;
> +		np->vpd.board_model[i + 0] = (tmp >> 24) & 0xff;
> +	}
> +	np->vpd.board_model[val] = '\0';
> +
> +	np->vpd.mac_num =
> +		nr64(ESPC_NUM_PORTS_MACS) & ESPC_NUM_PORTS_MACS_VAL;
> +	niudbg(PROBE, "SPROM: NUM_PORTS_MACS[%d]\n",
> +	       np->vpd.mac_num);
> +
> +	return 0;
> +}
> +
> +static int __devinit niu_get_and_validate_port(struct niu *np)
> +{
> +	struct niu_parent *parent = np->parent;
> +
> +	if (np->port <= 1)
> +		np->flags |= NIU_FLAGS_XMAC;
> +
> +	if (!parent->num_ports) {
> +		if (parent->plat_type == PLAT_TYPE_NIU) {
> +			parent->num_ports = 2;
> +		} else {
> +			parent->num_ports = nr64(ESPC_NUM_PORTS_MACS) &
> +				ESPC_NUM_PORTS_MACS_VAL;
> +
> +			if (!parent->num_ports)
> +				parent->num_ports = 4;
> +		}
> +	}
> +
> +	niudbg(PROBE, "niu_get_and_validate_port: port[%d] num_ports[%d]\n",
> +	       np->port, parent->num_ports);
> +	if (np->port >= parent->num_ports)
> +		return -ENODEV;
> +
> +	return 0;
> +}
> +
> +static int __devinit phy_record(struct niu_parent *parent,
> +				struct phy_probe_info *p,
> +				int dev_id_1, int dev_id_2, u8 phy_port,
> +				int type)
> +{
> +	u32 id = (dev_id_1 << 16) | dev_id_2;
> +	u8 idx;
> +
> +	if (dev_id_1 < 0 || dev_id_2 < 0)
> +		return 0;
> +	if (type == PHY_TYPE_PMA_PMD || type == PHY_TYPE_PCS) {
> +		if ((id & NIU_PHY_ID_MASK) != NIU_PHY_ID_BCM8704)
> +			return 0;
> +	} else {
> +		if ((id & NIU_PHY_ID_MASK) != NIU_PHY_ID_BCM5464R)
> +			return 0;
> +	}
> +
> +	printk(KERN_INFO PFX "niu%d: Found PHY %08x type %s at phy_port %u\n",
> +	       parent->index, id,
> +	       (type == PHY_TYPE_PMA_PMD ?
> +		"PMA/PMD" :
> +		(type == PHY_TYPE_PCS ?
> +		 "PCS" : "MII")),
> +	       phy_port);
> +
> +	if (p->cur[type] >= NIU_MAX_PORTS) {
> +		printk(KERN_ERR PFX "Too many PHY ports.\n");
> +		return -EINVAL;
> +	}
> +	idx = p->cur[type];
> +	p->phy_id[type][idx] = id;
> +	p->phy_port[type][idx] = phy_port;
> +	p->cur[type] = idx + 1;
> +	return 0;
> +}
> +
> +static int __devinit port_has_10g(struct phy_probe_info *p, int port)
> +{
> +	int i;
> +
> +	for (i = 0; i < p->cur[PHY_TYPE_PMA_PMD]; i++) {
> +		if (p->phy_port[PHY_TYPE_PMA_PMD][i] == port)
> +			return 1;
> +	}
> +	for (i = 0; i < p->cur[PHY_TYPE_PCS]; i++) {
> +		if (p->phy_port[PHY_TYPE_PCS][i] == port)
> +			return 1;
> +	}
> +
> +	return 0;
> +}
> +
> +static int __devinit count_10g_ports(struct phy_probe_info *p, int *lowest)
> +{
> +	int port, cnt;
> +
> +	cnt = 0;
> +	*lowest = 32;
> +	for (port = 8; port < 32; port++) {
> +		if (port_has_10g(p, port)) {
> +			if (!cnt)
> +				*lowest = port;
> +			cnt++;
> +		}
> +	}
> +
> +	return cnt;
> +}
> +
> +static int __devinit count_1g_ports(struct phy_probe_info *p, int *lowest)
> +{
> +	*lowest = 32;
> +	if (p->cur[PHY_TYPE_MII])
> +		*lowest = p->phy_port[PHY_TYPE_MII][0];
> +
> +	return p->cur[PHY_TYPE_MII];
> +}
> +
> +static void __devinit niu_n2_divide_channels(struct niu_parent *parent)
> +{
> +	int num_ports = parent->num_ports;
> +	int i;
> +
> +	for (i = 0; i < num_ports; i++) {
> +		parent->rxchan_per_port[i] = (16 / num_ports);
> +		parent->txchan_per_port[i] = (16 / num_ports);
> +
> +		printk(KERN_INFO PFX "niu%d: Port %u [%u RX chans] "
> +		       "[%u TX chans]\n",
> +		       parent->index, i,
> +		       parent->rxchan_per_port[i],
> +		       parent->txchan_per_port[i]);
> +	}
> +}
> +
> +static void __devinit niu_divide_channels(struct niu_parent *parent,
> +					  int num_10g, int num_1g)
> +{
> +	int num_ports = parent->num_ports;
> +	int rx_chans_per_10g, rx_chans_per_1g;
> +	int tx_chans_per_10g, tx_chans_per_1g;
> +	int i, tot_rx, tot_tx;
> +
> +	if (!num_10g || !num_1g) {
> +		rx_chans_per_10g = rx_chans_per_1g =
> +			(NIU_NUM_RXCHAN / num_ports);
> +		tx_chans_per_10g = tx_chans_per_1g =
> +			(NIU_NUM_TXCHAN / num_ports);
> +	} else {
> +		rx_chans_per_1g = NIU_NUM_RXCHAN / 8;
> +		rx_chans_per_10g = (NIU_NUM_RXCHAN -
> +				    (rx_chans_per_1g * num_1g)) /
> +			num_10g;
> +
> +		tx_chans_per_1g = NIU_NUM_TXCHAN / 6;
> +		tx_chans_per_10g = (NIU_NUM_TXCHAN -
> +				    (tx_chans_per_1g * num_1g)) /
> +			num_10g;
> +	}
> +
> +	tot_rx = tot_tx = 0;
> +	for (i = 0; i < num_ports; i++) {
> +		int type = phy_decode(parent->port_phy, i);
> +
> +		if (type == PORT_TYPE_10G) {
> +			parent->rxchan_per_port[i] = rx_chans_per_10g;
> +			parent->txchan_per_port[i] = tx_chans_per_10g;
> +		} else {
> +			parent->rxchan_per_port[i] = rx_chans_per_1g;
> +			parent->txchan_per_port[i] = tx_chans_per_1g;
> +		}
> +		printk(KERN_INFO PFX "niu%d: Port %u [%u RX chans] "
> +		       "[%u TX chans]\n",
> +		       parent->index, i,
> +		       parent->rxchan_per_port[i],
> +		       parent->txchan_per_port[i]);
> +		tot_rx += parent->rxchan_per_port[i];
> +		tot_tx += parent->txchan_per_port[i];
> +	}
> +
> +	if (tot_rx > NIU_NUM_RXCHAN) {
> +		printk(KERN_ERR PFX "niu%d: Too many RX channels (%d), "
> +		       "resetting to one per port.\n",
> +		       parent->index, tot_rx);
> +		for (i = 0; i < num_ports; i++)
> +			parent->rxchan_per_port[i] = 1;
> +	}
> +	if (tot_tx > NIU_NUM_TXCHAN) {
> +		printk(KERN_ERR PFX "niu%d: Too many TX channels (%d), "
> +		       "resetting to one per port.\n",
> +		       parent->index, tot_tx);
> +		for (i = 0; i < num_ports; i++)
> +			parent->txchan_per_port[i] = 1;
> +	}
> +	if (tot_rx < NIU_NUM_RXCHAN || tot_tx < NIU_NUM_TXCHAN) {
> +		printk(KERN_WARNING PFX "niu%d: Driver bug, wasted channels, "
> +		       "RX[%d] TX[%d]\n",
> +		       parent->index, tot_rx, tot_tx);
> +	}
> +}
> +
> +static void __devinit niu_divide_rdc_groups(struct niu_parent *parent,
> +					    int num_10g, int num_1g)
> +{
> +	int i, num_ports = parent->num_ports;
> +	int rdc_group, rdc_groups_per_port;
> +	int rdc_channel_base;
> +
> +	rdc_group = 0;
> +	rdc_groups_per_port = NIU_NUM_RDC_TABLES / num_ports;
> +
> +	rdc_channel_base = 0;
> +
> +	for (i = 0; i < num_ports; i++) {
> +		struct niu_rdc_tables *tp = &parent->rdc_group_cfg[i];
> +		int grp, num_channels = parent->rxchan_per_port[i];
> +		int this_channel_offset;
> +
> +		tp->first_table_num = rdc_group;
> +		tp->num_tables = rdc_groups_per_port;
> +		this_channel_offset = 0;
> +		for (grp = 0; grp < tp->num_tables; grp++) {
> +			struct rdc_table *rt = &tp->tables[grp];
> +			int slot;
> +
> +			printk(KERN_INFO PFX "niu%d: Port %d RDC tbl(%d) [ ",
> +			       parent->index, i, tp->first_table_num + grp);
> +			for (slot = 0; slot < NIU_RDC_TABLE_SLOTS; slot++) {
> +				rt->rxdma_channel[slot] =
> +					rdc_channel_base + this_channel_offset;
> +
> +				printk("%d ", rt->rxdma_channel[slot]);
> +
> +				if (++this_channel_offset == num_channels)
> +					this_channel_offset = 0;
> +			}
> +			printk("]\n");
> +		}
> +
> +		parent->rdc_default[i] = rdc_channel_base;
> +
> +		rdc_channel_base += num_channels;
> +		rdc_group += rdc_groups_per_port;
> +	}
> +}
> +
> +static int __devinit fill_phy_probe_info(struct niu *np,
> +					 struct niu_parent *parent,
> +					 struct phy_probe_info *info)
> +{
> +	unsigned long flags;
> +	int port, err;
> +
> +	memset(info, 0, sizeof(*info));
> +
> +	/* Port 0 to 7 are reserved for onboard Serdes, probe the rest.  */
> +	niu_lock_parent(np, flags);
> +	err = 0;
> +	for (port = 8; port < 32; port++) {
> +		int dev_id_1, dev_id_2;
> +
> +		dev_id_1 = mdio_read(np, port,
> +				     NIU_PMA_PMD_DEV_ADDR, MII_PHYSID1);
> +		dev_id_2 = mdio_read(np, port,
> +				     NIU_PMA_PMD_DEV_ADDR, MII_PHYSID2);
> +		err = phy_record(parent, info, dev_id_1, dev_id_2, port,
> +				 PHY_TYPE_PMA_PMD);
> +		if (err)
> +			break;
> +		dev_id_1 = mdio_read(np, port,
> +				     NIU_PCS_DEV_ADDR, MII_PHYSID1);
> +		dev_id_2 = mdio_read(np, port,
> +				     NIU_PCS_DEV_ADDR, MII_PHYSID2);
> +		err = phy_record(parent, info, dev_id_1, dev_id_2, port,
> +				 PHY_TYPE_PCS);
> +		if (err)
> +			break;
> +		dev_id_1 = mii_read(np, port, MII_PHYSID1);
> +		dev_id_2 = mii_read(np, port, MII_PHYSID2);
> +		err = phy_record(parent, info, dev_id_1, dev_id_2, port,
> +				 PHY_TYPE_MII);
> +		if (err)
> +			break;
> +	}
> +	niu_unlock_parent(np, flags);
> +
> +	return err;
> +}
> +
> +static int __devinit walk_phys(struct niu *np, struct niu_parent *parent)
> +{
> +	struct phy_probe_info *info = &parent->phy_probe_info;
> +	int lowest_10g, lowest_1g;
> +	int num_10g, num_1g;
> +	u32 val;
> +	int err;
> +
> +	err = fill_phy_probe_info(np, parent, info);
> +	if (err)
> +		return err;
> +
> +	num_10g = count_10g_ports(info, &lowest_10g);
> +	num_1g = count_1g_ports(info, &lowest_1g);
> +
> +	switch ((num_10g << 4) | num_1g) {
> +	case 0x24:
> +		if (lowest_1g == 10)
> +			parent->plat_type = PLAT_TYPE_VF_P0;
> +		else if (lowest_1g == 26)
> +			parent->plat_type = PLAT_TYPE_VF_P1;
> +		else
> +			goto unknown_vg_1g_port;
> +
> +		/* fallthru */
> +	case 0x22:
> +		val = (phy_encode(PORT_TYPE_10G, 0) |
> +		       phy_encode(PORT_TYPE_10G, 1) |
> +		       phy_encode(PORT_TYPE_1G, 2) |
> +		       phy_encode(PORT_TYPE_1G, 3));
> +		break;
> +
> +	case 0x20:
> +		val = (phy_encode(PORT_TYPE_10G, 0) |
> +		       phy_encode(PORT_TYPE_10G, 1));
> +		break;
> +
> +	case 0x10:
> +		val = phy_encode(PORT_TYPE_10G, np->port);
> +		break;
> +
> +	case 0x14:
> +		if (lowest_1g == 10)
> +			parent->plat_type = PLAT_TYPE_VF_P0;
> +		else if (lowest_1g == 26)
> +			parent->plat_type = PLAT_TYPE_VF_P1;
> +		else
> +			goto unknown_vg_1g_port;
> +
> +		/* fallthru */
> +	case 0x13:
> +		if ((lowest_10g & 0x7) == 0)
> +			val = (phy_encode(PORT_TYPE_10G, 0) |
> +			       phy_encode(PORT_TYPE_1G, 1) |
> +			       phy_encode(PORT_TYPE_1G, 2) |
> +			       phy_encode(PORT_TYPE_1G, 3));
> +		else
> +			val = (phy_encode(PORT_TYPE_1G, 0) |
> +			       phy_encode(PORT_TYPE_10G, 1) |
> +			       phy_encode(PORT_TYPE_1G, 2) |
> +			       phy_encode(PORT_TYPE_1G, 3));
> +		break;
> +
> +	case 0x04:
> +		if (lowest_1g == 10)
> +			parent->plat_type = PLAT_TYPE_VF_P0;
> +		else if (lowest_1g == 26)
> +			parent->plat_type = PLAT_TYPE_VF_P1;
> +		else
> +			goto unknown_vg_1g_port;
> +
> +		val = (phy_encode(PORT_TYPE_1G, 0) |
> +		       phy_encode(PORT_TYPE_1G, 1) |
> +		       phy_encode(PORT_TYPE_1G, 2) |
> +		       phy_encode(PORT_TYPE_1G, 3));
> +		break;
> +
> +	default:
> +		printk(KERN_ERR PFX "Unsupported port config "
> +		       "10G[%d] 1G[%d]\n",
> +		       num_10g, num_1g);
> +		return -EINVAL;
> +	}
> +
> +	parent->port_phy = val;
> +
> +	if (parent->plat_type == PLAT_TYPE_NIU)
> +		niu_n2_divide_channels(parent);
> +	else
> +		niu_divide_channels(parent, num_10g, num_1g);
> +
> +	niu_divide_rdc_groups(parent, num_10g, num_1g);
> +
> +	return 0;
> +
> +unknown_vg_1g_port:
> +	printk(KERN_ERR PFX "Cannot identify platform type, 1gport=%d\n",
> +	       lowest_1g);
> +	return -EINVAL;
> +}
> +
> +static int __devinit niu_probe_ports(struct niu *np)
> +{
> +	struct niu_parent *parent = np->parent;
> +	int err, i;
> +
> +	niudbg(PROBE, "niu_probe_ports(): port_phy[%08x]\n",
> +	       parent->port_phy);
> +
> +	if (parent->port_phy == PORT_PHY_UNKNOWN) {
> +		err = walk_phys(np, parent);
> +		if (err)
> +			return err;
> +
> +		niu_set_ldg_timer_res(np, 2);
> +		for (i = 0; i <= LDN_MAX; i++)
> +			niu_ldn_irq_enable(np, i, 0);
> +	}
> +
> +	if (parent->port_phy == PORT_PHY_INVALID)
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
> +static int __devinit niu_classifier_swstate_init(struct niu *np)
> +{
> +	struct niu_classifier *cp = &np->clas;
> +
> +	niudbg(PROBE, "niu_classifier_swstate_init: num_tcam(%d)\n",
> +	       np->parent->tcam_num_entries);
> +
> +	cp->tcam_index = (u16) np->port;
> +	cp->h1_init = 0xffffffff;
> +	cp->h2_init = 0xffff;
> +
> +	return fflp_early_init(np);
> +}
> +
> +static void __devinit niu_link_config_init(struct niu *np)
> +{
> +	struct niu_link_config *lp = &np->link_config;
> +
> +	lp->advertising = (ADVERTISED_10baseT_Half |
> +			   ADVERTISED_10baseT_Full |
> +			   ADVERTISED_100baseT_Half |
> +			   ADVERTISED_100baseT_Full |
> +			   ADVERTISED_1000baseT_Half |
> +			   ADVERTISED_1000baseT_Full |
> +			   ADVERTISED_10000baseT_Full |
> +			   ADVERTISED_Autoneg);
> +	lp->speed = lp->active_speed = SPEED_INVALID;
> +	lp->duplex = lp->active_duplex = DUPLEX_INVALID;
> +#if 0
> +	lp->loopback_mode = LOOPBACK_MAC;
> +	lp->active_speed = SPEED_10000;
> +	lp->active_duplex = DUPLEX_FULL;
> +#else
> +	lp->loopback_mode = LOOPBACK_DISABLED;
> +#endif
> +}
> +
> +static int __devinit niu_init_mac_ipp_pcs_base(struct niu *np)
> +{
> +	switch (np->port) {
> +	case 0:
> +		np->mac_regs = np->regs + XMAC_PORT0_OFF;
> +		np->ipp_off  = 0x00000;
> +		np->pcs_off  = 0x04000;
> +		np->xpcs_off = 0x02000;
> +		break;
> +
> +	case 1:
> +		np->mac_regs = np->regs + XMAC_PORT1_OFF;
> +		np->ipp_off  = 0x08000;
> +		np->pcs_off  = 0x0a000;
> +		np->xpcs_off = 0x08000;
> +		break;
> +
> +	case 2:
> +		np->mac_regs = np->regs + BMAC_PORT2_OFF;
> +		np->ipp_off  = 0x04000;
> +		np->pcs_off  = 0x0e000;
> +		np->xpcs_off = ~0UL;
> +		break;
> +
> +	case 3:
> +		np->mac_regs = np->regs + BMAC_PORT3_OFF;
> +		np->ipp_off  = 0x0c000;
> +		np->pcs_off  = 0x12000;
> +		np->xpcs_off = ~0UL;
> +		break;
> +
> +	default:
> +		printk(KERN_ERR PFX "Port %u is invalid, cannot "
> +		       "compute MAC block offset.\n", np->port);
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static void __devinit niu_try_msix(struct niu *np, u8 *ldg_num_map)
> +{
> +	struct msix_entry msi_vec[NIU_NUM_LDG];
> +	struct niu_parent *parent = np->parent;
> +	struct pci_dev *pdev = np->pdev;
> +	int i, num_irqs, err;
> +	u8 first_ldg;
> +
> +	first_ldg = (NIU_NUM_LDG / parent->num_ports) * np->port;
> +	for (i = 0; i < (NIU_NUM_LDG / parent->num_ports); i++)
> +		ldg_num_map[i] = first_ldg + i;
> +
> +	num_irqs = (parent->rxchan_per_port[np->port] +
> +		    parent->txchan_per_port[np->port] +
> +		    (np->port == 0 ? 3 : 1));
> +	BUG_ON(num_irqs > (NIU_NUM_LDG / parent->num_ports));
> +
> +retry:
> +	for (i = 0; i < num_irqs; i++) {
> +		msi_vec[i].vector = 0;
> +		msi_vec[i].entry = i;
> +	}
> +
> +	err = pci_enable_msix(pdev, msi_vec, num_irqs);
> +	if (err < 0) {
> +		np->flags &= ~NIU_FLAGS_MSIX;
> +		return;
> +	}
> +	if (err > 0) {
> +		num_irqs = err;
> +		goto retry;
> +	}
> +
> +	np->flags |= NIU_FLAGS_MSIX;
> +	for (i = 0; i < num_irqs; i++)
> +		np->ldg[i].irq = msi_vec[i].vector;
> +	np->num_ldg = num_irqs;
> +}
> +
> +static int __devinit niu_n2_irq_init(struct niu *np, u8 *ldg_num_map)
> +{
> +#ifdef CONFIG_SPARC64
> +	struct of_device *op = np->op;
> +	const u32 *int_prop;
> +	int i;
> +
> +	int_prop = of_get_property(op->node, "interrupts", NULL);
> +	if (!int_prop)
> +		return -ENODEV;
> +
> +	for (i = 0; i < op->num_irqs; i++) {
> +		ldg_num_map[i] = int_prop[i];
> +		np->ldg[i].irq = op->irqs[i];
> +	}
> +
> +	np->num_ldg = op->num_irqs;
> +
> +	return 0;
> +#else
> +	return -EINVAL;
> +#endif
> +}
> +
> +static int __devinit niu_ldg_init(struct niu *np)
> +{
> +	struct niu_parent *parent = np->parent;
> +	u8 ldg_num_map[NIU_NUM_LDG];
> +	int first_chan, num_chan;
> +	int i, err, ldg_rotor;
> +	u8 port;
> +
> +	np->num_ldg = 1;
> +	np->ldg[0].irq = np->dev->irq;
> +	if (parent->plat_type == PLAT_TYPE_NIU) {
> +		err = niu_n2_irq_init(np, ldg_num_map);
> +		if (err)
> +			return err;
> +	} else
> +		niu_try_msix(np, ldg_num_map);
> +
> +	port = np->port;
> +	for (i = 0; i < np->num_ldg; i++) {
> +		struct niu_ldg *lp = &np->ldg[i];
> +
> +		netif_napi_add(np->dev, &lp->napi, niu_poll, 64);
> +
> +		lp->np = np;
> +		lp->ldg_num = ldg_num_map[i];
> +		lp->timer = 2; /* XXX */
> +
> +		/* On N2 NIU the firmware has setup the SID mappings so they go
> +		 * to the correct values that will route the LDG to the proper
> +		 * interrupt in the NCU interrupt table.
> +		 */
> +		if (np->parent->plat_type != PLAT_TYPE_NIU) {
> +			err = niu_set_ldg_sid(np, lp->ldg_num, port, i);
> +			if (err)
> +				return err;
> +		}
> +	}
> +
> +	/* We adopt the LDG assignment ordering used by the N2 NIU
> +	 * 'interrupt' properties because that simplifies a lot of
> +	 * things.  This ordering is:
> +	 *
> +	 *	MAC
> +	 *	MIF	(if port zero)
> +	 *	SYSERR	(if port zero)
> +	 *	RX channels
> +	 *	TX channels
> +	 */
> +
> +	ldg_rotor = 0;
> +
> +	err = niu_ldg_assign_ldn(np, parent, ldg_num_map[ldg_rotor],
> +				  LDN_MAC(port));
> +	if (err)
> +		return err;
> +
> +	ldg_rotor++;
> +	if (ldg_rotor == np->num_ldg)
> +		ldg_rotor = 0;
> +
> +	if (port == 0) {
> +		err = niu_ldg_assign_ldn(np, parent,
> +					 ldg_num_map[ldg_rotor],
> +					 LDN_MIF);
> +		if (err)
> +			return err;
> +
> +		ldg_rotor++;
> +		if (ldg_rotor == np->num_ldg)
> +			ldg_rotor = 0;
> +
> +		err = niu_ldg_assign_ldn(np, parent,
> +					 ldg_num_map[ldg_rotor],
> +					 LDN_DEVICE_ERROR);
> +		if (err)
> +			return err;
> +
> +		ldg_rotor++;
> +		if (ldg_rotor == np->num_ldg)
> +			ldg_rotor = 0;
> +
> +	}
> +
> +	first_chan = 0;
> +	for (i = 0; i < port; i++)
> +		first_chan += parent->rxchan_per_port[port];
> +	num_chan = parent->rxchan_per_port[port];
> +
> +	for (i = first_chan; i < (first_chan + num_chan); i++) {
> +		err = niu_ldg_assign_ldn(np, parent,
> +					 ldg_num_map[ldg_rotor],
> +					 LDN_RXDMA(i));
> +		if (err)
> +			return err;
> +		ldg_rotor++;
> +		if (ldg_rotor == np->num_ldg)
> +			ldg_rotor = 0;
> +	}
> +
> +	first_chan = 0;
> +	for (i = 0; i < port; i++)
> +		first_chan += parent->txchan_per_port[port];
> +	num_chan = parent->txchan_per_port[port];
> +	for (i = first_chan; i < (first_chan + num_chan); i++) {
> +		err = niu_ldg_assign_ldn(np, parent,
> +					 ldg_num_map[ldg_rotor],
> +					 LDN_TXDMA(i));
> +		if (err)
> +			return err;
> +		ldg_rotor++;
> +		if (ldg_rotor == np->num_ldg)
> +			ldg_rotor = 0;
> +	}
> +
> +	return 0;
> +}
> +
> +static void __devexit niu_ldg_free(struct niu *np)
> +{
> +	if (np->flags & NIU_FLAGS_MSIX)
> +		pci_disable_msix(np->pdev);
> +}
> +
> +static int __devinit niu_get_of_props(struct niu *np)
> +{
> +#ifdef CONFIG_SPARC64
> +	struct net_device *dev = np->dev;
> +	struct device_node *dp;
> +	const char *phy_type;
> +	const u8 *mac_addr;
> +	int prop_len;
> +
> +	if (np->parent->plat_type == PLAT_TYPE_NIU)
> +		dp = np->op->node;
> +	else
> +		dp = pci_device_to_OF_node(np->pdev);
> +
> +	phy_type = of_get_property(dp, "phy-type", &prop_len);
> +	if (!phy_type) {
> +		printk(KERN_ERR PFX "%s: OF node lacks phy-type property\n",
> +		       dp->full_name);
> +		return -EINVAL;
> +	}
> +
> +	if (!strcmp(phy_type, "none"))
> +		return -ENODEV;
> +
> +	strcpy(np->vpd.phy_type, phy_type);
> +
> +	if (niu_phy_type_prop_decode(np, np->vpd.phy_type)) {
> +		printk(KERN_ERR PFX "%s: Illegal phy string [%s].\n",
> +		       dp->full_name, np->vpd.phy_type);
> +		return -EINVAL;
> +	}
> +
> +	mac_addr = of_get_property(dp, "local-mac-address", &prop_len);
> +	if (!mac_addr) {
> +		printk(KERN_ERR PFX "%s: OF node lacks local-mac-address property\n",
> +		       dp->full_name);
> +		return -EINVAL;
> +	}
> +	if (prop_len != dev->addr_len) {
> +		printk(KERN_ERR PFX "%s: OF MAC address prop len (%d) is wrong.\n",
> +		       dp->full_name, prop_len);
> +	}
> +	memcpy(dev->perm_addr, mac_addr, dev->addr_len);
> +	if (!is_valid_ether_addr(&dev->perm_addr[0])) {
> +		int i;
> +
> +		printk(KERN_ERR PFX "%s: OF MAC address is invalid\n",
> +		       dp->full_name);
> +		printk(KERN_ERR PFX "%s: [ \n",
> +		       dp->full_name);
> +		for (i = 0; i < 6; i++)
> +			printk("%02x ", dev->perm_addr[i]);
> +		printk("]\n");
> +		return -EINVAL;
> +	}
> +
> +	memcpy(dev->dev_addr, dev->perm_addr, dev->addr_len);
> +
> +	return 0;
> +#else
> +	return -EINVAL;
> +#endif
> +}
> +
> +static int __devinit niu_get_invariants(struct niu *np)
> +{
> +	int err, have_props;
> +	u32 offset;
> +
> +	err = niu_get_of_props(np);
> +	if (err == -ENODEV)
> +		return err;
> +
> +	have_props = !err;
> +
> +	err = niu_get_and_validate_port(np);
> +	if (err)
> +		return err;
> +
> +	err = niu_init_mac_ipp_pcs_base(np);
> +	if (err)
> +		return err;
> +
> +	if (!have_props) {
> +		if (np->parent->plat_type == PLAT_TYPE_NIU)
> +			return -EINVAL;
> +
> +		nw64(ESPC_PIO_EN, ESPC_PIO_EN_ENABLE);
> +		offset = niu_pci_vpd_offset(np);
> +		niudbg(PROBE, "niu_get_invariants: VPD offset [%08x]\n",
> +		       offset);
> +		if (offset)
> +			niu_pci_vpd_fetch(np, offset);
> +		nw64(ESPC_PIO_EN, 0);
> +
> +		if (np->flags & NIU_FLAGS_VPD_VALID)
> +			niu_pci_vpd_validate(np);
> +
> +		if (!(np->flags & NIU_FLAGS_VPD_VALID)) {
> +			err = niu_pci_probe_sprom(np);
> +			if (err)
> +				return err;
> +		}
> +	}
> +
> +	err = niu_probe_ports(np);
> +	if (err)
> +		return err;
> +
> +	niu_ldg_init(np);
> +
> +	niu_classifier_swstate_init(np);
> +	niu_link_config_init(np);
> +
> +	err = niu_determine_phy_disposition(np);
> +	if (!err)
> +		err = niu_init_link(np);
> +
> +	return err;
> +}
> +
> +static LIST_HEAD(niu_parent_list);
> +static DEFINE_MUTEX(niu_parent_lock);
> +static int niu_parent_index;
> +
> +static ssize_t show_port_phy(struct device *dev,
> +			     struct device_attribute *attr, char *buf)
> +{
> +	struct platform_device *plat_dev = to_platform_device(dev);
> +	struct niu_parent *p = plat_dev->dev.platform_data;
> +	u32 port_phy = p->port_phy;
> +	char *orig_buf = buf;
> +	int i;
> +
> +	if (port_phy == PORT_PHY_UNKNOWN ||
> +	    port_phy == PORT_PHY_INVALID)
> +		return 0;
> +
> +	for (i = 0; i < p->num_ports; i++) {
> +		const char *type_str;
> +		int type;
> +
> +		type = phy_decode(port_phy, i);
> +		if (type == PORT_TYPE_10G)
> +			type_str = "10G";
> +		else
> +			type_str = "1G";
> +		buf += sprintf(buf,
> +			       (i == 0) ? "%s" : " %s",
> +			       type_str);
> +	}
> +	buf += sprintf(buf, "\n");
> +	return buf - orig_buf;
> +}
> +
> +static ssize_t show_plat_type(struct device *dev,
> +			      struct device_attribute *attr, char *buf)
> +{
> +	struct platform_device *plat_dev = to_platform_device(dev);
> +	struct niu_parent *p = plat_dev->dev.platform_data;
> +	const char *type_str;
> +
> +	switch (p->plat_type) {
> +	case PLAT_TYPE_ATLAS:
> +		type_str = "atlas";
> +		break;
> +	case PLAT_TYPE_NIU:
> +		type_str = "niu";
> +		break;
> +	case PLAT_TYPE_VF_P0:
> +		type_str = "vf_p0";
> +		break;
> +	case PLAT_TYPE_VF_P1:
> +		type_str = "vf_p1";
> +		break;
> +	default:
> +		type_str = "unknown";
> +		break;
> +	}
> +
> +	return sprintf(buf, "%s\n", type_str);
> +}
> +
> +static ssize_t __show_chan_per_port(struct device *dev,
> +				    struct device_attribute *attr, char *buf,
> +				    int rx)
> +{
> +	struct platform_device *plat_dev = to_platform_device(dev);
> +	struct niu_parent *p = plat_dev->dev.platform_data;
> +	char *orig_buf = buf;
> +	u8 *arr;
> +	int i;
> +
> +	arr = (rx ? p->rxchan_per_port : p->txchan_per_port);
> +
> +	for (i = 0; i < p->num_ports; i++) {
> +		buf += sprintf(buf,
> +			       (i == 0) ? "%d" : " %d",
> +			       arr[i]);
> +	}
> +	buf += sprintf(buf, "\n");
> +
> +	return buf - orig_buf;
> +}
> +
> +static ssize_t show_rxchan_per_port(struct device *dev,
> +				    struct device_attribute *attr, char *buf)
> +{
> +	return __show_chan_per_port(dev, attr, buf, 1);
> +}
> +
> +static ssize_t show_txchan_per_port(struct device *dev,
> +				    struct device_attribute *attr, char *buf)
> +{
> +	return __show_chan_per_port(dev, attr, buf, 1);
> +}
> +
> +static ssize_t show_num_ports(struct device *dev,
> +			      struct device_attribute *attr, char *buf)
> +{
> +	struct platform_device *plat_dev = to_platform_device(dev);
> +	struct niu_parent *p = plat_dev->dev.platform_data;
> +
> +	return sprintf(buf, "%d\n", p->num_ports);
> +}
> +
> +static struct device_attribute niu_parent_attributes[] = {
> +	__ATTR(port_phy, S_IRUGO, show_port_phy, NULL),
> +	__ATTR(plat_type, S_IRUGO, show_plat_type, NULL),
> +	__ATTR(rxchan_per_port, S_IRUGO, show_rxchan_per_port, NULL),
> +	__ATTR(txchan_per_port, S_IRUGO, show_txchan_per_port, NULL),
> +	__ATTR(num_ports, S_IRUGO, show_num_ports, NULL),
> +	{}
> +};
> +
> +static struct niu_parent * __devinit niu_new_parent(struct niu *np,
> +						    union niu_parent_id *id,
> +						    u8 ptype)
> +{
> +	struct platform_device *plat_dev;
> +	struct niu_parent *p;
> +	int i;
> +
> +	niudbg(PROBE, "niu_new_parent: Creating new parent.\n");
> +
> +	plat_dev = platform_device_register_simple("niu", niu_parent_index,
> +						   NULL, 0);
> +	if (!plat_dev)
> +		return NULL;
> +
> +	for (i = 0; attr_name(niu_parent_attributes[i]); i++) {
> +		int err = device_create_file(&plat_dev->dev,
> +					     &niu_parent_attributes[i]);
> +		if (err)
> +			goto fail_unregister;
> +	}
> +
> +	p = kzalloc(sizeof(*p), GFP_KERNEL);
> +	if (!p)
> +		goto fail_unregister;
> +
> +	p->index = niu_parent_index++;
> +
> +	plat_dev->dev.platform_data = p;
> +	p->plat_dev = plat_dev;
> +
> +	memcpy(&p->id, id, sizeof(*id));
> +	p->plat_type = ptype;
> +	INIT_LIST_HEAD(&p->list);
> +	atomic_set(&p->refcnt, 0);
> +	list_add(&p->list, &niu_parent_list);
> +	spin_lock_init(&p->lock);
> +
> +	p->rxdma_clock_divider = 7500;
> +
> +	p->tcam_num_entries = NIU_PCI_TCAM_ENTRIES;
> +	if (p->plat_type == PLAT_TYPE_NIU)
> +		p->tcam_num_entries = NIU_NONPCI_TCAM_ENTRIES;
> +
> +	for (i = CLASS_CODE_USER_PROG1; i <= CLASS_CODE_SCTP_IPV6; i++) {
> +		int index = i - CLASS_CODE_USER_PROG1;
> +
> +		p->tcam_key[index] = TCAM_KEY_TSEL;
> +		p->flow_key[index] = (FLOW_KEY_IPSA |
> +				      FLOW_KEY_IPDA |
> +				      FLOW_KEY_PROTO |
> +				      (FLOW_KEY_L4_BYTE12 <<
> +				       FLOW_KEY_L4_0_SHIFT) |
> +				      (FLOW_KEY_L4_BYTE12 <<
> +				       FLOW_KEY_L4_1_SHIFT));
> +	}
> +
> +	for (i = 0; i < LDN_MAX + 1; i++)
> +		p->ldg_map[i] = LDG_INVALID;
> +
> +	return p;
> +
> +fail_unregister:
> +	platform_device_unregister(plat_dev);
> +	return NULL;
> +}
> +
> +static struct niu_parent * __devinit niu_get_parent(struct niu *np,
> +						    union niu_parent_id *id,
> +						    u8 ptype)
> +{
> +	struct niu_parent *p, *tmp;
> +	int port = np->port;
> +
> +	niudbg(PROBE, "niu_get_parent: platform_type[%u] port[%u]\n",
> +	       ptype, port);
> +
> +	mutex_lock(&niu_parent_lock);
> +	p = NULL;
> +	list_for_each_entry(tmp, &niu_parent_list, list) {
> +		if (!memcmp(id, &tmp->id, sizeof(*id))) {
> +			p = tmp;
> +			break;
> +		}
> +	}
> +	if (!p)
> +		p = niu_new_parent(np, id, ptype);
> +
> +	if (p) {
> +		char port_name[6];
> +		int err;
> +
> +		sprintf(port_name, "port%d", port);
> +		err = sysfs_create_link(&p->plat_dev->dev.kobj,
> +					&np->device->kobj,
> +					port_name);
> +		if (!err) {
> +			p->ports[port] = np;
> +			atomic_inc(&p->refcnt);
> +		}
> +	}
> +	mutex_unlock(&niu_parent_lock);
> +
> +	return p;
> +}
> +
> +static void niu_put_parent(struct niu *np)
> +{
> +	struct niu_parent *p = np->parent;
> +	u8 port = np->port;
> +	char port_name[6];
> +
> +	BUG_ON(!p || p->ports[port] != np);
> +
> +	niudbg(PROBE, "niu_put_parent: port[%u]\n", port);
> +
> +	sprintf(port_name, "port%d", port);
> +
> +	mutex_lock(&niu_parent_lock);
> +
> +	sysfs_remove_link(&p->plat_dev->dev.kobj, port_name);
> +
> +	p->ports[port] = NULL;
> +	np->parent = NULL;
> +
> +	if (atomic_dec_and_test(&p->refcnt)) {
> +		list_del(&p->list);
> +		platform_device_unregister(p->plat_dev);
> +	}
> +
> +	mutex_unlock(&niu_parent_lock);
> +}
> +
> +static void *niu_pci_alloc_coherent(struct device *dev, size_t size,
> +				    u64 *handle, gfp_t flag)
> +{
> +	dma_addr_t dh;
> +	void *ret;
> +
> +	ret = dma_alloc_coherent(dev, size, &dh, flag);
> +	if (ret)
> +		*handle = dh;
> +	return ret;
> +}
> +
> +static void niu_pci_free_coherent(struct device *dev, size_t size,
> +				  void *cpu_addr, u64 handle)
> +{
> +	dma_free_coherent(dev, size, cpu_addr, handle);
> +}
> +
> +static u64 niu_pci_map_page(struct device *dev, struct page *page,
> +			    unsigned long offset, size_t size,
> +			    enum dma_data_direction direction)
> +{
> +	return dma_map_page(dev, page, offset, size, direction);
> +}
> +
> +static void niu_pci_unmap_page(struct device *dev, u64 dma_address,
> +			       size_t size, enum dma_data_direction direction)
> +{
> +	return dma_unmap_page(dev, dma_address, size, direction);
> +}
> +
> +static u64 niu_pci_map_single(struct device *dev, void *cpu_addr,
> +			      size_t size,
> +			      enum dma_data_direction direction)
> +{
> +	return dma_map_single(dev, cpu_addr, size, direction);
> +}
> +
> +static void niu_pci_unmap_single(struct device *dev, u64 dma_address,
> +				 size_t size,
> +				 enum dma_data_direction direction)
> +{
> +	dma_unmap_single(dev, dma_address, size, direction);
> +}
> +
> +static const struct niu_ops niu_pci_ops = {
> +	.alloc_coherent	= niu_pci_alloc_coherent,
> +	.free_coherent	= niu_pci_free_coherent,
> +	.map_page	= niu_pci_map_page,
> +	.unmap_page	= niu_pci_unmap_page,
> +	.map_single	= niu_pci_map_single,
> +	.unmap_single	= niu_pci_unmap_single,
> +};
> +
> +static void __devinit niu_driver_version(void)
> +{
> +	static int niu_version_printed = 0;
> +
> +	if (niu_version_printed++ == 0)
> +		printk(KERN_INFO "%s", version);
> +}
> +
> +static struct net_device * __devinit niu_alloc_and_init(struct device *gen_dev,
> +							struct pci_dev *pdev,
> +							struct of_device *op,
> +							const struct niu_ops *ops,
> +							u8 port)
> +{
> +	struct net_device *dev = alloc_etherdev(sizeof(struct niu));
> +	struct niu *np;
> +
> +	if (!dev) {
> +		printk(KERN_ERR PFX "Etherdev alloc failed, aborting.\n");
> +		return NULL;
> +	}
> +
> +	SET_NETDEV_DEV(dev, gen_dev);
> +
> +	np = netdev_priv(dev);
> +	np->dev = dev;
> +	np->pdev = pdev;
> +	np->op = op;
> +	np->device = gen_dev;
> +	np->ops = ops;
> +
> +	spin_lock_init(&np->lock);
> +	INIT_WORK(&np->reset_task, niu_reset_task);
> +
> +	np->port = port;
> +
> +	return dev;
> +}
> +
> +static void __devinit niu_assign_netdev_ops(struct net_device *dev)
> +{
> +	dev->open = niu_open;
> +	dev->stop = niu_close;
> +	dev->get_stats = niu_get_stats;
> +	dev->set_multicast_list = niu_set_rx_mode;
> +	dev->set_mac_address = niu_set_mac_addr;
> +	dev->do_ioctl = niu_ioctl;
> +	dev->tx_timeout = niu_tx_timeout;
> +	dev->hard_start_xmit = niu_start_xmit;
> +	dev->ethtool_ops = &niu_ethtool_ops;
> +	dev->watchdog_timeo = NIU_TX_TIMEOUT;
> +	dev->change_mtu = niu_change_mtu;
> +}
> +
> +static void __devinit niu_device_announce(struct niu *np)
> +{
> +	struct net_device *dev = np->dev;
> +	int i;
> +
> +	printk(KERN_INFO "%s: NIU Ethernet ", dev->name);
> +	for (i = 0; i < 6; i++)
> +		printk("%2.2x%c", dev->dev_addr[i],
> +		       i == 5 ? '\n' : ':');
> +
> +	printk(KERN_INFO "%s: Port type[%s] mode[%s:%s] XCVR[%s] phy[%s]\n",
> +	       dev->name,
> +	       (np->flags & NIU_FLAGS_XMAC ? "XMAC" : "BMAC"),
> +	       (np->flags & NIU_FLAGS_10G ? "10G" : "1G"),
> +	       (np->flags & NIU_FLAGS_FIBER ? "FIBER" : "COPPER"),
> +	       (np->mac_xcvr == MAC_XCVR_MII ? "MII" :
> +		(np->mac_xcvr == MAC_XCVR_PCS ? "PCS" : "XPCS")),
> +	       np->vpd.phy_type);
> +}
> +
> +static int __devinit niu_pci_init_one(struct pci_dev *pdev,
> +				      const struct pci_device_id *ent)
> +{
> +	unsigned long niureg_base, niureg_len;
> +	union niu_parent_id parent_id;
> +	struct net_device *dev;
> +	struct niu *np;
> +	int err, pos;
> +	u64 dma_mask;
> +	u16 val16;
> +
> +	niu_driver_version();
> +
> +	err = pci_enable_device(pdev);
> +	if (err) {
> +		printk(KERN_ERR PFX "Cannot enable PCI device, "
> +		       "aborting.\n");
> +		return err;
> +	}
> +
> +	if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM) ||
> +	    !(pci_resource_flags(pdev, 2) & IORESOURCE_MEM)) {
> +		printk(KERN_ERR PFX "Cannot find proper PCI device "
> +		       "base addresses, aborting.\n");
> +		err = -ENODEV;
> +		goto err_out_disable_pdev;
> +	}
> +
> +	err = pci_request_regions(pdev, DRV_MODULE_NAME);
> +	if (err) {
> +		printk(KERN_ERR PFX "Cannot obtain PCI resources, "
> +		       "aborting.\n");
> +		goto err_out_disable_pdev;
> +	}
> +
> +	pos = pci_find_capability(pdev, PCI_CAP_ID_EXP);
> +	if (pos <= 0) {
> +		printk(KERN_ERR PFX "Cannot find PCI Express capability, "
> +		       "aborting.\n");
> +		goto err_out_free_res;
> +	}
> +
> +	dev = niu_alloc_and_init(&pdev->dev, pdev, NULL,
> +				 &niu_pci_ops, PCI_FUNC(pdev->devfn));
> +	if (!dev) {
> +		err = -ENOMEM;
> +		goto err_out_free_res;
> +	}
> +	np = netdev_priv(dev);
> +
> +	memset(&parent_id, 0, sizeof(parent_id));
> +	parent_id.pci.domain = pci_domain_nr(pdev->bus);
> +	parent_id.pci.bus = pdev->bus->number;
> +	parent_id.pci.device = PCI_SLOT(pdev->devfn);
> +
> +	np->parent = niu_get_parent(np, &parent_id,
> +				    PLAT_TYPE_ATLAS);
> +	if (!np->parent) {
> +		err = -ENOMEM;
> +		goto err_out_free_dev;
> +	}
> +
> +	pci_read_config_word(pdev, pos + PCI_EXP_DEVCTL, &val16);
> +	val16 &= ~PCI_EXP_DEVCTL_NOSNOOP_EN;
> +	val16 |= (PCI_EXP_DEVCTL_CERE |
> +		  PCI_EXP_DEVCTL_NFERE |
> +		  PCI_EXP_DEVCTL_FERE |
> +		  PCI_EXP_DEVCTL_URRE |
> +		  PCI_EXP_DEVCTL_RELAX_EN);
> +	pci_write_config_word(pdev, pos + PCI_EXP_DEVCTL, val16);
> +
> +	dma_mask = DMA_44BIT_MASK;
> +	err = pci_set_dma_mask(pdev, dma_mask);
> +	if (!err) {
> +		dev->features |= NETIF_F_HIGHDMA;
> +		err = pci_set_consistent_dma_mask(pdev, dma_mask);
> +		if (err) {
> +			printk(KERN_ERR PFX "Unable to obtain 44 bit "
> +			       "DMA for consistent allocations, "
> +			       "aborting.\n");
> +			goto err_out_release_parent;
> +		}
> +	}
> +	if (err || dma_mask == DMA_32BIT_MASK) {
> +		err = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
> +		if (err) {
> +			printk(KERN_ERR PFX "No usable DMA configuration, "
> +			       "aborting.\n");
> +			goto err_out_release_parent;
> +		}
> +	}
> +
> +	dev->features |= (NETIF_F_SG | NETIF_F_HW_CSUM);
> +
> +	niureg_base = pci_resource_start(pdev, 0);
> +	niureg_len = pci_resource_len(pdev, 0);
> +
> +	np->regs = ioremap_nocache(niureg_base, niureg_len);
> +	if (!np->regs) {
> +		printk(KERN_ERR PFX "Cannot map device registers, "
> +		       "aborting.\n");
> +		err = -ENOMEM;
> +		goto err_out_release_parent;
> +	}
> +
> +	pci_set_master(pdev);
> +	pci_save_state(pdev);
> +
> +	dev->irq = pdev->irq;
> +
> +	niu_assign_netdev_ops(dev);
> +
> +	err = niu_get_invariants(np);
> +	if (err) {
> +		if (err != -ENODEV)
> +			printk(KERN_ERR PFX "Problem fetching invariants "
> +			       "of chip, aborting.\n");
> +		goto err_out_iounmap;
> +	}
> +
> +	err = register_netdev(dev);
> +	if (err) {
> +		printk(KERN_ERR PFX "Cannot register net device, "
> +		       "aborting.\n");
> +		goto err_out_iounmap;
> +	}
> +
> +	pci_set_drvdata(pdev, dev);
> +
> +	niu_device_announce(np);
> +
> +	return 0;
> +
> +err_out_iounmap:
> +	if (np->regs) {
> +		iounmap(np->regs);
> +		np->regs = NULL;
> +	}
> +
> +err_out_release_parent:
> +	niu_put_parent(np);
> +
> +err_out_free_dev:
> +	free_netdev(dev);
> +
> +err_out_free_res:
> +	pci_release_regions(pdev);
> +
> +err_out_disable_pdev:
> +	pci_disable_device(pdev);
> +	pci_set_drvdata(pdev, NULL);
> +
> +	return err;
> +}
> +
> +static void __devexit niu_pci_remove_one(struct pci_dev *pdev)
> +{
> +	struct net_device *dev = pci_get_drvdata(pdev);
> +
> +	if (dev) {
> +		struct niu *np = netdev_priv(dev);
> +
> +		unregister_netdev(dev);
> +		if (np->regs) {
> +			iounmap(np->regs);
> +			np->regs = NULL;
> +		}
> +
> +		niu_ldg_free(np);
> +
> +		niu_put_parent(np);
> +
> +		free_netdev(dev);
> +		pci_release_regions(pdev);
> +		pci_disable_device(pdev);
> +		pci_set_drvdata(pdev, NULL);
> +	}
> +}
> +
> +static int niu_suspend(struct pci_dev *pdev, pm_message_t state)
> +{
> +	struct net_device *dev = pci_get_drvdata(pdev);
> +	struct niu *np = netdev_priv(dev);
> +	unsigned long flags;
> +
> +	if (!netif_running(dev))
> +		return 0;
> +
> +	flush_scheduled_work();
> +	niu_netif_stop(np);
> +
> +	del_timer_sync(&np->timer);
> +
> +	spin_lock_irqsave(&np->lock, flags);
> +	niu_enable_interrupts(np, 0);
> +	spin_unlock_irqrestore(&np->lock, flags);
> +
> +	netif_device_detach(dev);

No need to drop lock for detach? all it does is call stop_queue()

> +
> +	spin_lock_irqsave(&np->lock, flags);
> +	niu_stop_hw(np);
> +	spin_unlock_irqrestore(&np->lock, flags);
> +
> +	pci_save_state(pdev);
> +
> +	return 0;
> +}
> +
> +static int niu_resume(struct pci_dev *pdev)
> +{
> +	struct net_device *dev = pci_get_drvdata(pdev);
> +	struct niu *np = netdev_priv(dev);
> +	unsigned long flags;
> +	int err;
> +
> +	if (!netif_running(dev))
> +		return 0;
> +
> +	pci_restore_state(pdev);
> +
> +	netif_device_attach(dev);
> +
> +	spin_lock_irqsave(&np->lock, flags);
> +
> +	err = niu_init_hw(np);
> +	if (!err) {
> +		np->timer.expires = jiffies + HZ;
> +		add_timer(&np->timer);
> +		niu_netif_start(np);
> +	}
> +
> +	spin_unlock_irqrestore(&np->lock, flags);
> +
> +	return err;
> +}
> +
> +static struct pci_driver niu_pci_driver = {
> +	.name		= DRV_MODULE_NAME,
> +	.id_table	= niu_pci_tbl,
> +	.probe		= niu_pci_init_one,
> +	.remove		= __devexit_p(niu_pci_remove_one),
> +	.suspend	= niu_suspend,
> +	.resume		= niu_resume,
> +};
> +
> +#ifdef CONFIG_SPARC64
> +static void *niu_phys_alloc_coherent(struct device *dev, size_t size,
> +				     u64 *dma_addr, gfp_t flag)
> +{
> +	unsigned long order = get_order(size);
> +	unsigned long page = __get_free_pages(flag, order);
> +
> +	if (page == 0UL)
> +		return NULL;
> +	memset((char *)page, 0, PAGE_SIZE << order);
> +	*dma_addr = __pa(page);
> +
> +	return (void *) page;
> +}

Generic cod useful for other drivers??

> +
> +static void niu_phys_free_coherent(struct device *dev, size_t size,
> +				   void *cpu_addr, u64 handle)
> +{
> +	unsigned long order = get_order(size);
> +
> +	free_pages((unsigned long) cpu_addr, order);
> +}
> +
> +static u64 niu_phys_map_page(struct device *dev, struct page *page,
> +			     unsigned long offset, size_t size,
> +			     enum dma_data_direction direction)
> +{
> +	return page_to_phys(page) + offset;
> +}
> +
> +static void niu_phys_unmap_page(struct device *dev, u64 dma_address,
> +				size_t size, enum dma_data_direction direction)
> +{
> +	/* Nothing to do.  */
> +}
> +
> +static u64 niu_phys_map_single(struct device *dev, void *cpu_addr,
> +			       size_t size,
> +			       enum dma_data_direction direction)
> +{
> +	return __pa(cpu_addr);
> +}
> +
> +static void niu_phys_unmap_single(struct device *dev, u64 dma_address,
> +				  size_t size,
> +				  enum dma_data_direction direction)
> +{
> +	/* Nothing to do.  */
> +}
> +
> +static const struct niu_ops niu_phys_ops = {
> +	.alloc_coherent	= niu_phys_alloc_coherent,
> +	.free_coherent	= niu_phys_free_coherent,
> +	.map_page	= niu_phys_map_page,
> +	.unmap_page	= niu_phys_unmap_page,
> +	.map_single	= niu_phys_map_single,
> +	.unmap_single	= niu_phys_unmap_single,
> +};
> +
> +static unsigned long res_size(struct resource *r)
> +{
> +	return r->end - r->start + 1UL;
> +}
> +
> +static int __devinit niu_of_probe(struct of_device *op, const struct of_device_id *match)
> +{
> +	union niu_parent_id parent_id;
> +	struct net_device *dev;
> +	struct niu *np;
> +	const u32 *reg;
> +	int err;
> +
> +	niu_driver_version();
> +
> +	reg = of_get_property(op->node, "reg", NULL);
> +	if (!reg) {
> +		printk(KERN_ERR PFX "%s: No 'reg' property, aborting.\n",
> +		       op->node->full_name);
> +		return -ENODEV;
> +	}
> +
> +	dev = niu_alloc_and_init(&op->dev, NULL, op,
> +				 &niu_phys_ops, reg[0] & 0x1);
> +	if (!dev) {
> +		err = -ENOMEM;
> +		goto err_out;
> +	}
> +	np = netdev_priv(dev);
> +
> +	memset(&parent_id, 0, sizeof(parent_id));
> +	parent_id.of = of_get_parent(op->node);
> +
> +	np->parent = niu_get_parent(np, &parent_id,
> +				    PLAT_TYPE_NIU);
> +	if (!np->parent) {
> +		err = -ENOMEM;
> +		goto err_out_free_dev;
> +	}
> +
> +	dev->features |= (NETIF_F_SG | NETIF_F_HW_CSUM);
> +
> +	np->regs = of_ioremap(&op->resource[1], 0,
> +			      res_size(&op->resource[1]),
> +			      "niu regs");
> +	if (!np->regs) {
> +		printk(KERN_ERR PFX "Cannot map device registers, "
> +		       "aborting.\n");

dev_err() ??

> +		err = -ENOMEM;
> +		goto err_out_release_parent;
> +	}
> +
> +	np->vir_regs_1 = of_ioremap(&op->resource[2], 0,
> +				    res_size(&op->resource[2]),
> +				    "niu vregs-1");
> +	if (!np->vir_regs_1) {
> +		printk(KERN_ERR PFX "Cannot map device vir registers 1, "
> +		       "aborting.\n");

dev_err() ??

> +		err = -ENOMEM;
> +		goto err_out_iounmap;
> +	}
> +
> +	np->vir_regs_2 = of_ioremap(&op->resource[3], 0,
> +				    res_size(&op->resource[3]),
> +				    "niu vregs-2");
> +	if (!np->vir_regs_2) {
> +		printk(KERN_ERR PFX "Cannot map device vir registers 2, "
> +		       "aborting.\n");

dev_err() ??

> +		err = -ENOMEM;
> +		goto err_out_iounmap;
> +	}
> +
> +	niu_assign_netdev_ops(dev);
> +
> +	err = niu_get_invariants(np);
> +	if (err) {
> +		if (err != -ENODEV)
> +			printk(KERN_ERR PFX "Problem fetching invariants "
> +			       "of chip, aborting.\n");

dev_err() ??

> +		goto err_out_iounmap;
> +	}
> +
> +	err = register_netdev(dev);
> +	if (err) {
> +		printk(KERN_ERR PFX "Cannot register net device, "
> +		       "aborting.\n");

dev_err() ??

> +		goto err_out_iounmap;
> +	}
> +
> +	dev_set_drvdata(&op->dev, dev);
> +
> +	niu_device_announce(np);
> +
> +	return 0;
> +
> +err_out_iounmap:
> +	if (np->vir_regs_1) {
> +		of_iounmap(&op->resource[2], np->vir_regs_1,
> +			   res_size(&op->resource[2]));
> +		np->vir_regs_1 = NULL;
> +	}
> +
> +	if (np->vir_regs_2) {
> +		of_iounmap(&op->resource[3], np->vir_regs_2,
> +			   res_size(&op->resource[3]));
> +		np->vir_regs_2 = NULL;
> +	}
> +
> +	if (np->regs) {
> +		of_iounmap(&op->resource[1], np->regs,
> +			   res_size(&op->resource[1]));
> +		np->regs = NULL;
> +	}
> +
> +err_out_release_parent:
> +	niu_put_parent(np);
> +
> +err_out_free_dev:
> +	free_netdev(dev);
> +
> +err_out:
> +	return err;
> +}
> +
> +static int __devexit niu_of_remove(struct of_device *op)
> +{
> +	struct net_device *dev = dev_get_drvdata(&op->dev);
> +
> +	if (dev) {
> +		struct niu *np = netdev_priv(dev);
> +
> +		unregister_netdev(dev);
> +
> +		if (np->vir_regs_1) {
> +			of_iounmap(&op->resource[2], np->vir_regs_1,
> +				   res_size(&op->resource[2]));
> +			np->vir_regs_1 = NULL;
> +		}
> +
> +		if (np->vir_regs_2) {
> +			of_iounmap(&op->resource[3], np->vir_regs_2,
> +				   res_size(&op->resource[3]));
> +			np->vir_regs_2 = NULL;
> +		}
> +
> +		if (np->regs) {
> +			of_iounmap(&op->resource[1], np->regs,
> +				   res_size(&op->resource[1]));
> +			np->regs = NULL;
> +		}
> +
> +		niu_ldg_free(np);
> +
> +		niu_put_parent(np);
> +
> +		free_netdev(dev);
> +		dev_set_drvdata(&op->dev, NULL);
> +	}
> +	return 0;
> +}
> +
> +static struct of_device_id niu_match[] = {
> +	{
> +		.name = "network",
> +		.compatible = "SUNW,niusl",
> +	},
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, niu_match);
> +
> +static struct of_platform_driver niu_of_driver = {
> +	.name		= "niu",
> +	.match_table	= niu_match,
> +	.probe		= niu_of_probe,
> +	.remove		= __devexit_p(niu_of_remove),
> +};
> +
> +#endif /* CONFIG_SPARC64 */
> +
> +static int __init niu_init(void)
> +{
> +	int err = 0;
> +
> +	BUILD_BUG_ON((PAGE_SIZE < 4 * 1024) ||
> +		     ((PAGE_SIZE > 32 * 1024) &&
> +		      ((PAGE_SIZE % (32 * 1024)) != 0 &&
> +		       (PAGE_SIZE % (16 * 1024)) != 0 &&
> +		       (PAGE_SIZE % (8 * 1024)) != 0 &&
> +		       (PAGE_SIZE % (4 * 1024)) != 0)));
> +
> +#ifdef CONFIG_SPARC64
> +	err = of_register_driver(&niu_of_driver, &of_bus_type);
> +#endif
> +
> +	if (!err) {
> +		err = pci_register_driver(&niu_pci_driver);
> +#ifdef CONFIG_SPARC64
> +		if (err)
> +			of_unregister_driver(&niu_of_driver);
> +#endif
> +	}
> +
> +	return err;
> +}
> +
> +static void __exit niu_exit(void)
> +{
> +	pci_unregister_driver(&niu_pci_driver);
> +#ifdef CONFIG_SPARC64
> +	of_unregister_driver(&niu_of_driver);
> +#endif
> +}
> +
> +module_init(niu_init);
> +module_exit(niu_exit);
> diff --git a/drivers/net/niu.h b/drivers/net/niu.h

-- 
Stephen Hemminger <shemminger@linux-foundation.org>

^ permalink raw reply

* Re: [patch 0/5] s390: qeth and lcs patches for 2.6.24 - RESEND
From: Ursula Braun1 @ 2007-10-05 15:35 UTC (permalink / raw)
  To: Jeff Garzik; +Cc: davem, netdev, linux-s390
In-Reply-To: <47065321.9080403@garzik.org>

Jeff,

patches have not changed on resend, just the header part of [patch 5/5].

Ursula



                                                                           
             Jeff Garzik                                                   
             <jeff@garzik.org>                                             
             Sent by:                                                   To 
             linux-s390-owner@         Ursula Braun1/Germany/IBM@IBMDE     
             vger.kernel.org                                            cc 
                                       davem@davemloft.net,                
                                       netdev@vger.kernel.org,             
             05.10.2007 17:07          linux-s390@vger.kernel.org          
                                                                   Subject 
                                       Re: [patch 0/5] s390: qeth and lcs  
                                       patches for 2.6.24 - RESEND         
                                                                           
                                                                           
                                                                           
                                                                           
                                                                           
                                                                           




Did these patches change on resend?  Or just addressees?

             Jeff


-
To unsubscribe from this list: send the line "unsubscribe linux-s390" 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 0/5] s390: qeth and lcs patches for 2.6.24 - RESEND
From: Jeff Garzik @ 2007-10-05 15:07 UTC (permalink / raw)
  To: Ursula Braun; +Cc: davem, netdev, linux-s390
In-Reply-To: <20071005144543.448852000@linux.vnet.ibm.com>

Did these patches change on resend?  Or just addressees?

	Jeff



^ permalink raw reply

* [RFC/PATCH 4/4] UDP memory usage accounting (take 4): memory limitation
From: Satoshi OSHIMA @ 2007-10-05 15:02 UTC (permalink / raw)
  To: Andi Kleen, David Miller, Evgeniy Polyakov, Herbert Xu, netdev
  Cc: Yumiko SUGITA, ??@RedHat
In-Reply-To: <47065139.5030402@hitachi.com>

This patch introduces memory limitation for UDP.


signed-off-by: Satoshi Oshima <satoshi.oshima.fk@hitachi.com>
signed-off-by: Hideo Aoki <haoki@redhat.com>

Index: 2.6.23-rc9-udp_limit/include/net/udp.h
===================================================================
--- 2.6.23-rc9-udp_limit.orig/include/net/udp.h
+++ 2.6.23-rc9-udp_limit/include/net/udp.h
@@ -65,7 +65,10 @@ extern rwlock_t udp_hash_lock;
 
 extern struct proto udp_prot;
 
+/* Used by memory accounting and capping */
+#define UDP_MIN_SKB_PAGES	4096
 extern atomic_t udp_memory_allocated;
+extern int sysctl_udp_mem;
 
 struct sk_buff;
 
Index: 2.6.23-rc9-udp_limit/net/ipv4/udp.c
===================================================================
--- 2.6.23-rc9-udp_limit.orig/net/ipv4/udp.c
+++ 2.6.23-rc9-udp_limit/net/ipv4/udp.c
@@ -114,8 +114,10 @@ struct hlist_head udp_hash[UDP_HTABLE_SI
 DEFINE_RWLOCK(udp_hash_lock);
 
 atomic_t udp_memory_allocated;
+int sysctl_udp_mem = 0;
 
 EXPORT_SYMBOL(udp_memory_allocated);
+EXPORT_SYMBOL(sysctl_udp_mem);
 
 static int udp_port_rover;
 
@@ -1018,6 +1020,16 @@ int udp_queue_rcv_skb(struct sock * sk, 
 			goto drop;
 	}
 
+	if (sk->sk_prot->sysctl_mem[0] > UDP_MIN_SKB_PAGES) {
+		if ((atomic_read(sk->sk_prot->memory_allocated)
+			       + sk_datagram_pages(skb->truesize))
+			>= sk->sk_prot->sysctl_mem[0]) {
+			UDP_INC_STATS_BH(UDP_MIB_RCVBUFERRORS,
+				up->pcflag);
+			goto drop;
+		}
+	}
+
 	if ((rc = sock_queue_rcv_skb(sk,skb)) < 0) {
 		/* Note that an ENOMEM error is charged twice */
 		if (rc == -ENOMEM)
@@ -1453,6 +1465,7 @@ struct proto udp_prot = {
 	.unhash		   = udp_lib_unhash,
 	.get_port	   = udp_v4_get_port,
 	.memory_allocated  = &udp_memory_allocated,
+	.sysctl_mem	   = &sysctl_udp_mem,
 	.obj_size	   = sizeof(struct udp_sock),
 #ifdef CONFIG_COMPAT
 	.compat_setsockopt = compat_udp_setsockopt,
Index: 2.6.23-rc9-udp_limit/net/ipv4/sysctl_net_ipv4.c
===================================================================
--- 2.6.23-rc9-udp_limit.orig/net/ipv4/sysctl_net_ipv4.c
+++ 2.6.23-rc9-udp_limit/net/ipv4/sysctl_net_ipv4.c
@@ -17,6 +17,7 @@
 #include <net/ip.h>
 #include <net/route.h>
 #include <net/tcp.h>
+#include <net/udp.h>
 #include <net/cipso_ipv4.h>
 
 /* From af_inet.c */
@@ -599,6 +600,14 @@ ctl_table ipv4_table[] = {
 		.proc_handler	= &proc_dointvec
 	},
 	{
+		.ctl_name	= NET_UDP_MEM,
+		.procname	= "udp_mem",
+		.data		= &sysctl_udp_mem,
+		.maxlen		= sizeof(sysctl_udp_mem),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec
+	},
+	{
 		.ctl_name	= NET_TCP_APP_WIN,
 		.procname	= "tcp_app_win",
 		.data		= &sysctl_tcp_app_win,
Index: 2.6.23-rc9-udp_limit/include/linux/sysctl.h
===================================================================
--- 2.6.23-rc9-udp_limit.orig/include/linux/sysctl.h
+++ 2.6.23-rc9-udp_limit/include/linux/sysctl.h
@@ -441,6 +441,7 @@ enum
 	NET_TCP_ALLOWED_CONG_CONTROL=123,
 	NET_TCP_MAX_SSTHRESH=124,
 	NET_TCP_FRTO_RESPONSE=125,
+	NET_UDP_MEM=126,
 };
 
 enum {
Index: 2.6.23-rc9-udp_limit/net/ipv4/ip_output.c
===================================================================
--- 2.6.23-rc9-udp_limit.orig/net/ipv4/ip_output.c
+++ 2.6.23-rc9-udp_limit/net/ipv4/ip_output.c
@@ -75,6 +75,7 @@
 #include <net/icmp.h>
 #include <net/checksum.h>
 #include <net/inetpeer.h>
+#include <net/udp.h>
 #include <linux/igmp.h>
 #include <linux/netfilter_ipv4.h>
 #include <linux/netfilter_bridge.h>
@@ -910,6 +911,17 @@ alloc_new_skb:
 			if (datalen == length + fraggap)
 				alloclen += rt->u.dst.trailer_len;
 
+			if (sk->sk_prot->sysctl_mem)
+				if (sk->sk_prot->sysctl_mem[0] > UDP_MIN_SKB_PAGES)
+					if ((atomic_read(sk->sk_prot->memory_allocated)
+						       + sk_datagram_pages(
+							   SKB_DATA_ALIGN(alloclen + hh_len + 15)
+							 + sizeof(struct sk_buff)))
+					     >= sk->sk_prot->sysctl_mem[0]) {
+						err = -ENOBUFS;
+						goto error;
+					}
+
 			if (transhdrlen) {
 				skb = sock_alloc_send_skb(sk,
 						alloclen + hh_len + 15,
@@ -1009,6 +1021,15 @@ alloc_new_skb:
 					frag = &skb_shinfo(skb)->frags[i];
 				}
 			} else if (i < MAX_SKB_FRAGS) {
+				if (sk->sk_prot->sysctl_mem)
+					if (sk->sk_prot->sysctl_mem[0] > UDP_MIN_SKB_PAGES)
+						if ((atomic_read(sk->sk_prot->memory_allocated)
+							       + sk_datagram_pages(PAGE_SIZE))
+						     >= sk->sk_prot->sysctl_mem[0]) {
+							err = -ENOBUFS;
+							goto error;
+						}
+
 				if (atomic_read(&sk->sk_wmem_alloc) + PAGE_SIZE
 				    > 2 * sk->sk_sndbuf) {
 					err = -ENOBUFS;
@@ -1126,6 +1147,17 @@ ssize_t	ip_append_page(struct sock *sk, 
 			fraggap = skb_prev->len - maxfraglen;
 
 			alloclen = fragheaderlen + hh_len + fraggap + 15;
+
+			if (sk->sk_prot->sysctl_mem)
+				if (sk->sk_prot->sysctl_mem[0] > UDP_MIN_SKB_PAGES)
+					if((atomic_read(sk->sk_prot->memory_allocated)
+						      + sk_datagram_pages(alloclen
+									+ sizeof(struct sk_buff)))
+					    >= sk->sk_prot->sysctl_mem[0]) {
+						err = -ENOBUFS;
+						goto error;
+					}
+
 			skb = sock_wmalloc(sk, alloclen, 1, sk->sk_allocation);
 			if (unlikely(!skb)) {
 				err = -ENOBUFS;


^ permalink raw reply

* [RFC/PATCH 3/4] UDP memory usage accounting (take 4): memory usage accounting
From: Satoshi OSHIMA @ 2007-10-05 15:02 UTC (permalink / raw)
  To: Andi Kleen, David Miller, Evgeniy Polyakov, Herbert Xu, netdev
  Cc: Yumiko SUGITA, ??@RedHat
In-Reply-To: <47065139.5030402@hitachi.com>

This patch introduces memory usage accounting for UDP.


signed-off-by: Satoshi Oshima <satoshi.oshima.fk@hitachi.com>
signed-off-by: Hideo Aoki <haoki@redhat.com>

Index: 2.6.23-rc8-udp_limit/net/ipv4/ip_output.c
===================================================================
--- 2.6.23-rc8-udp_limit.orig/net/ipv4/ip_output.c
+++ 2.6.23-rc8-udp_limit/net/ipv4/ip_output.c
@@ -743,6 +743,8 @@ static inline int ip_ufo_append_data(str
 		/* specify the length of each IP datagram fragment*/
 		skb_shinfo(skb)->gso_size = mtu - fragheaderlen;
 		skb_shinfo(skb)->gso_type = SKB_GSO_UDP;
+		atomic_add(sk_datagram_pages(skb->truesize),
+			   sk->sk_prot->memory_allocated);
 		__skb_queue_tail(&sk->sk_write_queue, skb);
 
 		return 0;
@@ -924,6 +926,9 @@ alloc_new_skb:
 			}
 			if (skb == NULL)
 				goto error;
+			if (sk->sk_prot->memory_allocated)
+				atomic_add(sk_datagram_pages(skb->truesize),
+					   sk->sk_prot->memory_allocated);
 
 			/*
 			 *	Fill in the control structures
@@ -1023,6 +1028,8 @@ alloc_new_skb:
 				frag = &skb_shinfo(skb)->frags[i];
 				skb->truesize += PAGE_SIZE;
 				atomic_add(PAGE_SIZE, &sk->sk_wmem_alloc);
+				if (sk->sk_prot->memory_allocated)
+					atomic_inc(sk->sk_prot->memory_allocated);
 			} else {
 				err = -EMSGSIZE;
 				goto error;
@@ -1123,7 +1130,9 @@ ssize_t	ip_append_page(struct sock *sk, 
 			if (unlikely(!skb)) {
 				err = -ENOBUFS;
 				goto error;
-			}
+			} else if (sk->sk_prot->memory_allocated)
+				atomic_add(sk_datagram_pages(skb->truesize),
+					   sk->sk_prot->memory_allocated);
 
 			/*
 			 *	Fill in the control structures
@@ -1202,13 +1211,14 @@ int ip_push_pending_frames(struct sock *
 	struct iphdr *iph;
 	__be16 df = 0;
 	__u8 ttl;
-	int err = 0;
+	int err = 0, send_page_size;
 
 	if ((skb = __skb_dequeue(&sk->sk_write_queue)) == NULL)
 		goto out;
 	tail_skb = &(skb_shinfo(skb)->frag_list);
 
 	/* move skb->data to ip header from ext header */
+	send_page_size = sk_datagram_pages(skb->truesize);
 	if (skb->data < skb_network_header(skb))
 		__skb_pull(skb, skb_network_offset(skb));
 	while ((tmp_skb = __skb_dequeue(&sk->sk_write_queue)) != NULL) {
@@ -1218,6 +1228,7 @@ int ip_push_pending_frames(struct sock *
 		skb->len += tmp_skb->len;
 		skb->data_len += tmp_skb->len;
 		skb->truesize += tmp_skb->truesize;
+		send_page_size += sk_datagram_pages(tmp_skb->truesize);
 		__sock_put(tmp_skb->sk);
 		tmp_skb->destructor = NULL;
 		tmp_skb->sk = NULL;
@@ -1269,6 +1280,8 @@ int ip_push_pending_frames(struct sock *
 	/* Netfilter gets whole the not fragmented skb. */
 	err = NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL,
 		      skb->dst->dev, dst_output);
+	if (sk->sk_prot->memory_allocated)
+		atomic_sub(send_page_size, sk->sk_prot->memory_allocated);
 	if (err) {
 		if (err > 0)
 			err = inet->recverr ? net_xmit_errno(err) : 0;
@@ -1298,9 +1311,15 @@ void ip_flush_pending_frames(struct sock
 {
 	struct inet_sock *inet = inet_sk(sk);
 	struct sk_buff *skb;
+	int num_flush_mem = 0;
 
-	while ((skb = __skb_dequeue_tail(&sk->sk_write_queue)) != NULL)
+	while ((skb = __skb_dequeue_tail(&sk->sk_write_queue)) != NULL) {
+		num_flush_mem += sk_datagram_pages(skb->truesize);
 		kfree_skb(skb);
+	}
+
+	if (sk->sk_prot->memory_allocated)
+		atomic_sub(num_flush_mem, sk->sk_prot->memory_allocated);
 
 	inet->cork.flags &= ~IPCORK_OPT;
 	kfree(inet->cork.opt);
Index: 2.6.23-rc8-udp_limit/net/ipv4/udp.c
===================================================================
--- 2.6.23-rc8-udp_limit.orig/net/ipv4/udp.c
+++ 2.6.23-rc8-udp_limit/net/ipv4/udp.c
@@ -887,6 +887,9 @@ try_again:
 		err = ulen;
 
 out_free:
+	atomic_sub(sk_datagram_pages(skb->truesize),
+		   sk->sk_prot->memory_allocated);
+
 	skb_free_datagram(sk, skb);
 out:
 	return err;
@@ -894,6 +897,9 @@ out:
 csum_copy_err:
 	UDP_INC_STATS_BH(UDP_MIB_INERRORS, is_udplite);
 
+	atomic_sub(sk_datagram_pages(skb->truesize),
+		   sk->sk_prot->memory_allocated);
+
 	skb_kill_datagram(sk, skb, flags);
 
 	if (noblock)
@@ -1019,6 +1025,9 @@ int udp_queue_rcv_skb(struct sock * sk, 
 		goto drop;
 	}
 
+	atomic_add(sk_datagram_pages(skb->truesize),
+		   sk->sk_prot->memory_allocated);
+
 	UDP_INC_STATS_BH(UDP_MIB_INDATAGRAMS, up->pcflag);
 	return 0;
 
@@ -1443,6 +1452,7 @@ struct proto udp_prot = {
 	.hash		   = udp_lib_hash,
 	.unhash		   = udp_lib_unhash,
 	.get_port	   = udp_v4_get_port,
+	.memory_allocated  = &udp_memory_allocated,
 	.obj_size	   = sizeof(struct udp_sock),
 #ifdef CONFIG_COMPAT
 	.compat_setsockopt = compat_udp_setsockopt,
Index: 2.6.23-rc8-udp_limit/net/ipv4/af_inet.c
===================================================================
--- 2.6.23-rc8-udp_limit.orig/net/ipv4/af_inet.c
+++ 2.6.23-rc8-udp_limit/net/ipv4/af_inet.c
@@ -126,13 +126,41 @@ extern void ip_mc_drop_socket(struct soc
 static struct list_head inetsw[SOCK_MAX];
 static DEFINE_SPINLOCK(inetsw_lock);
 
+/**
+ *	__skb_queue_purge_and_sub_memory_allocated
+ *		- empty a list and subtruct memory allocation counter
+ *	@sk:   sk
+ *	@list: list to empty
+ *	Delete all buffers on an &sk_buff list and subtruct the
+ *	turesize of the sk_buff for memory accounting. Each buffer
+ *	is removed from the list and one reference dropped. This
+ *	function does not take the list lock and the caller must
+ *	hold the relevant locks to use it.
+ */
+void __skb_queue_purge_and_sub_memory_allocated(struct sock *sk,
+					struct sk_buff_head *list)
+{
+	struct sk_buff *skb;
+	int purged_skb_size = 0;
+	while ((skb = __skb_dequeue(list)) != NULL) {
+		purged_skb_size += sk_datagram_pages(skb->truesize);
+		kfree_skb(skb);
+	}
+	atomic_sub(purged_skb_size, sk->sk_prot->memory_allocated);
+}
+
 /* New destruction routine */
 
 void inet_sock_destruct(struct sock *sk)
 {
 	struct inet_sock *inet = inet_sk(sk);
 
-	__skb_queue_purge(&sk->sk_receive_queue);
+	if (sk->sk_prot->memory_allocated && sk->sk_type != SOCK_STREAM)
+		__skb_queue_purge_and_sub_memory_allocated(sk,
+				&sk->sk_receive_queue);
+	else
+		__skb_queue_purge(&sk->sk_receive_queue);
+
 	__skb_queue_purge(&sk->sk_error_queue);
 
 	if (sk->sk_type == SOCK_STREAM && sk->sk_state != TCP_CLOSE) {


^ permalink raw reply

* [RFC/PATCH 2/4] UDP memory usage accounting (take 4): accounting unit and variable
From: Satoshi OSHIMA @ 2007-10-05 15:01 UTC (permalink / raw)
  To: Andi Kleen, David Miller, Evgeniy Polyakov, Herbert Xu, netdev
  Cc: Yumiko SUGITA, ??@RedHat
In-Reply-To: <47065139.5030402@hitachi.com>

This patch introduces global variable for UDP memory accounting.
The unit is page.


signed-off-by: Satoshi Oshima <satoshi.oshima.fk@hitachi.com>
signed-off-by: Hideo Aoki <haoki@redhat.com>

Index: 2.6.23-rc3-udp_limit/include/net/sock.h
===================================================================
--- 2.6.23-rc3-udp_limit.orig/include/net/sock.h
+++ 2.6.23-rc3-udp_limit/include/net/sock.h
@@ -723,6 +723,13 @@ static inline int sk_stream_wmem_schedul
 	       sk_stream_mem_schedule(sk, size, 0);
 }
 
+#define SK_DATAGRAM_MEM_QUANTUM ((int)PAGE_SIZE)
+
+static inline int sk_datagram_pages(int amt)
+{
+	return DIV_ROUND_UP(amt, SK_DATAGRAM_MEM_QUANTUM);
+}
+
 /* Used by processes to "lock" a socket state, so that
  * interrupts and bottom half handlers won't change it
  * from under us. It essentially blocks any incoming
Index: 2.6.23-rc3-udp_limit/include/net/udp.h
===================================================================
--- 2.6.23-rc3-udp_limit.orig/include/net/udp.h
+++ 2.6.23-rc3-udp_limit/include/net/udp.h
@@ -65,6 +65,8 @@ extern rwlock_t udp_hash_lock;
 
 extern struct proto udp_prot;
 
+extern atomic_t udp_memory_allocated;
+
 struct sk_buff;
 
 /*
Index: 2.6.23-rc3-udp_limit/net/ipv4/proc.c
===================================================================
--- 2.6.23-rc3-udp_limit.orig/net/ipv4/proc.c
+++ 2.6.23-rc3-udp_limit/net/ipv4/proc.c
@@ -66,7 +66,8 @@ static int sockstat_seq_show(struct seq_
 		   fold_prot_inuse(&tcp_prot), atomic_read(&tcp_orphan_count),
 		   tcp_death_row.tw_count, atomic_read(&tcp_sockets_allocated),
 		   atomic_read(&tcp_memory_allocated));
-	seq_printf(seq, "UDP: inuse %d\n", fold_prot_inuse(&udp_prot));
+	seq_printf(seq, "UDP: inuse %d mem %d\n", fold_prot_inuse(&udp_prot),
+		   atomic_read(&udp_memory_allocated));
 	seq_printf(seq, "UDPLITE: inuse %d\n", fold_prot_inuse(&udplite_prot));
 	seq_printf(seq, "RAW: inuse %d\n", fold_prot_inuse(&raw_prot));
 	seq_printf(seq,  "FRAG: inuse %d memory %d\n", ip_frag_nqueues,
Index: 2.6.23-rc3-udp_limit/net/ipv4/udp.c
===================================================================
--- 2.6.23-rc3-udp_limit.orig/net/ipv4/udp.c
+++ 2.6.23-rc3-udp_limit/net/ipv4/udp.c
@@ -113,6 +113,10 @@ DEFINE_SNMP_STAT(struct udp_mib, udp_sta
 struct hlist_head udp_hash[UDP_HTABLE_SIZE];
 DEFINE_RWLOCK(udp_hash_lock);
 
+atomic_t udp_memory_allocated;
+
+EXPORT_SYMBOL(udp_memory_allocated);
+
 static int udp_port_rover;
 
 static inline int __udp_lib_lport_inuse(__u16 num, struct hlist_head udptable[])


^ permalink raw reply

* [RFC/PATCH 1/4] UDP memory usage accounting (take 4): fix send buffer check
From: Satoshi OSHIMA @ 2007-10-05 15:00 UTC (permalink / raw)
  To: Andi Kleen, David Miller, Evgeniy Polyakov, Herbert Xu, netdev
  Cc: Yumiko SUGITA, ??@RedHat
In-Reply-To: <47065139.5030402@hitachi.com>

This patch introduces sndbuf size check before
memory allcation for send buffer.


signed-off-by: Satoshi Oshima <satoshi.oshima.fk@hitachi.com>
signed-off-by: Hideo Aoki <haoki@redhat.com>

Index: 2.6.23-rc7-udp_limit/net/ipv4/ip_output.c
===================================================================
--- 2.6.23-rc7-udp_limit.orig/net/ipv4/ip_output.c
+++ 2.6.23-rc7-udp_limit/net/ipv4/ip_output.c
@@ -1004,6 +1004,11 @@ alloc_new_skb:
 					frag = &skb_shinfo(skb)->frags[i];
 				}
 			} else if (i < MAX_SKB_FRAGS) {
+				if (atomic_read(&sk->sk_wmem_alloc) + PAGE_SIZE
+				    > 2 * sk->sk_sndbuf) {
+					err = -ENOBUFS;
+					goto error;
+				}
 				if (copy > PAGE_SIZE)
 					copy = PAGE_SIZE;
 				page = alloc_pages(sk->sk_allocation, 0);


^ permalink raw reply

* [RFC/PATCH 0/4]UDP memory accounting (take 4)
From: Satoshi OSHIMA @ 2007-10-05 14:59 UTC (permalink / raw)
  To: Andi Kleen, David Miller, Evgeniy Polyakov, Herbert Xu, netdev
  Cc: Yumiko SUGITA, "??@RedHat"

Hi,

I post a patch set of UDP memory accounting and
limitation.

This patch set is for kernel 2.6.23-rc9. The 
differences from take 3 are

* fixing double accounting bug of ip_send_page()
* adding UDP memory limitation.

How to use UDP memory limitation:

This patch set add

/proc/sys/net/ipv4/udp_mem

as a tuning parameter. 

When you give the number that is greater than 4096,
UDP memory limitation will work. The number of pages
for socket buffer is limited up to udp_mem[pages].

Currently this function drops the packet when
it is sent or received and the number of pages for
socket buffer is beyond the limit. It won't collect
the buffer that is already allocated.

On the other hand, udp_mem is specified as 4096 or
smaller, UDP memory limitaion will not work.
The deafult number is 0. 

Comment, review and test are welcome.


By the way, David pointed out that we should have
the better solution such as memory reclaiming by
callback from vmm. I seriously consider it. But
As Herbert pointed out, it is very difficult to
apply it to TCP because some of the buffer is
already acked. I couldn't find the good solution 
that is applicable for TCP, UDP, route cache and so on.

Let me know, if you find the good way to solve
this problem.

Thanks,

Satoshi Oshima

^ permalink raw reply

* [patch 5/5] qeth: discard inbound packets with unknown header id
From: Ursula Braun @ 2007-10-05 14:45 UTC (permalink / raw)
  To: davem, netdev, linux-s390
In-Reply-To: <20071005144543.448852000@linux.vnet.ibm.com>

[-- Attachment #1: qeth-inbound.diff --]
[-- Type: text/plain, Size: 1217 bytes --]

From: Ursula Braun <braunu@de.ibm.com>

Debugging statements are added for inbound packets with unknown
header id. Those packets are discarded and no longer processed as
osn-packets.

Signed-off-by: Ursula Braun <braunu@de.ibm.com>
---

 drivers/s390/net/qeth_main.c |    7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

Index: linux-2.6-uschi/drivers/s390/net/qeth_main.c
===================================================================
--- linux-2.6-uschi.orig/drivers/s390/net/qeth_main.c
+++ linux-2.6-uschi/drivers/s390/net/qeth_main.c
@@ -2699,10 +2699,15 @@ qeth_process_inbound_buffer(struct qeth_
 			qeth_layer2_rebuild_skb(card, skb, hdr);
 		else if (hdr->hdr.l3.id == QETH_HEADER_TYPE_LAYER3)
 			vlan_tag = qeth_rebuild_skb(card, skb, hdr);
-		else { /*in case of OSN*/
+		else if (hdr->hdr.osn.id == QETH_HEADER_TYPE_OSN) {
 			skb_push(skb, sizeof(struct qeth_hdr));
 			skb_copy_to_linear_data(skb, hdr,
 						sizeof(struct qeth_hdr));
+		} else { /* unknown header type */
+			dev_kfree_skb_any(skb);
+			QETH_DBF_TEXT(trace, 3, "inbunkno");
+			QETH_DBF_HEX(control, 3, hdr, QETH_DBF_CONTROL_LEN);
+			continue;
 		}
 		/* is device UP ? */
 		if (!(card->dev->flags & IFF_UP)){

-- 

^ permalink raw reply

* [patch 4/5] lcs: Channel errors drive lcs_recovery which leads to kernel panic.
From: Ursula Braun @ 2007-10-05 14:45 UTC (permalink / raw)
  To: davem, netdev, linux-s390; +Cc: Klaus D. Wacker
In-Reply-To: <20071005144543.448852000@linux.vnet.ibm.com>

[-- Attachment #1: 710-lcs-panic.diff --]
[-- Type: text/plain, Size: 2164 bytes --]

From: Klaus D. Wacker <kdwacker@de.ibm.com>

When the lcs irq routine detects channel failures it drives device recovery.
After this event the device is no longer usable for shutdown requests,
because the lcs_irq routine may get wrong channel status information.
In such a case the lcs_irq routine marks the channel in 'error' state.
The channel state comes back to 'running' after restarting the channels.

Signed-off-by: Klaus D. Wacker <kdwacker@de.ibm.com>
Signed-off-by: Ursula Braun <braunu@de.ibm.com>
---

 drivers/s390/net/lcs.c |   11 ++++++++---
 drivers/s390/net/lcs.h |    1 +
 2 files changed, 9 insertions(+), 3 deletions(-)

Index: linux-2.6-uschi/drivers/s390/net/lcs.c
===================================================================
--- linux-2.6-uschi.orig/drivers/s390/net/lcs.c
+++ linux-2.6-uschi/drivers/s390/net/lcs.c
@@ -1400,11 +1400,14 @@ lcs_irq(struct ccw_device *cdev, unsigne
 		PRINT_WARN("check on device %s, dstat=0x%X, cstat=0x%X \n",
 			    cdev->dev.bus_id, dstat, cstat);
 		if (rc) {
-			lcs_schedule_recovery(card);
-			wake_up(&card->wait_q);
-			return;
+			channel->state = LCS_CH_STATE_ERROR;
 		}
 	}
+	if (channel->state == LCS_CH_STATE_ERROR) {
+		lcs_schedule_recovery(card);
+		wake_up(&card->wait_q);
+		return;
+	}
 	/* How far in the ccw chain have we processed? */
 	if ((channel->state != LCS_CH_STATE_INIT) &&
 	    (irb->scsw.fctl & SCSW_FCTL_START_FUNC)) {
@@ -1708,6 +1711,8 @@ lcs_stopcard(struct lcs_card *card)
 
 	if (card->read.state != LCS_CH_STATE_STOPPED &&
 	    card->write.state != LCS_CH_STATE_STOPPED &&
+	    card->read.state != LCS_CH_STATE_ERROR &&
+	    card->write.state != LCS_CH_STATE_ERROR &&
 	    card->state == DEV_STATE_UP) {
 		lcs_clear_multicast_list(card);
 		rc = lcs_send_stoplan(card,LCS_INITIATOR_TCPIP);
Index: linux-2.6-uschi/drivers/s390/net/lcs.h
===================================================================
--- linux-2.6-uschi.orig/drivers/s390/net/lcs.h
+++ linux-2.6-uschi/drivers/s390/net/lcs.h
@@ -138,6 +138,7 @@ enum lcs_channel_states {
 	LCS_CH_STATE_RUNNING,
 	LCS_CH_STATE_SUSPENDED,
 	LCS_CH_STATE_CLEARED,
+	LCS_CH_STATE_ERROR,
 };
 
 /**

-- 

^ permalink raw reply

* [patch 3/5] qeth: avoid duplicate deletion of multicast addresses
From: Ursula Braun @ 2007-10-05 14:45 UTC (permalink / raw)
  To: davem, netdev, linux-s390
In-Reply-To: <20071005144543.448852000@linux.vnet.ibm.com>

[-- Attachment #1: 709-qeth-multicast.diff --]
[-- Type: text/plain, Size: 1257 bytes --]

From: Ursula Braun <braunu@de.ibm.com>

if qeth_set_multicast_list() is performed on 2 CPUs in parallel,
card->ip_list may end corrupted.
Solution: In function __qeth_delete_all_mc()
          remove card->ip_list entry before invoking 
          qeth_deregister_addr_entry(). Thus a 2nd invocation of
          qeth_set_multicast_list() cannot try to remove the
          same entry twice.

Signed-off-by Ursula Braun <braunu@de.ibm.com>
---

 drivers/s390/net/qeth_main.c |    5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

Index: linux-2.6-uschi/drivers/s390/net/qeth_main.c
===================================================================
--- linux-2.6-uschi.orig/drivers/s390/net/qeth_main.c
+++ linux-2.6-uschi/drivers/s390/net/qeth_main.c
@@ -820,14 +820,15 @@ __qeth_delete_all_mc(struct qeth_card *c
 again:
 	list_for_each_entry_safe(addr, tmp, &card->ip_list, entry) {
 		if (addr->is_multicast) {
+			list_del(&addr->entry);
 			spin_unlock_irqrestore(&card->ip_lock, *flags);
 			rc = qeth_deregister_addr_entry(card, addr);
 			spin_lock_irqsave(&card->ip_lock, *flags);
 			if (!rc) {
-				list_del(&addr->entry);
 				kfree(addr);
 				goto again;
-			}
+			} else
+				list_add(&addr->entry, &card->ip_list);
 		}
 	}
 }

-- 

^ permalink raw reply

* [patch 2/5] qeth: EDDP does not work on large MTUs
From: Ursula Braun @ 2007-10-05 14:45 UTC (permalink / raw)
  To: davem, netdev, linux-s390; +Cc: Frank Blaschka
In-Reply-To: <20071005144543.448852000@linux.vnet.ibm.com>

[-- Attachment #1: 708-qeth-eddp.diff --]
[-- Type: text/plain, Size: 2048 bytes --]

From: Frank Blaschka <frank.blaschka@de.ibm.com>

Fix filling the qdio buffers in EDDP mode.

Signed-off-by: Frank Blaschka <frank.blaschka@de.ibm.com>
Signed-off-by: Ursula Braun <braunu@de.ibm.com>
---

 drivers/s390/net/qeth_eddp.c |   16 +++++++++-------
 drivers/s390/net/qeth_main.c |    3 ++-
 2 files changed, 11 insertions(+), 8 deletions(-)

Index: linux-2.6-uschi/drivers/s390/net/qeth_eddp.c
===================================================================
--- linux-2.6-uschi.orig/drivers/s390/net/qeth_eddp.c
+++ linux-2.6-uschi/drivers/s390/net/qeth_eddp.c
@@ -159,13 +159,15 @@ qeth_eddp_fill_buffer(struct qeth_qdio_o
 		buffer = buf->buffer;
 		/* fill one skb into buffer */
 		for (i = 0; i < ctx->elements_per_skb; ++i){
-			buffer->element[buf->next_element_to_fill].addr =
-				ctx->elements[element].addr;
-			buffer->element[buf->next_element_to_fill].length =
-				ctx->elements[element].length;
-			buffer->element[buf->next_element_to_fill].flags =
-				ctx->elements[element].flags;
-			buf->next_element_to_fill++;
+			if (ctx->elements[element].length != 0) {
+				buffer->element[buf->next_element_to_fill].
+				addr = ctx->elements[element].addr;
+				buffer->element[buf->next_element_to_fill].
+				length = ctx->elements[element].length;
+				buffer->element[buf->next_element_to_fill].
+				flags = ctx->elements[element].flags;
+				buf->next_element_to_fill++;
+			}
 			element++;
 			elements--;
 		}
Index: linux-2.6-uschi/drivers/s390/net/qeth_main.c
===================================================================
--- linux-2.6-uschi.orig/drivers/s390/net/qeth_main.c
+++ linux-2.6-uschi/drivers/s390/net/qeth_main.c
@@ -4500,7 +4500,8 @@ qeth_do_send_packet(struct qeth_card *ca
 			/* check if we have enough elements (including following
 			 * free buffers) to handle eddp context */
 			if (qeth_eddp_check_buffers_for_context(queue,ctx) < 0){
-				printk("eddp tx_dropped 1\n");
+				if (net_ratelimit())
+					PRINT_WARN("eddp tx_dropped 1\n");
 				rc = -EBUSY;
 				goto out;
 			}

-- 

^ permalink raw reply

* [patch 1/5] qeth: HiperSockets layer-3 interface drop non IPv4 or non IPv6 packets
From: Ursula Braun @ 2007-10-05 14:45 UTC (permalink / raw)
  To: davem, netdev, linux-s390; +Cc: Klaus D. Wacker
In-Reply-To: <20071005144543.448852000@linux.vnet.ibm.com>

[-- Attachment #1: 705-qeth-pktdrop.diff --]
[-- Type: text/plain, Size: 1195 bytes --]

From: Klaus D. Wacker <kdwacker@de.ibm.com>

HiperSockets infrastructure (layer-3 mode) supports only IPv4 or
IPv6 packets. Sending other packet types disturbs TCP/IP on z/VM,
which issues messages about invalid packets.
Qeth send routine will detect packet type on sending over a 
HiperSockets interface (in layer-3 mode) and drop non IP packets.
The error and drop count of the interface is incremented.

Signed-off-by: Klaus D. Wacker <kdwacker@de.ibm.com>
Signed-off-by: Ursula Braun <braunu@de.ibm.com>
---

 drivers/s390/net/qeth_main.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

Index: linux-2.6-uschi/drivers/s390/net/qeth_main.c
===================================================================
--- linux-2.6-uschi.orig/drivers/s390/net/qeth_main.c
+++ linux-2.6-uschi/drivers/s390/net/qeth_main.c
@@ -4711,8 +4711,8 @@ qeth_send_packet(struct qeth_card *card,
 		rc = qeth_do_send_packet(card, queue, new_skb, hdr,
 					 elements_needed, ctx);
 	else {
-		if ((skb->protocol == htons(ETH_P_ARP)) &&
-		    (card->dev->flags & IFF_NOARP)) {
+		if ((!card->options.layer2) &&
+		    (ipv == 0)) {
 			__qeth_free_new_skb(skb, new_skb);
 			return -EPERM;
 		}

-- 

^ permalink raw reply

* [patch 0/5] s390: qeth and lcs patches for 2.6.24 - RESEND
From: Ursula Braun @ 2007-10-05 14:45 UTC (permalink / raw)
  To: davem, netdev, linux-s390

-- 
Sorry, 2nd attempt, because patch 5/5 missed title line

The following patches in qeth and lcs driver are intended for 2.6.24:
- qeth: drop non-IP outbound packets on HiperSocket devices
- qeth: EDDP-mode - correct filling of qdio buffers
- qeth: avoid duplicate deletion of multicast addresses
- lcs:  avoid kernel panic in lcs_recovery driven by channel errors
- qeth: discard inbound packets with unknown header id



^ permalink raw reply

* [PATCH 5/5] make netlink user -> kernel interface synchronious
From: Denis V. Lunev @ 2007-10-05 14:48 UTC (permalink / raw)
  To: davem; +Cc: netdev, kuznet, devel, containers

This patch make processing netlink user -> kernel messages synchronious.
This change was inspired by the talk with Alexey Kuznetsov about current
netlink messages processing. He says that he was badly wrong when introduced 
asynchronious user -> kernel communication.

The call netlink_unicast is the only path to send message to the kernel
netlink socket. But, unfortunately, it is also used to send data to the
user.

Before this change the user message has been attached to the socket queue
and sk->sk_data_ready was called. The process has been blocked until all
pending messages were processed. The bad thing is that this processing
may occur in the arbitrary process context.

This patch changes nlk->data_ready callback to get 1 skb and force packet
processing right in the netlink_unicast.

Kernel -> user path in netlink_unicast remains untouched.

EINTR processing for in netlink_run_queue was changed. It forces rtnl_lock
drop, but the process remains in the cycle until the message will be fully
processed. So, there is no need to use this kludges now.

Signed-off-by: Denis V. Lunev <den@openvz.org>
Acked-by: Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>

--- ./drivers/connector/connector.c.nlk6	2007-10-05 16:27:16.000000000 +0400
+++ ./drivers/connector/connector.c	2007-10-05 16:33:04.000000000 +0400
@@ -235,18 +235,6 @@ out:
 }
 
 /*
- * Netlink socket input callback - dequeues the skbs and calls the
- * main netlink receiving function.
- */
-static void cn_input(struct sock *sk, int len)
-{
-	struct sk_buff *skb;
-
-	while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL)
-		cn_rx_skb(skb);
-}
-
-/*
  * Notification routing.
  *
  * Gets id and checks if there are notification request for it's idx
@@ -442,7 +430,7 @@ static int __devinit cn_init(void)
 	struct cn_dev *dev = &cdev;
 	int err;
 
-	dev->input = cn_input;
+	dev->input = cn_rx_skb;
 	dev->id.idx = cn_idx;
 	dev->id.val = cn_val;
 
--- ./drivers/scsi/scsi_netlink.c.nlk6	2007-10-05 16:27:16.000000000 +0400
+++ ./drivers/scsi/scsi_netlink.c	2007-10-05 16:49:45.000000000 +0400
@@ -64,7 +64,7 @@ scsi_nl_rcv_msg(struct sk_buff *skb)
 
 		if (nlh->nlmsg_type != SCSI_TRANSPORT_MSG) {
 			err = -EBADMSG;
-			goto next_msg;
+			return;
 		}
 
 		hdr = NLMSG_DATA(nlh);
@@ -99,27 +99,6 @@ next_msg:
 
 
 /**
- * scsi_nl_rcv_msg -
- *    Receive handler for a socket. Extracts a received message buffer from
- *    the socket, and starts message processing.
- *
- * @sk:		socket
- * @len:	unused
- *
- **/
-static void
-scsi_nl_rcv(struct sock *sk, int len)
-{
-	struct sk_buff *skb;
-
-	while ((skb = skb_dequeue(&sk->sk_receive_queue))) {
-		scsi_nl_rcv_msg(skb);
-		kfree_skb(skb);
-	}
-}
-
-
-/**
  * scsi_nl_rcv_event -
  *    Event handler for a netlink socket.
  *
@@ -168,7 +147,7 @@ scsi_netlink_init(void)
 	}
 
 	scsi_nl_sock = netlink_kernel_create(&init_net, NETLINK_SCSITRANSPORT,
-				SCSI_NL_GRP_CNT, scsi_nl_rcv, NULL,
+				SCSI_NL_GRP_CNT, scsi_nl_rcv_msg, NULL,
 				THIS_MODULE);
 	if (!scsi_nl_sock) {
 		printk(KERN_ERR "%s: register of recieve handler failed\n",
--- ./drivers/scsi/scsi_transport_iscsi.c.nlk6	2007-10-05 17:51:40.000000000 +0400
+++ ./drivers/scsi/scsi_transport_iscsi.c	2007-10-05 17:53:08.000000000 +0400
@@ -1097,61 +1097,49 @@ iscsi_if_recv_msg(struct sk_buff *skb, s
 }
 
 /*
- * Get message from skb (based on rtnetlink_rcv_skb).  Each message is
- * processed by iscsi_if_recv_msg.  Malformed skbs with wrong lengths or
- * invalid creds are discarded silently.
+ * Get message from skb.  Each message is processed by iscsi_if_recv_msg.
+ * Malformed skbs with wrong lengths or invalid creds are not processed.
  */
 static void
-iscsi_if_rx(struct sock *sk, int len)
+iscsi_if_rx(struct sk_buff *skb)
 {
-	struct sk_buff *skb;
-
 	mutex_lock(&rx_queue_mutex);
-	while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) {
-		if (NETLINK_CREDS(skb)->uid) {
-			skb_pull(skb, skb->len);
-			goto free_skb;
+	while (skb->len >= NLMSG_SPACE(0)) {
+		int err;
+		uint32_t rlen;
+		struct nlmsghdr	*nlh;
+		struct iscsi_uevent *ev;
+
+		nlh = nlmsg_hdr(skb);
+		if (nlh->nlmsg_len < sizeof(*nlh) ||
+		    skb->len < nlh->nlmsg_len) {
+			break;
 		}
 
-		while (skb->len >= NLMSG_SPACE(0)) {
-			int err;
-			uint32_t rlen;
-			struct nlmsghdr	*nlh;
-			struct iscsi_uevent *ev;
-
-			nlh = nlmsg_hdr(skb);
-			if (nlh->nlmsg_len < sizeof(*nlh) ||
-			    skb->len < nlh->nlmsg_len) {
-				break;
-			}
-
-			ev = NLMSG_DATA(nlh);
-			rlen = NLMSG_ALIGN(nlh->nlmsg_len);
-			if (rlen > skb->len)
-				rlen = skb->len;
-
-			err = iscsi_if_recv_msg(skb, nlh);
-			if (err) {
-				ev->type = ISCSI_KEVENT_IF_ERROR;
-				ev->iferror = err;
-			}
-			do {
-				/*
-				 * special case for GET_STATS:
-				 * on success - sending reply and stats from
-				 * inside of if_recv_msg(),
-				 * on error - fall through.
-				 */
-				if (ev->type == ISCSI_UEVENT_GET_STATS && !err)
-					break;
-				err = iscsi_if_send_reply(
-					NETLINK_CREDS(skb)->pid, nlh->nlmsg_seq,
-					nlh->nlmsg_type, 0, 0, ev, sizeof(*ev));
-			} while (err < 0 && err != -ECONNREFUSED);
-			skb_pull(skb, rlen);
+		ev = NLMSG_DATA(nlh);
+		rlen = NLMSG_ALIGN(nlh->nlmsg_len);
+		if (rlen > skb->len)
+			rlen = skb->len;
+
+		err = iscsi_if_recv_msg(skb, nlh);
+		if (err) {
+			ev->type = ISCSI_KEVENT_IF_ERROR;
+			ev->iferror = err;
 		}
-free_skb:
-		kfree_skb(skb);
+		do {
+			/*
+			 * special case for GET_STATS:
+			 * on success - sending reply and stats from
+			 * inside of if_recv_msg(),
+			 * on error - fall through.
+			 */
+			if (ev->type == ISCSI_UEVENT_GET_STATS && !err)
+				break;
+			err = iscsi_if_send_reply(
+				NETLINK_CREDS(skb)->pid, nlh->nlmsg_seq,
+				nlh->nlmsg_type, 0, 0, ev, sizeof(*ev));
+		} while (err < 0 && err != -ECONNREFUSED);
+		skb_pull(skb, rlen);
 	}
 	mutex_unlock(&rx_queue_mutex);
 }
--- ./fs/ecryptfs/netlink.c.nlk6	2007-10-05 16:27:16.000000000 +0400
+++ ./fs/ecryptfs/netlink.c	2007-10-05 16:33:04.000000000 +0400
@@ -165,22 +165,10 @@ static int ecryptfs_process_nl_quit(stru
  * it to its desired netlink context element and wake up the process
  * that is waiting for a response.
  */
-static void ecryptfs_receive_nl_message(struct sock *sk, int len)
+static void ecryptfs_receive_nl_message(struct sk_buff *skb)
 {
-	struct sk_buff *skb;
 	struct nlmsghdr *nlh;
-	int rc = 0;	/* skb_recv_datagram requires this */
 
-receive:
-	skb = skb_recv_datagram(sk, 0, 0, &rc);
-	if (rc == -EINTR)
-		goto receive;
-	else if (rc < 0) {
-		ecryptfs_printk(KERN_ERR, "Error occurred while "
-				"receiving eCryptfs netlink message; "
-				"rc = [%d]\n", rc);
-		return;
-	}
 	nlh = nlmsg_hdr(skb);
 	if (!NLMSG_OK(nlh, skb->len)) {
 		ecryptfs_printk(KERN_ERR, "Received corrupt netlink "
--- ./include/linux/connector.h.nlk6	2007-10-05 16:27:16.000000000 +0400
+++ ./include/linux/connector.h	2007-10-05 16:33:04.000000000 +0400
@@ -153,7 +153,7 @@ struct cn_dev {
 
 	u32 seq, groups;
 	struct sock *nls;
-	void (*input) (struct sock * sk, int len);
+	void (*input) (struct sk_buff *skb);
 
 	struct cn_queue_dev *cbdev;
 };
--- ./include/linux/netlink.h.nlk6	2007-10-05 16:28:17.000000000 +0400
+++ ./include/linux/netlink.h	2007-10-05 16:33:04.000000000 +0400
@@ -175,7 +175,7 @@ struct netlink_skb_parms
 
 extern struct sock *netlink_kernel_create(struct net *net,
 					  int unit,unsigned int groups,
-					  void (*input)(struct sock *sk, int len),
+		      			  void (*input)(struct sk_buff *skb),
 					  struct mutex *cb_mutex,
 					  struct module *module);
 extern int netlink_change_ngroups(struct sock *sk, unsigned int groups);
--- ./include/net/netlink.h.nlk6	2007-10-05 16:27:16.000000000 +0400
+++ ./include/net/netlink.h	2007-10-05 16:33:04.000000000 +0400
@@ -220,9 +220,9 @@ struct nl_info {
 	u32			pid;
 };
 
-extern unsigned int	netlink_run_queue(struct sock *sk, unsigned int qlen,
-					  int (*cb)(struct sk_buff *,
-						    struct nlmsghdr *));
+extern int		netlink_rcv_skb(struct sk_buff *skb,
+					int (*cb)(struct sk_buff *,
+						  struct nlmsghdr *));
 extern int		nlmsg_notify(struct sock *sk, struct sk_buff *skb,
 				     u32 pid, unsigned int group, int report,
 				     gfp_t flags);
--- ./kernel/audit.c.nlk6	2007-10-05 16:27:16.000000000 +0400
+++ ./kernel/audit.c	2007-10-05 16:50:01.000000000 +0400
@@ -847,18 +847,10 @@ static void audit_receive_skb(struct sk_
 }
 
 /* Receive messages from netlink socket. */
-static void audit_receive(struct sock *sk, int length)
+static void audit_receive(struct sk_buff  *skb)
 {
-	struct sk_buff  *skb;
-	unsigned int qlen;
-
 	mutex_lock(&audit_cmd_mutex);
-
-	for (qlen = skb_queue_len(&sk->sk_receive_queue); qlen; qlen--) {
-		skb = skb_dequeue(&sk->sk_receive_queue);
-		audit_receive_skb(skb);
-		kfree_skb(skb);
-	}
+	audit_receive_skb(skb);
 	mutex_unlock(&audit_cmd_mutex);
 }
 
--- ./net/core/rtnetlink.c.nlk6	2007-10-05 16:28:09.000000000 +0400
+++ ./net/core/rtnetlink.c	2007-10-05 16:50:29.000000000 +0400
@@ -1312,15 +1312,11 @@ static int rtnetlink_rcv_msg(struct sk_b
 	return doit(skb, nlh, (void *)&rta_buf[0]);
 }
 
-static void rtnetlink_rcv(struct sock *sk, int len)
+static void rtnetlink_rcv(struct sk_buff *skb)
 {
-	unsigned int qlen = 0;
-
-	do {
-		rtnl_lock();
-		qlen = netlink_run_queue(sk, qlen, &rtnetlink_rcv_msg);
-		rtnl_unlock();
-	} while (qlen);
+	rtnl_lock();
+	netlink_rcv_skb(skb, &rtnetlink_rcv_msg);
+	rtnl_unlock();
 }
 
 static int rtnetlink_event(struct notifier_block *this, unsigned long event, void *ptr)
--- ./net/decnet/netfilter/dn_rtmsg.c.nlk6	2007-10-05 16:27:16.000000000 +0400
+++ ./net/decnet/netfilter/dn_rtmsg.c	2007-10-05 16:50:45.000000000 +0400
@@ -115,17 +115,6 @@ static inline void dnrmg_receive_user_sk
 	RCV_SKB_FAIL(-EINVAL);
 }
 
-static void dnrmg_receive_user_sk(struct sock *sk, int len)
-{
-	struct sk_buff *skb;
-	unsigned int qlen = skb_queue_len(&sk->sk_receive_queue);
-
-	for (; qlen && (skb = skb_dequeue(&sk->sk_receive_queue)); qlen--) {
-		dnrmg_receive_user_skb(skb);
-		kfree_skb(skb);
-	}
-}
-
 static struct nf_hook_ops dnrmg_ops = {
 	.hook		= dnrmg_hook,
 	.pf		= PF_DECnet,
@@ -139,7 +128,8 @@ static int __init dn_rtmsg_init(void)
 
 	dnrmg = netlink_kernel_create(&init_net,
 				      NETLINK_DNRTMSG, DNRNG_NLGRP_MAX,
-				      dnrmg_receive_user_sk, NULL, THIS_MODULE);
+				      dnrmg_receive_user_skb,
+				      NULL, THIS_MODULE);
 	if (dnrmg == NULL) {
 		printk(KERN_ERR "dn_rtmsg: Cannot create netlink socket");
 		return -ENOMEM;
--- ./net/ipv4/fib_frontend.c.nlk6	2007-10-05 16:33:04.000000000 +0400
+++ ./net/ipv4/fib_frontend.c	2007-10-05 16:33:14.000000000 +0400
@@ -62,6 +62,9 @@ static struct hlist_head fib_table_hash[
 #define FIB_TABLE_HASHSZ 256
 static struct hlist_head fib_table_hash[FIB_TABLE_HASHSZ];
 
+static struct sock *fibnl = NULL;
+
+
 struct fib_table *fib_new_table(u32 id)
 {
 	struct fib_table *tb;
@@ -806,13 +809,13 @@ static void nl_fib_input(struct sk_buff 
 	pid = NETLINK_CB(skb).pid;       /* pid of sending process */
 	NETLINK_CB(skb).pid = 0;         /* from kernel */
 	NETLINK_CB(skb).dst_group = 0;  /* unicast */
-	netlink_unicast(sk, skb, pid, MSG_DONTWAIT);
+	netlink_unicast(fibnl, skb, pid, MSG_DONTWAIT);
 }
 
 static void nl_fib_lookup_init(void)
 {
-	netlink_kernel_create(&init_net, NETLINK_FIB_LOOKUP, 0, nl_fib_input,
-				NULL, THIS_MODULE);
+	fibnl = netlink_kernel_create(&init_net, NETLINK_FIB_LOOKUP, 0,
+				      nl_fib_input, NULL, THIS_MODULE);
 }
 
 static void fib_disable_ip(struct net_device *dev, int force)
--- ./net/ipv4/inet_diag.c.nlk6	2007-10-05 16:27:16.000000000 +0400
+++ ./net/ipv4/inet_diag.c	2007-10-05 16:50:56.000000000 +0400
@@ -839,15 +839,11 @@ static int inet_diag_rcv_msg(struct sk_b
 
 static DEFINE_MUTEX(inet_diag_mutex);
 
-static void inet_diag_rcv(struct sock *sk, int len)
+static void inet_diag_rcv(struct sk_buff *skb)
 {
-	unsigned int qlen = 0;
-
-	do {
-		mutex_lock(&inet_diag_mutex);
-		qlen = netlink_run_queue(sk, qlen, &inet_diag_rcv_msg);
-		mutex_unlock(&inet_diag_mutex);
-	} while (qlen);
+	mutex_lock(&inet_diag_mutex);
+	netlink_rcv_skb(skb, &inet_diag_rcv_msg);
+	mutex_unlock(&inet_diag_mutex);
 }
 
 static DEFINE_SPINLOCK(inet_diag_register_lock);
--- ./net/ipv4/netfilter/ip_queue.c.nlk6	2007-10-05 16:27:16.000000000 +0400
+++ ./net/ipv4/netfilter/ip_queue.c	2007-10-05 16:51:04.000000000 +0400
@@ -475,7 +475,7 @@ ipq_dev_drop(int ifindex)
 #define RCV_SKB_FAIL(err) do { netlink_ack(skb, nlh, (err)); return; } while (0)
 
 static inline void
-ipq_rcv_skb(struct sk_buff *skb)
+__ipq_rcv_skb(struct sk_buff *skb)
 {
 	int status, type, pid, flags, nlmsglen, skblen;
 	struct nlmsghdr *nlh;
@@ -533,19 +533,10 @@ ipq_rcv_skb(struct sk_buff *skb)
 }
 
 static void
-ipq_rcv_sk(struct sock *sk, int len)
+ipq_rcv_skb(struct sk_buff *skb)
 {
-	struct sk_buff *skb;
-	unsigned int qlen;
-
 	mutex_lock(&ipqnl_mutex);
-
-	for (qlen = skb_queue_len(&sk->sk_receive_queue); qlen; qlen--) {
-		skb = skb_dequeue(&sk->sk_receive_queue);
-		ipq_rcv_skb(skb);
-		kfree_skb(skb);
-	}
-
+	__ipq_rcv_skb(skb);
 	mutex_unlock(&ipqnl_mutex);
 }
 
@@ -670,7 +661,7 @@ static int __init ip_queue_init(void)
 
 	netlink_register_notifier(&ipq_nl_notifier);
 	ipqnl = netlink_kernel_create(&init_net, NETLINK_FIREWALL, 0,
-				      ipq_rcv_sk, NULL, THIS_MODULE);
+				      ipq_rcv_skb, NULL, THIS_MODULE);
 	if (ipqnl == NULL) {
 		printk(KERN_ERR "ip_queue: failed to create netlink socket\n");
 		goto cleanup_netlink_notifier;
--- ./net/ipv6/netfilter/ip6_queue.c.nlk6	2007-10-05 16:27:16.000000000 +0400
+++ ./net/ipv6/netfilter/ip6_queue.c	2007-10-05 16:51:11.000000000 +0400
@@ -464,7 +464,7 @@ ipq_dev_drop(int ifindex)
 #define RCV_SKB_FAIL(err) do { netlink_ack(skb, nlh, (err)); return; } while (0)
 
 static inline void
-ipq_rcv_skb(struct sk_buff *skb)
+__ipq_rcv_skb(struct sk_buff *skb)
 {
 	int status, type, pid, flags, nlmsglen, skblen;
 	struct nlmsghdr *nlh;
@@ -522,19 +522,10 @@ ipq_rcv_skb(struct sk_buff *skb)
 }
 
 static void
-ipq_rcv_sk(struct sock *sk, int len)
+ipq_rcv_skb(struct sk_buff *skb)
 {
-	struct sk_buff *skb;
-	unsigned int qlen;
-
 	mutex_lock(&ipqnl_mutex);
-
-	for (qlen = skb_queue_len(&sk->sk_receive_queue); qlen; qlen--) {
-		skb = skb_dequeue(&sk->sk_receive_queue);
-		ipq_rcv_skb(skb);
-		kfree_skb(skb);
-	}
-
+	__ipq_rcv_skb(skb);
 	mutex_unlock(&ipqnl_mutex);
 }
 
@@ -658,8 +649,8 @@ static int __init ip6_queue_init(void)
 	struct proc_dir_entry *proc;
 
 	netlink_register_notifier(&ipq_nl_notifier);
-	ipqnl = netlink_kernel_create(&init_net, NETLINK_IP6_FW, 0, ipq_rcv_sk,
-					NULL, THIS_MODULE);
+	ipqnl = netlink_kernel_create(&init_net, NETLINK_IP6_FW, 0,
+			              ipq_rcv_skb, NULL, THIS_MODULE);
 	if (ipqnl == NULL) {
 		printk(KERN_ERR "ip6_queue: failed to create netlink socket\n");
 		goto cleanup_netlink_notifier;
--- ./net/netfilter/nfnetlink.c.nlk6	2007-10-05 16:28:13.000000000 +0400
+++ ./net/netfilter/nfnetlink.c	2007-10-05 16:51:28.000000000 +0400
@@ -169,15 +169,11 @@ static int nfnetlink_rcv_msg(struct sk_b
 	}
 }
 
-static void nfnetlink_rcv(struct sock *sk, int len)
+static void nfnetlink_rcv(struct sk_buff *skb)
 {
-	unsigned int qlen = 0;
-
-	do {
-		nfnl_lock();
-		qlen = netlink_run_queue(sk, qlen, nfnetlink_rcv_msg);
-		nfnl_unlock();
-	} while (qlen);
+	nfnl_lock();
+	netlink_rcv_skb(skb, &nfnetlink_rcv_msg);
+	nfnl_unlock();
 }
 
 static void __exit nfnetlink_exit(void)
--- ./net/netlink/af_netlink.c.nlk6	2007-10-05 16:29:35.000000000 +0400
+++ ./net/netlink/af_netlink.c	2007-10-05 18:07:41.000000000 +0400
@@ -80,7 +80,7 @@ struct netlink_sock {
 	struct netlink_callback	*cb;
 	struct mutex		*cb_mutex;
 	struct mutex		cb_def_mutex;
-	void			(*data_ready)(struct sock *sk, int bytes);
+	void			(*netlink_rcv)(struct sk_buff *skb);
 	struct module		*module;
 };
 
@@ -127,7 +127,6 @@ static DECLARE_WAIT_QUEUE_HEAD(nl_table_
 
 static int netlink_dump(struct sock *sk);
 static void netlink_destroy_callback(struct netlink_callback *cb);
-static void netlink_queue_skip(struct nlmsghdr *nlh, struct sk_buff *skb);
 
 static DEFINE_RWLOCK(nl_table_lock);
 static atomic_t nl_table_users = ATOMIC_INIT(0);
@@ -711,21 +710,17 @@ static void netlink_overrun(struct sock 
 
 static struct sock *netlink_getsockbypid(struct sock *ssk, u32 pid)
 {
-	int protocol = ssk->sk_protocol;
-	struct net *net;
 	struct sock *sock;
 	struct netlink_sock *nlk;
 
-	net = ssk->sk_net;
-	sock = netlink_lookup(net, protocol, pid);
+	sock = netlink_lookup(ssk->sk_net, ssk->sk_protocol, pid);
 	if (!sock)
 		return ERR_PTR(-ECONNREFUSED);
 
 	/* Don't bother queuing skb if kernel socket has no input function */
 	nlk = nlk_sk(sock);
-	if ((netlink_is_kernel(sock) && !nlk->data_ready) ||
-	    (sock->sk_state == NETLINK_CONNECTED &&
-	     nlk->dst_pid != nlk_sk(ssk)->pid)) {
+	if (sock->sk_state == NETLINK_CONNECTED &&
+	    nlk->dst_pid != nlk_sk(ssk)->pid) {
 		sock_put(sock);
 		return ERR_PTR(-ECONNREFUSED);
 	}
@@ -838,7 +833,34 @@ static inline struct sk_buff *netlink_tr
 	return skb;
 }
 
-int netlink_unicast(struct sock *ssk, struct sk_buff *skb, u32 pid, int nonblock)
+static inline void netlink_rcv_wake(struct sock *sk)
+{
+	struct netlink_sock *nlk = nlk_sk(sk);
+
+	if (skb_queue_empty(&sk->sk_receive_queue))
+		clear_bit(0, &nlk->state);
+	if (!test_bit(0, &nlk->state))
+		wake_up_interruptible(&nlk->wait);
+}
+
+static inline int netlink_unicast_kernel(struct sock *sk, struct sk_buff *skb)
+{
+	int ret;
+	struct netlink_sock *nlk = nlk_sk(sk);
+
+	ret = -ECONNREFUSED;
+	if (nlk->netlink_rcv != NULL) {
+		ret = skb->len;
+		skb_set_owner_r(skb, sk);
+		nlk->netlink_rcv(skb);
+	}
+	kfree_skb(skb);
+	sock_put(sk);
+	return ret;
+}
+
+int netlink_unicast(struct sock *ssk, struct sk_buff *skb,
+		    u32 pid, int nonblock)
 {
 	struct sock *sk;
 	int err;
@@ -853,6 +875,9 @@ retry:
 		kfree_skb(skb);
 		return PTR_ERR(sk);
 	}
+	if (netlink_is_kernel(sk))
+		return netlink_unicast_kernel(sk, skb);
+
 	err = netlink_attachskb(sk, skb, nonblock, timeo, ssk);
 	if (err == 1)
 		goto retry;
@@ -1152,16 +1177,6 @@ static void netlink_cmsg_recv_pktinfo(st
 	put_cmsg(msg, SOL_NETLINK, NETLINK_PKTINFO, sizeof(info), &info);
 }
 
-static inline void netlink_rcv_wake(struct sock *sk)
-{
-	struct netlink_sock *nlk = nlk_sk(sk);
-
-	if (skb_queue_empty(&sk->sk_receive_queue))
-		clear_bit(0, &nlk->state);
-	if (!test_bit(0, &nlk->state))
-		wake_up_interruptible(&nlk->wait);
-}
-
 static int netlink_sendmsg(struct kiocb *kiocb, struct socket *sock,
 			   struct msghdr *msg, size_t len)
 {
@@ -1309,11 +1324,7 @@ out:
 
 static void netlink_data_ready(struct sock *sk, int len)
 {
-	struct netlink_sock *nlk = nlk_sk(sk);
-
-	if (nlk->data_ready)
-		nlk->data_ready(sk, len);
-	netlink_rcv_wake(sk);
+	BUG();
 }
 
 /*
@@ -1324,7 +1335,7 @@ static void netlink_data_ready(struct so
 
 struct sock *
 netlink_kernel_create(struct net *net, int unit, unsigned int groups,
-		      void (*input)(struct sock *sk, int len),
+		      void (*input)(struct sk_buff *skb),
 		      struct mutex *cb_mutex, struct module *module)
 {
 	struct socket *sock;
@@ -1353,7 +1364,7 @@ netlink_kernel_create(struct net *net, i
 	sk = sock->sk;
 	sk->sk_data_ready = netlink_data_ready;
 	if (input)
-		nlk_sk(sk)->data_ready = input;
+		nlk_sk(sk)->netlink_rcv = input;
 
 	if (netlink_insert(sk, net, 0))
 		goto out_sock_release;
@@ -1553,12 +1564,7 @@ int netlink_dump_start(struct sock *ssk,
 
 	netlink_dump(sk);
 	sock_put(sk);
-
-	/* We successfully started a dump, by returning -EINTR we
-	 * signal the queue mangement to interrupt processing of
-	 * any netlink messages so userspace gets a chance to read
-	 * the results. */
-	return -EINTR;
+	return 0;
 }
 
 void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err)
@@ -1595,13 +1601,15 @@ void netlink_ack(struct sk_buff *in_skb,
 	netlink_unicast(in_skb->sk, skb, NETLINK_CB(in_skb).pid, MSG_DONTWAIT);
 }
 
-static int netlink_rcv_skb(struct sk_buff *skb, int (*cb)(struct sk_buff *,
+int netlink_rcv_skb(struct sk_buff *skb, int (*cb)(struct sk_buff *,
 						     struct nlmsghdr *))
 {
 	struct nlmsghdr *nlh;
 	int err;
 
 	while (skb->len >= nlmsg_total_size(0)) {
+		int msglen;
+
 		nlh = nlmsg_hdr(skb);
 		err = 0;
 
@@ -1617,86 +1625,20 @@ static int netlink_rcv_skb(struct sk_buf
 			goto skip;
 
 		err = cb(skb, nlh);
-		if (err == -EINTR) {
-			/* Not an error, but we interrupt processing */
-			netlink_queue_skip(nlh, skb);
-			return err;
-		}
 skip:
 		if (nlh->nlmsg_flags & NLM_F_ACK || err)
 			netlink_ack(skb, nlh, err);
 
-		netlink_queue_skip(nlh, skb);
+	        msglen = NLMSG_ALIGN(nlh->nlmsg_len);
+		if (msglen > skb->len)
+			msglen = skb->len;
+		skb_pull(skb, msglen);
 	}
 
 	return 0;
 }
 
 /**
- * nelink_run_queue - Process netlink receive queue.
- * @sk: Netlink socket containing the queue
- * @qlen: Initial queue length
- * @cb: Callback function invoked for each netlink message found
- *
- * Processes as much as there was in the queue upon entry and invokes
- * a callback function for each netlink message found. The callback
- * function may refuse a message by returning a negative error code
- * but setting the error pointer to 0 in which case this function
- * returns with a qlen != 0.
- *
- * qlen must be initialized to 0 before the initial entry, afterwards
- * the function may be called repeatedly until the returned qlen is 0.
- *
- * The callback function may return -EINTR to signal that processing
- * of netlink messages shall be interrupted. In this case the message
- * currently being processed will NOT be requeued onto the receive
- * queue.
- */
-unsigned int netlink_run_queue(struct sock *sk, unsigned int qlen,
-			       int (*cb)(struct sk_buff *, struct nlmsghdr *))
-{
-	struct sk_buff *skb;
-
-	if (!qlen || qlen > skb_queue_len(&sk->sk_receive_queue))
-		qlen = skb_queue_len(&sk->sk_receive_queue);
-
-	for (; qlen; qlen--) {
-		skb = skb_dequeue(&sk->sk_receive_queue);
-		if (netlink_rcv_skb(skb, cb)) {
-			if (skb->len)
-				skb_queue_head(&sk->sk_receive_queue, skb);
-			else {
-				kfree_skb(skb);
-				qlen--;
-			}
-			break;
-		}
-
-		kfree_skb(skb);
-	}
-
-	return qlen;
-}
-
-/**
- * netlink_queue_skip - Skip netlink message while processing queue.
- * @nlh: Netlink message to be skipped
- * @skb: Socket buffer containing the netlink messages.
- *
- * Pulls the given netlink message off the socket buffer so the next
- * call to netlink_queue_run() will not reconsider the message.
- */
-static void netlink_queue_skip(struct nlmsghdr *nlh, struct sk_buff *skb)
-{
-	int msglen = NLMSG_ALIGN(nlh->nlmsg_len);
-
-	if (msglen > skb->len)
-		msglen = skb->len;
-
-	skb_pull(skb, msglen);
-}
-
-/**
  * nlmsg_notify - send a notification netlink message
  * @sk: netlink socket to use
  * @skb: notification message
@@ -2008,7 +1950,7 @@ panic:
 core_initcall(netlink_proto_init);
 
 EXPORT_SYMBOL(netlink_ack);
-EXPORT_SYMBOL(netlink_run_queue);
+EXPORT_SYMBOL(netlink_rcv_skb);
 EXPORT_SYMBOL(netlink_broadcast);
 EXPORT_SYMBOL(netlink_dump_start);
 EXPORT_SYMBOL(netlink_kernel_create);
--- ./net/netlink/genetlink.c.nlk6	2007-10-05 16:28:13.000000000 +0400
+++ ./net/netlink/genetlink.c	2007-10-05 16:52:21.000000000 +0400
@@ -470,15 +470,11 @@ static int genl_rcv_msg(struct sk_buff *
 	return ops->doit(skb, &info);
 }
 
-static void genl_rcv(struct sock *sk, int len)
+static void genl_rcv(struct sk_buff *skb)
 {
-	unsigned int qlen = 0;
-
-	do {
-		genl_lock();
-		qlen = netlink_run_queue(sk, qlen, genl_rcv_msg);
-		genl_unlock();
-	} while (qlen && genl_sock && genl_sock->sk_receive_queue.qlen);
+	genl_lock();
+	netlink_rcv_skb(skb, &genl_rcv_msg);
+	genl_unlock();
 }
 
 /**************************************************************************
--- ./net/xfrm/xfrm_user.c.nlk6	2007-10-05 16:27:16.000000000 +0400
+++ ./net/xfrm/xfrm_user.c	2007-10-05 16:52:31.000000000 +0400
@@ -1881,16 +1881,11 @@ static int xfrm_user_rcv_msg(struct sk_b
 	return link->doit(skb, nlh, attrs);
 }
 
-static void xfrm_netlink_rcv(struct sock *sk, int len)
+static void xfrm_netlink_rcv(struct sk_buff *skb)
 {
-	unsigned int qlen = 0;
-
-	do {
-		mutex_lock(&xfrm_cfg_mutex);
-		qlen = netlink_run_queue(sk, qlen, &xfrm_user_rcv_msg);
-		mutex_unlock(&xfrm_cfg_mutex);
-
-	} while (qlen);
+	mutex_lock(&xfrm_cfg_mutex);
+	netlink_rcv_skb(skb, &xfrm_user_rcv_msg);
+	mutex_unlock(&xfrm_cfg_mutex);
 }
 
 static inline size_t xfrm_expire_msgsize(void)

^ permalink raw reply

* [PATCH 4/5] unify netlink kernel socket recognition
From: Denis V. Lunev @ 2007-10-05 14:47 UTC (permalink / raw)
  To: davem; +Cc: netdev, kuznet, devel, containers

There are currently two ways to determine whether the netlink socket is a
kernel one or a user one. This patch creates a single inline call for
this purpose and unifies all the calls in the af_netlink.c

No similar calls are found outside af_netlink.c.

Signed-off-by: Denis V. Lunev <den@openvz.org>
Acked-by: Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>

--- ./net/netlink/af_netlink.c.nlk4	2007-08-26 19:30:38.000000000 +0400
+++ ./net/netlink/af_netlink.c	2007-10-02 12:27:33.000000000 +0400
@@ -92,6 +92,11 @@ static inline struct netlink_sock *nlk_s
 	return container_of(sk, struct netlink_sock, sk);
 }
 
+static inline int netlink_is_kernel(struct sock *sk)
+{
+	return nlk_sk(sk)->flags & NETLINK_KERNEL_SOCKET;
+}
+
 struct nl_pid_hash {
 	struct hlist_head *table;
 	unsigned long rehash_time;
@@ -489,7 +494,7 @@ static int netlink_release(struct socket
 	module_put(nlk->module);
 
 	netlink_table_grab();
-	if (nlk->flags & NETLINK_KERNEL_SOCKET) {
+	if (netlink_is_kernel(sk)) {
 		kfree(nl_table[sk->sk_protocol].listeners);
 		nl_table[sk->sk_protocol].module = NULL;
 		nl_table[sk->sk_protocol].registered = 0;
@@ -716,7 +721,7 @@ static struct sock *netlink_getsockbypid
 
 	/* Don't bother queuing skb if kernel socket has no input function */
 	nlk = nlk_sk(sock);
-	if ((nlk->pid == 0 && !nlk->data_ready) ||
+	if ((netlink_is_kernel(sock) && !nlk->data_ready) ||
 	    (sock->sk_state == NETLINK_CONNECTED &&
 	     nlk->dst_pid != nlk_sk(ssk)->pid)) {
 		sock_put(sock);
@@ -762,7 +767,7 @@ int netlink_attachskb(struct sock *sk, s
 	    test_bit(0, &nlk->state)) {
 		DECLARE_WAITQUEUE(wait, current);
 		if (!timeo) {
-			if (!ssk || nlk_sk(ssk)->pid == 0)
+			if (!ssk || netlink_is_kernel(ssk))
 				netlink_overrun(sk);
 			sock_put(sk);
 			kfree_skb(skb);
@@ -861,7 +875,7 @@ int netlink_has_listeners(struct sock *s
 	int res = 0;
 	unsigned long *listeners;
 
-	BUG_ON(!(nlk_sk(sk)->flags & NETLINK_KERNEL_SOCKET));
+	BUG_ON(!netlink_is_kernel(sk));
 
 	rcu_read_lock();
 	listeners = rcu_dereference(nl_table[sk->sk_protocol].listeners);

^ permalink raw reply

* [PATCH 3/5] cleanup 3rd argument in netlink_sendskb
From: Denis V. Lunev @ 2007-10-05 14:46 UTC (permalink / raw)
  To: davem; +Cc: netdev, kuznet, devel, containers

netlink_sendskb does not use third argument. Clean it and save a couple of
bytes.

Signed-off-by: Denis V. Lunev <den@openvz.org>
Acked-by: Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>

--- ./include/linux/netlink.h.nlk5	2007-08-26 19:30:37.000000000 +0400
+++ ./include/linux/netlink.h	2007-10-02 11:53:09.000000000 +0400
@@ -194,7 +194,7 @@ struct sock *netlink_getsockbyfilp(struc
 int netlink_attachskb(struct sock *sk, struct sk_buff *skb, int nonblock,
 		long timeo, struct sock *ssk);
 void netlink_detachskb(struct sock *sk, struct sk_buff *skb);
-int netlink_sendskb(struct sock *sk, struct sk_buff *skb, int protocol);
+int netlink_sendskb(struct sock *sk, struct sk_buff *skb);
 
 /*
  *	skb should fit one page. This choice is good for headerless malloc.
--- ./ipc/mqueue.c.nlk5	2007-08-26 19:30:37.000000000 +0400
+++ ./ipc/mqueue.c	2007-10-02 11:52:27.000000000 +0400
@@ -521,8 +521,7 @@ static void __do_notify(struct mqueue_in
 			break;
 		case SIGEV_THREAD:
 			set_cookie(info->notify_cookie, NOTIFY_WOKENUP);
-			netlink_sendskb(info->notify_sock,
-					info->notify_cookie, 0);
+			netlink_sendskb(info->notify_sock, info->notify_cookie);
 			break;
 		}
 		/* after notification unregisters process */
@@ -568,7 +567,7 @@ static void remove_notification(struct m
 	if (info->notify_owner != NULL &&
 	    info->notify.sigev_notify == SIGEV_THREAD) {
 		set_cookie(info->notify_cookie, NOTIFY_REMOVED);
-		netlink_sendskb(info->notify_sock, info->notify_cookie, 0);
+		netlink_sendskb(info->notify_sock, info->notify_cookie);
 	}
 	put_pid(info->notify_owner);
 	info->notify_owner = NULL;
--- ./net/netlink/af_netlink.c.nlk5	2007-10-02 11:43:09.000000000 +0400
+++ ./net/netlink/af_netlink.c	2007-10-02 11:50:19.000000000 +0400
@@ -796,7 +796,7 @@ int netlink_attachskb(struct sock *sk, s
 	return 0;
 }
 
-int netlink_sendskb(struct sock *sk, struct sk_buff *skb, int protocol)
+int netlink_sendskb(struct sock *sk, struct sk_buff *skb)
 {
 	int len = skb->len;
 
@@ -858,7 +858,7 @@ retry:
 	if (err)
 		return err;
 
-	return netlink_sendskb(sk, skb, ssk->sk_protocol);
+	return netlink_sendskb(sk, skb);
 }
 
 int netlink_has_listeners(struct sock *sk, unsigned int group)

^ permalink raw reply

* Re: (2.6.18.8) tg3: eth0: The system may be re-ordering memory-mapped I/O cycles to the network device, attempting to recover.
From: NetArt - Grzegorz Nosek @ 2007-10-05 14:44 UTC (permalink / raw)
  To: Michael Chan; +Cc: NetArt - Grzegorz Nosek, netdev
In-Reply-To: <1551EAE59135BE47B544934E30FC4FC002AABAA7@nt-irva-0751.brcm.ad.broadcom.com>

On Fri, Oct 05, 2007 at 07:16:50AM -0700, Michael Chan wrote:
> NetArt - Grzegorz Nosek wrote:
> 
> > 
> > I have encountered an error which looks tg3-related. Upon adding some
> > htb queue rules (which I don't have handy ATM but can provide if
> > needed), after some time we get such messages in the kernel log:
> > 
> 
> Please provide the HTB queue rules and the type of traffic to make
> this happen.  Thanks.
> 

The HTB rules are as simple as below:

INTERFACE="eth0"
TC="/sbin/tc"

$TC qdisc del dev $INTERFACE root
$TC qdisc add dev $INTERFACE root handle 1:0 htb
$TC class add dev $INTERFACE parent 1:0 classid 1:1 htb rate 1000Mbit ceil 1000Mbit
$TC class add dev $INTERFACE parent 1:1 classid 1:2 htb rate 5Mbit ceil 5Mbit
$TC qdisc add dev $INTERFACE parent 1:2 sfq
$TC filter add dev $INTERFACE parent 1:0 protocol ip prio 10 u32 match ip src 10.10.10.10 flowid 1:2

(the IP address was changed but it's one of the machine's 65 local IP
addresses)

The traffic is 4-5kpps both ways, 2Mbps in, 40Mbps out. There are
several database servers running. The point of these rules was to
limit the data rate of 10.10.10.10 (a mysql server) to 5Mbps. Currently
the traffic from this IP accounts for much of the whole traffic but overall
it's pretty bursty. It ranges from nothing at all to 10Mbps to 70Mbps
peaks.

It may be related that on other machines the HTB queues are saturated
only occasionally and usually the data rates are well below HTB
settings, so a drastically overlimit queue may well be unique to this
server.

Best regards,
 Grzegorz Nosek



^ permalink raw reply

* [PATCH 2/5] make netlink processing routines semi-synchronious (inspired by rtnl) v2
From: Denis V. Lunev @ 2007-10-05 14:45 UTC (permalink / raw)
  To: davem; +Cc: netdev, kuznet

The code in netfilter/nfnetlink.c and in ./net/netlink/genetlink.c looks
like outdated copy/paste from rtnetlink.c. Push them into sync with the
original.

Changes from v1:
- deleted comment in nfnetlink_rcv_msg by request of Patrick McHardy

Signed-off-by: Denis V. Lunev <den@openvz.org>
Acked-by: Patrick McHardy <kaber@trash.net>

--- ./net/netfilter/nfnetlink.c.nlk3	2007-10-01 09:47:53.000000000 +0400
+++ ./net/netfilter/nfnetlink.c	2007-10-01 17:13:09.000000000 +0400
@@ -44,26 +44,14 @@ static struct sock *nfnl = NULL;
 static const struct nfnetlink_subsystem *subsys_table[NFNL_SUBSYS_COUNT];
 static DEFINE_MUTEX(nfnl_mutex);
 
-static void nfnl_lock(void)
+static inline void nfnl_lock(void)
 {
 	mutex_lock(&nfnl_mutex);
 }
 
-static int nfnl_trylock(void)
-{
-	return !mutex_trylock(&nfnl_mutex);
-}
-
-static void __nfnl_unlock(void)
-{
-	mutex_unlock(&nfnl_mutex);
-}
-
-static void nfnl_unlock(void)
+static inline void nfnl_unlock(void)
 {
 	mutex_unlock(&nfnl_mutex);
-	if (nfnl->sk_receive_queue.qlen)
-		nfnl->sk_data_ready(nfnl, 0);
 }
 
 int nfnetlink_subsys_register(const struct nfnetlink_subsystem *n)
@@ -147,9 +135,7 @@ static int nfnetlink_rcv_msg(struct sk_b
 	ss = nfnetlink_get_subsys(type);
 	if (!ss) {
 #ifdef CONFIG_KMOD
-		/* don't call nfnl_unlock, since it would reenter
-		 * with further packet processing */
-		__nfnl_unlock();
+		nfnl_unlock();
 		request_module("nfnetlink-subsys-%d", NFNL_SUBSYS_ID(type));
 		nfnl_lock();
 		ss = nfnetlink_get_subsys(type);
@@ -188,10 +174,9 @@ static void nfnetlink_rcv(struct sock *s
 	unsigned int qlen = 0;
 
 	do {
-		if (nfnl_trylock())
-			return;
+		nfnl_lock();
 		qlen = netlink_run_queue(sk, qlen, nfnetlink_rcv_msg);
-		__nfnl_unlock();
+		nfnl_unlock();
 	} while (qlen);
 }
 
--- ./net/netlink/genetlink.c.nlk3	2007-08-26 19:30:38.000000000 +0400
+++ ./net/netlink/genetlink.c	2007-10-01 16:05:29.000000000 +0400
@@ -22,22 +22,14 @@ struct sock *genl_sock = NULL;
 
 static DEFINE_MUTEX(genl_mutex); /* serialization of message processing */
 
-static void genl_lock(void)
+static inline void genl_lock(void)
 {
 	mutex_lock(&genl_mutex);
 }
 
-static int genl_trylock(void)
-{
-	return !mutex_trylock(&genl_mutex);
-}
-
-static void genl_unlock(void)
+static inline void genl_unlock(void)
 {
 	mutex_unlock(&genl_mutex);
-
-	if (genl_sock && genl_sock->sk_receive_queue.qlen)
-		genl_sock->sk_data_ready(genl_sock, 0);
 }
 
 #define GENL_FAM_TAB_SIZE	16
@@ -483,8 +475,7 @@ static void genl_rcv(struct sock *sk, in
 	unsigned int qlen = 0;
 
 	do {
-		if (genl_trylock())
-			return;
+		genl_lock();
 		qlen = netlink_run_queue(sk, qlen, genl_rcv_msg);
 		genl_unlock();
 	} while (qlen && genl_sock && genl_sock->sk_receive_queue.qlen);

^ permalink raw reply

* [PATCH 1/5] rtnl_unlock cleanups
From: Denis V. Lunev @ 2007-10-05 14:44 UTC (permalink / raw)
  To: davem; +Cc: devel, containers, kuznet, netdev

There is no need to process outstanding netlink user->kernel packets
during rtnl_unlock now. There is no rtnl_trylock in the rtnetlink_rcv
anymore.

Normal code path is the following:
netlink_sendmsg
   netlink_unicast
       netlink_sendskb
           skb_queue_tail
           netlink_data_ready
               rtnetlink_rcv
                   mutex_lock(&rtnl_mutex);
                   netlink_run_queue(sk, qlen, &rtnetlink_rcv_msg);
                   mutex_unlock(&rtnl_mutex);

So, it is possible, that packets can be present in the rtnl->sk_receive_queue
during rtnl_unlock, but there is no need to process them at that moment as
rtnetlink_rcv for that packet is pending.

Signed-off-by: Denis V. Lunev <den@openvz.org>
Acked-by: Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>

--- ./net/core/rtnetlink.c.rtnl2	2007-08-26 19:30:38.000000000 +0400
+++ ./net/core/rtnetlink.c	2007-10-01 13:09:03.000000000 +0400
@@ -75,8 +75,6 @@ void __rtnl_unlock(void)
 void rtnl_unlock(void)
 {
 	mutex_unlock(&rtnl_mutex);
-	if (rtnl && rtnl->sk_receive_queue.qlen)
-		rtnl->sk_data_ready(rtnl, 0);
 	netdev_run_todo();
 }
 
@@ -1319,11 +1317,9 @@ static void rtnetlink_rcv(struct sock *s
 	unsigned int qlen = 0;
 
 	do {
-		mutex_lock(&rtnl_mutex);
+		rtnl_lock();
 		qlen = netlink_run_queue(sk, qlen, &rtnetlink_rcv_msg);
-		mutex_unlock(&rtnl_mutex);
-
-		netdev_run_todo();
+		rtnl_unlock();
 	} while (qlen);
 }
 

^ permalink raw reply

* Re: (2.6.18.8) tg3: eth0: The system may be re-ordering memory-mapped I/O cycles to the network device, attempting to recover.
From: Michael Chan @ 2007-10-05 14:16 UTC (permalink / raw)
  To: NetArt - Grzegorz Nosek; +Cc: netdev
In-Reply-To: <20071005095722.GA23224@tech.serwery.pl>

NetArt - Grzegorz Nosek wrote:

> 
> I have encountered an error which looks tg3-related. Upon adding some
> htb queue rules (which I don't have handy ATM but can provide if
> needed), after some time we get such messages in the kernel log:
> 

Please provide the HTB queue rules and the type of traffic to make
this happen.  Thanks.


^ 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