Netdev List
 help / color / mirror / Atom feed
* [Patch net-next 02/11] net:fec: add enet AVB feature macro define for imx6sx
From: y @ 2014-09-03 20:34 UTC (permalink / raw)
  To: b38611, davem, netdev, lznuaa
  Cc: shawn.guo, linux-arm-kernel, devicetree, Fugang Duan, Frank Li
In-Reply-To: <1409776486-1403-1-git-send-email-y>

From: Fugang Duan <B38611@freescale.com>

Add enet AVB feature macro define for imx6sx.

Signed-off-by: Fugang Duan <B38611@freescale.com>
Signed-off-by: Frank Li <Frank.Li@freescale.com>
---
 drivers/net/ethernet/freescale/fec_main.c | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index c21ecff1..ee9f04f 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -104,6 +104,16 @@ static void set_multicast_list(struct net_device *ndev);
  * ENET_TDAR[TDAR].
  */
 #define FEC_QUIRK_ERR006358            (1 << 7)
+/* ENET IP hw AVB
+ *
+ * i.MX6SX ENET IP add Audio Video Bridging (AVB) feature support.
+ * - Two class indicators on receive with configurable priority
+ * - Two class indicators and line speed timer on transmit allowing
+ *   implementation class credit based shapers externally
+ * - Additional DMA registers provisioned to allow managing up to 3
+ *   independent rings
+ */
+#define FEC_QUIRK_HAS_AVB		(1 << 8)
 
 static struct platform_device_id fec_devtype[] = {
 	{
@@ -128,6 +138,12 @@ static struct platform_device_id fec_devtype[] = {
 		.name = "mvf600-fec",
 		.driver_data = FEC_QUIRK_ENET_MAC,
 	}, {
+		.name = "imx6sx-fec",
+		.driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT |
+				FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM |
+				FEC_QUIRK_HAS_VLAN | FEC_QUIRK_ERR006358 |
+				FEC_QUIRK_HAS_AVB,
+	}, {
 		/* sentinel */
 	}
 };
-- 
1.9.1

^ permalink raw reply related

* [Patch net-next 06/11] net:fec: add enet-avb IP support
From: y @ 2014-09-03 20:34 UTC (permalink / raw)
  To: b38611, davem, netdev, lznuaa
  Cc: shawn.guo, linux-arm-kernel, devicetree, Frank Li, Fugang Duan
In-Reply-To: <1409776486-1403-1-git-send-email-y>

From: Frank Li <Frank.Li@freescale.com>

i.MX6SX Enet-AVB support 3 tx queues, 3 rx queues.
For tx queues: ring 0 -> best effort
	       ring 1 -> Class A
	       ring 2 -> Class B
For rx queues:
	       ring 0 -> best effort
	       ring 1 -> receive VLAN packet with classification match
	       ring 2 -> receive VLAN packet with classification match

Add enet-avb IP multiqueue support for the driver.

Signed-off-by: Fugang Duan <B38611@freescale.com>
Signed-off-by: Frank Li <Frank.Li@freescale.com>
---
 drivers/net/ethernet/freescale/fec.h      | 33 +++++++++++++++++++++++++++
 drivers/net/ethernet/freescale/fec_main.c | 38 +++++++++++++++----------------
 2 files changed, 52 insertions(+), 19 deletions(-)

diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h
index 984eaf2..16989bc 100644
--- a/drivers/net/ethernet/freescale/fec.h
+++ b/drivers/net/ethernet/freescale/fec.h
@@ -38,6 +38,12 @@
 #define FEC_ADDR_LOW		0x0e4 /* Low 32bits MAC address */
 #define FEC_ADDR_HIGH		0x0e8 /* High 16bits MAC address */
 #define FEC_OPD			0x0ec /* Opcode + Pause duration */
+#define FEC_TXIC0		0xF0  /* Tx Interrupt Coalescing for ring 0 */
+#define FEC_TXIC1		0xF4  /* Tx Interrupt Coalescing for ring 1 */
+#define FEC_TXIC2		0xF8  /* Tx Interrupt Coalescing for ring 2 */
+#define FEC_RXIC0		0x100 /* Rx Interrupt Coalescing for ring 0 */
+#define FEC_RXIC1		0x104 /* Rx Interrupt Coalescing for ring 1 */
+#define FEC_RXIC2		0x108 /* Rx Interrupt Coalescing for ring 2 */
 #define FEC_HASH_TABLE_HIGH	0x118 /* High 32bits hash table */
 #define FEC_HASH_TABLE_LOW	0x11c /* Low 32bits hash table */
 #define FEC_GRP_HASH_TABLE_HIGH	0x120 /* High 32bits hash table */
@@ -65,6 +71,7 @@
 #define FEC_X_DES_ACTIVE_1	0x1e4 /* Tx descriptor active for ring 1 */
 #define FEC_R_DES_ACTIVE_2	0x1e8 /* Rx descriptor active for ring 2 */
 #define FEC_X_DES_ACTIVE_2	0x1ec /* Tx descriptor active for ring 2 */
+#define FEC_QOS_SCHEME		0x1f0 /* Set multi queues Qos scheme */
 #define FEC_MIIGSK_CFGR		0x300 /* MIIGSK Configuration reg */
 #define FEC_MIIGSK_ENR		0x308 /* MIIGSK Enable reg */
 
@@ -305,6 +312,32 @@ struct bufdesc_ex {
 #define FLAG_RX_CSUM_ENABLED	(BD_ENET_RX_ICE | BD_ENET_RX_PCR)
 #define FLAG_RX_CSUM_ERROR	(BD_ENET_RX_ICE | BD_ENET_RX_PCR)
 
+/* Interrupt events/masks. */
+#define FEC_ENET_HBERR  ((uint)0x80000000)      /* Heartbeat error */
+#define FEC_ENET_BABR   ((uint)0x40000000)      /* Babbling receiver */
+#define FEC_ENET_BABT   ((uint)0x20000000)      /* Babbling transmitter */
+#define FEC_ENET_GRA    ((uint)0x10000000)      /* Graceful stop complete */
+#define FEC_ENET_TXF_0	((uint)0x08000000)	/* Full frame transmitted */
+#define FEC_ENET_TXF_1	((uint)0x00000008)	/* Full frame transmitted */
+#define FEC_ENET_TXF_2	((uint)0x00000080)	/* Full frame transmitted */
+#define FEC_ENET_TXB    ((uint)0x04000000)      /* A buffer was transmitted */
+#define FEC_ENET_RXF_0	((uint)0x02000000)	/* Full frame received */
+#define FEC_ENET_RXF_1	((uint)0x00000002)	/* Full frame received */
+#define FEC_ENET_RXF_2	((uint)0x00000020)	/* Full frame received */
+#define FEC_ENET_RXB    ((uint)0x01000000)      /* A buffer was received */
+#define FEC_ENET_MII    ((uint)0x00800000)      /* MII interrupt */
+#define FEC_ENET_EBERR  ((uint)0x00400000)      /* SDMA bus error */
+#define FEC_ENET_TXF	(FEC_ENET_TXF_0 | FEC_ENET_TXF_1 | FEC_ENET_TXF_2)
+#define FEC_ENET_RXF	(FEC_ENET_RXF_0 | FEC_ENET_RXF_1 | FEC_ENET_RXF_2)
+#define FEC_ENET_TS_AVAIL       ((uint)0x00010000)
+#define FEC_ENET_TS_TIMER       ((uint)0x00008000)
+
+#define FEC_DEFAULT_IMASK (FEC_ENET_TXF | FEC_ENET_RXF | FEC_ENET_MII | FEC_ENET_TS_TIMER)
+#define FEC_RX_DISABLED_IMASK (FEC_DEFAULT_IMASK & (~FEC_ENET_RXF))
+
+#define FEC_VLAN_TAG_LEN       0x04
+#define FEC_ETHTYPE_LEN                0x02
+
 struct fec_enet_priv_tx_q {
 	int index;
 	unsigned char *tx_bounce[TX_RING_SIZE];
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index 5dab6a6..6aab64b 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -193,21 +193,6 @@ MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address");
 #endif
 #endif /* CONFIG_M5272 */
 
-/* Interrupt events/masks. */
-#define FEC_ENET_HBERR	((uint)0x80000000)	/* Heartbeat error */
-#define FEC_ENET_BABR	((uint)0x40000000)	/* Babbling receiver */
-#define FEC_ENET_BABT	((uint)0x20000000)	/* Babbling transmitter */
-#define FEC_ENET_GRA	((uint)0x10000000)	/* Graceful stop complete */
-#define FEC_ENET_TXF	((uint)0x08000000)	/* Full frame transmitted */
-#define FEC_ENET_TXB	((uint)0x04000000)	/* A buffer was transmitted */
-#define FEC_ENET_RXF	((uint)0x02000000)	/* Full frame received */
-#define FEC_ENET_RXB	((uint)0x01000000)	/* A buffer was received */
-#define FEC_ENET_MII	((uint)0x00800000)	/* MII interrupt */
-#define FEC_ENET_EBERR	((uint)0x00400000)	/* SDMA bus error */
-
-#define FEC_DEFAULT_IMASK (FEC_ENET_TXF | FEC_ENET_RXF | FEC_ENET_MII)
-#define FEC_RX_DISABLED_IMASK (FEC_DEFAULT_IMASK & (~FEC_ENET_RXF))
-
 /* The FEC stores dest/src/type/vlan, data, and checksum for receive packets.
  */
 #define PKT_MAXBUF_SIZE		1522
@@ -885,7 +870,16 @@ static void fec_enet_bd_init(struct net_device *dev)
 	}
 }
 
-static inline void fec_enet_enable_ring(struct net_device *ndev)
+static void fec_enet_active_rxring(struct net_device *ndev)
+{
+	struct fec_enet_private *fep = netdev_priv(ndev);
+	int i;
+
+	for (i = 0; i < fep->num_rx_queues; i++)
+		writel(0, fep->hwp + FEC_R_DES_ACTIVE(i));
+}
+
+static void fec_enet_enable_ring(struct net_device *ndev)
 {
 	struct fec_enet_private *fep = netdev_priv(ndev);
 	struct fec_enet_priv_tx_q *tx_queue;
@@ -946,7 +940,6 @@ fec_restart(struct net_device *ndev)
 	u32 temp_mac[2];
 	u32 rcntl = OPT_FRAME_SIZE | 0x04;
 	u32 ecntl = 0x2; /* ETHEREN */
-	int i;
 
 	/* Whack a reset.  We should wait for this.
 	 * For i.MX6SX SOC, enet use AXI bus, we use disable MAC
@@ -1103,8 +1096,7 @@ fec_restart(struct net_device *ndev)
 
 	/* And last, enable the transmit and receive processing */
 	writel(ecntl, fep->hwp + FEC_ECNTRL);
-	for (i = 0; i < fep->num_rx_queues; i++)
-		writel(0, fep->hwp + FEC_R_DES_ACTIVE(i));
+	fec_enet_active_rxring(ndev);
 
 	if (fep->bufdesc_ex)
 		fec_ptp_start_cyclecounter(ndev);
@@ -1515,9 +1507,17 @@ fec_enet_collect_events(struct fec_enet_private *fep, uint int_events)
 
 	if (int_events & FEC_ENET_RXF)
 		fep->work_rx |= (1 << 2);
+	if (int_events & FEC_ENET_RXF_1)
+		fep->work_rx |= (1 << 0);
+	if (int_events & FEC_ENET_RXF_2)
+		fep->work_rx |= (1 << 1);
 
 	if (int_events & FEC_ENET_TXF)
 		fep->work_tx |= (1 << 2);
+	if (int_events & FEC_ENET_TXF_1)
+		fep->work_tx |= (1 << 0);
+	if (int_events & FEC_ENET_TXF_2)
+		fep->work_tx |= (1 << 1);
 
 	return true;
 }
-- 
1.9.1

^ permalink raw reply related

* [Patch net-next 08/11] net:fec: change FEC alignment to 64 bytes for ARM platform
From: y @ 2014-09-03 20:34 UTC (permalink / raw)
  To: b38611, davem, netdev, lznuaa
  Cc: shawn.guo, linux-arm-kernel, devicetree, Fugang Duan, Frank Li
In-Reply-To: <1409776486-1403-1-git-send-email-y>

From: Fugang Duan <B38611@freescale.com>

Since enet-avb has 64 bytes alignment limitation for rx DMA transfer.
The previous enet IP for ARM platform has 16 bytes alignment for tx
DMA transfer. 64 is the an integral number of 16, so change alignment
to 64 bytes for all ARM platform, which don't impact the performance
of previous platform.

Signed-off-by: Fugang Duan <B38611@freescale.com>
Signed-off-by: Frank Li <Frank.Li@freescale.com>
---
 drivers/net/ethernet/freescale/fec_main.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index a232245..b388e29f 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -65,7 +65,7 @@
 static void set_multicast_list(struct net_device *ndev);
 
 #if defined(CONFIG_ARM)
-#define FEC_ALIGNMENT	0xf
+#define FEC_ALIGNMENT	0x3f
 #else
 #define FEC_ALIGNMENT	0x3
 #endif
-- 
1.9.1

^ permalink raw reply related

* [Patch net-next 07/11] net:fec: Add fsl,imx6sx-fec  compatible strings
From: y @ 2014-09-03 20:34 UTC (permalink / raw)
  To: b38611, davem, netdev, lznuaa
  Cc: shawn.guo, linux-arm-kernel, devicetree, Fugang Duan, Frank Li
In-Reply-To: <1409776486-1403-1-git-send-email-y>

From: Fugang Duan <B38611@freescale.com>

Add compatible string "fsl,imx6sx-fec" for i.MX6SX.

Signed-off-by: Fugang Duan <B38611@freescale.com>
Signed-off-by: Frank Li <Frank.Li@freescale.com>
---
 drivers/net/ethernet/freescale/fec_main.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index 6aab64b..a232245 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -157,6 +157,7 @@ enum imx_fec_type {
 	IMX28_FEC,
 	IMX6Q_FEC,
 	MVF600_FEC,
+	IMX6SX_FEC,
 };
 
 static const struct of_device_id fec_dt_ids[] = {
@@ -165,6 +166,7 @@ static const struct of_device_id fec_dt_ids[] = {
 	{ .compatible = "fsl,imx28-fec", .data = &fec_devtype[IMX28_FEC], },
 	{ .compatible = "fsl,imx6q-fec", .data = &fec_devtype[IMX6Q_FEC], },
 	{ .compatible = "fsl,mvf600-fec", .data = &fec_devtype[MVF600_FEC], },
+	{ .compatible = "fsl,imx6sx-fec", .data = &fec_devtype[IMX6SX_FEC], },
 	{ /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, fec_dt_ids);
-- 
1.9.1

^ permalink raw reply related

* [Patch net-next 11/11] ARM: dts: imx6sx: add multi-queue support enet
From: y @ 2014-09-03 20:34 UTC (permalink / raw)
  To: b38611, davem, netdev, lznuaa
  Cc: shawn.guo, linux-arm-kernel, devicetree, Frank Li
In-Reply-To: <1409776486-1403-1-git-send-email-y>

From: Frank Li <Frank.Li@freescale.com>

Enable 3 queues suppport for ethernet

Signed-off-by: Frank Li <Frank.Li@freescale.com>
---
 arch/arm/boot/dts/imx6sx.dtsi | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/arch/arm/boot/dts/imx6sx.dtsi b/arch/arm/boot/dts/imx6sx.dtsi
index f4b9da6..1722748 100644
--- a/arch/arm/boot/dts/imx6sx.dtsi
+++ b/arch/arm/boot/dts/imx6sx.dtsi
@@ -776,6 +776,8 @@
 					 <&clks IMX6SX_CLK_ENET_PTP>;
 				clock-names = "ipg", "ahb", "ptp",
 					      "enet_clk_ref", "enet_out";
+				fsl,num_tx_queues=<3>;
+				fsl,num_rx_queues=<3>;
 				status = "disabled";
                         };
 
-- 
1.9.1

^ permalink raw reply related

* Re: [PATCH 2/2] net-timestamp: Make the clone operation stand-alone from phy timestamping
From: Eric Dumazet @ 2014-09-03 21:07 UTC (permalink / raw)
  To: Alexander Duyck; +Cc: netdev, richardcochran, davem, willemb
In-Reply-To: <20140903155357.1896.92538.stgit@ahduyck-bv4.jf.intel.com>

On Wed, 2014-09-03 at 11:53 -0400, Alexander Duyck wrote:
> The phy timestamping takes a different path than the regular timestamping
> does in that it will create a clone first so that the packets needing to be
> timestamped can be placed in a queue, or the context block could be used.
> 
> In order to support these use cases I am pulling the core of the code out
> so it can be used in other drivers beyond just phy devices.
> 
> Signed-off-by: Alexander Duyck <alexander.h.duyck@intel.com>
> ---
>  include/linux/skbuff.h  |    2 ++
>  net/core/skbuff.c       |   20 ++++++++++++++++++++
>  net/core/timestamping.c |   14 +++-----------
>  3 files changed, 25 insertions(+), 11 deletions(-)
> 
> diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
> index 02529fc..19eb787 100644
> --- a/include/linux/skbuff.h
> +++ b/include/linux/skbuff.h
> @@ -2690,6 +2690,8 @@ static inline ktime_t net_invalid_timestamp(void)
>  	return ktime_set(0, 0);
>  }
>  
> +struct sk_buff *__skb_clone_tx_timestamp(struct sk_buff *skb);
> +
>  #ifdef CONFIG_NETWORK_PHY_TIMESTAMPING
>  
>  void skb_clone_tx_timestamp(struct sk_buff *skb);
> diff --git a/net/core/skbuff.c b/net/core/skbuff.c
> index 488d902..3f5cf29 100644
> --- a/net/core/skbuff.c
> +++ b/net/core/skbuff.c
> @@ -3511,6 +3511,26 @@ struct sk_buff *sock_dequeue_err_skb(struct sock *sk)
>  }
>  EXPORT_SYMBOL(sock_dequeue_err_skb);
>  
> +struct sk_buff *__skb_clone_tx_timestamp(struct sk_buff *skb)
> +{
> +	struct sock *sk = skb->sk;
> +	struct sk_buff *clone;
> +
> +	if (!sk || !atomic_inc_not_zero(&sk->sk_refcnt))


> +		return NULL;
> +
> +	clone = skb_clone(skb, GFP_ATOMIC);
> +	if (!clone) {
> +		sock_put(sk);
> +		return NULL;
> +	}
> +
> +	clone->sk = sk;
> +
> +	return clone;
> +}
> +EXPORT_SYMBOL(__skb_clone_tx_timestamp);


Normally, if one skb holds a reference to a socket, it should have
skb->destructor set to a cleanup function.

Otherwise, we rely on callers following a convention, to release sk
reference.

If you believe its needed, it should be dully documented.

Thanks

^ permalink raw reply

* Re: [Patch net-next 00/11] net: fec: imx6sx multiqueue support
From: Zhi Li @ 2014-09-03 21:02 UTC (permalink / raw)
  Cc: Duan Fugang-B38611, David Miller, netdev@vger.kernel.org,
	Shawn Guo, linux-arm-kernel@lists.infradead.org, devicetree,
	Frank Li
In-Reply-To: <5407807e.c26c460a.65d3.71b9SMTPIN_ADDED_BROKEN@mx.google.com>

On Wed, Sep 3, 2014 at 3:34 PM,  <y@shlinux1.ap.freescale.net> wrote:
>
>
Sorry! Sent out with wrong sender email address.

^ permalink raw reply

* [Patch net-next 00/11] net: fec: imx6sx multiqueue support
From: Frank Li @ 2014-09-03 20:38 UTC (permalink / raw)
  To: b38611, davem, netdev, lznuaa
  Cc: shawn.guo, linux-arm-kernel, devicetree, Frank Li

These patches enable i.MX6SX multi queue support.
i.MX6SX support 3 queue and AVB feature. 

Frank Li (3):
  net:fec: add enet-avb IP support
  ARM: Documentation: Update fec dts binding doc
  ARM: dts: imx6sx: add multi-queue support enet

Fugang Duan (8):
  net:fec: add enet refrence clock for i.MX 6SX chip
  net:fec: add enet AVB feature macro define for imx6sx
  net:fec: use multiqueue interface to allocate Ethernet device
  net:fec: add multiqueue support
  net:fec: Disable enet-avb MAC instead of reset MAC
  net:fec: Add fsl,imx6sx-fec  compatible strings
  net:fec: change FEC alignment to 64 bytes for ARM platform
  net:fec: remove unnessary memory copy for address alignment in .xmit()

 Documentation/devicetree/bindings/net/fsl-fec.txt |   6 +
 arch/arm/boot/dts/imx6sx.dtsi                     |   2 +
 drivers/net/ethernet/freescale/fec.h              | 151 +++-
 drivers/net/ethernet/freescale/fec_main.c         | 877 +++++++++++++++-------
 4 files changed, 750 insertions(+), 286 deletions(-)

-- 
1.9.1

^ permalink raw reply

* [Patch net-next 07/11] net:fec: Add fsl,imx6sx-fec  compatible strings
From: Frank Li @ 2014-09-03 20:38 UTC (permalink / raw)
  To: b38611, davem, netdev, lznuaa
  Cc: shawn.guo, linux-arm-kernel, devicetree, Fugang Duan, Frank Li
In-Reply-To: <1409776697-1536-1-git-send-email-Frank.Li@freescale.com>

From: Fugang Duan <B38611@freescale.com>

Add compatible string "fsl,imx6sx-fec" for i.MX6SX.

Signed-off-by: Fugang Duan <B38611@freescale.com>
Signed-off-by: Frank Li <Frank.Li@freescale.com>
---
 drivers/net/ethernet/freescale/fec_main.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index 6aab64b..a232245 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -157,6 +157,7 @@ enum imx_fec_type {
 	IMX28_FEC,
 	IMX6Q_FEC,
 	MVF600_FEC,
+	IMX6SX_FEC,
 };
 
 static const struct of_device_id fec_dt_ids[] = {
@@ -165,6 +166,7 @@ static const struct of_device_id fec_dt_ids[] = {
 	{ .compatible = "fsl,imx28-fec", .data = &fec_devtype[IMX28_FEC], },
 	{ .compatible = "fsl,imx6q-fec", .data = &fec_devtype[IMX6Q_FEC], },
 	{ .compatible = "fsl,mvf600-fec", .data = &fec_devtype[MVF600_FEC], },
+	{ .compatible = "fsl,imx6sx-fec", .data = &fec_devtype[IMX6SX_FEC], },
 	{ /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, fec_dt_ids);
-- 
1.9.1

^ permalink raw reply related

* [Patch net-next 06/11] net:fec: add enet-avb IP support
From: Frank Li @ 2014-09-03 20:38 UTC (permalink / raw)
  To: b38611, davem, netdev, lznuaa
  Cc: shawn.guo, linux-arm-kernel, devicetree, Frank Li, Fugang Duan
In-Reply-To: <1409776697-1536-1-git-send-email-Frank.Li@freescale.com>

i.MX6SX Enet-AVB support 3 tx queues, 3 rx queues.
For tx queues: ring 0 -> best effort
	       ring 1 -> Class A
	       ring 2 -> Class B
For rx queues:
	       ring 0 -> best effort
	       ring 1 -> receive VLAN packet with classification match
	       ring 2 -> receive VLAN packet with classification match

Add enet-avb IP multiqueue support for the driver.

Signed-off-by: Fugang Duan <B38611@freescale.com>
Signed-off-by: Frank Li <Frank.Li@freescale.com>
---
 drivers/net/ethernet/freescale/fec.h      | 33 +++++++++++++++++++++++++++
 drivers/net/ethernet/freescale/fec_main.c | 38 +++++++++++++++----------------
 2 files changed, 52 insertions(+), 19 deletions(-)

diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h
index 984eaf2..16989bc 100644
--- a/drivers/net/ethernet/freescale/fec.h
+++ b/drivers/net/ethernet/freescale/fec.h
@@ -38,6 +38,12 @@
 #define FEC_ADDR_LOW		0x0e4 /* Low 32bits MAC address */
 #define FEC_ADDR_HIGH		0x0e8 /* High 16bits MAC address */
 #define FEC_OPD			0x0ec /* Opcode + Pause duration */
+#define FEC_TXIC0		0xF0  /* Tx Interrupt Coalescing for ring 0 */
+#define FEC_TXIC1		0xF4  /* Tx Interrupt Coalescing for ring 1 */
+#define FEC_TXIC2		0xF8  /* Tx Interrupt Coalescing for ring 2 */
+#define FEC_RXIC0		0x100 /* Rx Interrupt Coalescing for ring 0 */
+#define FEC_RXIC1		0x104 /* Rx Interrupt Coalescing for ring 1 */
+#define FEC_RXIC2		0x108 /* Rx Interrupt Coalescing for ring 2 */
 #define FEC_HASH_TABLE_HIGH	0x118 /* High 32bits hash table */
 #define FEC_HASH_TABLE_LOW	0x11c /* Low 32bits hash table */
 #define FEC_GRP_HASH_TABLE_HIGH	0x120 /* High 32bits hash table */
@@ -65,6 +71,7 @@
 #define FEC_X_DES_ACTIVE_1	0x1e4 /* Tx descriptor active for ring 1 */
 #define FEC_R_DES_ACTIVE_2	0x1e8 /* Rx descriptor active for ring 2 */
 #define FEC_X_DES_ACTIVE_2	0x1ec /* Tx descriptor active for ring 2 */
+#define FEC_QOS_SCHEME		0x1f0 /* Set multi queues Qos scheme */
 #define FEC_MIIGSK_CFGR		0x300 /* MIIGSK Configuration reg */
 #define FEC_MIIGSK_ENR		0x308 /* MIIGSK Enable reg */
 
@@ -305,6 +312,32 @@ struct bufdesc_ex {
 #define FLAG_RX_CSUM_ENABLED	(BD_ENET_RX_ICE | BD_ENET_RX_PCR)
 #define FLAG_RX_CSUM_ERROR	(BD_ENET_RX_ICE | BD_ENET_RX_PCR)
 
+/* Interrupt events/masks. */
+#define FEC_ENET_HBERR  ((uint)0x80000000)      /* Heartbeat error */
+#define FEC_ENET_BABR   ((uint)0x40000000)      /* Babbling receiver */
+#define FEC_ENET_BABT   ((uint)0x20000000)      /* Babbling transmitter */
+#define FEC_ENET_GRA    ((uint)0x10000000)      /* Graceful stop complete */
+#define FEC_ENET_TXF_0	((uint)0x08000000)	/* Full frame transmitted */
+#define FEC_ENET_TXF_1	((uint)0x00000008)	/* Full frame transmitted */
+#define FEC_ENET_TXF_2	((uint)0x00000080)	/* Full frame transmitted */
+#define FEC_ENET_TXB    ((uint)0x04000000)      /* A buffer was transmitted */
+#define FEC_ENET_RXF_0	((uint)0x02000000)	/* Full frame received */
+#define FEC_ENET_RXF_1	((uint)0x00000002)	/* Full frame received */
+#define FEC_ENET_RXF_2	((uint)0x00000020)	/* Full frame received */
+#define FEC_ENET_RXB    ((uint)0x01000000)      /* A buffer was received */
+#define FEC_ENET_MII    ((uint)0x00800000)      /* MII interrupt */
+#define FEC_ENET_EBERR  ((uint)0x00400000)      /* SDMA bus error */
+#define FEC_ENET_TXF	(FEC_ENET_TXF_0 | FEC_ENET_TXF_1 | FEC_ENET_TXF_2)
+#define FEC_ENET_RXF	(FEC_ENET_RXF_0 | FEC_ENET_RXF_1 | FEC_ENET_RXF_2)
+#define FEC_ENET_TS_AVAIL       ((uint)0x00010000)
+#define FEC_ENET_TS_TIMER       ((uint)0x00008000)
+
+#define FEC_DEFAULT_IMASK (FEC_ENET_TXF | FEC_ENET_RXF | FEC_ENET_MII | FEC_ENET_TS_TIMER)
+#define FEC_RX_DISABLED_IMASK (FEC_DEFAULT_IMASK & (~FEC_ENET_RXF))
+
+#define FEC_VLAN_TAG_LEN       0x04
+#define FEC_ETHTYPE_LEN                0x02
+
 struct fec_enet_priv_tx_q {
 	int index;
 	unsigned char *tx_bounce[TX_RING_SIZE];
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index 5dab6a6..6aab64b 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -193,21 +193,6 @@ MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address");
 #endif
 #endif /* CONFIG_M5272 */
 
-/* Interrupt events/masks. */
-#define FEC_ENET_HBERR	((uint)0x80000000)	/* Heartbeat error */
-#define FEC_ENET_BABR	((uint)0x40000000)	/* Babbling receiver */
-#define FEC_ENET_BABT	((uint)0x20000000)	/* Babbling transmitter */
-#define FEC_ENET_GRA	((uint)0x10000000)	/* Graceful stop complete */
-#define FEC_ENET_TXF	((uint)0x08000000)	/* Full frame transmitted */
-#define FEC_ENET_TXB	((uint)0x04000000)	/* A buffer was transmitted */
-#define FEC_ENET_RXF	((uint)0x02000000)	/* Full frame received */
-#define FEC_ENET_RXB	((uint)0x01000000)	/* A buffer was received */
-#define FEC_ENET_MII	((uint)0x00800000)	/* MII interrupt */
-#define FEC_ENET_EBERR	((uint)0x00400000)	/* SDMA bus error */
-
-#define FEC_DEFAULT_IMASK (FEC_ENET_TXF | FEC_ENET_RXF | FEC_ENET_MII)
-#define FEC_RX_DISABLED_IMASK (FEC_DEFAULT_IMASK & (~FEC_ENET_RXF))
-
 /* The FEC stores dest/src/type/vlan, data, and checksum for receive packets.
  */
 #define PKT_MAXBUF_SIZE		1522
@@ -885,7 +870,16 @@ static void fec_enet_bd_init(struct net_device *dev)
 	}
 }
 
-static inline void fec_enet_enable_ring(struct net_device *ndev)
+static void fec_enet_active_rxring(struct net_device *ndev)
+{
+	struct fec_enet_private *fep = netdev_priv(ndev);
+	int i;
+
+	for (i = 0; i < fep->num_rx_queues; i++)
+		writel(0, fep->hwp + FEC_R_DES_ACTIVE(i));
+}
+
+static void fec_enet_enable_ring(struct net_device *ndev)
 {
 	struct fec_enet_private *fep = netdev_priv(ndev);
 	struct fec_enet_priv_tx_q *tx_queue;
@@ -946,7 +940,6 @@ fec_restart(struct net_device *ndev)
 	u32 temp_mac[2];
 	u32 rcntl = OPT_FRAME_SIZE | 0x04;
 	u32 ecntl = 0x2; /* ETHEREN */
-	int i;
 
 	/* Whack a reset.  We should wait for this.
 	 * For i.MX6SX SOC, enet use AXI bus, we use disable MAC
@@ -1103,8 +1096,7 @@ fec_restart(struct net_device *ndev)
 
 	/* And last, enable the transmit and receive processing */
 	writel(ecntl, fep->hwp + FEC_ECNTRL);
-	for (i = 0; i < fep->num_rx_queues; i++)
-		writel(0, fep->hwp + FEC_R_DES_ACTIVE(i));
+	fec_enet_active_rxring(ndev);
 
 	if (fep->bufdesc_ex)
 		fec_ptp_start_cyclecounter(ndev);
@@ -1515,9 +1507,17 @@ fec_enet_collect_events(struct fec_enet_private *fep, uint int_events)
 
 	if (int_events & FEC_ENET_RXF)
 		fep->work_rx |= (1 << 2);
+	if (int_events & FEC_ENET_RXF_1)
+		fep->work_rx |= (1 << 0);
+	if (int_events & FEC_ENET_RXF_2)
+		fep->work_rx |= (1 << 1);
 
 	if (int_events & FEC_ENET_TXF)
 		fep->work_tx |= (1 << 2);
+	if (int_events & FEC_ENET_TXF_1)
+		fep->work_tx |= (1 << 0);
+	if (int_events & FEC_ENET_TXF_2)
+		fep->work_tx |= (1 << 1);
 
 	return true;
 }
-- 
1.9.1

^ permalink raw reply related

* [Patch net-next 11/11] ARM: dts: imx6sx: add multi-queue support enet
From: Frank Li @ 2014-09-03 20:38 UTC (permalink / raw)
  To: b38611, davem, netdev, lznuaa
  Cc: shawn.guo, linux-arm-kernel, devicetree, Frank Li
In-Reply-To: <1409776697-1536-1-git-send-email-Frank.Li@freescale.com>

Enable 3 queues suppport for ethernet

Signed-off-by: Frank Li <Frank.Li@freescale.com>
---
 arch/arm/boot/dts/imx6sx.dtsi | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/arch/arm/boot/dts/imx6sx.dtsi b/arch/arm/boot/dts/imx6sx.dtsi
index f4b9da6..1722748 100644
--- a/arch/arm/boot/dts/imx6sx.dtsi
+++ b/arch/arm/boot/dts/imx6sx.dtsi
@@ -776,6 +776,8 @@
 					 <&clks IMX6SX_CLK_ENET_PTP>;
 				clock-names = "ipg", "ahb", "ptp",
 					      "enet_clk_ref", "enet_out";
+				fsl,num_tx_queues=<3>;
+				fsl,num_rx_queues=<3>;
 				status = "disabled";
                         };
 
-- 
1.9.1

^ permalink raw reply related

* [Patch net-next 05/11] net:fec: Disable enet-avb MAC instead of reset MAC
From: Frank Li @ 2014-09-03 20:38 UTC (permalink / raw)
  To: b38611, davem, netdev, lznuaa
  Cc: shawn.guo, linux-arm-kernel, devicetree, Fugang Duan, Frank Li
In-Reply-To: <1409776697-1536-1-git-send-email-Frank.Li@freescale.com>

From: Fugang Duan <B38611@freescale.com>

For i.MX6SX enet use AXI bus, reset MAC will make system bus dead
if ENET-AXI bus has pending access (AHB bus should not have such issue).
So, disable enet with AVB MAC instead of reset MAC itself.

Signed-off-by: Fugang Duan <B38611@freescale.com>
Signed-off-by: Frank Li <Frank.Li@freescale.com>
---
 drivers/net/ethernet/freescale/fec_main.c | 26 ++++++++++++++++++++------
 1 file changed, 20 insertions(+), 6 deletions(-)

diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index f973a92..5dab6a6 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -948,9 +948,16 @@ fec_restart(struct net_device *ndev)
 	u32 ecntl = 0x2; /* ETHEREN */
 	int i;
 
-	/* Whack a reset.  We should wait for this. */
-	writel(1, fep->hwp + FEC_ECNTRL);
-	udelay(10);
+	/* Whack a reset.  We should wait for this.
+	 * For i.MX6SX SOC, enet use AXI bus, we use disable MAC
+	 * instead of reset MAC itself.
+	 */
+	if (id_entry && id_entry->driver_data & FEC_QUIRK_HAS_AVB) {
+		writel(0, fep->hwp + FEC_ECNTRL);
+	} else {
+		writel(1, fep->hwp + FEC_ECNTRL);
+		udelay(10);
+	}
 
 	/*
 	 * enet-mac reset will reset mac address registers too,
@@ -1122,9 +1129,16 @@ fec_stop(struct net_device *ndev)
 			netdev_err(ndev, "Graceful transmit stop did not complete!\n");
 	}
 
-	/* Whack a reset.  We should wait for this. */
-	writel(1, fep->hwp + FEC_ECNTRL);
-	udelay(10);
+	/* Whack a reset.  We should wait for this.
+	 * For i.MX6SX SOC, enet use AXI bus, we use disable MAC
+	 * instead of reset MAC itself.
+	 */
+	if (id_entry && id_entry->driver_data & FEC_QUIRK_HAS_AVB) {
+		writel(0, fep->hwp + FEC_ECNTRL);
+	} else {
+		writel(1, fep->hwp + FEC_ECNTRL);
+		udelay(10);
+	}
 	writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
 	writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
 
-- 
1.9.1

^ permalink raw reply related

* [Patch net-next 03/11] net:fec: use multiqueue interface to allocate Ethernet device
From: Frank Li @ 2014-09-03 20:38 UTC (permalink / raw)
  To: b38611, davem, netdev, lznuaa
  Cc: shawn.guo, linux-arm-kernel, devicetree, Fugang Duan, Frank Li
In-Reply-To: <1409776697-1536-1-git-send-email-Frank.Li@freescale.com>

From: Fugang Duan <B38611@freescale.com>

Since i.MX6SX enet-AVB IP support multi queues, so use multi queues
interface to allocate and set up an Ethernet device.

Signed-off-by: Fugang Duan <B38611@freescale.com>
Signed-off-by: Frank Li <Frank.Li@freescale.com>
---
 drivers/net/ethernet/freescale/fec.h      |  9 ++++++
 drivers/net/ethernet/freescale/fec_main.c | 49 ++++++++++++++++++++++++++++++-
 2 files changed, 57 insertions(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h
index 635772b..f77ed6f 100644
--- a/drivers/net/ethernet/freescale/fec.h
+++ b/drivers/net/ethernet/freescale/fec.h
@@ -233,6 +233,13 @@ struct bufdesc_ex {
 /* This device has up to three irqs on some platforms */
 #define FEC_IRQ_NUM		3
 
+/* Maximum number of queues supported
+ * ENET with AVB IP can support up to 3 independent tx queues and rx queues.
+ * User can point the queue number that is less than or equal to 3.
+ */
+#define FEC_ENET_MAX_TX_QS	3
+#define FEC_ENET_MAX_RX_QS	3
+
 /* The number of Tx and Rx buffers.  These are allocated from the page
  * pool.  The code may assume these are power of two, so it it best
  * to keep them that size.
@@ -278,6 +285,8 @@ struct fec_enet_private {
 
 	bool ptp_clk_on;
 	struct mutex ptp_clk_mutex;
+	int num_tx_queues;
+	int num_rx_queues;
 
 	/* The saved address of a sent-in-place packet/buffer, for skfree(). */
 	unsigned char *tx_bounce[TX_RING_SIZE];
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index ee9f04f..00fcadd 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -2573,6 +2573,39 @@ static void fec_reset_phy(struct platform_device *pdev)
 #endif /* CONFIG_OF */
 
 static int
+fec_enet_get_queue_num(struct platform_device *pdev, int *num_tx, int *num_rx)
+{
+	struct device_node *np = pdev->dev.of_node;
+	int err;
+
+	if (!np || !of_device_is_available(np))
+		return -ENODEV;
+
+	/* parse the num of tx and rx queues */
+	err = of_property_read_u32(np, "fsl,num_tx_queues", num_tx);
+	err |= of_property_read_u32(np, "fsl,num_rx_queues", num_rx);
+	if (err) {
+		*num_tx = 1;
+		*num_rx = 1;
+		return 0;
+	}
+
+	if (*num_tx < 1 || *num_tx > FEC_ENET_MAX_TX_QS) {
+		dev_err(&pdev->dev, "num_tx(=%d) greater than MAX_TX_QS(=%d)\n",
+			*num_tx, FEC_ENET_MAX_TX_QS);
+		return -EINVAL;
+	}
+
+	if (*num_rx < 1 || *num_rx > FEC_ENET_MAX_RX_QS) {
+		dev_err(&pdev->dev, "num_rx(=%d) greater than MAX_RX_QS(=%d)\n",
+			*num_rx, FEC_ENET_MAX_RX_QS);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int
 fec_probe(struct platform_device *pdev)
 {
 	struct fec_enet_private *fep;
@@ -2583,13 +2616,23 @@ fec_probe(struct platform_device *pdev)
 	const struct of_device_id *of_id;
 	static int dev_id;
 	struct device_node *np = pdev->dev.of_node, *phy_node;
+	int num_tx_qs = 1;
+	int num_rx_qs = 1;
 
 	of_id = of_match_device(fec_dt_ids, &pdev->dev);
 	if (of_id)
 		pdev->id_entry = of_id->data;
 
+	if (pdev->id_entry &&
+	    (pdev->id_entry->driver_data & FEC_QUIRK_HAS_AVB)) {
+		ret = fec_enet_get_queue_num(pdev, &num_tx_qs, &num_rx_qs);
+		if (ret)
+			return ret;
+	}
+
 	/* Init network device */
-	ndev = alloc_etherdev(sizeof(struct fec_enet_private));
+	ndev = alloc_etherdev_mqs(sizeof(struct fec_enet_private),
+				  num_tx_qs, num_rx_qs);
 	if (!ndev)
 		return -ENOMEM;
 
@@ -2598,6 +2641,10 @@ fec_probe(struct platform_device *pdev)
 	/* setup board info structure */
 	fep = netdev_priv(ndev);
 
+	fep->num_rx_queues = num_rx_qs;
+	fep->num_tx_queues = num_tx_qs;
+	netif_set_real_num_rx_queues(ndev, num_rx_qs);
+
 #if !defined(CONFIG_M5272)
 	/* default enable pause frame auto negotiation */
 	if (pdev->id_entry &&
-- 
1.9.1

^ permalink raw reply related

* [PATCH] iproute2: Refactor: using new nl_msg struct for the all Netlink handlers
From: Vadim Kochan @ 2014-09-03 20:47 UTC (permalink / raw)
  To: netdev; +Cc: Vadim Kochan

It will easy to extend with new parameters in future.

Signed-off-by: Vadim Kochan <vadim4j@gmail.com>
---
 bridge/br_common.h   | 10 +++------
 bridge/fdb.c         |  5 +++--
 bridge/link.c        |  6 +++---
 bridge/mdb.c         |  5 +++--
 bridge/monitor.c     | 12 +++++------
 bridge/vlan.c        |  7 +++---
 genl/ctrl.c          | 11 ++++++----
 genl/genl.c          |  5 ++---
 genl/genl_utils.h    |  2 +-
 include/libnetlink.h | 10 +++++++--
 include/ll_map.h     |  3 +--
 ip/ip_common.h       | 32 +++++++++-------------------
 ip/ipaddress.c       | 57 ++++++++++++++++++++++++++-----------------------
 ip/ipaddrlabel.c     |  8 ++++---
 ip/ipl2tp.c          | 12 +++++------
 ip/iplink.c          |  9 +++++---
 ip/ipmonitor.c       | 26 +++++++++++------------
 ip/ipmroute.c        |  5 +++--
 ip/ipneigh.c         |  5 +++--
 ip/ipnetconf.c       |  5 +++--
 ip/ipntable.c        |  5 +++--
 ip/ipprefix.c        |  5 +++--
 ip/iproute.c         | 31 ++++++++++++++++++---------
 ip/iprule.c          |  8 ++++---
 ip/iptoken.c         |  5 +++--
 ip/rtmon.c           |  6 +++---
 ip/tcp_metrics.c     | 11 ++++++----
 ip/xfrm.h            |  6 ++----
 ip/xfrm_monitor.c    | 60 ++++++++++++++++++++++++++--------------------------
 ip/xfrm_policy.c     | 18 +++++++++-------
 ip/xfrm_state.c      | 23 ++++++++++++--------
 lib/libnetlink.c     | 19 ++++++++++++++---
 lib/ll_map.c         |  4 ++--
 misc/ifstat.c        |  4 ++--
 tc/m_action.c        | 18 ++++++++++------
 tc/tc_class.c        |  6 +++---
 tc/tc_common.h       |  8 +++----
 tc/tc_filter.c       |  7 +++---
 tc/tc_monitor.c      | 14 ++++++------
 tc/tc_qdisc.c        |  7 +++---
 40 files changed, 272 insertions(+), 228 deletions(-)

diff --git a/bridge/br_common.h b/bridge/br_common.h
index 12fce3e..2fd2af8 100644
--- a/bridge/br_common.h
+++ b/bridge/br_common.h
@@ -1,10 +1,6 @@
-extern int print_linkinfo(const struct sockaddr_nl *who,
-			  struct nlmsghdr *n,
-			  void *arg);
-extern int print_fdb(const struct sockaddr_nl *who,
-		     struct nlmsghdr *n, void *arg);
-extern int print_mdb(const struct sockaddr_nl *who,
-		     struct nlmsghdr *n, void *arg);
+extern int print_linkinfo(struct nl_msg *msg);
+extern int print_fdb(struct nl_msg *msg);
+extern int print_mdb(struct nl_msg *msg);
 
 extern int do_fdb(int argc, char **argv);
 extern int do_mdb(int argc, char **argv);
diff --git a/bridge/fdb.c b/bridge/fdb.c
index a55fac1..46b46ef 100644
--- a/bridge/fdb.c
+++ b/bridge/fdb.c
@@ -58,9 +58,10 @@ static const char *state_n2a(unsigned s)
 	return buf;
 }
 
-int print_fdb(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+int print_fdb(struct nl_msg *msg)
 {
-	FILE *fp = arg;
+	struct nlmsghdr *n = msg->n;
+	FILE *fp = (FILE *)(msg->arg);
 	struct ndmsg *r = NLMSG_DATA(n);
 	int len = n->nlmsg_len;
 	struct rtattr * tb[NDA_MAX+1];
diff --git a/bridge/link.c b/bridge/link.c
index 90d9e7f..5f5f8ab 100644
--- a/bridge/link.c
+++ b/bridge/link.c
@@ -96,10 +96,10 @@ static void print_hwmode(FILE *f, __u16 mode)
 		fprintf(f, "hwmode %s ", hw_mode[mode]);
 }
 
-int print_linkinfo(const struct sockaddr_nl *who,
-		   struct nlmsghdr *n, void *arg)
+int print_linkinfo(struct nl_msg *msg)
 {
-	FILE *fp = arg;
+	struct nlmsghdr *n = msg->n;
+	FILE *fp = (FILE *)(msg->arg);
 	int len = n->nlmsg_len;
 	struct ifinfomsg *ifi = NLMSG_DATA(n);
 	struct rtattr * tb[IFLA_MAX+1];
diff --git a/bridge/mdb.c b/bridge/mdb.c
index 6c1c938..6dfe7e8 100644
--- a/bridge/mdb.c
+++ b/bridge/mdb.c
@@ -77,9 +77,10 @@ static void br_print_mdb_entry(FILE *f, int ifindex, struct rtattr *attr)
 	}
 }
 
-int print_mdb(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+int print_mdb(struct nl_msg *msg)
 {
-	FILE *fp = arg;
+	struct nlmsghdr *n = msg->n;
+	FILE *fp = (FILE *)(msg->arg);
 	struct br_port_msg *r = NLMSG_DATA(n);
 	int len = n->nlmsg_len;
 	struct rtattr * tb[MDBA_MAX+1];
diff --git a/bridge/monitor.c b/bridge/monitor.c
index 76e7d47..816cb32 100644
--- a/bridge/monitor.c
+++ b/bridge/monitor.c
@@ -46,10 +46,10 @@ static int show_mark(FILE *fp, const struct nlmsghdr *n)
 	return 0;
 }
 
-static int accept_msg(const struct sockaddr_nl *who,
-		      struct nlmsghdr *n, void *arg)
+static int accept_msg(struct nl_msg *msg)
 {
-	FILE *fp = arg;
+	struct nlmsghdr *n = msg->n;
+	FILE *fp = (FILE *)(msg->arg);
 
 	if (timestamp)
 		print_timestamp(fp);
@@ -60,19 +60,19 @@ static int accept_msg(const struct sockaddr_nl *who,
 		if (prefix_banner)
 			fprintf(fp, "[LINK]");
 
-		return print_linkinfo(who, n, arg);
+		return print_linkinfo(msg);
 
 	case RTM_NEWNEIGH:
 	case RTM_DELNEIGH:
 		if (prefix_banner)
 			fprintf(fp, "[NEIGH]");
-		return print_fdb(who, n, arg);
+		return print_fdb(msg);
 
 	case RTM_NEWMDB:
 	case RTM_DELMDB:
 		if (prefix_banner)
 			fprintf(fp, "[MDB]");
-		return print_mdb(who, n, arg);
+		return print_mdb(msg);
 
 	case 15:
 		return show_mark(fp, n);
diff --git a/bridge/vlan.c b/bridge/vlan.c
index 3bd7b0d..4282b59 100644
--- a/bridge/vlan.c
+++ b/bridge/vlan.c
@@ -101,11 +101,10 @@ static int vlan_modify(int cmd, int argc, char **argv)
 	return 0;
 }
 
-static int print_vlan(const struct sockaddr_nl *who,
-		      struct nlmsghdr *n,
-		      void *arg)
+static int print_vlan(struct nl_msg *msg)
 {
-	FILE *fp = arg;
+	struct nlmsghdr *n = msg->n;
+	FILE *fp = (FILE *)(msg->arg);
 	struct ifinfomsg *ifm = NLMSG_DATA(n);
 	int len = n->nlmsg_len;
 	struct rtattr * tb[IFLA_MAX+1];
diff --git a/genl/ctrl.c b/genl/ctrl.c
index 3546129..0832739 100644
--- a/genl/ctrl.c
+++ b/genl/ctrl.c
@@ -177,14 +177,14 @@ static int print_ctrl_grp(FILE *fp, struct rtattr *arg, __u32 ctrl_ver)
 /*
  * The controller sends one nlmsg per family
 */
-static int print_ctrl(const struct sockaddr_nl *who, struct nlmsghdr *n,
-		      void *arg)
+static int print_ctrl(struct nl_msg *msg)
 {
+	struct nlmsghdr *n = msg->n;
 	struct rtattr *tb[CTRL_ATTR_MAX + 1];
 	struct genlmsghdr *ghdr = NLMSG_DATA(n);
 	int len = n->nlmsg_len;
 	struct rtattr *attrs;
-	FILE *fp = (FILE *) arg;
+	FILE *fp = (FILE *)(msg->arg);
 	__u32 ctrl_v = 0x1;
 
 	if (n->nlmsg_type !=  GENL_ID_CTRL) {
@@ -292,6 +292,7 @@ static int ctrl_list(int cmd, int argc, char **argv)
 		struct nlmsghdr         n;
 		char                    buf[4096];
 	} req;
+	struct nl_msg msg = {};
 
 	memset(&req, 0, sizeof(req));
 
@@ -339,7 +340,9 @@ static int ctrl_list(int cmd, int argc, char **argv)
 			goto ctrl_done;
 		}
 
-		if (print_ctrl(NULL, nlh, (void *) stdout) < 0) {
+		msg.n = nlh;
+		msg.arg = (void *)stdout;
+		if (print_ctrl(&msg) < 0) {
 			fprintf(stderr, "Dump terminated\n");
 			goto ctrl_done;
 		}
diff --git a/genl/genl.c b/genl/genl.c
index 49b6596..ab4afc8 100644
--- a/genl/genl.c
+++ b/genl/genl.c
@@ -36,10 +36,9 @@ static void *BODY;
 static struct genl_util * genl_list;
 
 
-static int print_nofopt(const struct sockaddr_nl *who, struct nlmsghdr *n,
-			void *arg)
+static int print_nofopt(struct nl_msg *msg)
 {
-	fprintf((FILE *) arg, "unknown genl type ..\n");
+	fprintf((FILE *)(msg->arg), "unknown genl type ..\n");
 	return 0;
 }
 
diff --git a/genl/genl_utils.h b/genl/genl_utils.h
index 85b5183..b70b641 100644
--- a/genl/genl_utils.h
+++ b/genl/genl_utils.h
@@ -9,7 +9,7 @@ struct genl_util
 	struct  genl_util *next;
 	char	name[16];
 	int	(*parse_genlopt)(struct genl_util *fu, int argc, char **argv);
-	int	(*print_genlopt)(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg);
+	int	(*print_genlopt)(struct nl_msg *msg);
 };
 
 extern int genl_ctrl_resolve_family(const char *family);
diff --git a/include/libnetlink.h b/include/libnetlink.h
index fe7d5d3..aa47161 100644
--- a/include/libnetlink.h
+++ b/include/libnetlink.h
@@ -39,8 +39,14 @@ extern int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req,
 			     int len)
 	__attribute__((warn_unused_result));
 
-typedef int (*rtnl_filter_t)(const struct sockaddr_nl *,
-			     struct nlmsghdr *n, void *);
+struct nl_msg
+{
+	struct sockaddr_nl *who;
+	struct nlmsghdr *n;
+	void *arg;
+};
+
+typedef int (*rtnl_filter_t)(struct nl_msg *msg);
 
 struct rtnl_dump_filter_arg
 {
diff --git a/include/ll_map.h b/include/ll_map.h
index 4c78498..d3eb0ba 100644
--- a/include/ll_map.h
+++ b/include/ll_map.h
@@ -1,8 +1,7 @@
 #ifndef __LL_MAP_H__
 #define __LL_MAP_H__ 1
 
-extern int ll_remember_index(const struct sockaddr_nl *who,
-			     struct nlmsghdr *n, void *arg);
+extern int ll_remember_index(struct nl_msg *msg);
 
 extern void ll_init_map(struct rtnl_handle *rth);
 extern unsigned ll_name_to_index(const char *name);
diff --git a/ip/ip_common.h b/ip/ip_common.h
index e56d1ac..6a6065e 100644
--- a/ip/ip_common.h
+++ b/ip/ip_common.h
@@ -1,16 +1,9 @@
 extern int get_operstate(const char *name);
-extern int print_linkinfo(const struct sockaddr_nl *who,
-			  struct nlmsghdr *n,
-			  void *arg);
-extern int print_addrinfo(const struct sockaddr_nl *who,
-			  struct nlmsghdr *n,
-			  void *arg);
-extern int print_addrlabel(const struct sockaddr_nl *who,
-			   struct nlmsghdr *n, void *arg);
-extern int print_neigh(const struct sockaddr_nl *who,
-		       struct nlmsghdr *n, void *arg);
-extern int print_ntable(const struct sockaddr_nl *who,
-			struct nlmsghdr *n, void *arg);
+extern int print_linkinfo(struct nl_msg *msg);
+extern int print_addrinfo(struct nl_msg *msg);
+extern int print_addrlabel(struct nl_msg *msg);
+extern int print_neigh(struct nl_msg *msg);
+extern int print_ntable(struct nl_msg *msg);
 extern int ipaddr_list(int argc, char **argv);
 extern int ipaddr_list_link(int argc, char **argv);
 extern int iproute_monitor(int argc, char **argv);
@@ -21,16 +14,11 @@ void ipaddr_get_vf_rate(int, int *, int *, int);
 extern void ipaddr_reset_filter(int);
 extern void ipneigh_reset_filter(void);
 extern void ipntable_reset_filter(void);
-extern int print_route(const struct sockaddr_nl *who,
-		       struct nlmsghdr *n, void *arg);
-extern int print_mroute(const struct sockaddr_nl *who,
-			struct nlmsghdr *n, void *arg);
-extern int print_prefix(const struct sockaddr_nl *who,
-			struct nlmsghdr *n, void *arg);
-extern int print_rule(const struct sockaddr_nl *who,
-		      struct nlmsghdr *n, void *arg);
-extern int print_netconf(const struct sockaddr_nl *who,
-			 struct nlmsghdr *n, void *arg);
+extern int print_route(struct nl_msg *msg);
+extern int print_mroute(struct nl_msg *msg);
+extern int print_prefix(struct nl_msg *msg);
+extern int print_rule(struct nl_msg *msg);
+extern int print_netconf(struct nl_msg *msg);
 extern int do_ipaddr(int argc, char **argv);
 extern int do_ipaddrlabel(int argc, char **argv);
 extern int do_iproute(int argc, char **argv);
diff --git a/ip/ipaddress.c b/ip/ipaddress.c
index 245df39..97a5d20 100644
--- a/ip/ipaddress.c
+++ b/ip/ipaddress.c
@@ -425,10 +425,10 @@ static void print_link_stats(FILE *fp, const struct rtnl_link_stats *s,
 	}
 }
 
-int print_linkinfo(const struct sockaddr_nl *who,
-		   struct nlmsghdr *n, void *arg)
+int print_linkinfo(struct nl_msg *msg)
 {
-	FILE *fp = (FILE*)arg;
+	struct nlmsghdr *n = msg->n;
+	FILE *fp = (FILE*)(msg->arg);
 	struct ifinfomsg *ifi = NLMSG_DATA(n);
 	struct rtattr * tb[IFLA_MAX+1];
 	int len = n->nlmsg_len;
@@ -597,10 +597,10 @@ static unsigned int get_ifa_flags(struct ifaddrmsg *ifa,
 				ifa->ifa_flags;
 }
 
-int print_addrinfo(const struct sockaddr_nl *who, struct nlmsghdr *n,
-		   void *arg)
+int print_addrinfo(struct nl_msg *msg)
 {
-	FILE *fp = arg;
+	struct nlmsghdr *n = msg->n;
+	FILE *fp = (FILE *)(msg->arg);
 	struct ifaddrmsg *ifa = NLMSG_DATA(n);
 	int len = n->nlmsg_len;
 	int deprecated = 0;
@@ -796,26 +796,24 @@ int print_addrinfo(const struct sockaddr_nl *who, struct nlmsghdr *n,
 	return 0;
 }
 
-static int print_addrinfo_primary(const struct sockaddr_nl *who,
-				  struct nlmsghdr *n, void *arg)
+static int print_addrinfo_primary(struct nl_msg *msg)
 {
-	struct ifaddrmsg *ifa = NLMSG_DATA(n);
+	struct ifaddrmsg *ifa = NLMSG_DATA(msg->n);
 
 	if (ifa->ifa_flags & IFA_F_SECONDARY)
 		return 0;
 
-	return print_addrinfo(who, n, arg);
+	return print_addrinfo(msg);
 }
 
-static int print_addrinfo_secondary(const struct sockaddr_nl *who,
-				    struct nlmsghdr *n, void *arg)
+static int print_addrinfo_secondary(struct nl_msg *msg)
 {
-	struct ifaddrmsg *ifa = NLMSG_DATA(n);
+	struct ifaddrmsg *ifa = NLMSG_DATA(msg->n);
 
 	if (!(ifa->ifa_flags & IFA_F_SECONDARY))
 		return 0;
 
-	return print_addrinfo(who, n, arg);
+	return print_addrinfo(msg);
 }
 
 struct nlmsg_list
@@ -835,6 +833,7 @@ static int print_selected_addrinfo(int ifindex, struct nlmsg_list *ainfo, FILE *
 	for ( ;ainfo ;  ainfo = ainfo->next) {
 		struct nlmsghdr *n = &ainfo->h;
 		struct ifaddrmsg *ifa = NLMSG_DATA(n);
+		struct nl_msg msg = {};
 
 		if (n->nlmsg_type != RTM_NEWADDR)
 			continue;
@@ -846,16 +845,18 @@ static int print_selected_addrinfo(int ifindex, struct nlmsg_list *ainfo, FILE *
 		    (filter.family && filter.family != ifa->ifa_family))
 			continue;
 
-		print_addrinfo(NULL, n, fp);
+		msg.n = n;
+		msg.arg = fp;
+		print_addrinfo(&msg);
 	}
 	return 0;
 }
 
 
-static int store_nlmsg(const struct sockaddr_nl *who, struct nlmsghdr *n,
-		       void *arg)
+static int store_nlmsg(struct nl_msg *msg)
 {
-	struct nlmsg_chain *lchain = (struct nlmsg_chain *)arg;
+	struct nlmsghdr *n = msg->n;
+	struct nlmsg_chain *lchain = (struct nlmsg_chain *)(msg->arg);
 	struct nlmsg_list *h;
 
 	h = malloc(n->nlmsg_len+sizeof(void*));
@@ -871,7 +872,7 @@ static int store_nlmsg(const struct sockaddr_nl *who, struct nlmsghdr *n,
 		lchain->head = h;
 	lchain->tail = h;
 
-	ll_remember_index(who, n, NULL);
+	ll_remember_index(msg);
 	return 0;
 }
 
@@ -914,9 +915,9 @@ static int ipadd_dump_check_magic(void)
 	return 0;
 }
 
-static int save_nlmsg(const struct sockaddr_nl *who, struct nlmsghdr *n,
-		       void *arg)
+static int save_nlmsg(struct nl_msg *msg)
 {
+	struct nlmsghdr *n = msg->n;
 	int ret;
 
 	ret = write(STDOUT_FILENO, n, n->nlmsg_len);
@@ -928,12 +929,13 @@ static int save_nlmsg(const struct sockaddr_nl *who, struct nlmsghdr *n,
 	return ret == n->nlmsg_len ? 0 : ret;
 }
 
-static int show_handler(const struct sockaddr_nl *nl, struct nlmsghdr *n, void *arg)
+static int show_handler(struct nl_msg *msg)
 {
-	struct ifaddrmsg *ifa = NLMSG_DATA(n);
+	struct nl_msg nl_addr = {.n = msg->n, .arg = stdout };
+	struct ifaddrmsg *ifa = NLMSG_DATA(msg->n);
 
 	printf("if%d:\n", ifa->ifa_index);
-	print_addrinfo(NULL, n, stdout);
+	print_addrinfo(&nl_addr);
 	return 0;
 }
 
@@ -945,8 +947,9 @@ static int ipaddr_showdump(void)
 	exit(rtnl_from_file(stdin, &show_handler, NULL));
 }
 
-static int restore_handler(const struct sockaddr_nl *nl, struct nlmsghdr *n, void *arg)
+static int restore_handler(struct nl_msg *msg)
 {
+	struct nlmsghdr *n = msg->n;
 	int ret;
 
 	n->nlmsg_flags |= NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK;
@@ -1282,7 +1285,9 @@ static int ipaddr_list_flush_or_save(int argc, char **argv, int action)
 	}
 
 	for (l = linfo.head; l; l = l->next) {
-		if (no_link || print_linkinfo(NULL, &l->h, stdout) == 0) {
+		struct nl_msg msg = { .n = &l->h, .arg = stdout };
+
+		if (no_link || print_linkinfo(&msg) == 0) {
 			struct ifinfomsg *ifi = NLMSG_DATA(&l->h);
 			if (filter.family != AF_PACKET)
 				print_selected_addrinfo(ifi->ifi_index,
diff --git a/ip/ipaddrlabel.c b/ip/ipaddrlabel.c
index b34dd8b..1ecf0cb 100644
--- a/ip/ipaddrlabel.c
+++ b/ip/ipaddrlabel.c
@@ -53,9 +53,10 @@ static void usage(void)
 	exit(-1);
 }
 
-int print_addrlabel(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+int print_addrlabel(struct nl_msg *msg)
 {
-	FILE *fp = (FILE*)arg;
+	struct nlmsghdr *n = msg->n;
+	FILE *fp = (FILE*)(msg->arg);
 	struct ifaddrlblmsg *ifal = NLMSG_DATA(n);
 	int len = n->nlmsg_len;
 	struct rtattr *tb[IFAL_MAX+1];
@@ -189,8 +190,9 @@ static int ipaddrlabel_modify(int cmd, int argc, char **argv)
 }
 
 
-static int flush_addrlabel(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+static int flush_addrlabel(struct nl_msg *msg)
 {
+	struct nlmsghdr *n = msg->n;
 	struct rtnl_handle rth2;
 	struct rtmsg *r = NLMSG_DATA(n);
 	int len = n->nlmsg_len;
diff --git a/ip/ipl2tp.c b/ip/ipl2tp.c
index 5cd8632..23c23e1 100644
--- a/ip/ipl2tp.c
+++ b/ip/ipl2tp.c
@@ -354,12 +354,12 @@ static int get_response(struct nlmsghdr *n, void *arg)
 	return 0;
 }
 
-static int session_nlmsg(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+static int session_nlmsg(struct nl_msg *msg)
 {
-	int ret = get_response(n, arg);
+	int ret = get_response(msg->n, msg->arg);
 
 	if (ret == 0)
-		print_session(arg);
+		print_session(msg->arg);
 
 	return ret;
 }
@@ -388,12 +388,12 @@ static int get_session(struct l2tp_data *p)
 	return 0;
 }
 
-static int tunnel_nlmsg(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+static int tunnel_nlmsg(struct nl_msg *msg)
 {
-	int ret = get_response(n, arg);
+	int ret = get_response(msg->n, msg->arg);
 
 	if (ret == 0)
-		print_tunnel(arg);
+		print_tunnel(msg->arg);
 
 	return ret;
 }
diff --git a/ip/iplink.c b/ip/iplink.c
index 1a907d9..eb516ba 100644
--- a/ip/iplink.c
+++ b/ip/iplink.c
@@ -174,9 +174,9 @@ static int get_addr_gen_mode(const char *mode)
 #if IPLINK_IOCTL_COMPAT
 static int have_rtnl_newlink = -1;
 
-static int accept_msg(const struct sockaddr_nl *who,
-		      struct nlmsghdr *n, void *arg)
+static int accept_msg(struct nl_msg *msg)
 {
+	struct nlmsghdr *n = msg->n;
 	struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(n);
 
 	if (n->nlmsg_type == NLMSG_ERROR &&
@@ -736,6 +736,7 @@ int iplink_get(unsigned int flags, char *name, __u32 filt_mask)
 	int len;
 	struct iplink_req req;
 	char answer[16384];
+	struct nl_msg msg = {};
 
 	memset(&req, 0, sizeof(req));
 
@@ -758,7 +759,9 @@ int iplink_get(unsigned int flags, char *name, __u32 filt_mask)
 	if (rtnl_talk(&rth, &req.n, 0, 0, (struct nlmsghdr *)answer) < 0)
 		return -2;
 
-	print_linkinfo(NULL, (struct nlmsghdr *)answer, stdout);
+	msg.n = (struct nlmsghdr *)answer;
+	msg.arg = (void *)stdout;
+	print_linkinfo(&msg);
 
 	return 0;
 }
diff --git a/ip/ipmonitor.c b/ip/ipmonitor.c
index 70f2a7a..ad9b2d8 100644
--- a/ip/ipmonitor.c
+++ b/ip/ipmonitor.c
@@ -36,10 +36,10 @@ static void usage(void)
 	exit(-1);
 }
 
-static int accept_msg(const struct sockaddr_nl *who,
-		      struct nlmsghdr *n, void *arg)
+static int accept_msg(struct nl_msg *msg)
 {
-	FILE *fp = (FILE*)arg;
+	struct nlmsghdr *n = msg->n;
+	FILE *fp = (FILE *)(msg->arg);
 
 	if (timestamp)
 		print_timestamp(fp);
@@ -57,32 +57,32 @@ static int accept_msg(const struct sockaddr_nl *who,
 		    r->rtm_family == RTNL_FAMILY_IP6MR) {
 			if (prefix_banner)
 				fprintf(fp, "[MROUTE]");
-			print_mroute(who, n, arg);
+			print_mroute(msg);
 			return 0;
 		} else {
 			if (prefix_banner)
 				fprintf(fp, "[ROUTE]");
-			print_route(who, n, arg);
+			print_route(msg);
 			return 0;
 		}
 	}
 	if (n->nlmsg_type == RTM_NEWLINK || n->nlmsg_type == RTM_DELLINK) {
-		ll_remember_index(who, n, NULL);
+		ll_remember_index(msg);
 		if (prefix_banner)
 			fprintf(fp, "[LINK]");
-		print_linkinfo(who, n, arg);
+		print_linkinfo(msg);
 		return 0;
 	}
 	if (n->nlmsg_type == RTM_NEWADDR || n->nlmsg_type == RTM_DELADDR) {
 		if (prefix_banner)
 			fprintf(fp, "[ADDR]");
-		print_addrinfo(who, n, arg);
+		print_addrinfo(msg);
 		return 0;
 	}
 	if (n->nlmsg_type == RTM_NEWADDRLABEL || n->nlmsg_type == RTM_DELADDRLABEL) {
 		if (prefix_banner)
 			fprintf(fp, "[ADDRLABEL]");
-		print_addrlabel(who, n, arg);
+		print_addrlabel(msg);
 		return 0;
 	}
 	if (n->nlmsg_type == RTM_NEWNEIGH || n->nlmsg_type == RTM_DELNEIGH ||
@@ -96,25 +96,25 @@ static int accept_msg(const struct sockaddr_nl *who,
 
 		if (prefix_banner)
 			fprintf(fp, "[NEIGH]");
-		print_neigh(who, n, arg);
+		print_neigh(msg);
 		return 0;
 	}
 	if (n->nlmsg_type == RTM_NEWPREFIX) {
 		if (prefix_banner)
 			fprintf(fp, "[PREFIX]");
-		print_prefix(who, n, arg);
+		print_prefix(msg);
 		return 0;
 	}
 	if (n->nlmsg_type == RTM_NEWRULE || n->nlmsg_type == RTM_DELRULE) {
 		if (prefix_banner)
 			fprintf(fp, "[RULE]");
-		print_rule(who, n, arg);
+		print_rule(msg);
 		return 0;
 	}
 	if (n->nlmsg_type == RTM_NEWNETCONF) {
 		if (prefix_banner)
 			fprintf(fp, "[NETCONF]");
-		print_netconf(who, n, arg);
+		print_netconf(msg);
 		return 0;
 	}
 	if (n->nlmsg_type == 15) {
diff --git a/ip/ipmroute.c b/ip/ipmroute.c
index be93a98..4880661 100644
--- a/ip/ipmroute.c
+++ b/ip/ipmroute.c
@@ -53,9 +53,10 @@ struct rtfilter
 	inet_prefix msrc;
 } filter;
 
-int print_mroute(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+int print_mroute(struct nl_msg *msg)
 {
-	FILE *fp = (FILE*)arg;
+	struct nlmsghdr *n = msg->n;
+	FILE *fp = (FILE*)(msg->arg);
 	struct rtmsg *r = NLMSG_DATA(n);
 	int len = n->nlmsg_len;
 	struct rtattr * tb[RTA_MAX+1];
diff --git a/ip/ipneigh.c b/ip/ipneigh.c
index 71a4100..95d6cce 100644
--- a/ip/ipneigh.c
+++ b/ip/ipneigh.c
@@ -181,9 +181,10 @@ static int ipneigh_modify(int cmd, int flags, int argc, char **argv)
 }
 
 
-int print_neigh(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+int print_neigh(struct nl_msg *msg)
 {
-	FILE *fp = (FILE*)arg;
+	struct nlmsghdr *n = msg->n;
+	FILE *fp = (FILE*)(msg->arg);
 	struct ndmsg *r = NLMSG_DATA(n);
 	int len = n->nlmsg_len;
 	struct rtattr * tb[NDA_MAX+1];
diff --git a/ip/ipnetconf.c b/ip/ipnetconf.c
index 0e44cc8..396f8e6 100644
--- a/ip/ipnetconf.c
+++ b/ip/ipnetconf.c
@@ -40,9 +40,10 @@ static void usage(void)
 
 #define NETCONF_RTA(r)	((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct netconfmsg))))
 
-int print_netconf(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+int print_netconf(struct nl_msg *msg)
 {
-	FILE *fp = (FILE*)arg;
+	struct nlmsghdr *n = msg->n;
+	FILE *fp = (FILE*)(msg->arg);
 	struct netconfmsg *ncm = NLMSG_DATA(n);
 	int len = n->nlmsg_len;
 	struct rtattr *tb[NETCONFA_MAX+1];
diff --git a/ip/ipntable.c b/ip/ipntable.c
index ea7ca2d..acbb883 100644
--- a/ip/ipntable.c
+++ b/ip/ipntable.c
@@ -349,9 +349,10 @@ static const char *ntable_strtime_delta(__u32 msec)
 	return str;
 }
 
-int print_ntable(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+int print_ntable(struct nl_msg *msg)
 {
-	FILE *fp = (FILE*)arg;
+	struct nlmsghdr *n = msg->n;
+	FILE *fp = (FILE *)(msg->arg);
 	struct ndtmsg *ndtm = NLMSG_DATA(n);
 	int len = n->nlmsg_len;
 	struct rtattr *tb[NDTA_MAX+1];
diff --git a/ip/ipprefix.c b/ip/ipprefix.c
index 02c0efc..1288f90 100644
--- a/ip/ipprefix.c
+++ b/ip/ipprefix.c
@@ -35,9 +35,10 @@
 #define IF_PREFIX_ONLINK	0x01
 #define IF_PREFIX_AUTOCONF	0x02
 
-int print_prefix(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+int print_prefix(struct nl_msg *msg)
 {
-	FILE *fp = (FILE*)arg;
+	struct nlmsghdr *n = msg->n;
+	FILE *fp = (FILE *)(msg->arg);
 	struct prefixmsg *prefix = NLMSG_DATA(n);
 	int len = n->nlmsg_len;
 	struct rtattr * tb[RTA_MAX+1];
diff --git a/ip/iproute.c b/ip/iproute.c
index d77b1e3..2aaed91 100644
--- a/ip/iproute.c
+++ b/ip/iproute.c
@@ -280,9 +280,10 @@ static int calc_host_len(const struct rtmsg *r)
 		return -1;
 }
 
-int print_route(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+int print_route(struct nl_msg *msg)
 {
-	FILE *fp = (FILE*)arg;
+	struct nlmsghdr *n = msg->n;
+	FILE *fp = (FILE *)(msg->arg);
 	struct rtmsg *r = NLMSG_DATA(n);
 	int len = n->nlmsg_len;
 	struct rtattr * tb[RTA_MAX+1];
@@ -1078,9 +1079,9 @@ static int iproute_flush_cache(void)
 
 static __u32 route_dump_magic = 0x45311224;
 
-static int save_route(const struct sockaddr_nl *who, struct nlmsghdr *n,
-		      void *arg)
+static int save_route(struct nl_msg *p)
 {
+	struct nlmsghdr *n = p->n;
 	int ret;
 	int len = n->nlmsg_len;
 	struct rtmsg *r = NLMSG_DATA(n);
@@ -1382,6 +1383,7 @@ static int iproute_get(int argc, char **argv)
 	int connected = 0;
 	int from_ok = 0;
 	unsigned int mark = 0;
+	struct nl_msg p = {};
 
 	memset(&req, 0, sizeof(req));
 
@@ -1483,12 +1485,15 @@ static int iproute_get(int argc, char **argv)
 	if (rtnl_talk(&rth, &req.n, 0, 0, &req.n) < 0)
 		exit(2);
 
+	p.n = &req.n;
+	p.arg = (void *)stdout;
+
 	if (connected && !from_ok) {
 		struct rtmsg *r = NLMSG_DATA(&req.n);
 		int len = req.n.nlmsg_len;
 		struct rtattr * tb[RTA_MAX+1];
 
-		if (print_route(NULL, &req.n, (void*)stdout) < 0) {
+		if (print_route(&p) < 0) {
 			fprintf(stderr, "An error :-)\n");
 			exit(1);
 		}
@@ -1525,7 +1530,7 @@ static int iproute_get(int argc, char **argv)
 			exit(2);
 	}
 
-	if (print_route(NULL, &req.n, (void*)stdout) < 0) {
+	if (print_route(&p) < 0) {
 		fprintf(stderr, "An error :-)\n");
 		exit(1);
 	}
@@ -1533,9 +1538,9 @@ static int iproute_get(int argc, char **argv)
 	exit(0);
 }
 
-static int restore_handler(const struct sockaddr_nl *nl, struct nlmsghdr *n,
-			   void *arg)
+static int restore_handler(struct nl_msg *p)
 {
+	struct nlmsghdr *n = p->n;
 	int ret;
 
 	n->nlmsg_flags |= NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK;
@@ -1576,9 +1581,15 @@ static int iproute_restore(void)
 	exit(rtnl_from_file(stdin, &restore_handler, NULL));
 }
 
-static int show_handler(const struct sockaddr_nl *nl, struct nlmsghdr *n, void *arg)
+static int show_handler(struct nl_msg *p)
 {
-	print_route(nl, n, stdout);
+	struct nl_msg rt_param = {};
+
+	rt_param.n = p->n;
+       	rt_param.who = p->who;
+       	rt_param.arg = (void *)stdout;
+
+	print_route(&rt_param);
 	return 0;
 }
 
diff --git a/ip/iprule.c b/ip/iprule.c
index 366878e..2d7f0f8 100644
--- a/ip/iprule.c
+++ b/ip/iprule.c
@@ -46,9 +46,10 @@ static void usage(void)
 	exit(-1);
 }
 
-int print_rule(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+int print_rule(struct nl_msg *msg)
 {
-	FILE *fp = (FILE*)arg;
+	struct nlmsghdr *n = msg->n;
+	FILE *fp = (FILE *)(msg->arg);
 	struct rtmsg *r = NLMSG_DATA(n);
 	int len = n->nlmsg_len;
 	int host_len = -1;
@@ -392,8 +393,9 @@ static int iprule_modify(int cmd, int argc, char **argv)
 }
 
 
-static int flush_rule(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+static int flush_rule(struct nl_msg *msg)
 {
+	struct nlmsghdr *n = msg->n;
 	struct rtnl_handle rth2;
 	struct rtmsg *r = NLMSG_DATA(n);
 	int len = n->nlmsg_len;
diff --git a/ip/iptoken.c b/ip/iptoken.c
index 5689c2e..99bdb19 100644
--- a/ip/iptoken.c
+++ b/ip/iptoken.c
@@ -42,9 +42,10 @@ static void usage(void)
 	exit(-1);
 }
 
-static int print_token(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+static int print_token(struct nl_msg *msg)
 {
-	struct rtnl_dump_args *args = arg;
+	struct nlmsghdr *n = msg->n;
+	struct rtnl_dump_args *args = msg->arg;
 	FILE *fp = args->fp;
 	int ifindex = args->ifindex;
 	struct ifinfomsg *ifi = NLMSG_DATA(n);
diff --git a/ip/rtmon.c b/ip/rtmon.c
index 9227eac..3b3cbd8 100644
--- a/ip/rtmon.c
+++ b/ip/rtmon.c
@@ -45,10 +45,10 @@ static void write_stamp(FILE *fp)
 	fwrite((void*)n1, 1, NLMSG_ALIGN(n1->nlmsg_len), fp);
 }
 
-static int dump_msg(const struct sockaddr_nl *who, struct nlmsghdr *n,
-		    void *arg)
+static int dump_msg(struct nl_msg *msg)
 {
-	FILE *fp = (FILE*)arg;
+	struct nlmsghdr *n = msg->n;
+	FILE *fp = (FILE *)(msg->arg);
 	if (!init_phase)
 		write_stamp(fp);
 	fwrite((void*)n, 1, NLMSG_ALIGN(n->nlmsg_len), fp);
diff --git a/ip/tcp_metrics.c b/ip/tcp_metrics.c
index e0f0344..7ba78c1 100644
--- a/ip/tcp_metrics.c
+++ b/ip/tcp_metrics.c
@@ -88,10 +88,10 @@ static int flush_update(void)
 	return 0;
 }
 
-static int process_msg(const struct sockaddr_nl *who, struct nlmsghdr *n,
-		       void *arg)
+static int process_msg(struct nl_msg *msg)
 {
-	FILE *fp = (FILE *) arg;
+	struct nlmsghdr *n = msg->n;
+	FILE *fp = (FILE *)(msg->arg);
 	struct genlmsghdr *ghdr;
 	struct rtattr *attrs[TCP_METRICS_ATTR_MAX + 1], *a;
 	int len = n->nlmsg_len;
@@ -295,6 +295,7 @@ static int tcpm_do_cmd(int cmd, int argc, char **argv)
 	TCPM_REQUEST(req, 1024, TCP_METRICS_CMD_GET, NLM_F_REQUEST);
 	int atype = -1, stype = -1;
 	int ack;
+	struct nl_msg msg = {};
 
 	memset(&f, 0, sizeof(f));
 	f.daddr.bitlen = -1;
@@ -455,7 +456,9 @@ static int tcpm_do_cmd(int cmd, int argc, char **argv)
 	} else if (atype >= 0) {
 		if (rtnl_talk(&grth, &req.n, 0, 0, &req.n) < 0)
 			return -2;
-		if (process_msg(NULL, &req.n, stdout) < 0) {
+		msg.n = &req.n;
+		msg.arg = (void *)stdout;
+		if (process_msg(&msg) < 0) {
 			fprintf(stderr, "Dump terminated\n");
 			exit(1);
 		}
diff --git a/ip/xfrm.h b/ip/xfrm.h
index 773c92e..fabbe4a 100644
--- a/ip/xfrm.h
+++ b/ip/xfrm.h
@@ -107,10 +107,8 @@ struct xfrm_filter {
 
 extern struct xfrm_filter filter;
 
-int xfrm_state_print(const struct sockaddr_nl *who, struct nlmsghdr *n,
-		     void *arg);
-int xfrm_policy_print(const struct sockaddr_nl *who, struct nlmsghdr *n,
-		      void *arg);
+int xfrm_state_print(struct nl_msg *msg);
+int xfrm_policy_print(struct nl_msg *msg);
 int do_xfrm_state(int argc, char **argv);
 int do_xfrm_policy(int argc, char **argv);
 int do_xfrm_monitor(int argc, char **argv);
diff --git a/ip/xfrm_monitor.c b/ip/xfrm_monitor.c
index 79453e4..14d34c1 100644
--- a/ip/xfrm_monitor.c
+++ b/ip/xfrm_monitor.c
@@ -40,10 +40,10 @@ static void usage(void)
 	exit(-1);
 }
 
-static int xfrm_acquire_print(const struct sockaddr_nl *who,
-			      struct nlmsghdr *n, void *arg)
+static int xfrm_acquire_print(struct nl_msg *msg)
 {
-	FILE *fp = (FILE*)arg;
+	struct nlmsghdr *n = msg->n;
+	FILE *fp = (FILE *)(msg->arg);
 	struct xfrm_user_acquire *xacq = NLMSG_DATA(n);
 	int len = n->nlmsg_len;
 	struct rtattr * tb[XFRMA_MAX+1];
@@ -101,10 +101,10 @@ static int xfrm_acquire_print(const struct sockaddr_nl *who,
 	return 0;
 }
 
-static int xfrm_state_flush_print(const struct sockaddr_nl *who,
-				  struct nlmsghdr *n, void *arg)
+static int xfrm_state_flush_print(struct nl_msg *msg)
 {
-	FILE *fp = (FILE*)arg;
+	struct nlmsghdr *n = msg->n;
+	FILE *fp = (FILE *)(msg->arg);
 	struct xfrm_usersa_flush *xsf = NLMSG_DATA(n);
 	int len = n->nlmsg_len;
 	const char *str;
@@ -131,11 +131,11 @@ static int xfrm_state_flush_print(const struct sockaddr_nl *who,
 	return 0;
 }
 
-static int xfrm_policy_flush_print(const struct sockaddr_nl *who,
-				   struct nlmsghdr *n, void *arg)
+static int xfrm_policy_flush_print(struct nl_msg *msg)
 {
+	struct nlmsghdr *n = msg->n;
 	struct rtattr * tb[XFRMA_MAX+1];
-	FILE *fp = (FILE*)arg;
+	FILE *fp = (FILE *)(msg->arg);
 	int len = n->nlmsg_len;
 
 	len -= NLMSG_SPACE(0);
@@ -169,10 +169,10 @@ static int xfrm_policy_flush_print(const struct sockaddr_nl *who,
 	return 0;
 }
 
-static int xfrm_report_print(const struct sockaddr_nl *who,
-			     struct nlmsghdr *n, void *arg)
+static int xfrm_report_print(struct nl_msg *msg)
 {
-	FILE *fp = (FILE*)arg;
+	struct nlmsghdr *n = msg->n;
+	FILE *fp = (FILE *)(msg->arg);
 	struct xfrm_user_report *xrep = NLMSG_DATA(n);
 	int len = n->nlmsg_len;
 	struct rtattr * tb[XFRMA_MAX+1];
@@ -234,15 +234,15 @@ static void xfrm_usersa_print(const struct xfrm_usersa_id *sa_id, __u32 reqid, F
 	fprintf(fp, " SPI 0x%x", ntohl(sa_id->spi));
 }
 
-static int xfrm_ae_print(const struct sockaddr_nl *who,
-			     struct nlmsghdr *n, void *arg)
+static int xfrm_ae_print(struct nl_msg *msg)
 {
-	FILE *fp = (FILE*)arg;
+	struct nlmsghdr *n = msg->n;
+	FILE *fp = (FILE *)(msg->arg);
 	struct xfrm_aevent_id *id = NLMSG_DATA(n);
 	char abuf[256];
 
 	fprintf(fp, "Async event ");
-	xfrm_ae_flags_print(id->flags, arg);
+	xfrm_ae_flags_print(id->flags, msg->arg);
 	fprintf(fp,"\n\t");
 	memset(abuf, '\0', sizeof(abuf));
 	fprintf(fp, "src %s ", rt_addr_n2a(id->sa_id.family, &id->saddr,
@@ -264,10 +264,10 @@ static void xfrm_print_addr(FILE *fp, int family, xfrm_address_t *a)
 	fprintf(fp, "%s", rt_addr_n2a(family, a, buf, sizeof(buf)));
 }
 
-static int xfrm_mapping_print(const struct sockaddr_nl *who,
-			     struct nlmsghdr *n, void *arg)
+static int xfrm_mapping_print(struct nl_msg *msg)
 {
-	FILE *fp = (FILE*)arg;
+	struct nlmsghdr *n = msg->n;
+	FILE *fp = (FILE *)(msg->arg);
 	struct xfrm_user_mapping *map = NLMSG_DATA(n);
 
 	fprintf(fp, "Mapping change ");
@@ -284,10 +284,10 @@ static int xfrm_mapping_print(const struct sockaddr_nl *who,
 	return 0;
 }
 
-static int xfrm_accept_msg(const struct sockaddr_nl *who,
-			   struct nlmsghdr *n, void *arg)
+static int xfrm_accept_msg(struct nl_msg *msg)
 {
-	FILE *fp = (FILE*)arg;
+	struct nlmsghdr *n = msg->n;
+	FILE *fp = (FILE *)(msg->arg);
 
 	if (timestamp)
 		print_timestamp(fp);
@@ -297,31 +297,31 @@ static int xfrm_accept_msg(const struct sockaddr_nl *who,
 	case XFRM_MSG_DELSA:
 	case XFRM_MSG_UPDSA:
 	case XFRM_MSG_EXPIRE:
-		xfrm_state_print(who, n, arg);
+		xfrm_state_print(msg);
 		return 0;
 	case XFRM_MSG_NEWPOLICY:
 	case XFRM_MSG_DELPOLICY:
 	case XFRM_MSG_UPDPOLICY:
 	case XFRM_MSG_POLEXPIRE:
-		xfrm_policy_print(who, n, arg);
+		xfrm_policy_print(msg);
 		return 0;
 	case XFRM_MSG_ACQUIRE:
-		xfrm_acquire_print(who, n, arg);
+		xfrm_acquire_print(msg);
 		return 0;
 	case XFRM_MSG_FLUSHSA:
-		xfrm_state_flush_print(who, n, arg);
+		xfrm_state_flush_print(msg);
 		return 0;
 	case XFRM_MSG_FLUSHPOLICY:
-		xfrm_policy_flush_print(who, n, arg);
+		xfrm_policy_flush_print(msg);
 		return 0;
 	case XFRM_MSG_REPORT:
-		xfrm_report_print(who, n, arg);
+		xfrm_report_print(msg);
 		return 0;
 	case XFRM_MSG_NEWAE:
-		xfrm_ae_print(who, n, arg);
+		xfrm_ae_print(msg);
 		return 0;
 	case XFRM_MSG_MAPPING:
-		xfrm_mapping_print(who, n, arg);
+		xfrm_mapping_print(msg);
 		return 0;
 	default:
 		break;
diff --git a/ip/xfrm_policy.c b/ip/xfrm_policy.c
index 2337d35..f70c6b2 100644
--- a/ip/xfrm_policy.c
+++ b/ip/xfrm_policy.c
@@ -456,16 +456,16 @@ static int xfrm_policy_filter_match(struct xfrm_userpolicy_info *xpinfo,
 	return 1;
 }
 
-int xfrm_policy_print(const struct sockaddr_nl *who, struct nlmsghdr *n,
-		      void *arg)
+int xfrm_policy_print(struct nl_msg *msg)
 {
+	struct nlmsghdr *n = msg->n;
 	struct rtattr * tb[XFRMA_MAX+1];
 	struct rtattr * rta;
 	struct xfrm_userpolicy_info *xpinfo = NULL;
 	struct xfrm_user_polexpire *xpexp = NULL;
 	struct xfrm_userpolicy_id *xpid = NULL;
 	__u8 ptype = XFRM_POLICY_TYPE_MAIN;
-	FILE *fp = (FILE*)arg;
+	FILE *fp = (FILE *)(msg->arg);
 	int len = n->nlmsg_len;
 
 	if (n->nlmsg_type != XFRM_MSG_NEWPOLICY &&
@@ -686,12 +686,15 @@ static int xfrm_policy_get(int argc, char **argv)
 {
 	char buf[NLMSG_BUF_SIZE];
 	struct nlmsghdr *n = (struct nlmsghdr *)buf;
+	struct nl_msg msg = {};
 
 	memset(buf, 0, sizeof(buf));
 
 	xfrm_policy_get_or_delete(argc, argv, 0, n);
 
-	if (xfrm_policy_print(NULL, n, (void*)stdout) < 0) {
+	msg.n = n;
+	msg.arg = (void *)stdout;
+	if (xfrm_policy_print(&msg) < 0) {
 		fprintf(stderr, "An error :-)\n");
 		exit(1);
 	}
@@ -703,11 +706,10 @@ static int xfrm_policy_get(int argc, char **argv)
  * With an existing policy of nlmsg, make new nlmsg for deleting the policy
  * and store it to buffer.
  */
-static int xfrm_policy_keep(const struct sockaddr_nl *who,
-			    struct nlmsghdr *n,
-			    void *arg)
+static int xfrm_policy_keep(struct nl_msg *msg)
 {
-	struct xfrm_buffer *xb = (struct xfrm_buffer *)arg;
+	struct nlmsghdr *n = msg->n;
+	struct xfrm_buffer *xb = (struct xfrm_buffer *)(msg->arg);
 	struct rtnl_handle *rth = xb->rth;
 	struct xfrm_userpolicy_info *xpinfo = NLMSG_DATA(n);
 	int len = n->nlmsg_len;
diff --git a/ip/xfrm_state.c b/ip/xfrm_state.c
index fe7708e..ef4c258 100644
--- a/ip/xfrm_state.c
+++ b/ip/xfrm_state.c
@@ -665,6 +665,7 @@ static int xfrm_state_allocspi(int argc, char **argv)
 	struct xfrm_mark mark = {0, 0};
 	char res_buf[NLMSG_BUF_SIZE];
 	struct nlmsghdr *res_n = (struct nlmsghdr *)res_buf;
+	struct nl_msg msg = {};
 
 	memset(res_buf, 0, sizeof(res_buf));
 
@@ -783,7 +784,9 @@ static int xfrm_state_allocspi(int argc, char **argv)
 	if (rtnl_talk(&rth, &req.n, 0, 0, res_n) < 0)
 		exit(2);
 
-	if (xfrm_state_print(NULL, res_n, (void*)stdout) < 0) {
+	msg.n = res_n;
+	msg.arg = (void *)stdout;
+	if (xfrm_state_print(&msg) < 0) {
 		fprintf(stderr, "An error :-)\n");
 		exit(1);
 	}
@@ -821,10 +824,10 @@ static int xfrm_state_filter_match(struct xfrm_usersa_info *xsinfo)
 	return 1;
 }
 
-int xfrm_state_print(const struct sockaddr_nl *who, struct nlmsghdr *n,
-		     void *arg)
+int xfrm_state_print(struct nl_msg *msg)
 {
-	FILE *fp = (FILE*)arg;
+	struct nlmsghdr *n = msg->n;
+	FILE *fp = (FILE *)(msg->arg);
 	struct rtattr * tb[XFRMA_MAX+1];
 	struct rtattr * rta;
 	struct xfrm_usersa_info *xsinfo = NULL;
@@ -975,13 +978,16 @@ static int xfrm_state_get_or_delete(int argc, char **argv, int delete)
 	} else {
 		char buf[NLMSG_BUF_SIZE];
 		struct nlmsghdr *res_n = (struct nlmsghdr *)buf;
+		struct nl_msg msg = {};
 
 		memset(buf, 0, sizeof(buf));
 
 		if (rtnl_talk(&rth, &req.n, 0, 0, res_n) < 0)
 			exit(2);
 
-		if (xfrm_state_print(NULL, res_n, (void*)stdout) < 0) {
+		msg.n = res_n;
+		msg.arg = (void *)stdout;
+		if (xfrm_state_print(&msg) < 0) {
 			fprintf(stderr, "An error :-)\n");
 			exit(1);
 		}
@@ -996,11 +1002,10 @@ static int xfrm_state_get_or_delete(int argc, char **argv, int delete)
  * With an existing state of nlmsg, make new nlmsg for deleting the state
  * and store it to buffer.
  */
-static int xfrm_state_keep(const struct sockaddr_nl *who,
-			   struct nlmsghdr *n,
-			   void *arg)
+static int xfrm_state_keep(struct nl_msg *msg)
 {
-	struct xfrm_buffer *xb = (struct xfrm_buffer *)arg;
+	struct nlmsghdr *n = msg->n;
+	struct xfrm_buffer *xb = (struct xfrm_buffer *)(msg->arg);
 	struct rtnl_handle *rth = xb->rth;
 	struct xfrm_usersa_info *xsinfo = NLMSG_DATA(n);
 	int len = n->nlmsg_len;
diff --git a/lib/libnetlink.c b/lib/libnetlink.c
index 9e2a795..a156a59 100644
--- a/lib/libnetlink.c
+++ b/lib/libnetlink.c
@@ -220,6 +220,7 @@ int rtnl_dump_filter_l(struct rtnl_handle *rth,
 		}
 
 		for (a = arg; a->filter; a++) {
+			struct nl_msg msg = {};
 			struct nlmsghdr *h = (struct nlmsghdr*)buf;
 			msglen = status;
 
@@ -249,7 +250,11 @@ int rtnl_dump_filter_l(struct rtnl_handle *rth,
 					}
 					return -1;
 				}
-				err = a->filter(&nladdr, h, a->arg1);
+
+				msg.who = &nladdr;
+				msg.n = h;
+				msg.arg = a->arg1;
+				err = a->filter(&msg);
 				if (err < 0)
 					return err;
 
@@ -450,6 +455,7 @@ int rtnl_listen(struct rtnl_handle *rtnl,
 			exit(1);
 		}
 		for (h = (struct nlmsghdr*)buf; status >= sizeof(*h); ) {
+			struct nl_msg m = {};
 			int err;
 			int len = h->nlmsg_len;
 			int l = len - sizeof(*h);
@@ -463,7 +469,10 @@ int rtnl_listen(struct rtnl_handle *rtnl,
 				exit(1);
 			}
 
-			err = handler(&nladdr, h, jarg);
+			m.who = &nladdr;
+			m.n = h;
+			m.arg = jarg;
+			err = handler(&m);
 			if (err < 0)
 				return err;
 
@@ -495,6 +504,7 @@ int rtnl_from_file(FILE *rtnl, rtnl_filter_t handler,
 	nladdr.nl_groups = 0;
 
 	while (1) {
+		struct nl_msg msg = {};
 		int err, len;
 		int l;
 
@@ -529,7 +539,10 @@ int rtnl_from_file(FILE *rtnl, rtnl_filter_t handler,
 			return -1;
 		}
 
-		err = handler(&nladdr, h, jarg);
+		msg.who = &nladdr;
+		msg.n = h;
+		msg.arg = jarg;
+		err = handler(&msg);
 		if (err < 0)
 			return err;
 	}
diff --git a/lib/ll_map.c b/lib/ll_map.c
index db34a2a..5a64219 100644
--- a/lib/ll_map.c
+++ b/lib/ll_map.c
@@ -78,9 +78,9 @@ static struct ll_cache *ll_get_by_name(const char *name)
 	return NULL;
 }
 
-int ll_remember_index(const struct sockaddr_nl *who,
-		      struct nlmsghdr *n, void *arg)
+int ll_remember_index(struct nl_msg *msg)
 {
+	struct nlmsghdr *n = msg->n;
 	unsigned int h;
 	const char *ifname;
 	struct ifinfomsg *ifi = NLMSG_DATA(n);
diff --git a/misc/ifstat.c b/misc/ifstat.c
index a47c046..1eb29d6 100644
--- a/misc/ifstat.c
+++ b/misc/ifstat.c
@@ -105,9 +105,9 @@ static int match(const char *id)
 	return 0;
 }
 
-static int get_nlmsg(const struct sockaddr_nl *who,
-		     struct nlmsghdr *m, void *arg)
+static int get_nlmsg(struct nl_msg *msg)
 {
+	struct nlmsghdr *m = msg->n;
 	struct ifinfomsg *ifi = NLMSG_DATA(m);
 	struct rtattr * tb[IFLA_MAX+1];
 	int len = m->nlmsg_len;
diff --git a/tc/m_action.c b/tc/m_action.c
index 7dbcf5b..94ce569 100644
--- a/tc/m_action.c
+++ b/tc/m_action.c
@@ -319,11 +319,10 @@ tc_print_action(FILE * f, const struct rtattr *arg)
 	return 0;
 }
 
-int print_action(const struct sockaddr_nl *who,
-			   struct nlmsghdr *n,
-			   void *arg)
+int print_action(struct nl_msg *msg)
 {
-	FILE *fp = (FILE*)arg;
+	struct nlmsghdr *n = msg->n;
+	FILE *fp = (FILE *)(msg->arg);
 	struct tcamsg *t = NLMSG_DATA(n);
 	int len = n->nlmsg_len;
 	struct rtattr * tb[TCAA_MAX+1];
@@ -460,9 +459,14 @@ static int tc_action_gd(int cmd, unsigned flags, int *argc_p, char ***argv_p)
 		return 1;
 	}
 
-	if (ans && print_action(NULL, &req.n, (void*)stdout) < 0) {
-		fprintf(stderr, "Dump terminated\n");
-		return 1;
+	if (ans) {
+		struct nl_msg msg = {};
+		msg.n = &req.n;
+		msg.arg = (void *)stdout;
+		if (print_action(&msg) < 0) {
+			fprintf(stderr, "Dump terminated\n");
+			return 1;
+		}
 	}
 
 	*argc_p = argc;
diff --git a/tc/tc_class.c b/tc/tc_class.c
index e56bf07..19a8367 100644
--- a/tc/tc_class.c
+++ b/tc/tc_class.c
@@ -148,10 +148,10 @@ int filter_ifindex;
 __u32 filter_qdisc;
 __u32 filter_classid;
 
-int print_class(const struct sockaddr_nl *who,
-		       struct nlmsghdr *n, void *arg)
+int print_class(struct nl_msg *msg)
 {
-	FILE *fp = (FILE*)arg;
+	struct nlmsghdr *n = msg->n;
+	FILE *fp = (FILE *)(msg->arg);
 	struct tcmsg *t = NLMSG_DATA(n);
 	int len = n->nlmsg_len;
 	struct rtattr * tb[TCA_MAX+1];
diff --git a/tc/tc_common.h b/tc/tc_common.h
index 4f88856..3d75fb6 100644
--- a/tc/tc_common.h
+++ b/tc/tc_common.h
@@ -7,10 +7,10 @@ extern int do_class(int argc, char **argv);
 extern int do_filter(int argc, char **argv);
 extern int do_action(int argc, char **argv);
 extern int do_tcmonitor(int argc, char **argv);
-extern int print_action(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg);
-extern int print_filter(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg);
-extern int print_qdisc(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg);
-extern int print_class(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg);
+extern int print_action(struct nl_msg *msg);
+extern int print_filter(struct nl_msg *msg);
+extern int print_qdisc(struct nl_msg *msg);
+extern int print_class(struct nl_msg *msg);
 extern void print_size_table(FILE *fp, const char *prefix, struct rtattr *rta);
 
 struct tc_estimator;
diff --git a/tc/tc_filter.c b/tc/tc_filter.c
index c3f2d5f..3f0cda8 100644
--- a/tc/tc_filter.c
+++ b/tc/tc_filter.c
@@ -181,11 +181,10 @@ static __u32 filter_prio;
 static __u32 filter_protocol;
 __u16 f_proto = 0;
 
-int print_filter(const struct sockaddr_nl *who,
-			struct nlmsghdr *n,
-			void *arg)
+int print_filter(struct nl_msg *msg)
 {
-	FILE *fp = (FILE*)arg;
+	struct nlmsghdr *n = msg->n;
+	FILE *fp = (FILE *)(msg->arg);
 	struct tcmsg *t = NLMSG_DATA(n);
 	int len = n->nlmsg_len;
 	struct rtattr * tb[TCA_MAX+1];
diff --git a/tc/tc_monitor.c b/tc/tc_monitor.c
index 0efe034..cdf3d6b 100644
--- a/tc/tc_monitor.c
+++ b/tc/tc_monitor.c
@@ -35,26 +35,26 @@ static void usage(void)
 }
 
 
-static int accept_tcmsg(const struct sockaddr_nl *who,
-			struct nlmsghdr *n, void *arg)
+static int accept_tcmsg(struct nl_msg *msg)
 {
-	FILE *fp = (FILE*)arg;
+	struct nlmsghdr *n = msg->n;
+	FILE *fp = (FILE *)(msg->arg);
 
 	if (n->nlmsg_type == RTM_NEWTFILTER || n->nlmsg_type == RTM_DELTFILTER) {
-		print_filter(who, n, arg);
+		print_filter(msg);
 		return 0;
 	}
 	if (n->nlmsg_type == RTM_NEWTCLASS || n->nlmsg_type == RTM_DELTCLASS) {
-		print_class(who, n, arg);
+		print_class(msg);
 		return 0;
 	}
 	if (n->nlmsg_type == RTM_NEWQDISC || n->nlmsg_type == RTM_DELQDISC) {
-		print_qdisc(who, n, arg);
+		print_qdisc(msg);
 		return 0;
 	}
 	if (n->nlmsg_type == RTM_GETACTION || n->nlmsg_type == RTM_NEWACTION ||
 	    n->nlmsg_type == RTM_DELACTION) {
-		print_action(who, n, arg);
+		print_action(msg);
 		return 0;
 	}
 	if (n->nlmsg_type != NLMSG_ERROR && n->nlmsg_type != NLMSG_NOOP &&
diff --git a/tc/tc_qdisc.c b/tc/tc_qdisc.c
index e304858..3754819 100644
--- a/tc/tc_qdisc.c
+++ b/tc/tc_qdisc.c
@@ -195,11 +195,10 @@ static int tc_qdisc_modify(int cmd, unsigned flags, int argc, char **argv)
 
 static int filter_ifindex;
 
-int print_qdisc(const struct sockaddr_nl *who,
-		       struct nlmsghdr *n,
-		       void *arg)
+int print_qdisc(struct nl_msg *msg)
 {
-	FILE *fp = (FILE*)arg;
+	struct nlmsghdr *n = msg->n;
+	FILE *fp = (FILE *)(msg->arg);
 	struct tcmsg *t = NLMSG_DATA(n);
 	int len = n->nlmsg_len;
 	struct rtattr * tb[TCA_MAX+1];
-- 
2.1.0

^ permalink raw reply related

* Re: SCTP research inquiry
From: Vlad Yasevich @ 2014-09-03 20:44 UTC (permalink / raw)
  To: Kostantinos Katsaros, netdev
In-Reply-To: <CAHJk9CcBb5VUs9vibXW1QBxPONcmitFSzA9KPZj46dW+6WLKdQ@mail.gmail.com>

On 09/03/2014 01:07 PM, Kostantinos Katsaros wrote:
> Dear NetDev list,
> 
> I am a researcher from Univ. of Surrey. I know that this list is
> mainly for developers of linux-kernel but I want to ask for some help
> to understand the SCTP implementation and pointers to some aspects.
> 
> What I would actually like to do, is to modify the default SCTP
> operation with respect to primary/secondary interface selection and
> analyse it in simulations with NS-3. However, I haven't found an API
> or some other help and I was advised to contact this list. I know that
> NS-3 is capable of using linux-kernel implementations through the use
> of DCE, but my issue here is in the SCTP implementation.
> 
> 
> There a few questions I have in a basic scenario with two nodes
> connected via 2 interfaces.

Are you looking for user-space API that provide the information you need?
The reason I ask is that all I've seen of dce requires you to have
an posix-like application to run on top of the protocol you want to use.
I don't know if DCE allows you directly call linux kernel functions.

Based on the above answer, I can point you more resources.

-vlad

> 
> - How can I dynamically select which of the two interfaces is primary?
> Which method is responsible or should be called to switch?
> 
> - I guess that SCTP has some variable holding the estimated RTT on
> each path (also used for calculating RTO). Is this accessible and how?
> I would like to use this as a triggering mechanism to the switch
> method above. In principle select the path with the minimum RTT.
> 
> - In a similar way of TCP Westwood (Plus), I would like to estimate
> the available bandwidth and use this as trigger.
> 
> - Finally, I want to monitor the ratio (packets or bytes sent) on each
> of the available paths. How can I implement such mechanism? Do I have
> to implement a 'sniffer'?
> 
> 
> I would appreciate any help, pointers, code snippets.
> 
> 
> Regards,
> 
> Kostas
> --
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

^ permalink raw reply

* [Patch net-next 10/11] ARM: Documentation: Update fec dts binding doc
From: Frank Li @ 2014-09-03 20:38 UTC (permalink / raw)
  To: b38611-KZfg59tc24xl57MIdRCFDg, davem-fT/PcQaiUtIeIZ0/mPfg9Q,
	netdev-u79uwXL29TY76Z2rM5mHXA, lznuaa-Re5JQEeQqe8AvxtiuMwx3w
  Cc: shawn.guo-QSEj5FYQhm4dnm+yROfE0A,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Frank Li, Fugang Duan
In-Reply-To: <1409776697-1536-1-git-send-email-Frank.Li-KZfg59tc24xl57MIdRCFDg@public.gmane.org>

    This patch update fec devicetree binding doc that add Optional
    properties "fsl,num_tx_queues" and "fsl,num_rx_queues".

Signed-off-by: Fugang Duan <B38611-KZfg59tc24xl57MIdRCFDg@public.gmane.org>
Signed-off-by: Frank Li <Frank.Li-KZfg59tc24xl57MIdRCFDg@public.gmane.org>
---
 Documentation/devicetree/bindings/net/fsl-fec.txt | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/Documentation/devicetree/bindings/net/fsl-fec.txt b/Documentation/devicetree/bindings/net/fsl-fec.txt
index 8a2c7b5..4da148d 100644
--- a/Documentation/devicetree/bindings/net/fsl-fec.txt
+++ b/Documentation/devicetree/bindings/net/fsl-fec.txt
@@ -16,6 +16,12 @@ Optional properties:
 - phy-handle : phandle to the PHY device connected to this device.
 - fixed-link : Assume a fixed link. See fixed-link.txt in the same directory.
   Use instead of phy-handle.
+- fsl,num_tx_queues : The property is valid for enet-avb IP, which supports
+  hw multi queues. Should specify the tx queue number, otherwise set tx queue
+  number to 1.
+- fsl,num_rx_queues : The property is valid for enet-avb IP, which supports
+  hw multi queues. Should specify the rx queue number, otherwise set rx queue
+  number to 1.
 
 Optional subnodes:
 - mdio : specifies the mdio bus in the FEC, used as a container for phy nodes
-- 
1.9.1

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

^ permalink raw reply related

* [Patch net-next 09/11] net:fec: remove unnessary memory copy for address alignment in .xmit()
From: Frank Li @ 2014-09-03 20:38 UTC (permalink / raw)
  To: b38611-KZfg59tc24xl57MIdRCFDg, davem-fT/PcQaiUtIeIZ0/mPfg9Q,
	netdev-u79uwXL29TY76Z2rM5mHXA, lznuaa-Re5JQEeQqe8AvxtiuMwx3w
  Cc: shawn.guo-QSEj5FYQhm4dnm+yROfE0A,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Fugang Duan, Frank Li
In-Reply-To: <1409776697-1536-1-git-send-email-Frank.Li-KZfg59tc24xl57MIdRCFDg@public.gmane.org>

From: Fugang Duan <B38611-KZfg59tc24xl57MIdRCFDg@public.gmane.org>

i.MX6SX enet-avb IP support buffer address byte alignment for tx DMA
transfer, so remove the unnessary memory copy for the enet avb IP.

Signed-off-by: Fugang Duan <B38611-KZfg59tc24xl57MIdRCFDg@public.gmane.org>
Signed-off-by: Frank Li <Frank.Li-KZfg59tc24xl57MIdRCFDg@public.gmane.org>
---
 drivers/net/ethernet/freescale/fec_main.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index b388e29f..d09fe16 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -518,8 +518,9 @@ static int fec_enet_txq_submit_skb(struct fec_enet_priv_tx_q *txq,
 
 	queue = skb_get_queue_mapping(skb);
 	index = fec_enet_get_bd_index(txq->tx_bd_base, bdp, fep);
-	if (((unsigned long) bufaddr) & FEC_ALIGNMENT ||
-		id_entry->driver_data & FEC_QUIRK_SWAP_FRAME) {
+	if (!(id_entry->driver_data & FEC_QUIRK_HAS_AVB) &&
+	    ((((unsigned long)bufaddr) & FEC_ALIGNMENT ||
+		id_entry->driver_data & FEC_QUIRK_SWAP_FRAME))) {
 		memcpy(txq->tx_bounce[index], skb->data, buflen);
 		bufaddr = txq->tx_bounce[index];
 
-- 
1.9.1

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

^ permalink raw reply related

* [Patch net-next 08/11] net:fec: change FEC alignment to 64 bytes for ARM platform
From: Frank Li @ 2014-09-03 20:38 UTC (permalink / raw)
  To: b38611-KZfg59tc24xl57MIdRCFDg, davem-fT/PcQaiUtIeIZ0/mPfg9Q,
	netdev-u79uwXL29TY76Z2rM5mHXA, lznuaa-Re5JQEeQqe8AvxtiuMwx3w
  Cc: shawn.guo-QSEj5FYQhm4dnm+yROfE0A,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Fugang Duan, Frank Li
In-Reply-To: <1409776697-1536-1-git-send-email-Frank.Li-KZfg59tc24xl57MIdRCFDg@public.gmane.org>

From: Fugang Duan <B38611-KZfg59tc24xl57MIdRCFDg@public.gmane.org>

Since enet-avb has 64 bytes alignment limitation for rx DMA transfer.
The previous enet IP for ARM platform has 16 bytes alignment for tx
DMA transfer. 64 is the an integral number of 16, so change alignment
to 64 bytes for all ARM platform, which don't impact the performance
of previous platform.

Signed-off-by: Fugang Duan <B38611-KZfg59tc24xl57MIdRCFDg@public.gmane.org>
Signed-off-by: Frank Li <Frank.Li-KZfg59tc24xl57MIdRCFDg@public.gmane.org>
---
 drivers/net/ethernet/freescale/fec_main.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index a232245..b388e29f 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -65,7 +65,7 @@
 static void set_multicast_list(struct net_device *ndev);
 
 #if defined(CONFIG_ARM)
-#define FEC_ALIGNMENT	0xf
+#define FEC_ALIGNMENT	0x3f
 #else
 #define FEC_ALIGNMENT	0x3
 #endif
-- 
1.9.1

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

^ permalink raw reply related

* [Patch net-next 04/11] net:fec: add multiqueue support
From: Frank Li @ 2014-09-03 20:38 UTC (permalink / raw)
  To: b38611-KZfg59tc24xl57MIdRCFDg, davem-fT/PcQaiUtIeIZ0/mPfg9Q,
	netdev-u79uwXL29TY76Z2rM5mHXA, lznuaa-Re5JQEeQqe8AvxtiuMwx3w
  Cc: shawn.guo-QSEj5FYQhm4dnm+yROfE0A,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Fugang Duan, Frank Li
In-Reply-To: <1409776697-1536-1-git-send-email-Frank.Li-KZfg59tc24xl57MIdRCFDg@public.gmane.org>

From: Fugang Duan <B38611-KZfg59tc24xl57MIdRCFDg@public.gmane.org>

Add multiqueue support, which is compatible with previous single queue
driver for FEC/ENET IPs.

By default, the tx/rx queue number is 1, user can config the queue number
at DTS file like this:
	fsl,num_tx_queues=<3>;
	fsl,num_rx_queues=<3>;

Ethernet multiqueue mechanism can improve performance in SMP system.
For single hw queue, multiqueue can balance cpu loading.
For multi hw queues, multiple cores can process network packets in parallel,
and refer the article for the detail advantage for multiqueue:
http://vger.kernel.org/~davem/davem_nyc09.pdf

Signed-off-by: Fugang Duan <B38611-KZfg59tc24xl57MIdRCFDg@public.gmane.org>
Signed-off-by: Frank Li <frank.li-KZfg59tc24xl57MIdRCFDg@public.gmane.org>
---
 drivers/net/ethernet/freescale/fec.h      | 108 ++++-
 drivers/net/ethernet/freescale/fec_main.c | 730 ++++++++++++++++++++----------
 2 files changed, 577 insertions(+), 261 deletions(-)

diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h
index f77ed6f..984eaf2 100644
--- a/drivers/net/ethernet/freescale/fec.h
+++ b/drivers/net/ethernet/freescale/fec.h
@@ -27,8 +27,8 @@
  */
 #define FEC_IEVENT		0x004 /* Interrupt event reg */
 #define FEC_IMASK		0x008 /* Interrupt mask reg */
-#define FEC_R_DES_ACTIVE	0x010 /* Receive descriptor reg */
-#define FEC_X_DES_ACTIVE	0x014 /* Transmit descriptor reg */
+#define FEC_R_DES_ACTIVE_0	0x010 /* Receive descriptor reg */
+#define FEC_X_DES_ACTIVE_0	0x014 /* Transmit descriptor reg */
 #define FEC_ECNTRL		0x024 /* Ethernet control reg */
 #define FEC_MII_DATA		0x040 /* MII manage frame reg */
 #define FEC_MII_SPEED		0x044 /* MII speed control reg */
@@ -45,14 +45,26 @@
 #define FEC_X_WMRK		0x144 /* FIFO transmit water mark */
 #define FEC_R_BOUND		0x14c /* FIFO receive bound reg */
 #define FEC_R_FSTART		0x150 /* FIFO receive start reg */
-#define FEC_R_DES_START		0x180 /* Receive descriptor ring */
-#define FEC_X_DES_START		0x184 /* Transmit descriptor ring */
+#define FEC_R_DES_START_1	0x160 /* Receive descriptor ring 1 */
+#define FEC_X_DES_START_1	0x164 /* Transmit descriptor ring 1 */
+#define FEC_R_DES_START_2	0x16c /* Receive descriptor ring 2 */
+#define FEC_X_DES_START_2	0x170 /* Transmit descriptor ring 2 */
+#define FEC_R_DES_START_0	0x180 /* Receive descriptor ring */
+#define FEC_X_DES_START_0	0x184 /* Transmit descriptor ring */
 #define FEC_R_BUFF_SIZE		0x188 /* Maximum receive buff size */
 #define FEC_R_FIFO_RSFL		0x190 /* Receive FIFO section full threshold */
 #define FEC_R_FIFO_RSEM		0x194 /* Receive FIFO section empty threshold */
 #define FEC_R_FIFO_RAEM		0x198 /* Receive FIFO almost empty threshold */
 #define FEC_R_FIFO_RAFL		0x19c /* Receive FIFO almost full threshold */
 #define FEC_RACC		0x1C4 /* Receive Accelerator function */
+#define FEC_RCMR_1		0x1c8 /* Receive classification match ring 1 */
+#define FEC_RCMR_2		0x1cc /* Receive classification match ring 2 */
+#define FEC_DMA_CFG_1		0x1d8 /* DMA class configuration for ring 1 */
+#define FEC_DMA_CFG_2		0x1dc /* DMA class Configuration for ring 2 */
+#define FEC_R_DES_ACTIVE_1	0x1e0 /* Rx descriptor active for ring 1 */
+#define FEC_X_DES_ACTIVE_1	0x1e4 /* Tx descriptor active for ring 1 */
+#define FEC_R_DES_ACTIVE_2	0x1e8 /* Rx descriptor active for ring 2 */
+#define FEC_X_DES_ACTIVE_2	0x1ec /* Tx descriptor active for ring 2 */
 #define FEC_MIIGSK_CFGR		0x300 /* MIIGSK Configuration reg */
 #define FEC_MIIGSK_ENR		0x308 /* MIIGSK Enable reg */
 
@@ -240,6 +252,36 @@ struct bufdesc_ex {
 #define FEC_ENET_MAX_TX_QS	3
 #define FEC_ENET_MAX_RX_QS	3
 
+#define FEC_R_DES_START(X)	((X == 1) ? FEC_R_DES_START_1 : \
+				((X == 2) ? \
+					FEC_R_DES_START_2 : FEC_R_DES_START_0))
+#define FEC_X_DES_START(X)	((X == 1) ? FEC_X_DES_START_1 : \
+				((X == 2) ? \
+					FEC_X_DES_START_2 : FEC_X_DES_START_0))
+#define FEC_R_DES_ACTIVE(X)	((X == 1) ? FEC_R_DES_ACTIVE_1 : \
+				((X == 2) ? \
+				   FEC_R_DES_ACTIVE_2 : FEC_R_DES_ACTIVE_0))
+#define FEC_X_DES_ACTIVE(X)	((X == 1) ? FEC_X_DES_ACTIVE_1 : \
+				((X == 2) ? \
+				   FEC_X_DES_ACTIVE_2 : FEC_X_DES_ACTIVE_0))
+
+#define FEC_DMA_CFG(X)		((X == 2) ? FEC_DMA_CFG_2 : FEC_DMA_CFG_1)
+
+#define DMA_CLASS_EN		(1 << 16)
+#define FEC_RCMR(X)		((X == 2) ? FEC_RCMR_2 : FEC_RCMR_1)
+#define IDLE_SLOPE_MASK		0xFFFF
+#define IDLE_SLOPE_1		0x200 /* BW fraction: 0.5 */
+#define IDLE_SLOPE_2		0x200 /* BW fraction: 0.5 */
+#define IDLE_SLOPE(X)		((X == 1) ? (IDLE_SLOPE_1 & IDLE_SLOPE_MASK) : \
+				(IDLE_SLOPE_2 & IDLE_SLOPE_MASK))
+#define RCMR_MATCHEN            (0x1 << 16)
+#define RCMR_CMP_CFG(v, n)	((v & 0x7) <<  (n << 2))
+#define RCMR_CMP_1		(RCMR_CMP_CFG(0, 0) | RCMR_CMP_CFG(1, 1) | \
+				RCMR_CMP_CFG(2, 2) | RCMR_CMP_CFG(3, 3))
+#define RCMR_CMP_2		(RCMR_CMP_CFG(4, 0) | RCMR_CMP_CFG(5, 1) | \
+				RCMR_CMP_CFG(6, 2) | RCMR_CMP_CFG(7, 3))
+#define RCMR_CMP(X)		((X == 1) ? RCMR_CMP_1 : RCMR_CMP_2)
+
 /* The number of Tx and Rx buffers.  These are allocated from the page
  * pool.  The code may assume these are power of two, so it it best
  * to keep them that size.
@@ -263,6 +305,35 @@ struct bufdesc_ex {
 #define FLAG_RX_CSUM_ENABLED	(BD_ENET_RX_ICE | BD_ENET_RX_PCR)
 #define FLAG_RX_CSUM_ERROR	(BD_ENET_RX_ICE | BD_ENET_RX_PCR)
 
+struct fec_enet_priv_tx_q {
+	int index;
+	unsigned char *tx_bounce[TX_RING_SIZE];
+	struct  sk_buff *tx_skbuff[TX_RING_SIZE];
+
+	dma_addr_t	bd_dma;
+	struct bufdesc	*tx_bd_base;
+	uint tx_ring_size;
+
+	unsigned short tx_stop_threshold;
+	unsigned short tx_wake_threshold;
+
+	struct bufdesc	*cur_tx;
+	struct bufdesc	*dirty_tx;
+	char *tso_hdrs;
+	dma_addr_t tso_hdrs_dma;
+};
+
+struct fec_enet_priv_rx_q {
+	int index;
+	struct  sk_buff *rx_skbuff[RX_RING_SIZE];
+
+	dma_addr_t	bd_dma;
+	struct bufdesc	*rx_bd_base;
+	uint rx_ring_size;
+
+	struct bufdesc	*cur_rx;
+};
+
 /* The FEC buffer descriptors track the ring buffers.  The rx_bd_base and
  * tx_bd_base always point to the base of the buffer descriptors.  The
  * cur_rx and cur_tx point to the currently available buffer.
@@ -289,29 +360,18 @@ struct fec_enet_private {
 	int num_rx_queues;
 
 	/* The saved address of a sent-in-place packet/buffer, for skfree(). */
-	unsigned char *tx_bounce[TX_RING_SIZE];
-	struct	sk_buff *tx_skbuff[TX_RING_SIZE];
-	struct	sk_buff *rx_skbuff[RX_RING_SIZE];
+	struct fec_enet_priv_tx_q *tx_queue[FEC_ENET_MAX_TX_QS];
+	struct fec_enet_priv_rx_q *rx_queue[FEC_ENET_MAX_RX_QS];
 
-	/* CPM dual port RAM relative addresses */
-	dma_addr_t	bd_dma;
-	/* Address of Rx and Tx buffers */
-	struct bufdesc	*rx_bd_base;
-	struct bufdesc	*tx_bd_base;
-	/* The next free ring entry */
-	struct bufdesc	*cur_rx, *cur_tx;
-	/* The ring entries to be free()ed */
-	struct bufdesc	*dirty_tx;
+	unsigned int total_tx_ring_size;
+	unsigned int total_rx_ring_size;
 
-	unsigned short bufdesc_size;
-	unsigned short tx_ring_size;
-	unsigned short rx_ring_size;
-	unsigned short tx_stop_threshold;
-	unsigned short tx_wake_threshold;
+	unsigned long work_tx;
+	unsigned long work_rx;
+	unsigned long work_ts;
+	unsigned long work_mdio;
 
-	/* Software TSO */
-	char *tso_hdrs;
-	dma_addr_t tso_hdrs_dma;
+	unsigned short bufdesc_size;
 
 	struct	platform_device *pdev;
 
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index 00fcadd..f973a92 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -72,6 +72,8 @@ static void set_multicast_list(struct net_device *ndev);
 
 #define DRIVER_NAME	"fec"
 
+#define FEC_ENET_GET_QUQUE(_x) ((_x == 0) ? 1 : ((_x == 1) ? 2 : 0))
+
 /* Pause frame feild and FIFO threshold */
 #define FEC_ENET_FCE	(1 << 5)
 #define FEC_ENET_RSEM_V	0x84
@@ -258,22 +260,26 @@ MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address");
 static int mii_cnt;
 
 static inline
-struct bufdesc *fec_enet_get_nextdesc(struct bufdesc *bdp, struct fec_enet_private *fep)
+struct bufdesc *fec_enet_get_nextdesc(struct bufdesc *bdp,
+				      struct fec_enet_private *fep,
+				      int queue_id)
 {
 	struct bufdesc *new_bd = bdp + 1;
 	struct bufdesc_ex *ex_new_bd = (struct bufdesc_ex *)bdp + 1;
+	struct fec_enet_priv_tx_q *tx_queue = fep->tx_queue[queue_id];
+	struct fec_enet_priv_rx_q *rx_queue = fep->rx_queue[queue_id];
 	struct bufdesc_ex *ex_base;
 	struct bufdesc *base;
 	int ring_size;
 
-	if (bdp >= fep->tx_bd_base) {
-		base = fep->tx_bd_base;
-		ring_size = fep->tx_ring_size;
-		ex_base = (struct bufdesc_ex *)fep->tx_bd_base;
+	if (bdp >= tx_queue->tx_bd_base) {
+		base = tx_queue->tx_bd_base;
+		ring_size = tx_queue->tx_ring_size;
+		ex_base = (struct bufdesc_ex *)tx_queue->tx_bd_base;
 	} else {
-		base = fep->rx_bd_base;
-		ring_size = fep->rx_ring_size;
-		ex_base = (struct bufdesc_ex *)fep->rx_bd_base;
+		base = rx_queue->rx_bd_base;
+		ring_size = rx_queue->rx_ring_size;
+		ex_base = (struct bufdesc_ex *)rx_queue->rx_bd_base;
 	}
 
 	if (fep->bufdesc_ex)
@@ -285,22 +291,26 @@ struct bufdesc *fec_enet_get_nextdesc(struct bufdesc *bdp, struct fec_enet_priva
 }
 
 static inline
-struct bufdesc *fec_enet_get_prevdesc(struct bufdesc *bdp, struct fec_enet_private *fep)
+struct bufdesc *fec_enet_get_prevdesc(struct bufdesc *bdp,
+				      struct fec_enet_private *fep,
+				      int queue_id)
 {
 	struct bufdesc *new_bd = bdp - 1;
 	struct bufdesc_ex *ex_new_bd = (struct bufdesc_ex *)bdp - 1;
+	struct fec_enet_priv_tx_q *tx_queue = fep->tx_queue[queue_id];
+	struct fec_enet_priv_rx_q *rx_queue = fep->rx_queue[queue_id];
 	struct bufdesc_ex *ex_base;
 	struct bufdesc *base;
 	int ring_size;
 
-	if (bdp >= fep->tx_bd_base) {
-		base = fep->tx_bd_base;
-		ring_size = fep->tx_ring_size;
-		ex_base = (struct bufdesc_ex *)fep->tx_bd_base;
+	if (bdp >= tx_queue->tx_bd_base) {
+		base = tx_queue->tx_bd_base;
+		ring_size = tx_queue->tx_ring_size;
+		ex_base = (struct bufdesc_ex *)tx_queue->tx_bd_base;
 	} else {
-		base = fep->rx_bd_base;
-		ring_size = fep->rx_ring_size;
-		ex_base = (struct bufdesc_ex *)fep->rx_bd_base;
+		base = rx_queue->rx_bd_base;
+		ring_size = rx_queue->rx_ring_size;
+		ex_base = (struct bufdesc_ex *)rx_queue->rx_bd_base;
 	}
 
 	if (fep->bufdesc_ex)
@@ -316,14 +326,15 @@ static int fec_enet_get_bd_index(struct bufdesc *base, struct bufdesc *bdp,
 	return ((const char *)bdp - (const char *)base) / fep->bufdesc_size;
 }
 
-static int fec_enet_get_free_txdesc_num(struct fec_enet_private *fep)
+static int fec_enet_get_free_txdesc_num(struct fec_enet_private *fep,
+					struct fec_enet_priv_tx_q *txq)
 {
 	int entries;
 
-	entries = ((const char *)fep->dirty_tx -
-			(const char *)fep->cur_tx) / fep->bufdesc_size - 1;
+	entries = ((const char *)txq->dirty_tx -
+			(const char *)txq->cur_tx) / fep->bufdesc_size - 1;
 
-	return entries > 0 ? entries : entries + fep->tx_ring_size;
+	return entries > 0 ? entries : entries + txq->tx_ring_size;
 }
 
 static void *swap_buffer(void *bufaddr, int len)
@@ -340,22 +351,30 @@ static void *swap_buffer(void *bufaddr, int len)
 static void fec_dump(struct net_device *ndev)
 {
 	struct fec_enet_private *fep = netdev_priv(ndev);
-	struct bufdesc *bdp = fep->tx_bd_base;
-	unsigned int index = 0;
+	struct bufdesc *bdp;
+	struct fec_enet_priv_tx_q *txq;
+	int index = 0;
+	int i;
 
 	netdev_info(ndev, "TX ring dump\n");
 	pr_info("Nr     SC     addr       len  SKB\n");
 
-	do {
-		pr_info("%3u %c%c 0x%04x 0x%08lx %4u %p\n",
-			index,
-			bdp == fep->cur_tx ? 'S' : ' ',
-			bdp == fep->dirty_tx ? 'H' : ' ',
-			bdp->cbd_sc, bdp->cbd_bufaddr, bdp->cbd_datlen,
-			fep->tx_skbuff[index]);
-		bdp = fec_enet_get_nextdesc(bdp, fep);
-		index++;
-	} while (bdp != fep->tx_bd_base);
+	for (i = 0; i < fep->num_tx_queues; i++) {
+		txq = fep->tx_queue[i];
+		bdp = txq->tx_bd_base;
+		pr_info("Tx Queue %d\n", i);
+
+		do {
+			pr_info("%3u %c%c 0x%04x 0x%08lx %4u %p\n",
+				index,
+				bdp == txq->cur_tx ? 'S' : ' ',
+				bdp == txq->dirty_tx ? 'H' : ' ',
+				bdp->cbd_sc, bdp->cbd_bufaddr, bdp->cbd_datlen,
+				txq->tx_skbuff[index]);
+			bdp = fec_enet_get_nextdesc(bdp, fep, i);
+			index++;
+		} while (bdp != txq->tx_bd_base);
+	}
 }
 
 static inline bool is_ipv4_pkt(struct sk_buff *skb)
@@ -381,14 +400,17 @@ fec_enet_clear_csum(struct sk_buff *skb, struct net_device *ndev)
 }
 
 static int
-fec_enet_txq_submit_frag_skb(struct sk_buff *skb, struct net_device *ndev)
+fec_enet_txq_submit_frag_skb(struct fec_enet_priv_tx_q *txq,
+			     struct sk_buff *skb,
+			     struct net_device *ndev)
 {
 	struct fec_enet_private *fep = netdev_priv(ndev);
 	const struct platform_device_id *id_entry =
 				platform_get_device_id(fep->pdev);
-	struct bufdesc *bdp = fep->cur_tx;
+	struct bufdesc *bdp = txq->cur_tx;
 	struct bufdesc_ex *ebdp;
 	int nr_frags = skb_shinfo(skb)->nr_frags;
+	unsigned short queue = skb_get_queue_mapping(skb);
 	int frag, frag_len;
 	unsigned short status;
 	unsigned int estatus = 0;
@@ -400,7 +422,7 @@ fec_enet_txq_submit_frag_skb(struct sk_buff *skb, struct net_device *ndev)
 
 	for (frag = 0; frag < nr_frags; frag++) {
 		this_frag = &skb_shinfo(skb)->frags[frag];
-		bdp = fec_enet_get_nextdesc(bdp, fep);
+		bdp = fec_enet_get_nextdesc(bdp, fep, queue);
 		ebdp = (struct bufdesc_ex *)bdp;
 
 		status = bdp->cbd_sc;
@@ -428,11 +450,11 @@ fec_enet_txq_submit_frag_skb(struct sk_buff *skb, struct net_device *ndev)
 
 		bufaddr = page_address(this_frag->page.p) + this_frag->page_offset;
 
-		index = fec_enet_get_bd_index(fep->tx_bd_base, bdp, fep);
+		index = fec_enet_get_bd_index(txq->tx_bd_base, bdp, fep);
 		if (((unsigned long) bufaddr) & FEC_ALIGNMENT ||
 			id_entry->driver_data & FEC_QUIRK_SWAP_FRAME) {
-			memcpy(fep->tx_bounce[index], bufaddr, frag_len);
-			bufaddr = fep->tx_bounce[index];
+			memcpy(txq->tx_bounce[index], bufaddr, frag_len);
+			bufaddr = txq->tx_bounce[index];
 
 			if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME)
 				swap_buffer(bufaddr, frag_len);
@@ -452,21 +474,22 @@ fec_enet_txq_submit_frag_skb(struct sk_buff *skb, struct net_device *ndev)
 		bdp->cbd_sc = status;
 	}
 
-	fep->cur_tx = bdp;
+	txq->cur_tx = bdp;
 
 	return 0;
 
 dma_mapping_error:
-	bdp = fep->cur_tx;
+	bdp = txq->cur_tx;
 	for (i = 0; i < frag; i++) {
-		bdp = fec_enet_get_nextdesc(bdp, fep);
+		bdp = fec_enet_get_nextdesc(bdp, fep, queue);
 		dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr,
 				bdp->cbd_datlen, DMA_TO_DEVICE);
 	}
 	return NETDEV_TX_OK;
 }
 
-static int fec_enet_txq_submit_skb(struct sk_buff *skb, struct net_device *ndev)
+static int fec_enet_txq_submit_skb(struct fec_enet_priv_tx_q *txq,
+				   struct sk_buff *skb, struct net_device *ndev)
 {
 	struct fec_enet_private *fep = netdev_priv(ndev);
 	const struct platform_device_id *id_entry =
@@ -477,12 +500,13 @@ static int fec_enet_txq_submit_skb(struct sk_buff *skb, struct net_device *ndev)
 	dma_addr_t addr;
 	unsigned short status;
 	unsigned short buflen;
+	unsigned short queue;
 	unsigned int estatus = 0;
 	unsigned int index;
 	int entries_free;
 	int ret;
 
-	entries_free = fec_enet_get_free_txdesc_num(fep);
+	entries_free = fec_enet_get_free_txdesc_num(fep, txq);
 	if (entries_free < MAX_SKB_FRAGS + 1) {
 		dev_kfree_skb_any(skb);
 		if (net_ratelimit())
@@ -497,7 +521,7 @@ static int fec_enet_txq_submit_skb(struct sk_buff *skb, struct net_device *ndev)
 	}
 
 	/* Fill in a Tx ring entry */
-	bdp = fep->cur_tx;
+	bdp = txq->cur_tx;
 	status = bdp->cbd_sc;
 	status &= ~BD_ENET_TX_STATS;
 
@@ -505,11 +529,12 @@ static int fec_enet_txq_submit_skb(struct sk_buff *skb, struct net_device *ndev)
 	bufaddr = skb->data;
 	buflen = skb_headlen(skb);
 
-	index = fec_enet_get_bd_index(fep->tx_bd_base, bdp, fep);
+	queue = skb_get_queue_mapping(skb);
+	index = fec_enet_get_bd_index(txq->tx_bd_base, bdp, fep);
 	if (((unsigned long) bufaddr) & FEC_ALIGNMENT ||
 		id_entry->driver_data & FEC_QUIRK_SWAP_FRAME) {
-		memcpy(fep->tx_bounce[index], skb->data, buflen);
-		bufaddr = fep->tx_bounce[index];
+		memcpy(txq->tx_bounce[index], skb->data, buflen);
+		bufaddr = txq->tx_bounce[index];
 
 		if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME)
 			swap_buffer(bufaddr, buflen);
@@ -525,7 +550,7 @@ static int fec_enet_txq_submit_skb(struct sk_buff *skb, struct net_device *ndev)
 	}
 
 	if (nr_frags) {
-		ret = fec_enet_txq_submit_frag_skb(skb, ndev);
+		ret = fec_enet_txq_submit_frag_skb(txq, skb, ndev);
 		if (ret)
 			return ret;
 	} else {
@@ -553,10 +578,10 @@ static int fec_enet_txq_submit_skb(struct sk_buff *skb, struct net_device *ndev)
 		ebdp->cbd_esc = estatus;
 	}
 
-	last_bdp = fep->cur_tx;
-	index = fec_enet_get_bd_index(fep->tx_bd_base, last_bdp, fep);
+	last_bdp = txq->cur_tx;
+	index = fec_enet_get_bd_index(txq->tx_bd_base, last_bdp, fep);
 	/* Save skb pointer */
-	fep->tx_skbuff[index] = skb;
+	txq->tx_skbuff[index] = skb;
 
 	bdp->cbd_datlen = buflen;
 	bdp->cbd_bufaddr = addr;
@@ -568,22 +593,23 @@ static int fec_enet_txq_submit_skb(struct sk_buff *skb, struct net_device *ndev)
 	bdp->cbd_sc = status;
 
 	/* If this was the last BD in the ring, start at the beginning again. */
-	bdp = fec_enet_get_nextdesc(last_bdp, fep);
+	bdp = fec_enet_get_nextdesc(last_bdp, fep, queue);
 
 	skb_tx_timestamp(skb);
 
-	fep->cur_tx = bdp;
+	txq->cur_tx = bdp;
 
 	/* Trigger transmission start */
-	writel(0, fep->hwp + FEC_X_DES_ACTIVE);
+	writel(0, fep->hwp + FEC_X_DES_ACTIVE(queue));
 
 	return 0;
 }
 
 static int
-fec_enet_txq_put_data_tso(struct sk_buff *skb, struct net_device *ndev,
-			struct bufdesc *bdp, int index, char *data,
-			int size, bool last_tcp, bool is_last)
+fec_enet_txq_put_data_tso(struct fec_enet_priv_tx_q *txq, struct sk_buff *skb,
+			  struct net_device *ndev,
+			  struct bufdesc *bdp, int index, char *data,
+			  int size, bool last_tcp, bool is_last)
 {
 	struct fec_enet_private *fep = netdev_priv(ndev);
 	const struct platform_device_id *id_entry =
@@ -600,8 +626,8 @@ fec_enet_txq_put_data_tso(struct sk_buff *skb, struct net_device *ndev,
 
 	if (((unsigned long) data) & FEC_ALIGNMENT ||
 		id_entry->driver_data & FEC_QUIRK_SWAP_FRAME) {
-		memcpy(fep->tx_bounce[index], data, size);
-		data = fep->tx_bounce[index];
+		memcpy(txq->tx_bounce[index], data, size);
+		data = txq->tx_bounce[index];
 
 		if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME)
 			swap_buffer(data, size);
@@ -640,8 +666,9 @@ fec_enet_txq_put_data_tso(struct sk_buff *skb, struct net_device *ndev,
 }
 
 static int
-fec_enet_txq_put_hdr_tso(struct sk_buff *skb, struct net_device *ndev,
-			struct bufdesc *bdp, int index)
+fec_enet_txq_put_hdr_tso(struct fec_enet_priv_tx_q *txq,
+			 struct sk_buff *skb, struct net_device *ndev,
+			 struct bufdesc *bdp, int index)
 {
 	struct fec_enet_private *fep = netdev_priv(ndev);
 	const struct platform_device_id *id_entry =
@@ -657,12 +684,12 @@ fec_enet_txq_put_hdr_tso(struct sk_buff *skb, struct net_device *ndev,
 	status &= ~BD_ENET_TX_STATS;
 	status |= (BD_ENET_TX_TC | BD_ENET_TX_READY);
 
-	bufaddr = fep->tso_hdrs + index * TSO_HEADER_SIZE;
-	dmabuf = fep->tso_hdrs_dma + index * TSO_HEADER_SIZE;
+	bufaddr = txq->tso_hdrs + index * TSO_HEADER_SIZE;
+	dmabuf = txq->tso_hdrs_dma + index * TSO_HEADER_SIZE;
 	if (((unsigned long) bufaddr) & FEC_ALIGNMENT ||
 		id_entry->driver_data & FEC_QUIRK_SWAP_FRAME) {
-		memcpy(fep->tx_bounce[index], skb->data, hdr_len);
-		bufaddr = fep->tx_bounce[index];
+		memcpy(txq->tx_bounce[index], skb->data, hdr_len);
+		bufaddr = txq->tx_bounce[index];
 
 		if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME)
 			swap_buffer(bufaddr, hdr_len);
@@ -692,17 +719,20 @@ fec_enet_txq_put_hdr_tso(struct sk_buff *skb, struct net_device *ndev,
 	return 0;
 }
 
-static int fec_enet_txq_submit_tso(struct sk_buff *skb, struct net_device *ndev)
+static int fec_enet_txq_submit_tso(struct fec_enet_priv_tx_q *txq,
+				   struct sk_buff *skb,
+				   struct net_device *ndev)
 {
 	struct fec_enet_private *fep = netdev_priv(ndev);
 	int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
 	int total_len, data_left;
-	struct bufdesc *bdp = fep->cur_tx;
+	struct bufdesc *bdp = txq->cur_tx;
+	unsigned short queue = skb_get_queue_mapping(skb);
 	struct tso_t tso;
 	unsigned int index = 0;
 	int ret;
 
-	if (tso_count_descs(skb) >= fec_enet_get_free_txdesc_num(fep)) {
+	if (tso_count_descs(skb) >= fec_enet_get_free_txdesc_num(fep, txq)) {
 		dev_kfree_skb_any(skb);
 		if (net_ratelimit())
 			netdev_err(ndev, "NOT enough BD for TSO!\n");
@@ -722,14 +752,14 @@ static int fec_enet_txq_submit_tso(struct sk_buff *skb, struct net_device *ndev)
 	while (total_len > 0) {
 		char *hdr;
 
-		index = fec_enet_get_bd_index(fep->tx_bd_base, bdp, fep);
+		index = fec_enet_get_bd_index(txq->tx_bd_base, bdp, fep);
 		data_left = min_t(int, skb_shinfo(skb)->gso_size, total_len);
 		total_len -= data_left;
 
 		/* prepare packet headers: MAC + IP + TCP */
-		hdr = fep->tso_hdrs + index * TSO_HEADER_SIZE;
+		hdr = txq->tso_hdrs + index * TSO_HEADER_SIZE;
 		tso_build_hdr(skb, hdr, &tso, data_left, total_len == 0);
-		ret = fec_enet_txq_put_hdr_tso(skb, ndev, bdp, index);
+		ret = fec_enet_txq_put_hdr_tso(txq, skb, ndev, bdp, index);
 		if (ret)
 			goto err_release;
 
@@ -737,10 +767,13 @@ static int fec_enet_txq_submit_tso(struct sk_buff *skb, struct net_device *ndev)
 			int size;
 
 			size = min_t(int, tso.size, data_left);
-			bdp = fec_enet_get_nextdesc(bdp, fep);
-			index = fec_enet_get_bd_index(fep->tx_bd_base, bdp, fep);
-			ret = fec_enet_txq_put_data_tso(skb, ndev, bdp, index, tso.data,
-							size, size == data_left,
+			bdp = fec_enet_get_nextdesc(bdp, fep, queue);
+			index = fec_enet_get_bd_index(txq->tx_bd_base,
+						      bdp, fep);
+			ret = fec_enet_txq_put_data_tso(txq, skb, ndev,
+							bdp, index,
+							tso.data, size,
+							size == data_left,
 							total_len == 0);
 			if (ret)
 				goto err_release;
@@ -749,17 +782,17 @@ static int fec_enet_txq_submit_tso(struct sk_buff *skb, struct net_device *ndev)
 			tso_build_data(skb, &tso, size);
 		}
 
-		bdp = fec_enet_get_nextdesc(bdp, fep);
+		bdp = fec_enet_get_nextdesc(bdp, fep, queue);
 	}
 
 	/* Save skb pointer */
-	fep->tx_skbuff[index] = skb;
+	txq->tx_skbuff[index] = skb;
 
 	skb_tx_timestamp(skb);
-	fep->cur_tx = bdp;
+	txq->cur_tx = bdp;
 
 	/* Trigger transmission start */
-	writel(0, fep->hwp + FEC_X_DES_ACTIVE);
+	writel(0, fep->hwp + FEC_X_DES_ACTIVE(queue));
 
 	return 0;
 
@@ -773,18 +806,25 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
 {
 	struct fec_enet_private *fep = netdev_priv(ndev);
 	int entries_free;
+	unsigned short queue;
+	struct fec_enet_priv_tx_q *txq;
+	struct netdev_queue *nq;
 	int ret;
 
+	queue = skb_get_queue_mapping(skb);
+	txq = fep->tx_queue[queue];
+	nq = netdev_get_tx_queue(ndev, queue);
+
 	if (skb_is_gso(skb))
-		ret = fec_enet_txq_submit_tso(skb, ndev);
+		ret = fec_enet_txq_submit_tso(txq, skb, ndev);
 	else
-		ret = fec_enet_txq_submit_skb(skb, ndev);
+		ret = fec_enet_txq_submit_skb(txq, skb, ndev);
 	if (ret)
 		return ret;
 
-	entries_free = fec_enet_get_free_txdesc_num(fep);
-	if (entries_free <= fep->tx_stop_threshold)
-		netif_stop_queue(ndev);
+	entries_free = fec_enet_get_free_txdesc_num(fep, txq);
+	if (entries_free <= txq->tx_stop_threshold)
+		netif_tx_stop_queue(nq);
 
 	return NETDEV_TX_OK;
 }
@@ -794,46 +834,101 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
 static void fec_enet_bd_init(struct net_device *dev)
 {
 	struct fec_enet_private *fep = netdev_priv(dev);
+	struct fec_enet_priv_tx_q *tx_queue;
+	struct fec_enet_priv_rx_q *rx_queue;
 	struct bufdesc *bdp;
-	unsigned int i;
+	unsigned int i, j;
 
 	/* Initialize the receive buffer descriptors. */
-	bdp = fep->rx_bd_base;
-	for (i = 0; i < fep->rx_ring_size; i++) {
+	for (i = 0; i < fep->num_rx_queues; i++) {
+		rx_queue = fep->rx_queue[i];
+		bdp = rx_queue->rx_bd_base;
 
-		/* Initialize the BD for every fragment in the page. */
-		if (bdp->cbd_bufaddr)
-			bdp->cbd_sc = BD_ENET_RX_EMPTY;
-		else
-			bdp->cbd_sc = 0;
-		bdp = fec_enet_get_nextdesc(bdp, fep);
-	}
+		for (j = 0; j < rx_queue->rx_ring_size; j++) {
 
-	/* Set the last buffer to wrap */
-	bdp = fec_enet_get_prevdesc(bdp, fep);
-	bdp->cbd_sc |= BD_SC_WRAP;
+			/* Initialize the BD for every fragment in the page. */
+			if (bdp->cbd_bufaddr)
+				bdp->cbd_sc = BD_ENET_RX_EMPTY;
+			else
+				bdp->cbd_sc = 0;
+			bdp = fec_enet_get_nextdesc(bdp, fep, i);
+		}
 
-	fep->cur_rx = fep->rx_bd_base;
+		/* Set the last buffer to wrap */
+		bdp = fec_enet_get_prevdesc(bdp, fep, i);
+		bdp->cbd_sc |= BD_SC_WRAP;
+
+		rx_queue->cur_rx = rx_queue->rx_bd_base;
+	}
 
 	/* ...and the same for transmit */
-	bdp = fep->tx_bd_base;
-	fep->cur_tx = bdp;
-	for (i = 0; i < fep->tx_ring_size; i++) {
-
-		/* Initialize the BD for every fragment in the page. */
-		bdp->cbd_sc = 0;
-		if (fep->tx_skbuff[i]) {
-			dev_kfree_skb_any(fep->tx_skbuff[i]);
-			fep->tx_skbuff[i] = NULL;
+	for (i = 0; i < fep->num_tx_queues; i++) {
+		tx_queue = fep->tx_queue[i];
+		bdp = tx_queue->tx_bd_base;
+		tx_queue->cur_tx = bdp;
+
+		for (j = 0; j < tx_queue->tx_ring_size; j++) {
+			/* Initialize the BD for every fragment in the page. */
+			bdp->cbd_sc = 0;
+			if (tx_queue->tx_skbuff[j]) {
+				dev_kfree_skb_any(tx_queue->tx_skbuff[j]);
+				tx_queue->tx_skbuff[j] = NULL;
+			}
+			bdp->cbd_bufaddr = 0;
+			bdp = fec_enet_get_nextdesc(bdp, fep, i);
 		}
-		bdp->cbd_bufaddr = 0;
-		bdp = fec_enet_get_nextdesc(bdp, fep);
+
+		/* Set the last buffer to wrap */
+		bdp = fec_enet_get_prevdesc(bdp, fep, i);
+		bdp->cbd_sc |= BD_SC_WRAP;
+		tx_queue->dirty_tx = bdp;
+	}
+}
+
+static inline void fec_enet_enable_ring(struct net_device *ndev)
+{
+	struct fec_enet_private *fep = netdev_priv(ndev);
+	struct fec_enet_priv_tx_q *tx_queue;
+	struct fec_enet_priv_rx_q *rx_queue;
+	int i;
+
+	for (i = 0; i < fep->num_rx_queues; i++) {
+		rx_queue = fep->rx_queue[i];
+		writel(rx_queue->bd_dma, fep->hwp + FEC_R_DES_START(i));
+
+		/* enable DMA1/2 */
+		if (i)
+			writel(RCMR_MATCHEN | RCMR_CMP(i),
+			       fep->hwp + FEC_RCMR(i));
 	}
 
-	/* Set the last buffer to wrap */
-	bdp = fec_enet_get_prevdesc(bdp, fep);
-	bdp->cbd_sc |= BD_SC_WRAP;
-	fep->dirty_tx = bdp;
+	for (i = 0; i < fep->num_tx_queues; i++) {
+		tx_queue = fep->tx_queue[i];
+		writel(tx_queue->bd_dma, fep->hwp + FEC_X_DES_START(i));
+
+		/* enable DMA1/2 */
+		if (i)
+			writel(DMA_CLASS_EN | IDLE_SLOPE(i),
+			       fep->hwp + FEC_DMA_CFG(i));
+	}
+}
+
+static void fec_enet_reset_skb(struct net_device *ndev)
+{
+	struct fec_enet_private *fep = netdev_priv(ndev);
+	struct fec_enet_priv_tx_q *tx_queue;
+	int i, j;
+
+	for (i = 0; i < fep->num_tx_queues; i++) {
+		tx_queue = fep->tx_queue[i];
+
+		for (j = 0; j < tx_queue->tx_ring_size; j++) {
+			if (tx_queue->tx_skbuff[j]) {
+				dev_kfree_skb_any(tx_queue->tx_skbuff[j]);
+				tx_queue->tx_skbuff[j] = NULL;
+			}
+		}
+	}
 }
 
 /*
@@ -847,11 +942,11 @@ fec_restart(struct net_device *ndev)
 	struct fec_enet_private *fep = netdev_priv(ndev);
 	const struct platform_device_id *id_entry =
 				platform_get_device_id(fep->pdev);
-	int i;
 	u32 val;
 	u32 temp_mac[2];
 	u32 rcntl = OPT_FRAME_SIZE | 0x04;
 	u32 ecntl = 0x2; /* ETHEREN */
+	int i;
 
 	/* Whack a reset.  We should wait for this. */
 	writel(1, fep->hwp + FEC_ECNTRL);
@@ -876,21 +971,10 @@ fec_restart(struct net_device *ndev)
 	fec_enet_bd_init(ndev);
 
 	/* Set receive and transmit descriptor base. */
-	writel(fep->bd_dma, fep->hwp + FEC_R_DES_START);
-	if (fep->bufdesc_ex)
-		writel((unsigned long)fep->bd_dma + sizeof(struct bufdesc_ex)
-			* fep->rx_ring_size, fep->hwp + FEC_X_DES_START);
-	else
-		writel((unsigned long)fep->bd_dma + sizeof(struct bufdesc)
-			* fep->rx_ring_size,	fep->hwp + FEC_X_DES_START);
-
+	fec_enet_enable_ring(ndev);
 
-	for (i = 0; i <= TX_RING_MOD_MASK; i++) {
-		if (fep->tx_skbuff[i]) {
-			dev_kfree_skb_any(fep->tx_skbuff[i]);
-			fep->tx_skbuff[i] = NULL;
-		}
-	}
+	/* Reset tx SKB buffers. */
+	fec_enet_reset_skb(ndev);
 
 	/* Enable MII mode */
 	if (fep->full_duplex == DUPLEX_FULL) {
@@ -1012,7 +1096,8 @@ fec_restart(struct net_device *ndev)
 
 	/* And last, enable the transmit and receive processing */
 	writel(ecntl, fep->hwp + FEC_ECNTRL);
-	writel(0, fep->hwp + FEC_R_DES_ACTIVE);
+	for (i = 0; i < fep->num_rx_queues; i++)
+		writel(0, fep->hwp + FEC_R_DES_ACTIVE(i));
 
 	if (fep->bufdesc_ex)
 		fec_ptp_start_cyclecounter(ndev);
@@ -1097,37 +1182,45 @@ fec_enet_hwtstamp(struct fec_enet_private *fep, unsigned ts,
 }
 
 static void
-fec_enet_tx(struct net_device *ndev)
+fec_enet_tx_queue(struct net_device *ndev, u16 queue_id)
 {
 	struct	fec_enet_private *fep;
 	struct bufdesc *bdp;
 	unsigned short status;
 	struct	sk_buff	*skb;
+	struct fec_enet_priv_tx_q *txq;
+	struct netdev_queue *nq;
 	int	index = 0;
 	int	entries_free;
 
 	fep = netdev_priv(ndev);
-	bdp = fep->dirty_tx;
 
+	queue_id = FEC_ENET_GET_QUQUE(queue_id);
+
+	txq = fep->tx_queue[queue_id];
 	/* get next bdp of dirty_tx */
-	bdp = fec_enet_get_nextdesc(bdp, fep);
+	nq = netdev_get_tx_queue(ndev, queue_id);
+	bdp = txq->dirty_tx;
+
+	/* get next bdp of dirty_tx */
+	bdp = fec_enet_get_nextdesc(bdp, fep, queue_id);
 
 	while (((status = bdp->cbd_sc) & BD_ENET_TX_READY) == 0) {
 
 		/* current queue is empty */
-		if (bdp == fep->cur_tx)
+		if (bdp == txq->cur_tx)
 			break;
 
-		index = fec_enet_get_bd_index(fep->tx_bd_base, bdp, fep);
+		index = fec_enet_get_bd_index(txq->tx_bd_base, bdp, fep);
 
-		skb = fep->tx_skbuff[index];
-		fep->tx_skbuff[index] = NULL;
-		if (!IS_TSO_HEADER(fep, bdp->cbd_bufaddr))
+		skb = txq->tx_skbuff[index];
+		txq->tx_skbuff[index] = NULL;
+		if (!IS_TSO_HEADER(txq, bdp->cbd_bufaddr))
 			dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr,
 					bdp->cbd_datlen, DMA_TO_DEVICE);
 		bdp->cbd_bufaddr = 0;
 		if (!skb) {
-			bdp = fec_enet_get_nextdesc(bdp, fep);
+			bdp = fec_enet_get_nextdesc(bdp, fep, queue_id);
 			continue;
 		}
 
@@ -1169,23 +1262,37 @@ fec_enet_tx(struct net_device *ndev)
 		/* Free the sk buffer associated with this last transmit */
 		dev_kfree_skb_any(skb);
 
-		fep->dirty_tx = bdp;
+		txq->dirty_tx = bdp;
 
 		/* Update pointer to next buffer descriptor to be transmitted */
-		bdp = fec_enet_get_nextdesc(bdp, fep);
+		bdp = fec_enet_get_nextdesc(bdp, fep, queue_id);
 
 		/* Since we have freed up a buffer, the ring is no longer full
 		 */
 		if (netif_queue_stopped(ndev)) {
-			entries_free = fec_enet_get_free_txdesc_num(fep);
-			if (entries_free >= fep->tx_wake_threshold)
-				netif_wake_queue(ndev);
+			entries_free = fec_enet_get_free_txdesc_num(fep, txq);
+			if (entries_free >= txq->tx_wake_threshold)
+				netif_tx_wake_queue(nq);
 		}
 	}
 
 	/* ERR006538: Keep the transmitter going */
-	if (bdp != fep->cur_tx && readl(fep->hwp + FEC_X_DES_ACTIVE) == 0)
-		writel(0, fep->hwp + FEC_X_DES_ACTIVE);
+	if (bdp != txq->cur_tx &&
+	    readl(fep->hwp + FEC_X_DES_ACTIVE(queue_id)) == 0)
+		writel(0, fep->hwp + FEC_X_DES_ACTIVE(queue_id));
+}
+
+static void
+fec_enet_tx(struct net_device *ndev)
+{
+	struct fec_enet_private *fep = netdev_priv(ndev);
+	u16 queue_id;
+	/* First process class A queue, then Class B and Best Effort queue */
+	for_each_set_bit(queue_id, &fep->work_tx, FEC_ENET_MAX_TX_QS) {
+		clear_bit(queue_id, &fep->work_tx);
+		fec_enet_tx_queue(ndev, queue_id);
+	}
+	return;
 }
 
 /* During a receive, the cur_rx points to the current incoming buffer.
@@ -1194,11 +1301,12 @@ fec_enet_tx(struct net_device *ndev)
  * effectively tossing the packet.
  */
 static int
-fec_enet_rx(struct net_device *ndev, int budget)
+fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id)
 {
 	struct fec_enet_private *fep = netdev_priv(ndev);
 	const struct platform_device_id *id_entry =
 				platform_get_device_id(fep->pdev);
+	struct fec_enet_priv_rx_q *rxq;
 	struct bufdesc *bdp;
 	unsigned short status;
 	struct	sk_buff	*skb;
@@ -1213,11 +1321,13 @@ fec_enet_rx(struct net_device *ndev, int budget)
 #ifdef CONFIG_M532x
 	flush_cache_all();
 #endif
+	queue_id = FEC_ENET_GET_QUQUE(queue_id);
+	rxq = fep->rx_queue[queue_id];
 
 	/* First, grab all of the stats for the incoming packet.
 	 * These get messed up if we get called due to a busy condition.
 	 */
-	bdp = fep->cur_rx;
+	bdp = rxq->cur_rx;
 
 	while (!((status = bdp->cbd_sc) & BD_ENET_RX_EMPTY)) {
 
@@ -1231,7 +1341,6 @@ fec_enet_rx(struct net_device *ndev, int budget)
 		if ((status & BD_ENET_RX_LAST) == 0)
 			netdev_err(ndev, "rcv is not +last\n");
 
-		writel(FEC_ENET_RXF, fep->hwp + FEC_IEVENT);
 
 		/* Check for errors. */
 		if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH | BD_ENET_RX_NO |
@@ -1264,8 +1373,8 @@ fec_enet_rx(struct net_device *ndev, int budget)
 		pkt_len = bdp->cbd_datlen;
 		ndev->stats.rx_bytes += pkt_len;
 
-		index = fec_enet_get_bd_index(fep->rx_bd_base, bdp, fep);
-		data = fep->rx_skbuff[index]->data;
+		index = fec_enet_get_bd_index(rxq->rx_bd_base, bdp, fep);
+		data = rxq->rx_skbuff[index]->data;
 		dma_sync_single_for_cpu(&fep->pdev->dev, bdp->cbd_bufaddr,
 					FEC_ENET_RX_FRSIZE, DMA_FROM_DEVICE);
 
@@ -1280,7 +1389,7 @@ fec_enet_rx(struct net_device *ndev, int budget)
 		/* If this is a VLAN packet remove the VLAN Tag */
 		vlan_packet_rcvd = false;
 		if ((ndev->features & NETIF_F_HW_VLAN_CTAG_RX) &&
-		    fep->bufdesc_ex && (ebdp->cbd_esc & BD_ENET_RX_VLAN)) {
+			fep->bufdesc_ex && (ebdp->cbd_esc & BD_ENET_RX_VLAN)) {
 			/* Push and remove the vlan tag */
 			struct vlan_hdr *vlan_header =
 					(struct vlan_hdr *) (data + ETH_HLEN);
@@ -1308,7 +1417,7 @@ fec_enet_rx(struct net_device *ndev, int budget)
 			skb_copy_to_linear_data(skb, data, (2 * ETH_ALEN));
 			if (vlan_packet_rcvd)
 				payload_offset = (2 * ETH_ALEN) + VLAN_HLEN;
-			skb_copy_to_linear_data_offset(skb, (2 * ETH_ALEN),
+				skb_copy_to_linear_data_offset(skb, (2 * ETH_ALEN),
 						       data + payload_offset,
 						       pkt_len - 4 - (2 * ETH_ALEN));
 
@@ -1357,19 +1466,48 @@ rx_processing_done:
 		}
 
 		/* Update BD pointer to next entry */
-		bdp = fec_enet_get_nextdesc(bdp, fep);
+		bdp = fec_enet_get_nextdesc(bdp, fep, queue_id);
 
 		/* Doing this here will keep the FEC running while we process
 		 * incoming frames.  On a heavily loaded network, we should be
 		 * able to keep up at the expense of system resources.
 		 */
-		writel(0, fep->hwp + FEC_R_DES_ACTIVE);
+		writel(0, fep->hwp + FEC_R_DES_ACTIVE(queue_id));
 	}
-	fep->cur_rx = bdp;
+	rxq->cur_rx = bdp;
+	return pkt_received;
+}
 
+static int
+fec_enet_rx(struct net_device *ndev, int budget)
+{
+	int     pkt_received = 0;
+	u16	queue_id;
+	struct fec_enet_private *fep = netdev_priv(ndev);
+
+	for_each_set_bit(queue_id, &fep->work_rx, FEC_ENET_MAX_RX_QS) {
+		clear_bit(queue_id, &fep->work_rx);
+		pkt_received += fec_enet_rx_queue(ndev,
+					budget - pkt_received, queue_id);
+	}
 	return pkt_received;
 }
 
+static bool
+fec_enet_collect_events(struct fec_enet_private *fep, uint int_events)
+{
+	if (int_events == 0)
+		return false;
+
+	if (int_events & FEC_ENET_RXF)
+		fep->work_rx |= (1 << 2);
+
+	if (int_events & FEC_ENET_TXF)
+		fep->work_tx |= (1 << 2);
+
+	return true;
+}
+
 static irqreturn_t
 fec_enet_interrupt(int irq, void *dev_id)
 {
@@ -1381,6 +1519,7 @@ fec_enet_interrupt(int irq, void *dev_id)
 
 	int_events = readl(fep->hwp + FEC_IEVENT);
 	writel(int_events & ~napi_mask, fep->hwp + FEC_IEVENT);
+	fec_enet_collect_events(fep, int_events);
 
 	if (int_events & napi_mask) {
 		ret = IRQ_HANDLED;
@@ -2129,93 +2268,117 @@ static int fec_enet_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd)
 static void fec_enet_free_buffers(struct net_device *ndev)
 {
 	struct fec_enet_private *fep = netdev_priv(ndev);
-	unsigned int i;
+	struct fec_enet_priv_tx_q *tx_queue;
+	struct fec_enet_priv_rx_q *rx_queue;
+	unsigned int i, j;
 	struct sk_buff *skb;
 	struct bufdesc	*bdp;
 
-	bdp = fep->rx_bd_base;
-	for (i = 0; i < fep->rx_ring_size; i++) {
-		skb = fep->rx_skbuff[i];
-		fep->rx_skbuff[i] = NULL;
-		if (skb) {
-			dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr,
-					FEC_ENET_RX_FRSIZE, DMA_FROM_DEVICE);
-			dev_kfree_skb(skb);
+	for (j = 0; j < fep->num_rx_queues; j++) {
+		rx_queue = fep->rx_queue[j];
+		bdp = rx_queue->rx_bd_base;
+		for (i = 0; i < rx_queue->rx_ring_size; i++) {
+			skb = rx_queue->rx_skbuff[i];
+			rx_queue->rx_skbuff[i] = NULL;
+			if (skb) {
+				dma_unmap_single(&fep->pdev->dev,
+						 bdp->cbd_bufaddr,
+						 FEC_ENET_RX_FRSIZE,
+						 DMA_FROM_DEVICE);
+				dev_kfree_skb(skb);
+			}
+			bdp = fec_enet_get_nextdesc(bdp, fep, j);
 		}
-		bdp = fec_enet_get_nextdesc(bdp, fep);
 	}
 
-	bdp = fep->tx_bd_base;
-	for (i = 0; i < fep->tx_ring_size; i++) {
-		kfree(fep->tx_bounce[i]);
-		fep->tx_bounce[i] = NULL;
-		skb = fep->tx_skbuff[i];
-		fep->tx_skbuff[i] = NULL;
-		dev_kfree_skb(skb);
+	for (j = 0; j < fep->num_tx_queues; j++) {
+		tx_queue = fep->tx_queue[j];
+		bdp = tx_queue->tx_bd_base;
+		for (i = 0; i < tx_queue->tx_ring_size; i++) {
+			kfree(tx_queue->tx_bounce[i]);
+			tx_queue->tx_bounce[i] = NULL;
+			skb = tx_queue->tx_skbuff[i];
+			tx_queue->tx_skbuff[i] = NULL;
+			dev_kfree_skb(skb);
+		}
 	}
 }
 
 static int fec_enet_alloc_buffers(struct net_device *ndev)
 {
 	struct fec_enet_private *fep = netdev_priv(ndev);
-	unsigned int i;
+	struct fec_enet_priv_tx_q *tx_queue;
+	struct fec_enet_priv_rx_q *rx_queue;
+	unsigned int i, j;
 	struct sk_buff *skb;
 	struct bufdesc	*bdp;
 
-	bdp = fep->rx_bd_base;
-	for (i = 0; i < fep->rx_ring_size; i++) {
-		dma_addr_t addr;
+	for (j = 0; j < fep->num_rx_queues; j++) {
+		rx_queue = fep->rx_queue[j];
+		bdp = rx_queue->rx_bd_base;
+		for (i = 0; i < rx_queue->rx_ring_size; i++) {
+			dma_addr_t addr;
+
+			skb = netdev_alloc_skb(ndev, FEC_ENET_RX_FRSIZE);
+			if (!skb)
+				goto err_alloc;
+
+			addr = dma_map_single(&fep->pdev->dev, skb->data,
+					      FEC_ENET_RX_FRSIZE,
+					      DMA_FROM_DEVICE);
+			if (dma_mapping_error(&fep->pdev->dev, addr)) {
+				dev_kfree_skb(skb);
+				if (net_ratelimit())
+					netdev_err(ndev, "Rx DMA memory map failed\n");
+				goto err_alloc;
+			}
 
-		skb = netdev_alloc_skb(ndev, FEC_ENET_RX_FRSIZE);
-		if (!skb)
-			goto err_alloc;
+			rx_queue->rx_skbuff[i] = skb;
+			bdp->cbd_bufaddr = addr;
+			bdp->cbd_sc = BD_ENET_RX_EMPTY;
 
-		addr = dma_map_single(&fep->pdev->dev, skb->data,
-				FEC_ENET_RX_FRSIZE, DMA_FROM_DEVICE);
-		if (dma_mapping_error(&fep->pdev->dev, addr)) {
-			dev_kfree_skb(skb);
-			if (net_ratelimit())
-				netdev_err(ndev, "Rx DMA memory map failed\n");
-			goto err_alloc;
-		}
+			if (fep->bufdesc_ex) {
+				struct bufdesc_ex *ebdp;
 
-		fep->rx_skbuff[i] = skb;
-		bdp->cbd_bufaddr = addr;
-		bdp->cbd_sc = BD_ENET_RX_EMPTY;
+				ebdp = (struct bufdesc_ex *)bdp;
+				ebdp->cbd_esc = BD_ENET_RX_INT;
+			}
 
-		if (fep->bufdesc_ex) {
-			struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
-			ebdp->cbd_esc = BD_ENET_RX_INT;
+			bdp = fec_enet_get_nextdesc(bdp, fep, j);
 		}
 
-		bdp = fec_enet_get_nextdesc(bdp, fep);
+		/* Set the last buffer to wrap. */
+		bdp = fec_enet_get_prevdesc(bdp, fep, j);
+		bdp->cbd_sc |= BD_SC_WRAP;
 	}
 
-	/* Set the last buffer to wrap. */
-	bdp = fec_enet_get_prevdesc(bdp, fep);
-	bdp->cbd_sc |= BD_SC_WRAP;
+	for (j = 0; j < fep->num_tx_queues; j++) {
+		tx_queue = fep->tx_queue[j];
+		bdp = tx_queue->tx_bd_base;
 
-	bdp = fep->tx_bd_base;
-	for (i = 0; i < fep->tx_ring_size; i++) {
-		fep->tx_bounce[i] = kmalloc(FEC_ENET_TX_FRSIZE, GFP_KERNEL);
-		if (!fep->tx_bounce[i])
-			goto err_alloc;
+		for (i = 0; i < tx_queue->tx_ring_size; i++) {
+			tx_queue->tx_bounce[i] = kmalloc(FEC_ENET_TX_FRSIZE,
+							 GFP_KERNEL);
+			if (!tx_queue->tx_bounce[i])
+				goto err_alloc;
 
-		bdp->cbd_sc = 0;
-		bdp->cbd_bufaddr = 0;
+			bdp->cbd_sc = 0;
+			bdp->cbd_bufaddr = 0;
 
-		if (fep->bufdesc_ex) {
-			struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
-			ebdp->cbd_esc = BD_ENET_TX_INT;
-		}
+			if (fep->bufdesc_ex) {
+				struct bufdesc_ex *ebdp;
 
-		bdp = fec_enet_get_nextdesc(bdp, fep);
-	}
+				ebdp = (struct bufdesc_ex *)bdp;
+				ebdp->cbd_esc = BD_ENET_TX_INT;
+			}
 
-	/* Set the last buffer to wrap. */
-	bdp = fec_enet_get_prevdesc(bdp, fep);
-	bdp->cbd_sc |= BD_SC_WRAP;
+			bdp = fec_enet_get_nextdesc(bdp, fep, j);
+		}
 
+		/* Set the last buffer to wrap. */
+		bdp = fec_enet_get_prevdesc(bdp, fep, j);
+		bdp->cbd_sc |= BD_SC_WRAP;
+	}
 	return 0;
 
  err_alloc:
@@ -2252,7 +2415,8 @@ fec_enet_open(struct net_device *ndev)
 	fec_restart(ndev);
 	napi_enable(&fep->napi);
 	phy_start(fep->phy_dev);
-	netif_start_queue(ndev);
+	netif_tx_start_all_queues(ndev);
+
 	return 0;
 }
 
@@ -2426,7 +2590,7 @@ static int fec_set_features(struct net_device *netdev,
 	/* Resume the device after updates */
 	if (netif_running(netdev) && changed & FEATURES_NEED_QUIESCE) {
 		fec_restart(netdev);
-		netif_wake_queue(netdev);
+		netif_tx_wake_all_queues(netdev);
 		netif_tx_unlock_bh(netdev);
 		napi_enable(&fep->napi);
 	}
@@ -2434,10 +2598,17 @@ static int fec_set_features(struct net_device *netdev,
 	return 0;
 }
 
+u16 fec_enet_select_queue(struct net_device *ndev, struct sk_buff *skb,
+			  void *accel_priv, select_queue_fallback_t fallback)
+{
+	return skb_tx_hash(ndev, skb);
+}
+
 static const struct net_device_ops fec_netdev_ops = {
 	.ndo_open		= fec_enet_open,
 	.ndo_stop		= fec_enet_close,
 	.ndo_start_xmit		= fec_enet_start_xmit,
+	.ndo_select_queue       = fec_enet_select_queue,
 	.ndo_set_rx_mode	= set_multicast_list,
 	.ndo_change_mtu		= eth_change_mtu,
 	.ndo_validate_addr	= eth_validate_addr,
@@ -2450,6 +2621,76 @@ static const struct net_device_ops fec_netdev_ops = {
 	.ndo_set_features	= fec_set_features,
 };
 
+static void fec_enet_free_queue(struct net_device *ndev)
+{
+	struct fec_enet_private *fep = netdev_priv(ndev);
+	int i;
+
+	for (i = 0; i < fep->num_tx_queues; i++)
+		if (fep->tx_queue[i] && fep->tx_queue[i]->tso_hdrs)
+			dma_free_coherent(NULL,
+				fep->tx_queue[i]->tx_ring_size * TSO_HEADER_SIZE,
+				fep->tx_queue[i]->tso_hdrs,
+				fep->tx_queue[i]->tso_hdrs_dma);
+
+	for (i = 0; i < fep->num_rx_queues; i++)
+		if (fep->rx_queue[i])
+			kfree(fep->rx_queue[i]);
+
+	for (i = 0; i < fep->num_tx_queues; i++)
+		if (fep->tx_queue[i])
+			kfree(fep->tx_queue[i]);
+}
+
+static int fec_enet_alloc_queue(struct net_device *ndev)
+{
+	struct fec_enet_private *fep = netdev_priv(ndev);
+	int i;
+	int ret = 0;
+	struct fec_enet_priv_tx_q *txq;
+
+	for (i = 0; i < fep->num_tx_queues; i++) {
+		txq = kzalloc(sizeof(*txq), GFP_KERNEL);
+		if (!txq) {
+			ret = -ENOMEM;
+			goto alloc_failed;
+		}
+
+		fep->tx_queue[i] = txq;
+		txq->tx_ring_size = TX_RING_SIZE;
+		fep->total_tx_ring_size += fep->tx_queue[i]->tx_ring_size;
+
+		txq->tx_stop_threshold = FEC_MAX_SKB_DESCS;
+		txq->tx_wake_threshold =
+			(txq->tx_ring_size - txq->tx_stop_threshold) / 2;
+
+		txq->tso_hdrs = dma_alloc_coherent(NULL,
+					txq->tx_ring_size * TSO_HEADER_SIZE,
+					&txq->tso_hdrs_dma,
+					GFP_KERNEL);
+		if (!txq->tso_hdrs) {
+			ret = -ENOMEM;
+			goto alloc_failed;
+		}
+	}
+
+	for (i = 0; i < fep->num_rx_queues; i++) {
+		fep->rx_queue[i] = kzalloc(sizeof(*fep->rx_queue[i]),
+				GFP_KERNEL);
+		if (!fep->rx_queue[i]) {
+			ret = -ENOMEM;
+			goto alloc_failed;
+		}
+
+		fep->rx_queue[i]->rx_ring_size = RX_RING_SIZE;
+		fep->total_rx_ring_size += fep->rx_queue[i]->rx_ring_size;
+	}
+	return ret;
+
+alloc_failed:
+	fec_enet_free_queue(ndev);
+	return ret;
+}
  /*
   * XXX:  We need to clean up on failure exits here.
   *
@@ -2459,37 +2700,60 @@ static int fec_enet_init(struct net_device *ndev)
 	struct fec_enet_private *fep = netdev_priv(ndev);
 	const struct platform_device_id *id_entry =
 				platform_get_device_id(fep->pdev);
+	struct fec_enet_priv_tx_q *tx_queue;
+	struct fec_enet_priv_rx_q *rx_queue;
 	struct bufdesc *cbd_base;
+	dma_addr_t bd_dma;
 	int bd_size;
+	int i;
 
-	/* init the tx & rx ring size */
-	fep->tx_ring_size = TX_RING_SIZE;
-	fep->rx_ring_size = RX_RING_SIZE;
-
-	fep->tx_stop_threshold = FEC_MAX_SKB_DESCS;
-	fep->tx_wake_threshold = (fep->tx_ring_size - fep->tx_stop_threshold) / 2;
+	fec_enet_alloc_queue(ndev);
 
 	if (fep->bufdesc_ex)
 		fep->bufdesc_size = sizeof(struct bufdesc_ex);
 	else
 		fep->bufdesc_size = sizeof(struct bufdesc);
-	bd_size = (fep->tx_ring_size + fep->rx_ring_size) *
+	bd_size = (fep->total_tx_ring_size + fep->total_rx_ring_size) *
 			fep->bufdesc_size;
 
 	/* Allocate memory for buffer descriptors. */
-	cbd_base = dma_alloc_coherent(NULL, bd_size, &fep->bd_dma,
+	cbd_base = dma_alloc_coherent(NULL, bd_size, &bd_dma,
 				      GFP_KERNEL);
 	if (!cbd_base)
 		return -ENOMEM;
 
-	fep->tso_hdrs = dma_alloc_coherent(NULL, fep->tx_ring_size * TSO_HEADER_SIZE,
-						&fep->tso_hdrs_dma, GFP_KERNEL);
-	if (!fep->tso_hdrs) {
-		dma_free_coherent(NULL, bd_size, cbd_base, fep->bd_dma);
-		return -ENOMEM;
+	memset(cbd_base, 0, bd_size);
+
+	for (i = 0; i < fep->num_rx_queues; i++) {
+		rx_queue = fep->rx_queue[i];
+		rx_queue->index = i;
+		rx_queue->rx_bd_base = (struct bufdesc *)cbd_base;
+		rx_queue->bd_dma = bd_dma;
+		if (fep->bufdesc_ex) {
+			bd_dma += sizeof(struct bufdesc_ex) * rx_queue->rx_ring_size;
+			cbd_base = (struct bufdesc *)
+				(((struct bufdesc_ex *)cbd_base) + rx_queue->rx_ring_size);
+		} else {
+			bd_dma += sizeof(struct bufdesc) * rx_queue->rx_ring_size;
+			cbd_base += rx_queue->rx_ring_size;
+		}
+	}
+
+	for (i = 0; i < fep->num_tx_queues; i++) {
+		tx_queue = fep->tx_queue[i];
+		tx_queue->index = i;
+		tx_queue->tx_bd_base = (struct bufdesc *)cbd_base;
+		tx_queue->bd_dma = bd_dma;
+		if (fep->bufdesc_ex) {
+			bd_dma += sizeof(struct bufdesc_ex) * tx_queue->tx_ring_size;
+			cbd_base = (struct bufdesc *)
+			(((struct bufdesc_ex *)cbd_base) + tx_queue->tx_ring_size);
+		} else {
+			bd_dma += sizeof(struct bufdesc) * tx_queue->tx_ring_size;
+			cbd_base += tx_queue->tx_ring_size;
+		}
 	}
 
-	memset(cbd_base, 0, PAGE_SIZE);
 
 	fep->netdev = ndev;
 
@@ -2498,14 +2762,6 @@ static int fec_enet_init(struct net_device *ndev)
 	/* make sure MAC we just acquired is programmed into the hw */
 	fec_set_mac_address(ndev, NULL);
 
-	/* Set receive and transmit descriptor base. */
-	fep->rx_bd_base = cbd_base;
-	if (fep->bufdesc_ex)
-		fep->tx_bd_base = (struct bufdesc *)
-			(((struct bufdesc_ex *)cbd_base) + fep->rx_ring_size);
-	else
-		fep->tx_bd_base = cbd_base + fep->rx_ring_size;
-
 	/* The FEC Ethernet specific entries in the device structure */
 	ndev->watchdog_timeo = TX_TIMEOUT;
 	ndev->netdev_ops = &fec_netdev_ops;
-- 
1.9.1

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

^ permalink raw reply related

* [Patch net-next 02/11] net:fec: add enet AVB feature macro define for imx6sx
From: Frank Li @ 2014-09-03 20:38 UTC (permalink / raw)
  To: b38611-KZfg59tc24xl57MIdRCFDg, davem-fT/PcQaiUtIeIZ0/mPfg9Q,
	netdev-u79uwXL29TY76Z2rM5mHXA, lznuaa-Re5JQEeQqe8AvxtiuMwx3w
  Cc: shawn.guo-QSEj5FYQhm4dnm+yROfE0A,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Fugang Duan, Frank Li
In-Reply-To: <1409776697-1536-1-git-send-email-Frank.Li-KZfg59tc24xl57MIdRCFDg@public.gmane.org>

From: Fugang Duan <B38611-KZfg59tc24xl57MIdRCFDg@public.gmane.org>

Add enet AVB feature macro define for imx6sx.

Signed-off-by: Fugang Duan <B38611-KZfg59tc24xl57MIdRCFDg@public.gmane.org>
Signed-off-by: Frank Li <Frank.Li-KZfg59tc24xl57MIdRCFDg@public.gmane.org>
---
 drivers/net/ethernet/freescale/fec_main.c | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index c21ecff1..ee9f04f 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -104,6 +104,16 @@ static void set_multicast_list(struct net_device *ndev);
  * ENET_TDAR[TDAR].
  */
 #define FEC_QUIRK_ERR006358            (1 << 7)
+/* ENET IP hw AVB
+ *
+ * i.MX6SX ENET IP add Audio Video Bridging (AVB) feature support.
+ * - Two class indicators on receive with configurable priority
+ * - Two class indicators and line speed timer on transmit allowing
+ *   implementation class credit based shapers externally
+ * - Additional DMA registers provisioned to allow managing up to 3
+ *   independent rings
+ */
+#define FEC_QUIRK_HAS_AVB		(1 << 8)
 
 static struct platform_device_id fec_devtype[] = {
 	{
@@ -128,6 +138,12 @@ static struct platform_device_id fec_devtype[] = {
 		.name = "mvf600-fec",
 		.driver_data = FEC_QUIRK_ENET_MAC,
 	}, {
+		.name = "imx6sx-fec",
+		.driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT |
+				FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM |
+				FEC_QUIRK_HAS_VLAN | FEC_QUIRK_ERR006358 |
+				FEC_QUIRK_HAS_AVB,
+	}, {
 		/* sentinel */
 	}
 };
-- 
1.9.1

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

^ permalink raw reply related

* [Patch net-next 01/11] net:fec: add enet refrence clock for i.MX 6SX chip
From: Frank Li @ 2014-09-03 20:38 UTC (permalink / raw)
  To: b38611-KZfg59tc24xl57MIdRCFDg, davem-fT/PcQaiUtIeIZ0/mPfg9Q,
	netdev-u79uwXL29TY76Z2rM5mHXA, lznuaa-Re5JQEeQqe8AvxtiuMwx3w
  Cc: shawn.guo-QSEj5FYQhm4dnm+yROfE0A,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Fugang Duan, Frank Li
In-Reply-To: <1409776697-1536-1-git-send-email-Frank.Li-KZfg59tc24xl57MIdRCFDg@public.gmane.org>

From: Fugang Duan <B38611-KZfg59tc24xl57MIdRCFDg@public.gmane.org>

i.MX6sx enet has below clocks for user config:
clk_ipg: ipg_clk_s, ipg_clk_mac0_s, 66Mhz
clk_ahb: enet system clock, it is enet AXI clock for imx6sx.
	 For imx6sx, it alos is the clock source of interrupt coalescing.
	 The clock range: 200Mhz ~ 266Mhz.
clk_ref: refrence clock for tx and rx. For imx6sx enet RGMII mode,
	 the refrence clock is 125Mhz coming from internal PLL or external.
	 In i.MX6sx-arm2 board, the clock is from internal PLL.
	 clk_ref is optional, depends on board.
clk_enet_out: The clock can be output from internal PLL. It can supply 50Mhz
	 clock for phy. clk_enet_out is optional, depends on chip and board.
clk_ptp: 1588 ts clock. It is optional, depends on chip.

The patch add clk_ref to distiguish the different clocks.

Signed-off-by: Fugang Duan <B38611-KZfg59tc24xl57MIdRCFDg@public.gmane.org>
Signed-off-by: Frank Li <Frank.Li-KZfg59tc24xl57MIdRCFDg@public.gmane.org>
---
 drivers/net/ethernet/freescale/fec.h      |  1 +
 drivers/net/ethernet/freescale/fec_main.c | 17 +++++++++++++++++
 2 files changed, 18 insertions(+)

diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h
index ee41d98..635772b 100644
--- a/drivers/net/ethernet/freescale/fec.h
+++ b/drivers/net/ethernet/freescale/fec.h
@@ -272,6 +272,7 @@ struct fec_enet_private {
 
 	struct clk *clk_ipg;
 	struct clk *clk_ahb;
+	struct clk *clk_ref;
 	struct clk *clk_enet_out;
 	struct clk *clk_ptp;
 
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index 89355a7..c21ecff1 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -1621,6 +1621,11 @@ static int fec_enet_clk_enable(struct net_device *ndev, bool enable)
 			}
 			mutex_unlock(&fep->ptp_clk_mutex);
 		}
+		if (fep->clk_ref) {
+			ret = clk_prepare_enable(fep->clk_ref);
+			if (ret)
+				goto failed_clk_ref;
+		}
 	} else {
 		clk_disable_unprepare(fep->clk_ahb);
 		clk_disable_unprepare(fep->clk_ipg);
@@ -1632,9 +1637,15 @@ static int fec_enet_clk_enable(struct net_device *ndev, bool enable)
 			fep->ptp_clk_on = false;
 			mutex_unlock(&fep->ptp_clk_mutex);
 		}
+		if (fep->clk_ref)
+			clk_disable_unprepare(fep->clk_ref);
 	}
 
 	return 0;
+
+failed_clk_ref:
+	if (fep->clk_ref)
+		clk_disable_unprepare(fep->clk_ref);
 failed_clk_ptp:
 	if (fep->clk_enet_out)
 		clk_disable_unprepare(fep->clk_enet_out);
@@ -2637,6 +2648,12 @@ fec_probe(struct platform_device *pdev)
 
 	fep->ptp_clk_on = false;
 	mutex_init(&fep->ptp_clk_mutex);
+
+	/* clk_ref is optional, depends on board */
+	fep->clk_ref = devm_clk_get(&pdev->dev, "enet_clk_ref");
+	if (IS_ERR(fep->clk_ref))
+		fep->clk_ref = NULL;
+
 	fep->clk_ptp = devm_clk_get(&pdev->dev, "ptp");
 	fep->bufdesc_ex =
 		pdev->id_entry->driver_data & FEC_QUIRK_HAS_BUFDESC_EX;
-- 
1.9.1

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

^ permalink raw reply related

* Re: [PATCH v2] openvswitch: distinguish between the dropped and consumed skb
From: Pravin Shelar @ 2014-09-03 20:36 UTC (permalink / raw)
  To: roy.qing.li; +Cc: netdev, Thomas Graf
In-Reply-To: <1409737426-22245-1-git-send-email-roy.qing.li@gmail.com>

On Wed, Sep 3, 2014 at 2:43 AM,  <roy.qing.li@gmail.com> wrote:
> From: Li RongQing <roy.qing.li@gmail.com>
>
> distinguish between the dropped and consumed skb, not assume the skb
> is consumed always
>
> Cc: Thomas Graf <tgraf@noironetworks.com>
> Cc: Pravin Shelar <pshelar@nicira.com>
> Signed-off-by: Li RongQing <roy.qing.li@gmail.com>

Acked-by: Pravin B Shelar <pshelar@nicira.com>

Thanks.
> ---
>  net/openvswitch/datapath.c |    7 +++++--
>  1 file changed, 5 insertions(+), 2 deletions(-)
>
> diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c
> index 35d866f..91d66b7 100644
> --- a/net/openvswitch/datapath.c
> +++ b/net/openvswitch/datapath.c
> @@ -265,8 +265,11 @@ void ovs_dp_process_received_packet(struct vport *p, struct sk_buff *skb)
>                 upcall.key = &key;
>                 upcall.userdata = NULL;
>                 upcall.portid = ovs_vport_find_upcall_portid(p, skb);
> -               ovs_dp_upcall(dp, skb, &upcall);
> -               consume_skb(skb);
> +               error = ovs_dp_upcall(dp, skb, &upcall);
> +               if (unlikely(error))
> +                       kfree_skb(skb);
> +               else
> +                       consume_skb(skb);
>                 stats_counter = &stats->n_missed;
>                 goto out;
>         }
> --
> 1.7.10.4
>

^ permalink raw reply

* [Patch net-next 11/11] ARM: dts: imx6sx: add multi-queue support enet
From: y @ 2014-09-03 20:34 UTC (permalink / raw)
  To: b38611-KZfg59tc24xl57MIdRCFDg, davem-fT/PcQaiUtIeIZ0/mPfg9Q,
	netdev-u79uwXL29TY76Z2rM5mHXA, lznuaa-Re5JQEeQqe8AvxtiuMwx3w
  Cc: shawn.guo-QSEj5FYQhm4dnm+yROfE0A,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Frank Li
In-Reply-To: <1409776486-1403-1-git-send-email-y>

From: Frank Li <Frank.Li-KZfg59tc24xl57MIdRCFDg@public.gmane.org>

Enable 3 queues suppport for ethernet

Signed-off-by: Frank Li <Frank.Li-KZfg59tc24xl57MIdRCFDg@public.gmane.org>
---
 arch/arm/boot/dts/imx6sx.dtsi | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/arch/arm/boot/dts/imx6sx.dtsi b/arch/arm/boot/dts/imx6sx.dtsi
index f4b9da6..1722748 100644
--- a/arch/arm/boot/dts/imx6sx.dtsi
+++ b/arch/arm/boot/dts/imx6sx.dtsi
@@ -776,6 +776,8 @@
 					 <&clks IMX6SX_CLK_ENET_PTP>;
 				clock-names = "ipg", "ahb", "ptp",
 					      "enet_clk_ref", "enet_out";
+				fsl,num_tx_queues=<3>;
+				fsl,num_rx_queues=<3>;
 				status = "disabled";
                         };
 
-- 
1.9.1

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

^ permalink raw reply related

* [Patch net-next 10/11] ARM: Documentation: Update fec dts binding doc
From: y @ 2014-09-03 20:34 UTC (permalink / raw)
  To: b38611-KZfg59tc24xl57MIdRCFDg, davem-fT/PcQaiUtIeIZ0/mPfg9Q,
	netdev-u79uwXL29TY76Z2rM5mHXA, lznuaa-Re5JQEeQqe8AvxtiuMwx3w
  Cc: shawn.guo-QSEj5FYQhm4dnm+yROfE0A,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Frank Li, Fugang Duan
In-Reply-To: <1409776486-1403-1-git-send-email-y>

From: Frank Li <Frank.Li-KZfg59tc24xl57MIdRCFDg@public.gmane.org>

    This patch update fec devicetree binding doc that add Optional
    properties "fsl,num_tx_queues" and "fsl,num_rx_queues".

Signed-off-by: Fugang Duan <B38611-KZfg59tc24xl57MIdRCFDg@public.gmane.org>
Signed-off-by: Frank Li <Frank.Li-KZfg59tc24xl57MIdRCFDg@public.gmane.org>
---
 Documentation/devicetree/bindings/net/fsl-fec.txt | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/Documentation/devicetree/bindings/net/fsl-fec.txt b/Documentation/devicetree/bindings/net/fsl-fec.txt
index 8a2c7b5..4da148d 100644
--- a/Documentation/devicetree/bindings/net/fsl-fec.txt
+++ b/Documentation/devicetree/bindings/net/fsl-fec.txt
@@ -16,6 +16,12 @@ Optional properties:
 - phy-handle : phandle to the PHY device connected to this device.
 - fixed-link : Assume a fixed link. See fixed-link.txt in the same directory.
   Use instead of phy-handle.
+- fsl,num_tx_queues : The property is valid for enet-avb IP, which supports
+  hw multi queues. Should specify the tx queue number, otherwise set tx queue
+  number to 1.
+- fsl,num_rx_queues : The property is valid for enet-avb IP, which supports
+  hw multi queues. Should specify the rx queue number, otherwise set rx queue
+  number to 1.
 
 Optional subnodes:
 - mdio : specifies the mdio bus in the FEC, used as a container for phy nodes
-- 
1.9.1

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

^ permalink raw reply related

* [Patch net-next 09/11] net:fec: remove unnessary memory copy for address alignment in .xmit()
From: y @ 2014-09-03 20:34 UTC (permalink / raw)
  To: b38611-KZfg59tc24xl57MIdRCFDg, davem-fT/PcQaiUtIeIZ0/mPfg9Q,
	netdev-u79uwXL29TY76Z2rM5mHXA, lznuaa-Re5JQEeQqe8AvxtiuMwx3w
  Cc: shawn.guo-QSEj5FYQhm4dnm+yROfE0A,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Fugang Duan, Frank Li
In-Reply-To: <1409776486-1403-1-git-send-email-y>

From: Fugang Duan <B38611-KZfg59tc24xl57MIdRCFDg@public.gmane.org>

i.MX6SX enet-avb IP support buffer address byte alignment for tx DMA
transfer, so remove the unnessary memory copy for the enet avb IP.

Signed-off-by: Fugang Duan <B38611-KZfg59tc24xl57MIdRCFDg@public.gmane.org>
Signed-off-by: Frank Li <Frank.Li-KZfg59tc24xl57MIdRCFDg@public.gmane.org>
---
 drivers/net/ethernet/freescale/fec_main.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index b388e29f..d09fe16 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -518,8 +518,9 @@ static int fec_enet_txq_submit_skb(struct fec_enet_priv_tx_q *txq,
 
 	queue = skb_get_queue_mapping(skb);
 	index = fec_enet_get_bd_index(txq->tx_bd_base, bdp, fep);
-	if (((unsigned long) bufaddr) & FEC_ALIGNMENT ||
-		id_entry->driver_data & FEC_QUIRK_SWAP_FRAME) {
+	if (!(id_entry->driver_data & FEC_QUIRK_HAS_AVB) &&
+	    ((((unsigned long)bufaddr) & FEC_ALIGNMENT ||
+		id_entry->driver_data & FEC_QUIRK_SWAP_FRAME))) {
 		memcpy(txq->tx_bounce[index], skb->data, buflen);
 		bufaddr = txq->tx_bounce[index];
 
-- 
1.9.1

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

^ permalink raw reply related


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