Netdev List
 help / color / mirror / Atom feed
* [PATCH net-next 03/27] net: mvpp2: handle too large value in mvpp2_rx_time_coal_set()
From: Thomas Petazzoni @ 2016-12-21 11:16 UTC (permalink / raw)
  To: David S. Miller, netdev
  Cc: Nadav Haklai, Hanna Hawa, Yehuda Yitschak, Jason Cooper,
	Andrew Lunn, Sebastian Hesselbarth, Gregory Clement,
	linux-arm-kernel, Stefan Chulski, Marcin Wojtas, Thomas Petazzoni
In-Reply-To: <1482318994-23488-1-git-send-email-thomas.petazzoni@free-electrons.com>

When configuring the MVPP2_ISR_RX_THRESHOLD_REG with the RX coalescing
time threshold, we do not check for the maximum allowed value supported
by the driver, which means we might overflow and use a bogus value. This
commit adds a check for this situation, and if a value higher than what
is supported by the hardware is provided, then we use the maximum value
supported by the hardware.

Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
---
 drivers/net/ethernet/marvell/mvpp2.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c
index 4c9e3ea..12edefe 100644
--- a/drivers/net/ethernet/marvell/mvpp2.c
+++ b/drivers/net/ethernet/marvell/mvpp2.c
@@ -154,6 +154,7 @@
 
 /* Interrupt Cause and Mask registers */
 #define MVPP2_ISR_RX_THRESHOLD_REG(rxq)		(0x5200 + 4 * (rxq))
+#define     MVPP2_MAX_ISR_RX_THRESHOLD		0xfffff0
 #define MVPP2_ISR_RXQ_GROUP_REG(rxq)		(0x5400 + 4 * (rxq))
 #define MVPP2_ISR_ENABLE_REG(port)		(0x5420 + 4 * (port))
 #define     MVPP2_ISR_ENABLE_INTERRUPT(mask)	((mask) & 0xffff)
@@ -4397,6 +4398,12 @@ static void mvpp2_rx_time_coal_set(struct mvpp2_port *port,
 	u32 val;
 
 	val = (port->priv->tclk / USEC_PER_SEC) * usec;
+
+	if (val > MVPP2_MAX_ISR_RX_THRESHOLD) {
+		val = MVPP2_MAX_ISR_RX_THRESHOLD;
+		usec = (val * USEC_PER_SEC) / port->priv->tclk;
+	}
+
 	mvpp2_write(port->priv, MVPP2_ISR_RX_THRESHOLD_REG(rxq->id), val);
 
 	rxq->time_coal = usec;
-- 
2.7.4

^ permalink raw reply related

* [PATCH net-next 02/27] net: mvpp2: handle too large value handling in mvpp2_rx_pkts_coal_set()
From: Thomas Petazzoni @ 2016-12-21 11:16 UTC (permalink / raw)
  To: David S. Miller, netdev
  Cc: Nadav Haklai, Hanna Hawa, Yehuda Yitschak, Jason Cooper,
	Andrew Lunn, Sebastian Hesselbarth, Gregory Clement,
	linux-arm-kernel, Stefan Chulski, Marcin Wojtas, Thomas Petazzoni
In-Reply-To: <1482318994-23488-1-git-send-email-thomas.petazzoni@free-electrons.com>

Currently, mvpp2_rx_pkts_coal_set() does the following to avoid setting
a too large value for the RX coalescing by packet number:

  val = (pkts & MVPP2_OCCUPIED_THRESH_MASK);

This means that if you set a value that is slightly higher the the
maximum number of packets, you in fact get a very low value. It makes a
lot more sense to simply check if the value is too high, and if it's too
high, limit it to the maximum possible value.

Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
---
 drivers/net/ethernet/marvell/mvpp2.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c
index 930c816..4c9e3ea 100644
--- a/drivers/net/ethernet/marvell/mvpp2.c
+++ b/drivers/net/ethernet/marvell/mvpp2.c
@@ -4381,11 +4381,11 @@ static void mvpp2_txp_max_tx_size_set(struct mvpp2_port *port)
 static void mvpp2_rx_pkts_coal_set(struct mvpp2_port *port,
 				   struct mvpp2_rx_queue *rxq, u32 pkts)
 {
-	u32 val;
+	if (pkts > MVPP2_OCCUPIED_THRESH_MASK)
+		pkts = MVPP2_OCCUPIED_THRESH_MASK;
 
-	val = (pkts & MVPP2_OCCUPIED_THRESH_MASK);
 	mvpp2_write(port->priv, MVPP2_RXQ_NUM_REG, rxq->id);
-	mvpp2_write(port->priv, MVPP2_RXQ_THRESH_REG, val);
+	mvpp2_write(port->priv, MVPP2_RXQ_THRESH_REG, pkts);
 
 	rxq->pkts_coal = pkts;
 }
-- 
2.7.4

^ permalink raw reply related

* [PATCH net-next 04/27] net: mvpp2: release reference to txq_cpu[] entry after unmapping
From: Thomas Petazzoni @ 2016-12-21 11:16 UTC (permalink / raw)
  To: David S. Miller, netdev
  Cc: Nadav Haklai, Hanna Hawa, Yehuda Yitschak, Jason Cooper,
	Andrew Lunn, Sebastian Hesselbarth, Gregory Clement,
	linux-arm-kernel, Stefan Chulski, Marcin Wojtas, Thomas Petazzoni
In-Reply-To: <1482318994-23488-1-git-send-email-thomas.petazzoni@free-electrons.com>

The mvpp2_txq_bufs_free() function is called upon TX completion to DMA
unmap TX buffers, and free the corresponding SKBs. It gets the
references to the SKB to free and the DMA buffer to unmap from a per-CPU
txq_pcpu data structure.

However, the code currently increments the pointer to the next entry
before doing the DMA unmap and freeing the SKB. It does not cause any
visible problem because for a given SKB the TX completion is guaranteed
to take place on the CPU where the TX was started. However, it is much
more logical to increment the pointer to the next entry once the current
entry has been completely unmapped/released.

Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
---
 drivers/net/ethernet/marvell/mvpp2.c | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c
index 12edefe..84eb5fb 100644
--- a/drivers/net/ethernet/marvell/mvpp2.c
+++ b/drivers/net/ethernet/marvell/mvpp2.c
@@ -4420,13 +4420,12 @@ static void mvpp2_txq_bufs_free(struct mvpp2_port *port,
 		struct mvpp2_txq_pcpu_buf *tx_buf =
 			txq_pcpu->buffs + txq_pcpu->txq_get_index;
 
-		mvpp2_txq_inc_get(txq_pcpu);
-
 		dma_unmap_single(port->dev->dev.parent, tx_buf->phys,
 				 tx_buf->size, DMA_TO_DEVICE);
-		if (!tx_buf->skb)
-			continue;
-		dev_kfree_skb_any(tx_buf->skb);
+		if (tx_buf->skb)
+			dev_kfree_skb_any(tx_buf->skb);
+
+		mvpp2_txq_inc_get(txq_pcpu);
 	}
 }
 
-- 
2.7.4

^ permalink raw reply related

* [PATCH net-next 10/27] net: mvpp2: simplify MVPP2_PRS_RI_* definitions
From: Thomas Petazzoni @ 2016-12-21 11:16 UTC (permalink / raw)
  To: David S. Miller, netdev
  Cc: Thomas Petazzoni, Andrew Lunn, Yehuda Yitschak, Jason Cooper,
	Hanna Hawa, Nadav Haklai, Gregory Clement, Stefan Chulski,
	Marcin Wojtas, linux-arm-kernel, Sebastian Hesselbarth
In-Reply-To: <1482318994-23488-1-git-send-email-thomas.petazzoni@free-electrons.com>

Some of the MVPP2_PRS_RI_* definitions use the ~(value) syntax, which
doesn't compile nicely on 64-bit. Moreover, those definitions are in
fact unneeded, since they are always used in combination with a bit
mask that ensures only the appropriate bits are modified.

Therefore, such definitions should just be set to 0x0. For example:

 #define MVPP2_PRS_RI_L2_CAST_MASK              0x600
 #define MVPP2_PRS_RI_L2_UCAST                  ~(BIT(9) | BIT(10))
 #define MVPP2_PRS_RI_L2_MCAST                  BIT(9)
 #define MVPP2_PRS_RI_L2_BCAST                  BIT(10)

becomes

 #define MVPP2_PRS_RI_L2_CAST_MASK              0x600
 #define MVPP2_PRS_RI_L2_UCAST                  0x0
 #define MVPP2_PRS_RI_L2_MCAST                  BIT(9)
 #define MVPP2_PRS_RI_L2_BCAST                  BIT(10)

Because the values (MVPP2_PRS_RI_L2_UCAST, MVPP2_PRS_RI_L2_MCAST and
MVPP2_PRS_RI_L2_BCAST) are always applied with
MVPP2_PRS_RI_L2_CAST_MASK, and therefore there is no need for
MVPP2_PRS_RI_L2_UCAST to be defined as ~(BIT(9) | BIT(10)).

It fixes the following warnings when building the driver on a 64-bit
platform (which is not possible as of this commit, but will be enabled
in a follow-up commit):

drivers/net/ethernet/marvell/mvpp2.c: In function ‘mvpp2_prs_mac_promisc_set’:
drivers/net/ethernet/marvell/mvpp2.c:524:33: warning: large integer implicitly truncated to unsigned type [-Woverflow]
 #define MVPP2_PRS_RI_L2_UCAST   ~(BIT(9) | BIT(10))
                                 ^
drivers/net/ethernet/marvell/mvpp2.c:1459:33: note: in expansion of macro ‘MVPP2_PRS_RI_L2_UCAST’
   mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L2_UCAST,

Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
---
 drivers/net/ethernet/marvell/mvpp2.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c
index 6ec1135..a713e7d 100644
--- a/drivers/net/ethernet/marvell/mvpp2.c
+++ b/drivers/net/ethernet/marvell/mvpp2.c
@@ -511,19 +511,19 @@ enum mvpp2_tag_type {
 #define MVPP2_PRS_RI_MAC_ME_MASK		0x1
 #define MVPP2_PRS_RI_DSA_MASK			0x2
 #define MVPP2_PRS_RI_VLAN_MASK			0xc
-#define MVPP2_PRS_RI_VLAN_NONE			~(BIT(2) | BIT(3))
+#define MVPP2_PRS_RI_VLAN_NONE			0x0
 #define MVPP2_PRS_RI_VLAN_SINGLE		BIT(2)
 #define MVPP2_PRS_RI_VLAN_DOUBLE		BIT(3)
 #define MVPP2_PRS_RI_VLAN_TRIPLE		(BIT(2) | BIT(3))
 #define MVPP2_PRS_RI_CPU_CODE_MASK		0x70
 #define MVPP2_PRS_RI_CPU_CODE_RX_SPEC		BIT(4)
 #define MVPP2_PRS_RI_L2_CAST_MASK		0x600
-#define MVPP2_PRS_RI_L2_UCAST			~(BIT(9) | BIT(10))
+#define MVPP2_PRS_RI_L2_UCAST			0x0
 #define MVPP2_PRS_RI_L2_MCAST			BIT(9)
 #define MVPP2_PRS_RI_L2_BCAST			BIT(10)
 #define MVPP2_PRS_RI_PPPOE_MASK			0x800
 #define MVPP2_PRS_RI_L3_PROTO_MASK		0x7000
-#define MVPP2_PRS_RI_L3_UN			~(BIT(12) | BIT(13) | BIT(14))
+#define MVPP2_PRS_RI_L3_UN			0x0
 #define MVPP2_PRS_RI_L3_IP4			BIT(12)
 #define MVPP2_PRS_RI_L3_IP4_OPT			BIT(13)
 #define MVPP2_PRS_RI_L3_IP4_OTHER		(BIT(12) | BIT(13))
@@ -531,7 +531,7 @@ enum mvpp2_tag_type {
 #define MVPP2_PRS_RI_L3_IP6_EXT			(BIT(12) | BIT(14))
 #define MVPP2_PRS_RI_L3_ARP			(BIT(13) | BIT(14))
 #define MVPP2_PRS_RI_L3_ADDR_MASK		0x18000
-#define MVPP2_PRS_RI_L3_UCAST			~(BIT(15) | BIT(16))
+#define MVPP2_PRS_RI_L3_UCAST			0x0
 #define MVPP2_PRS_RI_L3_MCAST			BIT(15)
 #define MVPP2_PRS_RI_L3_BCAST			(BIT(15) | BIT(16))
 #define MVPP2_PRS_RI_IP_FRAG_MASK		0x20000
-- 
2.7.4


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related

* [PATCH net-next 09/27] net: mvpp2: fix indentation of MVPP2_EXT_GLOBAL_CTRL_DEFAULT
From: Thomas Petazzoni @ 2016-12-21 11:16 UTC (permalink / raw)
  To: David S. Miller, netdev
  Cc: Thomas Petazzoni, Andrew Lunn, Yehuda Yitschak, Jason Cooper,
	Hanna Hawa, Nadav Haklai, Gregory Clement, Stefan Chulski,
	Marcin Wojtas, linux-arm-kernel, Sebastian Hesselbarth
In-Reply-To: <1482318994-23488-1-git-send-email-thomas.petazzoni@free-electrons.com>

Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
---
 drivers/net/ethernet/marvell/mvpp2.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c
index e11eb6f..6ec1135 100644
--- a/drivers/net/ethernet/marvell/mvpp2.c
+++ b/drivers/net/ethernet/marvell/mvpp2.c
@@ -254,7 +254,7 @@
 #define MVPP2_PHY_AN_CFG0_REG			0x34
 #define     MVPP2_PHY_AN_STOP_SMI0_MASK		BIT(7)
 #define MVPP2_MNG_EXTENDED_GLOBAL_CTRL_REG	0x305c
-#define MVPP2_EXT_GLOBAL_CTRL_DEFAULT		0x27
+#define     MVPP2_EXT_GLOBAL_CTRL_DEFAULT	0x27
 
 /* Per-port registers */
 #define MVPP2_GMAC_CTRL_0_REG			0x0
-- 
2.7.4

^ permalink raw reply related

* [PATCH net-next 07/27] net: mvpp2: simplify mvpp2_bm_bufs_add()
From: Thomas Petazzoni @ 2016-12-21 11:16 UTC (permalink / raw)
  To: David S. Miller, netdev
  Cc: Thomas Petazzoni, Andrew Lunn, Yehuda Yitschak, Jason Cooper,
	Hanna Hawa, Nadav Haklai, Gregory Clement, Stefan Chulski,
	Marcin Wojtas, linux-arm-kernel, Sebastian Hesselbarth
In-Reply-To: <1482318994-23488-1-git-send-email-thomas.petazzoni@free-electrons.com>

The mvpp2_bm_bufs_add() currently creates a fake cookie by calling
mvpp2_bm_cookie_pool_set(), just to be able to call
mvpp2_pool_refill(). But all what mvpp2_pool_refill() does is extract
the pool ID from the cookie, and call mvpp2_bm_pool_put() with this ID.

Instead of doing this convoluted thing, just call mvpp2_bm_pool_put()
directly, since we have the BM pool ID.

Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
---
 drivers/net/ethernet/marvell/mvpp2.c | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c
index 868ef4c..98edcb6 100644
--- a/drivers/net/ethernet/marvell/mvpp2.c
+++ b/drivers/net/ethernet/marvell/mvpp2.c
@@ -3626,7 +3626,6 @@ static int mvpp2_bm_bufs_add(struct mvpp2_port *port,
 {
 	struct sk_buff *skb;
 	int i, buf_size, total_size;
-	u32 bm;
 	dma_addr_t phys_addr;
 
 	buf_size = MVPP2_RX_BUF_SIZE(bm_pool->pkt_size);
@@ -3640,13 +3639,12 @@ static int mvpp2_bm_bufs_add(struct mvpp2_port *port,
 		return 0;
 	}
 
-	bm = mvpp2_bm_cookie_pool_set(0, bm_pool->id);
 	for (i = 0; i < buf_num; i++) {
 		skb = mvpp2_skb_alloc(port, bm_pool, &phys_addr, GFP_KERNEL);
 		if (!skb)
 			break;
 
-		mvpp2_pool_refill(port, bm, (u32)phys_addr, (u32)skb);
+		mvpp2_bm_pool_put(port, bm_pool->id, (u32)phys_addr, (u32)skb);
 	}
 
 	/* Update BM driver with number of buffers added to pool */
-- 
2.7.4

^ permalink raw reply related

* [PATCH net-next 06/27] net: mvpp2: drop useless fields in mvpp2_bm_pool and related code
From: Thomas Petazzoni @ 2016-12-21 11:16 UTC (permalink / raw)
  To: David S. Miller, netdev
  Cc: Thomas Petazzoni, Andrew Lunn, Yehuda Yitschak, Jason Cooper,
	Hanna Hawa, Nadav Haklai, Gregory Clement, Stefan Chulski,
	Marcin Wojtas, linux-arm-kernel, Sebastian Hesselbarth
In-Reply-To: <1482318994-23488-1-git-send-email-thomas.petazzoni@free-electrons.com>

This commit drops dead code from the mvpp2 driver. The 'in_use' and
'in_use_thresh' fields of 'struct mvpp2_bm_pool' are
incremented/decremented/initialized in various places. But they are only
used in one place:

       if (is_recycle &&
           (atomic_read(&bm_pool->in_use) < bm_pool->in_use_thresh))
               return 0;

However 'is_recycle', passed as argument to mvpp2_rx_refill() is always
false. So in fact, this code is never reached, and the 'is_recycle'
argument is useless. So let's drop this code.

Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
---
 drivers/net/ethernet/marvell/mvpp2.c | 18 +++---------------
 1 file changed, 3 insertions(+), 15 deletions(-)

diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c
index fe854b3..868ef4c 100644
--- a/drivers/net/ethernet/marvell/mvpp2.c
+++ b/drivers/net/ethernet/marvell/mvpp2.c
@@ -930,10 +930,6 @@ struct mvpp2_bm_pool {
 
 	/* Ports using BM pool */
 	u32 port_map;
-
-	/* Occupied buffers indicator */
-	atomic_t in_use;
-	int in_use_thresh;
 };
 
 struct mvpp2_buff_hdr {
@@ -3399,7 +3395,6 @@ static int mvpp2_bm_pool_create(struct platform_device *pdev,
 	bm_pool->size = size;
 	bm_pool->pkt_size = 0;
 	bm_pool->buf_num = 0;
-	atomic_set(&bm_pool->in_use, 0);
 
 	return 0;
 }
@@ -3656,7 +3651,6 @@ static int mvpp2_bm_bufs_add(struct mvpp2_port *port,
 
 	/* Update BM driver with number of buffers added to pool */
 	bm_pool->buf_num += i;
-	bm_pool->in_use_thresh = bm_pool->buf_num / 4;
 
 	netdev_dbg(port->dev,
 		   "%s pool %d: pkt_size=%4d, buf_size=%4d, total_size=%4d\n",
@@ -4997,23 +4991,18 @@ static void mvpp2_rx_csum(struct mvpp2_port *port, u32 status,
 
 /* Reuse skb if possible, or allocate a new skb and add it to BM pool */
 static int mvpp2_rx_refill(struct mvpp2_port *port,
-			   struct mvpp2_bm_pool *bm_pool,
-			   u32 bm, int is_recycle)
+			   struct mvpp2_bm_pool *bm_pool, u32 bm)
 {
 	struct sk_buff *skb;
 	dma_addr_t phys_addr;
 
-	if (is_recycle &&
-	    (atomic_read(&bm_pool->in_use) < bm_pool->in_use_thresh))
-		return 0;
-
 	/* No recycle or too many buffers are in use, so allocate a new skb */
 	skb = mvpp2_skb_alloc(port, bm_pool, &phys_addr, GFP_ATOMIC);
 	if (!skb)
 		return -ENOMEM;
 
 	mvpp2_pool_refill(port, bm, (u32)phys_addr, (u32)skb);
-	atomic_dec(&bm_pool->in_use);
+
 	return 0;
 }
 
@@ -5139,7 +5128,7 @@ static int mvpp2_rx(struct mvpp2_port *port, int rx_todo,
 
 		skb = (struct sk_buff *)rx_desc->buf_cookie;
 
-		err = mvpp2_rx_refill(port, bm_pool, bm, 0);
+		err = mvpp2_rx_refill(port, bm_pool, bm);
 		if (err) {
 			netdev_err(port->dev, "failed to refill BM pools\n");
 			goto err_drop_frame;
@@ -5150,7 +5139,6 @@ static int mvpp2_rx(struct mvpp2_port *port, int rx_todo,
 
 		rcvd_pkts++;
 		rcvd_bytes += rx_bytes;
-		atomic_inc(&bm_pool->in_use);
 
 		skb_reserve(skb, MVPP2_MH_SIZE);
 		skb_put(skb, rx_bytes);
-- 
2.7.4

^ permalink raw reply related

* [PATCH net-next 01/27] dt-bindings: net: update Marvell PPv2 binding for PPv2.2 support
From: Thomas Petazzoni @ 2016-12-21 11:16 UTC (permalink / raw)
  To: David S. Miller, netdev
  Cc: Thomas Petazzoni, Andrew Lunn, Yehuda Yitschak, Jason Cooper,
	Hanna Hawa, Nadav Haklai, Gregory Clement, Stefan Chulski,
	Marcin Wojtas, linux-arm-kernel, Sebastian Hesselbarth
In-Reply-To: <1482318994-23488-1-git-send-email-thomas.petazzoni@free-electrons.com>

The Marvell PPv2 Device Tree binding was so far only used to describe
the PPv2.1 network controller, used in the Marvell Armada 375.

A new version of this IP block, PPv2.2 is used in the Marvell Armada
7K/8K processor. This commit extends the existing binding so that it can
also be used to describe PPv2.2 hardware.

Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
---
 .../devicetree/bindings/net/marvell-pp2.txt        | 66 ++++++++++++++++++----
 1 file changed, 55 insertions(+), 11 deletions(-)

diff --git a/Documentation/devicetree/bindings/net/marvell-pp2.txt b/Documentation/devicetree/bindings/net/marvell-pp2.txt
index aa4f423..76071f3 100644
--- a/Documentation/devicetree/bindings/net/marvell-pp2.txt
+++ b/Documentation/devicetree/bindings/net/marvell-pp2.txt
@@ -1,17 +1,28 @@
-* Marvell Armada 375 Ethernet Controller (PPv2)
+* Marvell Armada 375 Ethernet Controller (PPv2.1)
+  Marvell Armada 7K/8K Ethernet Controller (PPv2.2)
 
 Required properties:
 
-- compatible: should be "marvell,armada-375-pp2"
+- compatible: should be one of:
+    "marvell,armada-375-pp2"
+    "marvell,armada-7k-pp2"
 - reg: addresses and length of the register sets for the device.
-  Must contain the following register sets:
+  For "marvell,armada-375-pp2", must contain the following register
+  sets:
 	- common controller registers
 	- LMS registers
-  In addition, at least one port register set is required.
-- clocks: a pointer to the reference clocks for this device, consequently:
-	- main controller clock
-	- GOP clock
-- clock-names: names of used clocks, must be "pp_clk" and "gop_clk".
+	- one register area per Ethernet port
+  For "marvell,armda-7k-pp2", must contain the following register
+  sets:
+        - common controller registers
+	- per-port registers
+
+- clocks: pointers to the reference clocks for this device, consequently:
+	- main controller clock (for both armada-375-pp2 and armada-7k-pp2)
+	- GOP clock (for both armada-375-pp2 and armada-7k-pp2)
+	- MG clock (only for armada-7k-pp2)
+- clock-names: names of used clocks, must be "pp_clk", "gop_clk" and
+  "mg_clk" (the latter only for armada-7k-pp2).
 
 The ethernet ports are represented by subnodes. At least one port is
 required.
@@ -19,8 +30,9 @@ required.
 Required properties (port):
 
 - interrupts: interrupt for the port
-- port-id: should be '0' or '1' for ethernet ports, and '2' for the
-           loopback port
+- port-id: ID of the port from the MAC point of view
+- gop-port-id: only for marvell,armada-7k-pp2, ID of the port from the
+  GOP (Group Of Ports) point of view
 - phy-mode: See ethernet.txt file in the same directory
 
 Optional properties (port):
@@ -31,7 +43,7 @@ Optional properties (port):
   then fixed link is assumed, and the 'fixed-link' property is
   mandatory.
 
-Example:
+Example for marvell,armada-375-pp2:
 
 ethernet@f0000 {
 	compatible = "marvell,armada-375-pp2";
@@ -59,3 +71,35 @@ ethernet@f0000 {
 		phy-mode = "gmii";
 	};
 };
+
+Example for marvell,armada-7k-pp2:
+
+cpm_ethernet: ethernet@0 {
+	compatible = "marvell,armada-7k-pp22";
+	reg = <0x0 0x100000>,
+	      <0x100000 0x80000>;
+	clocks = <&cpm_syscon0 1 3>, <&cpm_syscon0 1 9>, <&cpm_syscon0 1 5>;
+	clock-names = "pp_clk", "gop_clk", "gp_clk";
+	status = "disabled";
+
+	eth0: eth@0 {
+		interrupts = <GIC_SPI 37 IRQ_TYPE_LEVEL_HIGH>;
+		port-id = <0>;
+		gop-port-id = <0>;
+		status = "disabled";
+	};
+
+	eth1: eth@1 {
+		interrupts = <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>;
+		port-id = <1>;
+		gop-port-id = <2>;
+		status = "disabled";
+	};
+
+	eth2: eth@2 {
+		interrupts = <GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH>;
+		port-id = <2>;
+		gop-port-id = <3>;
+		status = "disabled";
+	};
+};
-- 
2.7.4

^ permalink raw reply related

* [PATCH net-next 00/27] net: mvpp2: add basic support for PPv2.2
From: Thomas Petazzoni @ 2016-12-21 11:16 UTC (permalink / raw)
  To: David S. Miller, netdev
  Cc: Thomas Petazzoni, Andrew Lunn, Yehuda Yitschak, Jason Cooper,
	Hanna Hawa, Nadav Haklai, Gregory Clement, Stefan Chulski,
	Marcin Wojtas, linux-arm-kernel, Sebastian Hesselbarth

Hello,

The goal of this patch series is to add basic support for PPv2.2 in
the existing mvpp2 driver. mvpp2 currently supported the PPv2.1
version of the IP, used in the 32 bits Marvell Armada 375 SoC. PPv2.2
is an evolution of this IP block, used in the 64 bits Marvell Armada
7K/8K SoCs.

In order to ease the review, the introduction of PPv2.2 support has
been made into multiple small commits, with the final commit adding
the compatible string that makes the PPv2.2 support actually
usable. The series remain fully bisectable.

Here is a quick overview of the series:

 - Patch 1 updates the Device Tree binding documentation.

 - Patch 2 to 10 make improvements to the mvpp2 not directly related
   to the PPv2.2 support, but useful to have as they cleanup some dead
   code, fix a few issues, etc.

 - Patch 11 switches the driver to build_skb(), which is needed to
   support PPv2.2 (details in the commit log itself)

 - Patch 12 makes the driver build on 64 bits platform.

 - Patches 13 to 26 add the PPv2.2 support step by step.

 - Patch 27 finally adds the compatible string, which makes the PPv2.2
   support usable.

I'd like to thank Stefan Chulski and Marcin Wojtas, who helped me a
lot in the development of this patch series, by reviewing the patches,
and giving lots of useful hints to debug the driver on PPv2.2. Thanks!

Best regards,

Thomas


Thomas Petazzoni (27):
  dt-bindings: net: update Marvell PPv2 binding for PPv2.2 support
  net: mvpp2: handle too large value handling in
    mvpp2_rx_pkts_coal_set()
  net: mvpp2: handle too large value in mvpp2_rx_time_coal_set()
  net: mvpp2: release reference to txq_cpu[] entry after unmapping
  net: mvpp2: remove unused 'tx_skb' field of 'struct mvpp2_tx_queue'
  net: mvpp2: drop useless fields in mvpp2_bm_pool and related code
  net: mvpp2: simplify mvpp2_bm_bufs_add()
  net: mvpp2: remove unused register definitions
  net: mvpp2: fix indentation of MVPP2_EXT_GLOBAL_CTRL_DEFAULT
  net: mvpp2: simplify MVPP2_PRS_RI_* definitions
  net: mvpp2: switch to build_skb() in the RX path
  net: mvpp2: enable building on 64-bit platforms
  net: mvpp2: add and use accessors for TX/RX descriptors
  net: mvpp2: add ip_version field in "struct mvpp2"
  net: mvpp2: introduce an intermediate union for the TX/RX descriptors
  net: mvpp2: introduce PPv2.2 HW descriptors and adapt accessors
  net: mvpp2: adjust the allocation/free of BM pools for PPv2.2
  net: mvpp2: adapt the mvpp2_rxq_*_pool_set functions to PPv2.2
  net: mvpp2: adapt mvpp2_defaults_set() to PPv2.2
  net: mvpp2: adjust mvpp2_{rxq,txq}_init for PPv2.2
  net: mvpp2: handle register mapping and access for PPv2.2
  net: mvpp2: handle misc PPv2.1/PPv2.2 differences
  net: mvpp2: add AXI bridge initialization for PPv2.2
  net: mvpp2: rework RXQ interrupt group initialization for PPv2.2
  net: mvpp2: adapt rxq distribution to PPv2.2
  net: mvpp2: add support for an additional clock needed for PPv2.2
  net: mvpp2: finally add the PPv2.2 compatible string

 .../devicetree/bindings/net/marvell-pp2.txt        |  66 +-
 drivers/net/ethernet/marvell/Kconfig               |   2 +-
 drivers/net/ethernet/marvell/mvpp2.c               | 940 ++++++++++++++++-----
 3 files changed, 789 insertions(+), 219 deletions(-)

-- 
2.7.4

^ permalink raw reply

* [PATCHv2] net: mvpp2: fix dma unmapping of TX buffers for fragments
From: Thomas Petazzoni @ 2016-12-21 10:28 UTC (permalink / raw)
  To: David S. Miller, netdev
  Cc: Jason Cooper, Andrew Lunn, Sebastian Hesselbarth, Gregory Clement,
	Nadav Haklai, Hanna Hawa, Yehuda Yitschak, Marcin Wojtas,
	Thomas Petazzoni, Raphael G, stable

Since commit 71ce391dfb784 ("net: mvpp2: enable proper per-CPU TX
buffers unmapping"), we are not correctly DMA unmapping TX buffers for
fragments.

Indeed, the mvpp2_txq_inc_put() function only stores in the
txq_cpu->tx_buffs[] array the physical address of the buffer to be
DMA-unmapped when skb != NULL. In addition, when DMA-unmapping, we use
skb_headlen(skb) to get the size to be unmapped. Both of this works fine
for TX descriptors that are associated directly to a SKB, but not the
ones that are used for fragments, with a NULL pointer as skb:

 - We have a NULL physical address when calling DMA unmap
 - skb_headlen(skb) crashes because skb is NULL

This causes random crashes when fragments are used.

To solve this problem, we need to:

 - Store the physical address of the buffer to be unmapped
   unconditionally, regardless of whether it is tied to a SKB or not.

 - Store the length of the buffer to be unmapped, which requires a new
   field.

Instead of adding a third array to store the length of the buffer to be
unmapped, and as suggested by David Miller, this commit refactors the
tx_buffs[] and tx_skb[] arrays of 'struct mvpp2_txq_pcpu' into a
separate structure 'mvpp2_txq_pcpu_buf', to which a 'size' field is
added. Therefore, instead of having three arrays to allocate/free, we
have a single one, which also improve data locality, reducing the
impact on the CPU cache.

Fixes: 71ce391dfb784 ("net: mvpp2: enable proper per-CPU TX buffers unmapping")
Reported-by: Raphael G <raphael.glon@corp.ovh.com>
Cc: Raphael G <raphael.glon@corp.ovh.com>
Cc: stable@vger.kernel.org
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
---
Changes since v1:
 - As requested by David Miller, move the per TX buffer fields into a
   separate structure, 'struct mvpp2_txq_pcpu_buf', so that instead of
   allocating three arrays, we allocate a single array.

 drivers/net/ethernet/marvell/mvpp2.c | 59 ++++++++++++++++++------------------
 1 file changed, 30 insertions(+), 29 deletions(-)

diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c
index 1026c45..930c816 100644
--- a/drivers/net/ethernet/marvell/mvpp2.c
+++ b/drivers/net/ethernet/marvell/mvpp2.c
@@ -770,6 +770,17 @@ struct mvpp2_rx_desc {
 	u32 reserved8;
 };
 
+struct mvpp2_txq_pcpu_buf {
+	/* Transmitted SKB */
+	struct sk_buff *skb;
+
+	/* Physical address of transmitted buffer */
+	dma_addr_t phys;
+
+	/* Size transmitted */
+	size_t size;
+};
+
 /* Per-CPU Tx queue control */
 struct mvpp2_txq_pcpu {
 	int cpu;
@@ -785,11 +796,8 @@ struct mvpp2_txq_pcpu {
 	/* Number of Tx DMA descriptors reserved for each CPU */
 	int reserved_num;
 
-	/* Array of transmitted skb */
-	struct sk_buff **tx_skb;
-
-	/* Array of transmitted buffers' physical addresses */
-	dma_addr_t *tx_buffs;
+	/* Infos about transmitted buffers */
+	struct mvpp2_txq_pcpu_buf *buffs;
 
 	/* Index of last TX DMA descriptor that was inserted */
 	int txq_put_index;
@@ -979,10 +987,11 @@ static void mvpp2_txq_inc_put(struct mvpp2_txq_pcpu *txq_pcpu,
 			      struct sk_buff *skb,
 			      struct mvpp2_tx_desc *tx_desc)
 {
-	txq_pcpu->tx_skb[txq_pcpu->txq_put_index] = skb;
-	if (skb)
-		txq_pcpu->tx_buffs[txq_pcpu->txq_put_index] =
-							 tx_desc->buf_phys_addr;
+	struct mvpp2_txq_pcpu_buf *tx_buf =
+		txq_pcpu->buffs + txq_pcpu->txq_put_index;
+	tx_buf->skb = skb;
+	tx_buf->size = tx_desc->data_size;
+	tx_buf->phys = tx_desc->buf_phys_addr;
 	txq_pcpu->txq_put_index++;
 	if (txq_pcpu->txq_put_index == txq_pcpu->size)
 		txq_pcpu->txq_put_index = 0;
@@ -4401,17 +4410,16 @@ static void mvpp2_txq_bufs_free(struct mvpp2_port *port,
 	int i;
 
 	for (i = 0; i < num; i++) {
-		dma_addr_t buf_phys_addr =
-				    txq_pcpu->tx_buffs[txq_pcpu->txq_get_index];
-		struct sk_buff *skb = txq_pcpu->tx_skb[txq_pcpu->txq_get_index];
+		struct mvpp2_txq_pcpu_buf *tx_buf =
+			txq_pcpu->buffs + txq_pcpu->txq_get_index;
 
 		mvpp2_txq_inc_get(txq_pcpu);
 
-		dma_unmap_single(port->dev->dev.parent, buf_phys_addr,
-				 skb_headlen(skb), DMA_TO_DEVICE);
-		if (!skb)
+		dma_unmap_single(port->dev->dev.parent, tx_buf->phys,
+				 tx_buf->size, DMA_TO_DEVICE);
+		if (!tx_buf->skb)
 			continue;
-		dev_kfree_skb_any(skb);
+		dev_kfree_skb_any(tx_buf->skb);
 	}
 }
 
@@ -4651,15 +4659,10 @@ static int mvpp2_txq_init(struct mvpp2_port *port,
 	for_each_present_cpu(cpu) {
 		txq_pcpu = per_cpu_ptr(txq->pcpu, cpu);
 		txq_pcpu->size = txq->size;
-		txq_pcpu->tx_skb = kmalloc(txq_pcpu->size *
-					   sizeof(*txq_pcpu->tx_skb),
-					   GFP_KERNEL);
-		if (!txq_pcpu->tx_skb)
-			goto error;
-
-		txq_pcpu->tx_buffs = kmalloc(txq_pcpu->size *
-					     sizeof(dma_addr_t), GFP_KERNEL);
-		if (!txq_pcpu->tx_buffs)
+		txq_pcpu->buffs = kmalloc(txq_pcpu->size *
+					  sizeof(struct mvpp2_txq_pcpu_buf),
+					  GFP_KERNEL);
+		if (!txq_pcpu->buffs)
 			goto error;
 
 		txq_pcpu->count = 0;
@@ -4673,8 +4676,7 @@ static int mvpp2_txq_init(struct mvpp2_port *port,
 error:
 	for_each_present_cpu(cpu) {
 		txq_pcpu = per_cpu_ptr(txq->pcpu, cpu);
-		kfree(txq_pcpu->tx_skb);
-		kfree(txq_pcpu->tx_buffs);
+		kfree(txq_pcpu->buffs);
 	}
 
 	dma_free_coherent(port->dev->dev.parent,
@@ -4693,8 +4695,7 @@ static void mvpp2_txq_deinit(struct mvpp2_port *port,
 
 	for_each_present_cpu(cpu) {
 		txq_pcpu = per_cpu_ptr(txq->pcpu, cpu);
-		kfree(txq_pcpu->tx_skb);
-		kfree(txq_pcpu->tx_buffs);
+		kfree(txq_pcpu->buffs);
 	}
 
 	if (txq->descs)
-- 
2.7.4

^ permalink raw reply related

* Re: Soft lockup in tc_classify
From: Daniel Borkmann @ 2016-12-21 10:15 UTC (permalink / raw)
  To: Cong Wang, Shahar Klein
  Cc: Or Gerlitz, Linux Netdev List, Roi Dayan, David Miller,
	Jiri Pirko, John Fastabend, Hadar Hen Zion
In-Reply-To: <CAM_iQpX3jL4URUQn5+-wV+3g2UKHWGBWYHr31wyZk2FQKVCzVA@mail.gmail.com>

On 12/21/2016 08:03 AM, Cong Wang wrote:
> On Tue, Dec 20, 2016 at 10:44 PM, Shahar Klein <shahark@mellanox.com> wrote:
[...]
> Looks like you added a debug printk inside tcf_destroy() too,
> which seems racy with filter creation, it should not happen since
> in both cases we take RTNL lock.
>
> Don't know if changing all RCU_INIT_POINTER in that file to
> rcu_assign_pointer could help anything or not. Mind to try?

I don't think at this point that it's RCU related at all.

I have a theory on what is happening. Quoting the piece in question from Shahar's log:

  1: thread-2845[cpu-1] setting tp_created to 1 tp=ffff94b5b0280780 back=ffff94b9ea932060
  2: thread-2856[cpu-1] setting tp_created to 1 tp=ffff94b9ea9322a0 back=ffff94b9ea932060
  3: thread-2843[cpu-1] setting tp_created to 1 tp=ffff94b5b402c960 back=ffff94b9ea932060
  4: destroy ffff94b5b669fea0 tcf_destroy:1905
  5: thread-2853[cpu-1] setting tp_created to 1 tp=ffff94b5b02805a0 back=ffff94b9ea932060
  6: thread-2853[cpu-1] add/change filter by: fl_get [cls_flower] tp=ffff94b5b02805a0 tp->next=ffff94b9ea932060
  7: destroy ffff94b5b0280780 tcf_destroy:1905
  8: thread-2845[cpu-1] add/change filter by: fl_get [cls_flower] tp=ffff94b5b02805a0 tp->next=ffff94b5b02805a0

The interesting thing is that all this happens on CPU1, so as you say we're under rtnl.
In 1), thread-2845 creates tp=ffff94b5b0280780, which is destroyed in 7), presumably also
by thread-2845, and the weird part is why suddenly in 8) thread-2845 adds a created filter
without actually creating it. Plus, thread-2845 got interrupted, which means it must have
dropped rntl in the middle. We drop it in tc_ctl_tfilter() when we do tcf_proto_lookup_ops()
and need to pull in a module, but here this doesn't make sense at all since i) at this
point we haven't created the tp yet and 2) flower was already there. Thus the only explanation
where this must have happened is where we called tp->ops->change(). So here the return
code must have been -EAGAIN, which makes sense because in 7) we destroyed that specific
tp instance. Which means we goto replay but *do not* clear tp_created. I think that is
the bug in question. So, while we dropped rtnl in the meantime, some other tp instance
was added (tp=ffff94b5b02805a0) that we had a match on in round 2, but we still think it
was newly created which wasn't the actual case. So we'd need to deal with the fact that
->change() callback could return -EAGAIN as well. Now looking at flower, I think the call
chain must have been fl_change() -> fl_set_parms() -> tcf_exts_validate() -> tcf_action_init()
-> tcf_action_init_1(). And here one possibility I see is that tc_lookup_action_n()
failed, therefore we shortly dropped rtnl for the request_module() where the module
got loaded successfully and thus error code from there is -EAGAIN that got propagated
all the way through ->change() from tc_ctl_tfilter(). So it looks like a generic issue
not specifically tied to flower.

Shahar, can you test the following? Thanks!

  net/sched/cls_api.c | 4 +++-
  1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c
index 3fbba79..1ecdf80 100644
--- a/net/sched/cls_api.c
+++ b/net/sched/cls_api.c
@@ -148,13 +148,15 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n)
  	unsigned long cl;
  	unsigned long fh;
  	int err;
-	int tp_created = 0;
+	int tp_created;

  	if ((n->nlmsg_type != RTM_GETTFILTER) &&
  	    !netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN))
  		return -EPERM;

  replay:
+	tp_created = 0;
+
  	err = nlmsg_parse(n, sizeof(*t), tca, TCA_MAX, NULL);
  	if (err < 0)
  		return err;
-- 
1.9.3

^ permalink raw reply related

* Re: [PATCH v3 net-next 3/3] arm64: dts: marvell: Add ethernet switch definition for the ESPRESSObin
From: Gregory CLEMENT @ 2016-12-21  9:41 UTC (permalink / raw)
  To: Romain Perier
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, Jason Cooper,
	Sebastian Hesselbarth, netdev, devicetree, Rob Herring,
	Ian Campbell, Pawel Moll, Mark Rutland, Kumar Gala,
	linux-arm-kernel, Thomas Petazzoni, Nadav Haklai
In-Reply-To: <20161221090045.474-4-romain.perier@free-electrons.com>

Hi Romain,
 
 On mer., déc. 21 2016, Romain Perier <romain.perier@free-electrons.com> wrote:

> This defines and enables the Marvell ethernet switch MVE886341 on the
> Marvell ESPRESSObin board.

This patch looks OK now.

Applied on mvebu/dt64-4.11

So you can remove it from you next version because I don't want this
patch will be applied through the netdev branch. Indeed I expect more
changes in this file for v4.11 and it will be easier to have the change
in a single branch to avoid the merge conflict.

Thanks,

Gregory


>
> Signed-off-by: Romain Perier <romain.perier@free-electrons.com>
> ---
>
> Changes in v3:
>  - Changed the node switch0 to be at 1
>  - Removed reg=<1> from the mdio node, finally that's not required
>
> Changes in v2:
>  - EXPRESSObin -> ESPRESSObin
>  - phy nodes definition must contain the internal bus address after the @
>
>  .../boot/dts/marvell/armada-3720-espressobin.dts   | 66 ++++++++++++++++++++++
>  1 file changed, 66 insertions(+)
>
> diff --git a/arch/arm64/boot/dts/marvell/armada-3720-espressobin.dts b/arch/arm64/boot/dts/marvell/armada-3720-espressobin.dts
> index 83178d9..12d9f65 100644
> --- a/arch/arm64/boot/dts/marvell/armada-3720-espressobin.dts
> +++ b/arch/arm64/boot/dts/marvell/armada-3720-espressobin.dts
> @@ -80,3 +80,69 @@
>  &usb3 {
>  	status = "okay";
>  };
> +
> +&mdio {
> +	switch0: switch0@1 {
> +		compatible = "marvell,mv88e6085";
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +		reg = <1>;
> +
> +		dsa,member = <0 0>;
> +
> +		ports {
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +
> +			port@0 {
> +				reg = <0>;
> +				label = "cpu";
> +				ethernet = <&eth0>;
> +			};
> +
> +			port@1 {
> +				reg = <1>;
> +				label = "wan";
> +				phy-handle = <&switch0phy0>;
> +			};
> +
> +			port@2 {
> +				reg = <2>;
> +				label = "lan0";
> +				phy-handle = <&switch0phy1>;
> +			};
> +
> +			port@3 {
> +				reg = <3>;
> +				label = "lan1";
> +				phy-handle = <&switch0phy2>;
> +			};
> +
> +		};
> +
> +		mdio {
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +
> +			switch0phy0: switch0phy0@11 {
> +				reg = <0x11>;
> +			};
> +			switch0phy1: switch0phy1@12 {
> +				reg = <0x12>;
> +			};
> +			switch0phy2: switch0phy2@13 {
> +				reg = <0x13>;
> +			};
> +		};
> +	};
> +};
> +
> +&eth0 {
> +	phy-mode = "rgmii-id";
> +	status = "okay";
> +
> +	fixed-link {
> +		speed = <1000>;
> +		full-duplex;
> +	};
> +};
> -- 
> 2.9.3
>

-- 
Gregory Clement, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com

^ permalink raw reply

* Re: Soft lockup in tc_classify
From: Shahar Klein @ 2016-12-21  6:44 UTC (permalink / raw)
  To: Daniel Borkmann, Cong Wang
  Cc: shahark, Or Gerlitz, Linux Netdev List, Roi Dayan, David Miller,
	Jiri Pirko, John Fastabend, Hadar Hen Zion
In-Reply-To: <58591A46.4060703@iogearbox.net>

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



On 12/20/2016 1:47 PM, Daniel Borkmann wrote:
> Hi Shahar,
>
> On 12/20/2016 07:22 AM, Shahar Klein wrote:
>> On 12/19/2016 7:58 PM, Cong Wang wrote:
>>> On Mon, Dec 19, 2016 at 8:39 AM, Shahar Klein <shahark@mellanox.com>
>>> wrote:
>>>> On 12/13/2016 12:51 AM, Cong Wang wrote:
>>>>>
>>>>> On Mon, Dec 12, 2016 at 1:18 PM, Or Gerlitz <gerlitz.or@gmail.com>
>>>>> wrote:
>>>>>>
>>>>>> On Mon, Dec 12, 2016 at 3:28 PM, Daniel Borkmann
>>>>>> <daniel@iogearbox.net>
>>>>>> wrote:
>>>>>>
>>>>>>> Note that there's still the RCU fix missing for the deletion race
>>>>>>> that
>>>>>>> Cong will still send out, but you say that the only thing you do
>>>>>>> is to
>>>>>>> add a single rule, but no other operation in involved during that
>>>>>>> test?
>>>>>>
>>>>>> What's missing to have the deletion race fixed? making a patch or
>>>>>> testing to a patch which was sent?
>>>>>
>>>>> If you think it would help for this problem, here is my patch rebased
>>>>> on the latest net-next.
>>>>>
>>>>> Again, I don't see how it could help this case yet, especially I don't
>>>>> see how we could have a loop in this singly linked list.
>>>>
>>>> I've applied cong's patch and hit a different lockup(full log
>>>> attached):
>>>
>>> Are you sure this is really different? For me, it is still inside the
>>> loop
>>> in tc_classify(), with only a slightly different offset.
>>>
>>>>
>>>> Daniel suggested I'll add a print:
>>>>                 case RTM_DELTFILTER:
>>>> -                   err = tp->ops->delete(tp, fh);
>>>> +                 printk(KERN_ERR "DEBUGG:SK %s:%d\n", __func__,
>>>> __LINE__);
>>>> +                 err = tp->ops->delete(tp, fh, &last);
>>>>                         if (err == 0) {
>>>>
>>>> and I couldn't see this print in the output.....
>>>
>>> Hmm, that is odd, if this never prints, then my patch should not make
>>> any
>>> difference.
>>>
>>> There are still two other cases where we could change tp->next, so do
>>> you
>>> mind to add two more printk's for debugging?
>>>
>>> Attached is the delta patch.
>>>
>>> Thanks!
>>
>> I've added a slightly different debug print:
>> @@ -368,11 +375,12 @@ static int tc_ctl_tfilter(struct sk_buff *skb,
>> struct nlmsghdr *n)
>>                  if (tp_created) {
>>                          RCU_INIT_POINTER(tp->next,
>> rtnl_dereference(*back));
>>                          rcu_assign_pointer(*back, tp);
>> +                 printk(KERN_ERR "DEBUGG:SK add/change filter by: %pf
>> tp=%p tp->next=%p\n", tp->ops->get, tp, tp->next);
>>                  }
>>                  tfilter_notify(net, skb, n, tp, fh, RTM_NEWTFILTER,
>> false);
>
> I'm curious, could you be a bit more verbose why you didn't go with Cong's
> debug patch?
>
> In particular, why you removed the hunk from the condition
> 'n->nlmsg_type ==
> RTM_DELTFILTER && t->tcm_handle == 0' where we delete the whole tp
> instance?

I didn't remove this hunk, just added more. anyway I'm attaching the 
diff to make sure I didn't miss anything.
I've added some more prints in the destroy and added some current info
run log attached also

>
> Is it because if you have that printk() there, then the issue doesn't
> trigger
> for you anymore? Or any other reason?
>
> How many CPUs does your test machine have, I suspect more than 1, right?
>
> So iff RTM_DELTFILTER with tcm_handle of 0 really played a role in this,
> I'm
> wondering whether there was a subtle deletion + add race where the newly
> added
> filter on the other CPU still saw a stale pointer in the list. But just
> a wild
> guess at this point.
>
> Hmm, could you try this below to see whether the issue still appears?
>
> Thanks,
> Daniel
>
> diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c
> index 3fbba79..4eee1cb 100644
> --- a/net/sched/cls_api.c
> +++ b/net/sched/cls_api.c
> @@ -317,7 +317,7 @@ static int tc_ctl_tfilter(struct sk_buff *skb,
> struct nlmsghdr *n)
>          if (n->nlmsg_type == RTM_DELTFILTER && t->tcm_handle == 0) {
>              struct tcf_proto *next = rtnl_dereference(tp->next);
>
> -            RCU_INIT_POINTER(*back, next);
> +            rcu_assign_pointer(*back, next);
>
>              tfilter_notify(net, skb, n, tp, fh,
>                         RTM_DELTFILTER, false);
>
>

Tried it with same results

>> full output attached:
>>
>> [  283.290271] Mirror/redirect action on
>> [  283.305031] DEBUGG:SK add/change filter by: fl_get [cls_flower]
>> tp=ffff9432d704df60 tp->next=          (null)
>> [  283.322563] DEBUGG:SK add/change filter by: fl_get [cls_flower]
>> tp=ffff9436e718d240 tp->next=          (null)
>> [  283.359997] GACT probability on
>> [  283.365923] DEBUGG:SK add/change filter by: fl_get [cls_flower]
>> tp=ffff9436e718d3c0 tp->next=ffff9436e718d240
>> [  283.378725] DEBUGG:SK add/change filter by: fl_get [cls_flower]
>> tp=ffff9436e718d3c0 tp->next=ffff9436e718d3c0
>> [  283.391310] DEBUGG:SK add/change filter by: fl_get [cls_flower]
>> tp=ffff9436e718d3c0 tp->next=ffff9436e718d3c0
>> [  283.403923] DEBUGG:SK add/change filter by: fl_get [cls_flower]
>> tp=ffff9436e718d3c0 tp->next=ffff9436e718d3c0
>> [  283.416542] DEBUGG:SK add/change filter by: fl_get [cls_flower]
>> tp=ffff9436e718d3c0 tp->next=ffff9436e718d3c0
>> [  308.538571] NMI watchdog: BUG: soft lockup - CPU#0 stuck for 23s!
>> [swapper/0:0]
>>
>> Thanks
>> Shahar

[-- Attachment #2: cls_api.c.diff --]
[-- Type: text/plain, Size: 2508 bytes --]

diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c
index 3fbba79..1b31fc9 100644
--- a/net/sched/cls_api.c
+++ b/net/sched/cls_api.c
@@ -305,7 +305,7 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n)
 			kfree(tp);
 			goto errout;
 		}
-
+		printk(KERN_ERR "DEBUGG:SK thread-%d[cpu-%d] setting tp_created to 1 tp=%p back=%p\n", current->pid, current->on_cpu, tp, rtnl_dereference(*back));
 		tp_created = 1;
 
 	} else if (tca[TCA_KIND] && nla_strcmp(tca[TCA_KIND], tp->ops->kind))
@@ -317,11 +317,13 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n)
 		if (n->nlmsg_type == RTM_DELTFILTER && t->tcm_handle == 0) {
 			struct tcf_proto *next = rtnl_dereference(tp->next);
 
-			RCU_INIT_POINTER(*back, next);
+			printk(KERN_ERR "DEBUGG:SK delete filter by: %pf\n", tp->ops->get);
+
+			rcu_assign_pointer(*back, next);
 
 			tfilter_notify(net, skb, n, tp, fh,
 				       RTM_DELTFILTER, false);
-			tcf_destroy(tp, true);
+			tcf_destroy(tp);
 			err = 0;
 			goto errout;
 		}
@@ -331,25 +333,30 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n)
 		    !(n->nlmsg_flags & NLM_F_CREATE))
 			goto errout;
 	} else {
+		bool last;
+
 		switch (n->nlmsg_type) {
 		case RTM_NEWTFILTER:
 			err = -EEXIST;
 			if (n->nlmsg_flags & NLM_F_EXCL) {
 				if (tp_created)
-					tcf_destroy(tp, true);
+					tcf_destroy(tp);
 				goto errout;
 			}
 			break;
 		case RTM_DELTFILTER:
-			err = tp->ops->delete(tp, fh);
+			printk(KERN_ERR "DEBUGG:SK %s:%d\n", __func__, __LINE__);
+			err = tp->ops->delete(tp, fh, &last);
 			if (err == 0) {
-				struct tcf_proto *next = rtnl_dereference(tp->next);
-
 				tfilter_notify(net, skb, n, tp,
 					       t->tcm_handle,
 					       RTM_DELTFILTER, false);
-				if (tcf_destroy(tp, false))
+				if (last) {
+					struct tcf_proto *next = rtnl_dereference(tp->next);
+
 					RCU_INIT_POINTER(*back, next);
+					tcf_destroy(tp);
+				}
 			}
 			goto errout;
 		case RTM_GETTFILTER:
@@ -368,11 +375,12 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n)
 		if (tp_created) {
 			RCU_INIT_POINTER(tp->next, rtnl_dereference(*back));
 			rcu_assign_pointer(*back, tp);
+			printk(KERN_ERR "DEBUGG:SK thread-%d[cpu-%d] add/change filter by: %pf tp=%p tp->next=%p\n", current->pid, current->on_cpu, tp->ops->get, tp, tp->next);
 		}
 		tfilter_notify(net, skb, n, tp, fh, RTM_NEWTFILTER, false);
 	} else {
 		if (tp_created)
-			tcf_destroy(tp, true);
+			tcf_destroy(tp);
 	}
 
 errout:

[-- Attachment #3: tp_p_debug_pid.log --]
[-- Type: text/plain, Size: 17231 bytes --]

[  354.117396] DEBUGG:SK tcf_destroy_chain:1915
[  354.123731] DEBUGG:SK tcf_destroy_chain:1915
[  354.128962] DEBUGG:SK tcf_destroy_chain:1915
[  354.134999] DEBUGG:SK tcf_destroy_chain:1915
[  354.145185] DEBUGG:SK tcf_destroy_chain:1915
[  354.155601] DEBUGG:SK tcf_destroy_chain:1915

[  408.197159] DEBUGG:SK thread-2852[cpu-1] setting tp_created to 1 tp=ffff94b5b669f8a0 back=          (null)
[  408.246733] Mirror/redirect action on
[  408.251498] DEBUGG:SK destroy ffff94b5b669f8a0 tcf_destroy:1905
[  408.258485] DEBUGG:SK thread-2852[cpu-1] setting tp_created to 1 tp=ffff94b5b669f900 back=          (null)
[  408.280217] DEBUGG:SK thread-2852[cpu-1] add/change filter by: fl_get [cls_flower] tp=ffff94b5b669f900 tp->next=          (null)
[  408.294257] DEBUGG:SK thread-2858[cpu-1] setting tp_created to 1 tp=ffff94b5b5de2060 back=          (null)
[  408.305684] DEBUGG:SK thread-2849[cpu-1] setting tp_created to 1 tp=ffff94b9df4fd420 back=          (null)
[  408.317103] DEBUGG:SK thread-2856[cpu-1] setting tp_created to 1 tp=ffff94b9ea932060 back=          (null)
[  408.333832] DEBUGG:SK thread-2856[cpu-1] add/change filter by: fl_get [cls_flower] tp=ffff94b9ea932060 tp->next=          (null)
[  408.347806] GACT probability on
[  408.348815] DEBUGG:SK thread-2851[cpu-1] setting tp_created to 1 tp=ffff94b5bfaab900 back=ffff94b9ea932060
[  408.348841] DEBUGG:SK thread-2854[cpu-1] setting tp_created to 1 tp=ffff94b5b669fcc0 back=ffff94b9ea932060
[  408.349841] DEBUGG:SK thread-2846[cpu-1] setting tp_created to 1 tp=ffff94b5b4ea9720 back=ffff94b9ea932060
[  408.349868] DEBUGG:SK thread-2847[cpu-1] setting tp_created to 1 tp=ffff94b9ec249ea0 back=ffff94b9ea932060
[  408.350880] DEBUGG:SK thread-2848[cpu-1] setting tp_created to 1 tp=ffff94b9e9542ea0 back=ffff94b9ea932060
[  408.355302] DEBUGG:SK thread-2855[cpu-1] setting tp_created to 1 tp=ffff94b5b402ca80 back=ffff94b9ea932060
[  408.358941] DEBUGG:SK thread-2844[cpu-1] setting tp_created to 1 tp=ffff94b5b0280960 back=ffff94b9ea932060
[  408.358962] DEBUGG:SK thread-2852[cpu-1] setting tp_created to 1 tp=ffff94b9e8e17840 back=ffff94b9ea932060
[  408.358986] DEBUGG:SK thread-2842[cpu-1] setting tp_created to 1 tp=ffff94b5b5de2f00 back=ffff94b9ea932060
[  408.360920] DEBUGG:SK thread-2850[cpu-1] setting tp_created to 1 tp=ffff94b5cfe38ea0 back=ffff94b9ea932060
[  408.360940] DEBUGG:SK thread-2857[cpu-1] setting tp_created to 1 tp=ffff94b5b0280840 back=ffff94b9ea932060
[  408.361827] DEBUGG:SK thread-2856[cpu-1] setting tp_created to 1 tp=ffff94b9ea9321e0 back=          (null)
[  408.362812] DEBUGG:SK thread-2856[cpu-1] add/change filter by: fl_get [cls_flower] tp=ffff94b9ea9321e0 tp->next=          (null)
[  408.362850] DEBUGG:SK thread-2853[cpu-1] setting tp_created to 1 tp=ffff94b5b669fea0 back=ffff94b9ea932060
[  408.362895] DEBUGG:SK thread-2845[cpu-1] setting tp_created to 1 tp=ffff94b5b0280780 back=ffff94b9ea932060
[  408.362904] DEBUGG:SK thread-2856[cpu-1] setting tp_created to 1 tp=ffff94b9ea9322a0 back=ffff94b9ea932060
[  408.362942] DEBUGG:SK thread-2843[cpu-1] setting tp_created to 1 tp=ffff94b5b402c960 back=ffff94b9ea932060
[  408.547689] DEBUGG:SK destroy ffff94b5b669fea0 tcf_destroy:1905
[  408.554689] DEBUGG:SK thread-2853[cpu-1] setting tp_created to 1 tp=ffff94b5b02805a0 back=ffff94b9ea932060
[  408.574258] DEBUGG:SK thread-2853[cpu-1] add/change filter by: fl_get [cls_flower] tp=ffff94b5b02805a0 tp->next=ffff94b9ea932060
[  408.587849] DEBUGG:SK destroy ffff94b5b0280780 tcf_destroy:1905
[  408.595862] DEBUGG:SK thread-2845[cpu-1] add/change filter by: fl_get [cls_flower] tp=ffff94b5b02805a0 tp->next=ffff94b5b02805a0
[  408.609476] DEBUGG:SK destroy ffff94b9ec249ea0 tcf_destroy:1905
[  408.617394] DEBUGG:SK thread-2847[cpu-1] add/change filter by: fl_get [cls_flower] tp=ffff94b5b02805a0 tp->next=ffff94b5b02805a0
[  408.631068] DEBUGG:SK destroy ffff94b9df4fd420 tcf_destroy:1905
[  408.639090] DEBUGG:SK thread-2849[cpu-1] add/change filter by: fl_get [cls_flower] tp=ffff94b5b02805a0 tp->next=ffff94b5b02805a0
[  408.652656] DEBUGG:SK destroy ffff94b5b5de2060 tcf_destroy:1905
[  408.660530] DEBUGG:SK thread-2858[cpu-1] add/change filter by: fl_get [cls_flower] tp=ffff94b5b02805a0 tp->next=ffff94b5b02805a0
[  432.583406] NMI watchdog: BUG: soft lockup - CPU#0 stuck for 22s! [CPU 0/KVM:2574]
[  432.592457] Modules linked in: act_gact act_mirred openvswitch nf_conntrack_ipv6 nf_nat_ipv6 nf_defrag_ipv6 vfio_pci vfio_virqfd vfio_iommu_type1 vfio cls_flower mlx5_ib mlx5_core devlink sch_ingress nfsv3 nfs fscache xt_CHECKSUM iptable_mangle ipt_MASQUERADE nf_nat_masquerade_ipv4 iptable_nat nf_nat_ipv4 nf_nat libcrc32c nf_conntrack_ipv4 nf_defrag_ipv4 xt_conntrack nf_conntrack tun ebtable_filter ebtables ip6table_filter ip6_tables netconsole rpcrdma ib_isert iscsi_target_mod ib_iser libiscsi scsi_transport_iscsi bridge stp ib_srpt llc ib_srp scsi_transport_srp ib_ipoib rdma_ucm ib_ucm ib_uverbs ib_umad rdma_cm ib_cm iw_cm ib_core intel_rapl sb_edac edac_core x86_pkg_temp_thermal coretemp kvm_intel kvm irqbypass crct10dif_pclmul igb joydev crc32_pclmul ipmi_ssif crc32c_intel iTCO_wdt ptp ghash_clmulni_intel pps_core iTCO_vendor_support i2c_algo_bit pcspkr ipmi_si wmi ipmi_msghandler ioatdma mei_me lpc_ich tpm_tis dca tpm_tis_core mei shpchp i2c_i801 i2c_smbus tpm target_core_mod nfsd auth_rpcgss nfs_acl lockd grace sunrpc isci libsas serio_raw scsi_transport_sas [last unloaded: devlink]
[  432.714385] CPU: 0 PID: 2574 Comm: CPU 0/KVM Not tainted 4.9.0+ #40
[  432.721734] Hardware name: Supermicro X9DRW/X9DRW, BIOS 3.0a 08/08/2013
[  432.729469] task: ffff94b5b69d3b00 task.stack: ffffad7948840000
[  432.736428] RIP: 0010:tc_classify+0x5a/0x120
[  432.741526] RSP: 0018:ffff94b5efa03c38 EFLAGS: 00000286 ORIG_RAX: ffffffffffffff10
[  432.750677] RAX: 00000000ffffffff RBX: ffff94b5b159a100 RCX: 0000000000000000
[  432.758982] RDX: ffff94b5efa03c98 RSI: ffff94b5b02805a0 RDI: ffff94b5b159a100
[  432.767291] RBP: ffff94b5efa03c70 R08: 000000000000270f R09: 0000000000000000
[  432.775603] R10: 0000000000000000 R11: 0000000000000004 R12: ffff94b5efa03c98
[  432.783907] R13: 0000000000000008 R14: ffff94b5b02805a0 R15: 0000000000000001
[  432.792226] FS:  00007f35c1aec700(0000) GS:ffff94b5efa00000(0000) knlGS:0000000000000000
[  432.801861] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[  432.808626] CR2: 0000562a2a56b0c8 CR3: 000000042c714000 CR4: 00000000000426f0
[  432.816932] Call Trace:
[  432.819993]  <IRQ>
[  432.822580]  __netif_receive_skb_core+0x623/0xa00
[  432.828173]  ? udp4_gro_receive+0x10b/0x2d0
[  432.833189]  __netif_receive_skb+0x18/0x60
[  432.838095]  netif_receive_skb_internal+0x40/0xb0
[  432.843692]  napi_gro_receive+0xcd/0x120
[  432.848425]  mlx5e_handle_rx_cqe_rep+0x61b/0x890 [mlx5_core]
[  432.855103]  ? kvm_irq_delivery_to_apic+0x2c0/0x2c0 [kvm]
[  432.861482]  mlx5e_poll_rx_cq+0x83/0x840 [mlx5_core]
[  432.867373]  mlx5e_napi_poll+0x89/0x480 [mlx5_core]
[  432.873164]  net_rx_action+0x260/0x3c0
[  432.877693]  __do_softirq+0xc9/0x28c
[  432.882020]  irq_exit+0xd7/0xe0
[  432.885855]  do_IRQ+0x51/0xd0
[  432.889506]  common_interrupt+0x93/0x93
[  432.894140] RIP: 0010:kvm_arch_vcpu_ioctl_run+0xa64/0x1590 [kvm]
[  432.896408] NMI watchdog: BUG: soft lockup - CPU#5 stuck for 23s! [handler129:2849]
[  432.896436] Modules linked in: act_gact act_mirred openvswitch nf_conntrack_ipv6 nf_nat_ipv6 nf_defrag_ipv6 vfio_pci vfio_virqfd vfio_iommu_type1 vfio cls_flower mlx5_ib mlx5_core devlink sch_ingress nfsv3 nfs fscache xt_CHECKSUM iptable_mangle ipt_MASQUERADE nf_nat_masquerade_ipv4 iptable_nat nf_nat_ipv4 nf_nat libcrc32c nf_conntrack_ipv4 nf_defrag_ipv4 xt_conntrack nf_conntrack tun ebtable_filter ebtables ip6table_filter ip6_tables netconsole rpcrdma ib_isert iscsi_target_mod ib_iser libiscsi scsi_transport_iscsi bridge stp ib_srpt llc ib_srp scsi_transport_srp ib_ipoib rdma_ucm ib_ucm ib_uverbs ib_umad rdma_cm ib_cm iw_cm ib_core intel_rapl sb_edac edac_core x86_pkg_temp_thermal coretemp kvm_intel kvm irqbypass crct10dif_pclmul igb joydev crc32_pclmul ipmi_ssif crc32c_intel iTCO_wdt ptp ghash_clmulni_intel
[  432.896448]  pps_core iTCO_vendor_support i2c_algo_bit pcspkr ipmi_si wmi ipmi_msghandler ioatdma mei_me lpc_ich tpm_tis dca tpm_tis_core mei shpchp i2c_i801 i2c_smbus tpm target_core_mod nfsd auth_rpcgss nfs_acl lockd grace sunrpc isci libsas serio_raw scsi_transport_sas [last unloaded: devlink]
[  432.896450] CPU: 5 PID: 2849 Comm: handler129 Not tainted 4.9.0+ #40
[  432.896451] Hardware name: Supermicro X9DRW/X9DRW, BIOS 3.0a 08/08/2013
[  432.896452] task: ffff94b5c1870000 task.stack: ffffad7944bb4000
[  432.896456] RIP: 0010:tc_ctl_tfilter+0x1cc/0x8c0
[  432.896458] RSP: 0018:ffffad7944bb7a88 EFLAGS: 00000286 ORIG_RAX: ffffffffffffff10
[  432.896459] RAX: 0000000000020000 RBX: ffff94b5b02805a0 RCX: 000000000000002c
[  432.896459] RDX: ffff94b9e3729400 RSI: 0000000000000000 RDI: ffff94b5c0663400
[  432.896460] RBP: ffffad7944bb7b88 R08: 0000000000000000 R09: ffffad7944bb7af8
[  432.896461] R10: ffff94b5b02805a0 R11: 00000000ffffffea R12: ffff94b5c0663400
[  432.896462] R13: 00000000ffff0000 R14: ffff94b5e7e9c800 R15: 0000000000030000
[  432.896463] FS:  00007f08c93cb700(0000) GS:ffff94b5efb40000(0000) knlGS:0000000000000000
[  432.896464] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[  432.896465] CR2: 00007f08b80200a8 CR3: 000000085dfad000 CR4: 00000000000426e0
[  432.896465] Call Trace:
[  432.896470]  ? ns_capable+0x13/0x20
[  432.896475]  rtnetlink_rcv_msg+0xa4/0x230
[  432.896478]  ? __kmalloc_node_track_caller+0x191/0x290
[  432.896481]  ? __alloc_skb+0x8d/0x2b0
[  432.896483]  ? rtnl_newlink+0x870/0x870
[  432.896486]  netlink_rcv_skb+0xa7/0xc0
[  432.896488]  rtnetlink_rcv+0x28/0x30
[  432.896490]  netlink_unicast+0x181/0x240
[  432.896491]  netlink_sendmsg+0x32e/0x3b0
[  432.896495]  sock_sendmsg+0x38/0x50
[  432.896496]  ___sys_sendmsg+0x279/0x290
[  432.896500]  ? task_numa_fault+0x7e5/0xa90
[  432.896502]  ? migrate_misplaced_page+0x169/0x240
[  432.896506]  ? handle_mm_fault+0xa0d/0x1520
[  432.896510]  ? __fget_light+0x25/0x60
[  432.896512]  __sys_sendmsg+0x54/0x90
[  432.896514]  SyS_sendmsg+0x12/0x20
[  432.896516]  entry_SYSCALL_64_fastpath+0x1a/0xa9
[  432.896518] RIP: 0033:0x7f08cd4fc2fd
[  432.896519] RSP: 002b:00007f08c938c110 EFLAGS: 00000293 ORIG_RAX: 000000000000002e
[  432.896520] RAX: ffffffffffffffda RBX: 0000000000000001 RCX: 00007f08cd4fc2fd
[  432.896520] RDX: 0000000000000000 RSI: 00007f08c938c170 RDI: 000000000000000e
[  432.896521] RBP: 00007f08b801e8d0 R08: 0000000000008000 R09: 00007f08b8000158
[  432.896522] R10: 00007f08b8020610 R11: 0000000000000293 R12: 000000000000003f
[  432.896522] R13: 0000564e0421ea18 R14: 00007f08b801e8f8 R15: 00007f08c93ca388
[  432.896538] Code: b5 58 ff ff ff 4c 89 e7 ff d0 48 85 c0 49 89 c2 41 bb ea ff ff ff 74 48 41 0f b7 4e 04 48 8b 18 66 83 f9 2d 75 17 e9 64 02 00 00 <8b> 43 1c 41 39 c7 0f 86 8c 00 00 00 49 89 da 48 8b 1b 48 85 db 
[  432.896540] Kernel panic - not syncing: softlockup: hung tasks
[  432.896541] CPU: 5 PID: 2849 Comm: handler129 Tainted: G             L  4.9.0+ #40
[  432.896542] Hardware name: Supermicro X9DRW/X9DRW, BIOS 3.0a 08/08/2013
[  432.896542] Call Trace:
[  432.896543]  <IRQ>
[  432.896546]  dump_stack+0x63/0x8c
[  432.896549]  panic+0xeb/0x239
[  432.896552]  watchdog_timer_fn+0x1e5/0x1f0
[  432.896554]  ? watchdog+0x40/0x40
[  432.896557]  __hrtimer_run_queues+0xee/0x270
[  432.896558]  hrtimer_interrupt+0xa8/0x190
[  432.896563]  local_apic_timer_interrupt+0x35/0x60
[  432.896565]  smp_apic_timer_interrupt+0x38/0x50
[  432.896566]  apic_timer_interrupt+0x93/0xa0
[  432.896568] RIP: 0010:tc_ctl_tfilter+0x1cc/0x8c0
[  432.896569] RSP: 0018:ffffad7944bb7a88 EFLAGS: 00000286 ORIG_RAX: ffffffffffffff10
[  432.896569] RAX: 0000000000020000 RBX: ffff94b5b02805a0 RCX: 000000000000002c
[  432.896570] RDX: ffff94b9e3729400 RSI: 0000000000000000 RDI: ffff94b5c0663400
[  432.896571] RBP: ffffad7944bb7b88 R08: 0000000000000000 R09: ffffad7944bb7af8
[  432.896572] R10: ffff94b5b02805a0 R11: 00000000ffffffea R12: ffff94b5c0663400
[  432.896573] R13: 00000000ffff0000 R14: ffff94b5e7e9c800 R15: 0000000000030000
[  432.896573]  </IRQ>
[  432.896575]  ? tc_ctl_tfilter+0x1ab/0x8c0
[  432.896577]  ? ns_capable+0x13/0x20
[  432.896579]  rtnetlink_rcv_msg+0xa4/0x230
[  432.896580]  ? __kmalloc_node_track_caller+0x191/0x290
[  432.896581]  ? __alloc_skb+0x8d/0x2b0
[  432.896583]  ? rtnl_newlink+0x870/0x870
[  432.896585]  netlink_rcv_skb+0xa7/0xc0
[  432.896586]  rtnetlink_rcv+0x28/0x30
[  432.896588]  netlink_unicast+0x181/0x240
[  432.896590]  netlink_sendmsg+0x32e/0x3b0
[  432.896592]  sock_sendmsg+0x38/0x50
[  432.896593]  ___sys_sendmsg+0x279/0x290
[  432.896594]  ? task_numa_fault+0x7e5/0xa90
[  432.896596]  ? migrate_misplaced_page+0x169/0x240
[  432.896597]  ? handle_mm_fault+0xa0d/0x1520
[  432.896599]  ? __fget_light+0x25/0x60
[  432.896601]  __sys_sendmsg+0x54/0x90
[  432.896603]  SyS_sendmsg+0x12/0x20
[  432.896605]  entry_SYSCALL_64_fastpath+0x1a/0xa9
[  432.896605] RIP: 0033:0x7f08cd4fc2fd
[  432.896606] RSP: 002b:00007f08c938c110 EFLAGS: 00000293 ORIG_RAX: 000000000000002e
[  432.896607] RAX: ffffffffffffffda RBX: 0000000000000001 RCX: 00007f08cd4fc2fd
[  432.896607] RDX: 0000000000000000 RSI: 00007f08c938c170 RDI: 000000000000000e
[  432.896608] RBP: 00007f08b801e8d0 R08: 0000000000008000 R09: 00007f08b8000158
[  432.896609] R10: 00007f08b8020610 R11: 0000000000000293 R12: 000000000000003f
[  432.896609] R13: 0000564e0421ea18 R14: 00007f08b801e8f8 R15: 00007f08c93ca388
[  432.900868] ------------[ cut here ]------------
[  432.900872] WARNING: CPU: 0 PID: 2574 at arch/x86/kernel/smp.c:127 native_smp_send_reschedule+0x3f/0x50
[  432.900893] Modules linked in: act_gact act_mirred openvswitch nf_conntrack_ipv6 nf_nat_ipv6 nf_defrag_ipv6 vfio_pci vfio_virqfd vfio_iommu_type1 vfio cls_flower mlx5_ib mlx5_core devlink sch_ingress nfsv3 nfs fscache xt_CHECKSUM iptable_mangle ipt_MASQUERADE nf_nat_masquerade_ipv4 iptable_nat nf_nat_ipv4 nf_nat libcrc32c nf_conntrack_ipv4 nf_defrag_ipv4 xt_conntrack nf_conntrack tun ebtable_filter ebtables ip6table_filter ip6_tables netconsole rpcrdma ib_isert iscsi_target_mod ib_iser libiscsi scsi_transport_iscsi bridge stp ib_srpt llc ib_srp scsi_transport_srp ib_ipoib rdma_ucm ib_ucm ib_uverbs ib_umad rdma_cm ib_cm iw_cm ib_core intel_rapl sb_edac edac_core x86_pkg_temp_thermal coretemp kvm_intel kvm irqbypass crct10dif_pclmul igb joydev crc32_pclmul ipmi_ssif crc32c_intel iTCO_wdt ptp ghash_clmulni_intel
[  432.900902]  pps_core iTCO_vendor_support i2c_algo_bit pcspkr ipmi_si wmi ipmi_msghandler ioatdma mei_me lpc_ich tpm_tis dca tpm_tis_core mei shpchp i2c_i801 i2c_smbus tpm target_core_mod nfsd auth_rpcgss nfs_acl lockd grace sunrpc isci libsas serio_raw scsi_transport_sas [last unloaded: devlink]
[  432.900904] CPU: 0 PID: 2574 Comm: CPU 0/KVM Tainted: G             L  4.9.0+ #40
[  432.900905] Hardware name: Supermicro X9DRW/X9DRW, BIOS 3.0a 08/08/2013
[  432.900905] Call Trace:
[  432.900906]  <IRQ>
[  432.900909]  dump_stack+0x63/0x8c
[  432.900911]  __warn+0xd1/0xf0
[  432.900913]  warn_slowpath_null+0x1d/0x20
[  432.900914]  native_smp_send_reschedule+0x3f/0x50
[  432.900917]  try_to_wake_up+0x312/0x390
[  432.900919]  default_wake_function+0x12/0x20
[  432.900921]  pollwake+0x73/0x90
[  432.900922]  ? wake_up_q+0x80/0x80
[  432.900926]  __wake_up_common+0x55/0x90
[  432.900928]  __wake_up_sync_key+0x45/0x60
[  432.900929]  sock_def_readable+0x3d/0x70
[  432.900933]  tun_net_xmit+0x1d8/0x360 [tun]
[  432.900936]  netpoll_start_xmit+0x11d/0x1a0
[  432.900937]  netpoll_send_skb_on_dev+0x19b/0x250
[  432.900945]  __br_forward+0x1b2/0x1d0 [bridge]
[  432.900946]  ? skb_clone+0x4c/0xa0
[  432.900948]  ? __skb_clone+0x2e/0x140
[  432.900951]  deliver_clone+0x39/0x60 [bridge]
[  432.900955]  br_flood+0x13b/0x190 [bridge]
[  432.900958]  br_dev_xmit+0x218/0x2c0 [bridge]
[  432.900959]  netpoll_start_xmit+0x11d/0x1a0
[  432.900961]  ? __alloc_skb+0x5d/0x2b0
[  432.900962]  netpoll_send_skb_on_dev+0x19b/0x250
[  432.900963]  netpoll_send_udp+0x2da/0x440
[  432.900967]  write_msg+0xb2/0xf0 [netconsole]
[  432.900970]  call_console_drivers.isra.10.constprop.29+0xef/0x100
[  432.900971]  console_unlock+0x22d/0x540
[  432.900972]  vprintk_emit+0x2eb/0x4b0
[  432.900975]  ? common_interrupt+0x93/0x93
[  432.900976]  vprintk_default+0x29/0x40
[  432.900978]  printk+0x5d/0x74
[  432.900991]  ? kvm_arch_vcpu_ioctl_run+0xa64/0x1590 [kvm]
[  432.900994]  __show_regs+0x37/0x2e0
[  432.900996]  ? update_stack_state+0x63/0x80
[  432.900998]  ? unwind_next_frame+0x83/0x1f0
[  432.901000]  ? common_interrupt+0x93/0x93
[  432.901001]  ? common_interrupt+0x93/0x93
[  432.901004]  show_trace_log_lvl+0x22d/0x380
[  432.901006]  show_regs+0x9f/0x1a0
[  432.901008]  watchdog_timer_fn+0x197/0x1f0
[  432.901009]  ? watchdog+0x40/0x40
[  432.901011]  __hrtimer_run_queues+0xee/0x270
[  432.901013]  hrtimer_interrupt+0xa8/0x190
[  432.901015]  local_apic_timer_interrupt+0x35/0x60
[  432.901016]  smp_apic_timer_interrupt+0x38/0x50
[  432.901017]  apic_timer_interrupt+0x93/0xa0
[  432.901018] RIP: 0010:tc_classify+0x5a/0x120



^ permalink raw reply related

* Re: [PATCH v3 net-next 1/3] net: dsa: mv88e6xxx: Don't forbid MDIO I/Os for PHY addr >= num_of_ports
From: Gregory CLEMENT @ 2016-12-21  9:06 UTC (permalink / raw)
  To: Romain Perier
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, Jason Cooper,
	Sebastian Hesselbarth, netdev, devicetree, Rob Herring,
	Ian Campbell, Pawel Moll, Mark Rutland, Kumar Gala,
	linux-arm-kernel, Thomas Petazzoni, Nadav Haklai
In-Reply-To: <20161221090045.474-2-romain.perier@free-electrons.com>

Hi Romain,


it seems there is a remaining typo :)

 On mer., déc. 21 2016, Romain Perier <romain.perier@free-electrons.com> wrote:

> Some Marvell ethernet switches have internal ethernet transceivers with
> hardcoded phy addresses. These addresses can be grearer than the number
                                                  greater
> of ports or its value might be different than the associated port number.
> This is for example the case for MV88E6341 that has 6 ports and internal
> Port 1 to Port4 PHYs mapped at SMI addresses from 0x11 to 0x14.
>
> This commits fixes the issue by removing the condition in MDIO callbacks.
>
> Signed-off-by: Romain Perier <romain.perier@free-electrons.com>
> Reviewed-by: Andrew Lunn <andrew@lunn.ch>
> ---
>
> Changes in v2:
>  - Added tag "Reviewed-by" by Andrew
>  - Fixed typo in the commit log
>
>  drivers/net/dsa/mv88e6xxx/chip.c | 6 ------
>  1 file changed, 6 deletions(-)
>
> diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
> index b5f0e1e..76d944e 100644
> --- a/drivers/net/dsa/mv88e6xxx/chip.c
> +++ b/drivers/net/dsa/mv88e6xxx/chip.c
> @@ -2881,9 +2881,6 @@ static int mv88e6xxx_mdio_read(struct mii_bus *bus, int phy, int reg)
>  	u16 val;
>  	int err;
>  
> -	if (phy >= mv88e6xxx_num_ports(chip))
> -		return 0xffff;
> -
>  	mutex_lock(&chip->reg_lock);
>  	err = mv88e6xxx_phy_read(chip, phy, reg, &val);
>  	mutex_unlock(&chip->reg_lock);
> @@ -2896,9 +2893,6 @@ static int mv88e6xxx_mdio_write(struct mii_bus *bus, int phy, int reg, u16 val)
>  	struct mv88e6xxx_chip *chip = bus->priv;
>  	int err;
>  
> -	if (phy >= mv88e6xxx_num_ports(chip))
> -		return 0xffff;
> -
>  	mutex_lock(&chip->reg_lock);
>  	err = mv88e6xxx_phy_write(chip, phy, reg, val);
>  	mutex_unlock(&chip->reg_lock);
> -- 
> 2.9.3
>

-- 
Gregory Clement, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com

^ permalink raw reply

* [PATCH v3 net-next 3/3] arm64: dts: marvell: Add ethernet switch definition for the ESPRESSObin
From: Romain Perier @ 2016-12-21  9:00 UTC (permalink / raw)
  To: Andrew Lunn, Vivien Didelot, Florian Fainelli, Jason Cooper,
	Sebastian Hesselbarth, Gregory Clement
  Cc: netdev, devicetree, Rob Herring, Ian Campbell, Pawel Moll,
	Mark Rutland, Kumar Gala, linux-arm-kernel, Thomas Petazzoni,
	Nadav Haklai, Romain Perier
In-Reply-To: <20161221090045.474-1-romain.perier@free-electrons.com>

This defines and enables the Marvell ethernet switch MVE886341 on the
Marvell ESPRESSObin board.

Signed-off-by: Romain Perier <romain.perier@free-electrons.com>
---

Changes in v3:
 - Changed the node switch0 to be at 1
 - Removed reg=<1> from the mdio node, finally that's not required

Changes in v2:
 - EXPRESSObin -> ESPRESSObin
 - phy nodes definition must contain the internal bus address after the @

 .../boot/dts/marvell/armada-3720-espressobin.dts   | 66 ++++++++++++++++++++++
 1 file changed, 66 insertions(+)

diff --git a/arch/arm64/boot/dts/marvell/armada-3720-espressobin.dts b/arch/arm64/boot/dts/marvell/armada-3720-espressobin.dts
index 83178d9..12d9f65 100644
--- a/arch/arm64/boot/dts/marvell/armada-3720-espressobin.dts
+++ b/arch/arm64/boot/dts/marvell/armada-3720-espressobin.dts
@@ -80,3 +80,69 @@
 &usb3 {
 	status = "okay";
 };
+
+&mdio {
+	switch0: switch0@1 {
+		compatible = "marvell,mv88e6085";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		reg = <1>;
+
+		dsa,member = <0 0>;
+
+		ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			port@0 {
+				reg = <0>;
+				label = "cpu";
+				ethernet = <&eth0>;
+			};
+
+			port@1 {
+				reg = <1>;
+				label = "wan";
+				phy-handle = <&switch0phy0>;
+			};
+
+			port@2 {
+				reg = <2>;
+				label = "lan0";
+				phy-handle = <&switch0phy1>;
+			};
+
+			port@3 {
+				reg = <3>;
+				label = "lan1";
+				phy-handle = <&switch0phy2>;
+			};
+
+		};
+
+		mdio {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			switch0phy0: switch0phy0@11 {
+				reg = <0x11>;
+			};
+			switch0phy1: switch0phy1@12 {
+				reg = <0x12>;
+			};
+			switch0phy2: switch0phy2@13 {
+				reg = <0x13>;
+			};
+		};
+	};
+};
+
+&eth0 {
+	phy-mode = "rgmii-id";
+	status = "okay";
+
+	fixed-link {
+		speed = <1000>;
+		full-duplex;
+	};
+};
-- 
2.9.3

^ permalink raw reply related

* [PATCH v3 net-next 2/3] net: dsa: mv88e6xxx: Add support for ethernet switch 88E6341/88E6141
From: Romain Perier @ 2016-12-21  9:00 UTC (permalink / raw)
  To: Andrew Lunn, Vivien Didelot, Florian Fainelli, Jason Cooper,
	Sebastian Hesselbarth, Gregory Clement
  Cc: netdev-u79uwXL29TY76Z2rM5mHXA, devicetree-u79uwXL29TY76Z2rM5mHXA,
	Rob Herring, Ian Campbell, Pawel Moll, Mark Rutland, Kumar Gala,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	Thomas Petazzoni, Nadav Haklai, Romain Perier
In-Reply-To: <20161221090045.474-1-romain.perier-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>

The Marvell 88E6341 device is single-chip, 6-port ethernet switch with
four integrated 10/100/1000Mbps ethernet transceivers and one high speed
SerDes interfaces. It is compatible with switches of family 88E6352.

This commit adds basic support for this switch by describing its
capabilities to the driver.

Signed-off-by: Romain Perier <romain.perier-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
Reviewed-by: Andrew Lunn <andrew-g2DYL2Zd6BY@public.gmane.org>
---

Changes in v3:
 - Added tag "Reviewed-by" by Andrew
 
Changes in v2:
 - Add a dedicated data structure for the operations of the 88E6341
 - Re-ordered PORT_SWITCH_ID_PROD_NUM_6341 in alphabetic order with other
   macros

 drivers/net/dsa/mv88e6xxx/chip.c      | 42 +++++++++++++++++++++++++++++++++++
 drivers/net/dsa/mv88e6xxx/mv88e6xxx.h |  4 +++-
 2 files changed, 45 insertions(+), 1 deletion(-)

diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index 76d944e..5e97dc4 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -3625,6 +3625,34 @@ static const struct mv88e6xxx_ops mv88e6321_ops = {
 	.reset = mv88e6352_g1_reset,
 };
 
+static const struct mv88e6xxx_ops mv88e6341_ops = {
+	/* MV88E6XXX_FAMILY_6352 */
+	.get_eeprom = mv88e6xxx_g2_get_eeprom16,
+	.set_eeprom = mv88e6xxx_g2_set_eeprom16,
+	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
+	.phy_read = mv88e6xxx_g2_smi_phy_read,
+	.phy_write = mv88e6xxx_g2_smi_phy_write,
+	.port_set_link = mv88e6xxx_port_set_link,
+	.port_set_duplex = mv88e6xxx_port_set_duplex,
+	.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay,
+	.port_set_speed = mv88e6352_port_set_speed,
+	.port_tag_remap = mv88e6095_port_tag_remap,
+	.port_set_frame_mode = mv88e6351_port_set_frame_mode,
+	.port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns,
+	.port_set_ether_type = mv88e6351_port_set_ether_type,
+	.port_jumbo_config = mv88e6165_port_jumbo_config,
+	.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
+	.port_pause_config = mv88e6097_port_pause_config,
+	.stats_snapshot = mv88e6320_g1_stats_snapshot,
+	.stats_get_sset_count = mv88e6095_stats_get_sset_count,
+	.stats_get_strings = mv88e6095_stats_get_strings,
+	.stats_get_stats = mv88e6095_stats_get_stats,
+	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
+	.g1_set_egress_port = mv88e6095_g1_set_egress_port,
+	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
+	.reset = mv88e6352_g1_reset,
+};
+
 static const struct mv88e6xxx_ops mv88e6350_ops = {
 	/* MV88E6XXX_FAMILY_6351 */
 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
@@ -4086,6 +4114,20 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
 		.ops = &mv88e6321_ops,
 	},
 
+	[MV88E6341] = {
+		.prod_num = PORT_SWITCH_ID_PROD_NUM_6341,
+		.family = MV88E6XXX_FAMILY_6352,
+		.name = "Marvell 88E6341",
+		.num_databases = 4096,
+		.num_ports = 6,
+		.port_base_addr = 0x10,
+		.global1_addr = 0x1b,
+		.age_time_coeff = 15000,
+		.tag_protocol = DSA_TAG_PROTO_EDSA,
+		.flags = MV88E6XXX_FLAGS_FAMILY_6352,
+		.ops = &mv88e6341_ops,
+	},
+
 	[MV88E6350] = {
 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6350,
 		.family = MV88E6XXX_FAMILY_6351,
diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h
index af54bae..cb55fdb 100644
--- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h
+++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h
@@ -100,6 +100,7 @@
 #define PORT_SWITCH_ID_PROD_NUM_6240	0x240
 #define PORT_SWITCH_ID_PROD_NUM_6290	0x290
 #define PORT_SWITCH_ID_PROD_NUM_6321	0x310
+#define PORT_SWITCH_ID_PROD_NUM_6341	0x340
 #define PORT_SWITCH_ID_PROD_NUM_6352	0x352
 #define PORT_SWITCH_ID_PROD_NUM_6350	0x371
 #define PORT_SWITCH_ID_PROD_NUM_6351	0x375
@@ -432,6 +433,7 @@ enum mv88e6xxx_model {
 	MV88E6290,
 	MV88E6320,
 	MV88E6321,
+	MV88E6341,
 	MV88E6350,
 	MV88E6351,
 	MV88E6352,
@@ -448,7 +450,7 @@ enum mv88e6xxx_family {
 	MV88E6XXX_FAMILY_6185,	/* 6108 6121 6122 6131 6152 6155 6182 6185 */
 	MV88E6XXX_FAMILY_6320,	/* 6320 6321 */
 	MV88E6XXX_FAMILY_6351,	/* 6171 6175 6350 6351 */
-	MV88E6XXX_FAMILY_6352,	/* 6172 6176 6240 6352 */
+	MV88E6XXX_FAMILY_6352,	/* 6172 6176 6240 6341 6352 */
 	MV88E6XXX_FAMILY_6390,  /* 6190 6190X 6191 6290 6390 6390X */
 };
 
-- 
2.9.3

--
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 v3 net-next 1/3] net: dsa: mv88e6xxx: Don't forbid MDIO I/Os for PHY addr >= num_of_ports
From: Romain Perier @ 2016-12-21  9:00 UTC (permalink / raw)
  To: Andrew Lunn, Vivien Didelot, Florian Fainelli, Jason Cooper,
	Sebastian Hesselbarth, Gregory Clement
  Cc: netdev-u79uwXL29TY76Z2rM5mHXA, devicetree-u79uwXL29TY76Z2rM5mHXA,
	Rob Herring, Ian Campbell, Pawel Moll, Mark Rutland, Kumar Gala,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	Thomas Petazzoni, Nadav Haklai, Romain Perier
In-Reply-To: <20161221090045.474-1-romain.perier-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>

Some Marvell ethernet switches have internal ethernet transceivers with
hardcoded phy addresses. These addresses can be grearer than the number
of ports or its value might be different than the associated port number.
This is for example the case for MV88E6341 that has 6 ports and internal
Port 1 to Port4 PHYs mapped at SMI addresses from 0x11 to 0x14.

This commits fixes the issue by removing the condition in MDIO callbacks.

Signed-off-by: Romain Perier <romain.perier-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
Reviewed-by: Andrew Lunn <andrew-g2DYL2Zd6BY@public.gmane.org>
---

Changes in v2:
 - Added tag "Reviewed-by" by Andrew
 - Fixed typo in the commit log

 drivers/net/dsa/mv88e6xxx/chip.c | 6 ------
 1 file changed, 6 deletions(-)

diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index b5f0e1e..76d944e 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -2881,9 +2881,6 @@ static int mv88e6xxx_mdio_read(struct mii_bus *bus, int phy, int reg)
 	u16 val;
 	int err;
 
-	if (phy >= mv88e6xxx_num_ports(chip))
-		return 0xffff;
-
 	mutex_lock(&chip->reg_lock);
 	err = mv88e6xxx_phy_read(chip, phy, reg, &val);
 	mutex_unlock(&chip->reg_lock);
@@ -2896,9 +2893,6 @@ static int mv88e6xxx_mdio_write(struct mii_bus *bus, int phy, int reg, u16 val)
 	struct mv88e6xxx_chip *chip = bus->priv;
 	int err;
 
-	if (phy >= mv88e6xxx_num_ports(chip))
-		return 0xffff;
-
 	mutex_lock(&chip->reg_lock);
 	err = mv88e6xxx_phy_write(chip, phy, reg, val);
 	mutex_unlock(&chip->reg_lock);
-- 
2.9.3

--
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 v3 net-next 0/3] Add support for the ethernet switch on the ESPRESSObin
From: Romain Perier @ 2016-12-21  9:00 UTC (permalink / raw)
  To: Andrew Lunn, Vivien Didelot, Florian Fainelli, Jason Cooper,
	Sebastian Hesselbarth, Gregory Clement
  Cc: netdev-u79uwXL29TY76Z2rM5mHXA, devicetree-u79uwXL29TY76Z2rM5mHXA,
	Rob Herring, Ian Campbell, Pawel Moll, Mark Rutland, Kumar Gala,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	Thomas Petazzoni, Nadav Haklai, Romain Perier

This set of patches adds support for the Marvell ethernet switch 88E6341.
It also add the devicetree definition of this switch to the DT board.

Romain Perier (3):
  net: dsa: mv88e6xxx: Don't forbid MDIO I/Os for PHY addr >=
    num_of_ports
  net: dsa: mv88e6xxx: Add support for ethernet switch 88E6341/88E6141
  arm64: dts: marvell: Add ethernet switch definition for the
    ESPRESSObin

 .../boot/dts/marvell/armada-3720-espressobin.dts   | 66 ++++++++++++++++++++++
 drivers/net/dsa/mv88e6xxx/chip.c                   | 48 ++++++++++++++--
 drivers/net/dsa/mv88e6xxx/mv88e6xxx.h              |  4 +-
 3 files changed, 111 insertions(+), 7 deletions(-)

-- 
2.9.3

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

* Re: [PATCH v2 3/3] arm64: dts: marvell: Add ethernet switch definition for the ESPRESSObin
From: Romain Perier @ 2016-12-21  8:57 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: Vivien Didelot, Florian Fainelli, Jason Cooper,
	Sebastian Hesselbarth, Gregory Clement, netdev, devicetree,
	Rob Herring, Ian Campbell, Pawel Moll, Mark Rutland, Kumar Gala,
	linux-arm-kernel, Thomas Petazzoni, Nadav Haklai
In-Reply-To: <20161220161714.GD30952@lunn.ch>

Hi,

Le 20/12/2016 à 17:17, Andrew Lunn a écrit :
>>>> +		mdio {
>>>> +			#address-cells = <1>;
>>>> +			#size-cells = <0>;
>>>> +			reg = <1>;
>>>
>>> what is this reg value for?
>>>
>>>     Andrew
>>>
>>
>> It was required to avoid a warning thrown by the mdio subsystem
>
> Do you remember what the warning was?
>
> This seems odd to me. I don't see why a reg is needed here.
>
> Thanks
> 	Andrew
>

Mhhh, in fact, I did changes to this mdio node, I relocated it...
the warning was probably before the relocation, because I no longer see 
it. So I have removed this reg value.

Thanks,
Romain

^ permalink raw reply

* [PATCH] net: macb: Applied changes according to review made by Andy Shevchenko.
From: Bartosz Folta @ 2016-12-21  8:53 UTC (permalink / raw)
  To: Nicolas Ferre, David S. Miller, Niklas Cassel, Alexandre Torgue,
	Satanand Burla, Raghu Vatsavayi, Simon Horman,
	linux-kernel@vger.kernel.org, netdev@vger.kernel.org
  Cc: Bartosz Folta, Rafal Ozieblo
In-Reply-To: <1482309315-582-1-git-send-email-bfolta@cadence.com>

This patch is sent in regard to recently applied patch
Commit 83a77e9ec4150ee4acc635638f7dedd9da523a26
net: macb: Added PCI wrapper for Platform Driver.
Andy's review does not appear on mailing lists. I can't reference it.

Signed-off-by: Bartosz Folta <bfolta@cadence.com>
---
 drivers/net/ethernet/cadence/macb_pci.c | 25 +++++++++----------------
 1 file changed, 9 insertions(+), 16 deletions(-)

diff --git a/drivers/net/ethernet/cadence/macb_pci.c b/drivers/net/ethernet/cadence/macb_pci.c
index 92be2cd..6cec52c 100644
--- a/drivers/net/ethernet/cadence/macb_pci.c
+++ b/drivers/net/ethernet/cadence/macb_pci.c
@@ -1,5 +1,5 @@
 /**
- * macb_pci.c - Cadence GEM PCI wrapper.
+ * Cadence GEM PCI wrapper.
  *
  * Copyright (C) 2016 Cadence Design Systems - http://www.cadence.com
  *
@@ -45,32 +45,27 @@ static int macb_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 	struct macb_platform_data plat_data;
 	struct resource res[2];
 
-	/* sanity check */
-	if (!id)
-		return -EINVAL;
-
 	/* enable pci device */
-	err = pci_enable_device(pdev);
+	err = pcim_enable_device(pdev);
 	if (err < 0) {
-		dev_err(&pdev->dev, "Enabling PCI device has failed: 0x%04X",
-			err);
-		return -EACCES;
+		dev_err(&pdev->dev, "Enabling PCI device has failed: %d", err);
+		return err;
 	}
 
 	pci_set_master(pdev);
 
 	/* set up resources */
 	memset(res, 0x00, sizeof(struct resource) * ARRAY_SIZE(res));
-	res[0].start = pdev->resource[0].start;
-	res[0].end = pdev->resource[0].end;
+	res[0].start = pci_resource_start(pdev, 0);
+	res[0].end = pci_resource_end(pdev, 0);
 	res[0].name = PCI_DRIVER_NAME;
 	res[0].flags = IORESOURCE_MEM;
-	res[1].start = pdev->irq;
+	res[1].start = pci_irq_vector(pdev, 0);
 	res[1].name = PCI_DRIVER_NAME;
 	res[1].flags = IORESOURCE_IRQ;
 
-	dev_info(&pdev->dev, "EMAC physical base addr = 0x%p\n",
-		 (void *)(uintptr_t)pci_resource_start(pdev, 0));
+	dev_info(&pdev->dev, "EMAC physical base addr: %pa\n",
+		 &res[0].start);
 
 	/* set up macb platform data */
 	memset(&plat_data, 0, sizeof(plat_data));
@@ -120,7 +115,6 @@ static int macb_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 	clk_unregister(plat_data.pclk);
 
 err_pclk_register:
-	pci_disable_device(pdev);
 	return err;
 }
 
@@ -130,7 +124,6 @@ static void macb_remove(struct pci_dev *pdev)
 	struct macb_platform_data *plat_data = dev_get_platdata(&plat_dev->dev);
 
 	platform_device_unregister(plat_dev);
-	pci_disable_device(pdev);
 	clk_unregister(plat_data->pclk);
 	clk_unregister(plat_data->hclk);
 }
-- 
1.9.1

^ permalink raw reply related

* [RFC PATCH 7/7] Get notifications from PF driver and configure filter block based on request data
From: Satha Koteswara Rao @ 2016-12-21  8:46 UTC (permalink / raw)
  To: linux-kernel
  Cc: sgoutham, rric, davem, david.daney, rvatsavayi, derek.chickles,
	satha.rao, philip.romanov, netdev, linux-arm-kernel
In-Reply-To: <1482310011-1862-1-git-send-email-satha.rao@caviumnetworks.com>

---
 drivers/net/ethernet/cavium/thunder/pf_filter.c | 1678 +++++++++++++++++++++++
 1 file changed, 1678 insertions(+)
 create mode 100644 drivers/net/ethernet/cavium/thunder/pf_filter.c

diff --git a/drivers/net/ethernet/cavium/thunder/pf_filter.c b/drivers/net/ethernet/cavium/thunder/pf_filter.c
new file mode 100644
index 0000000..5a04da6
--- /dev/null
+++ b/drivers/net/ethernet/cavium/thunder/pf_filter.c
@@ -0,0 +1,1678 @@
+/*
+ * Copyright (C) 2015 Cavium, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/version.h>
+#include <linux/proc_fs.h>
+#include <linux/device.h>
+#include <linux/mman.h>
+#include <linux/uaccess.h>
+#include <linux/delay.h>
+#include <linux/cdev.h>
+#include <linux/err.h>
+#include <linux/device.h>
+#include "pf_globals.h"
+#include "pf_locals.h"
+#include "nic.h"
+
+u32 intr_to_ingressgrp[MAX_NUMNODES][TNS_MAC_FILTER_MAX_SYS_PORTS];
+struct vf_register_s vf_reg_data[MAX_NUMNODES][TNS_MAX_VF];
+struct ing_grp_gblvif ingressgrp_gblvif[MAX_NUMNODES][TNS_MAX_INGRESS_GROUP];
+
+u32 macfilter_freeindex[MAX_NUMNODES];
+u32 vlanfilter_freeindex[MAX_NUMNODES];
+
+int tns_filter_valid_entry(int node, int req_type, int vf, int vlan)
+{
+	if (req_type == NIC_MBOX_MSG_UC_MC) {
+		if (vf_reg_data[node][vf].vf_in_mcast_promis ||
+		    (macfilter_freeindex[node] >= TNS_MAC_FILTER_MAX_INDEX))
+			return TNS_ERR_MAX_LIMIT;
+		if (vf_reg_data[node][vf].filter_count >= TNS_MAX_MAC_PER_VF) {
+			tns_enable_mcast_promis(node, vf);
+			vf_reg_data[node][vf].vf_in_mcast_promis = 1;
+			return TNS_ERR_MAX_LIMIT;
+		}
+	} else if (req_type == NIC_MBOX_MSG_VLAN ||
+		   req_type == NIC_MBOX_MSG_ADMIN_VLAN) {
+		if (vf_reg_data[node][vf].vlan_count >= TNS_MAX_VLAN_PER_VF)
+			return TNS_ERR_MAX_LIMIT;
+
+		if (vlanfilter_freeindex[node] >= TNS_VLAN_FILTER_MAX_INDEX) {
+			int ret;
+			struct vlan_filter_entry tbl_entry;
+			int vlan_tbl_idx = -1;
+
+			tbl_entry.key.is_valid = 1;
+			tbl_entry.key.key_type.key_value  = 0x0ull;
+			tbl_entry.mask.key_type.key_value = ~0x0ull;
+			tbl_entry.key.key_type.s.ingress_grp =
+				intr_to_ingressgrp[node][vf];
+			tbl_entry.mask.key_type.s.ingress_grp = 0x0;
+			tbl_entry.key.key_type.s.vlan = vlan;
+			tbl_entry.mask.key_type.s.vlan = 0x0;
+
+			ret = filter_tbl_lookup(node, VLAN_FILTER_TABLE,
+						&tbl_entry, &vlan_tbl_idx);
+			if (ret || vlan_tbl_idx == -1)
+				return TNS_ERR_MAX_LIMIT;
+		}
+	} else {
+		filter_dbg(FERR, "Invalid Request %d VF %d\n", req_type, vf);
+	}
+
+	return TNS_NO_ERR;
+}
+
+int dump_port_cfg_etry(struct itt_entry_s *port_cfg_entry)
+{
+	filter_dbg(FINFO, "PortConfig Entry\n");
+	filter_dbg(FINFO, "pkt_dir:			0x%x\n",
+		   port_cfg_entry->pkt_dir);
+	filter_dbg(FINFO, "is_admin_vlan_enabled:	0x%x\n",
+		   port_cfg_entry->is_admin_vlan_enabled);
+	filter_dbg(FINFO, "default_evif:		0x%x\n",
+		   port_cfg_entry->default_evif);
+	filter_dbg(FINFO, "admin_vlan:			0x%x\n",
+		   port_cfg_entry->admin_vlan);
+
+	return TNS_NO_ERR;
+}
+
+int dump_evif_entry(struct evif_entry *evif_dat)
+{
+	filter_dbg(FINFO, "EVIF Entry\n");
+	filter_dbg(FINFO, "prt_bmap_136_73: 0x%llx\n",
+		   evif_dat->prt_bmap_136_73);
+	filter_dbg(FINFO, "prt_bmap_72_9:   0x%llx\n",
+		   evif_dat->prt_bmap_72_9);
+	filter_dbg(FINFO, "prt_bmap_8:      0x%x\n", evif_dat->prt_bmap_8);
+	filter_dbg(FINFO, "mre_ptr:         0x%x\n", evif_dat->mre_ptr);
+	filter_dbg(FINFO, "insert_ptr2:     0x%x\n", evif_dat->insert_ptr2);
+	filter_dbg(FINFO, "insert_ptr1:     0x%x\n", evif_dat->insert_ptr1);
+	filter_dbg(FINFO, "insert_ptr0:     0x%x\n", evif_dat->insert_ptr0);
+	filter_dbg(FINFO, "data31_0:        0x%x\n", evif_dat->data31_0);
+	filter_dbg(FINFO, "rewrite_ptr1:    0x%x\n", evif_dat->rewrite_ptr1);
+	filter_dbg(FINFO, "rewrite_ptr0:    0x%x\n", evif_dat->rewrite_ptr0);
+	filter_dbg(FINFO, "prt_bmap7_0:     0x%x\n", evif_dat->prt_bmap7_0);
+	filter_dbg(FINFO, "q_mirror_en:     0x%x\n", evif_dat->q_mirror_en);
+	filter_dbg(FINFO, "mirror_en:       0x%x\n", evif_dat->mirror_en);
+	filter_dbg(FINFO, "mtu_prf:         0x%x\n", evif_dat->mtu_prf);
+	filter_dbg(FINFO, "truncate:        0x%x\n", evif_dat->truncate);
+	filter_dbg(FINFO, "rsp_type:        0x%x\n", evif_dat->rsp_type);
+
+	return TNS_NO_ERR;
+}
+
+static inline int validate_port(int port_num)
+{
+	if (port_num < 0 && port_num >= TNS_MAC_FILTER_MAX_SYS_PORTS) {
+		filter_dbg(FERR, "%s Invalid Port: %d (Valid range 0-136)\n",
+			   __func__, port_num);
+		return TNS_ERR_WRONG_PORT_NUMBER;
+	}
+	return TNS_NO_ERR;
+}
+
+int enable_port(int port_num, struct evif_entry *tbl_entry)
+{
+	s64 port_base;
+
+	if (validate_port(port_num))
+		return TNS_ERR_WRONG_PORT_NUMBER;
+
+	if (port_num < 8) {
+		tbl_entry->prt_bmap7_0 = tbl_entry->prt_bmap7_0 |
+					 (0x1 << port_num);
+	} else if (port_num == 8) {
+		tbl_entry->prt_bmap_8 = 1;
+	} else if (port_num <= 72) {
+		port_base = port_num - 9;
+		tbl_entry->prt_bmap_72_9 = tbl_entry->prt_bmap_72_9 |
+						(0x1ull << port_base);
+	} else if (port_num <= TNS_MAC_FILTER_MAX_SYS_PORTS) {
+		port_base = port_num - 73;
+		tbl_entry->prt_bmap_136_73 = tbl_entry->prt_bmap_136_73 |
+						(0x1ull << port_base);
+	}
+
+	return TNS_NO_ERR;
+}
+
+int disable_port(int port_num, struct evif_entry *tbl_entry)
+{
+	s64 port_base;
+
+	if (validate_port(port_num))
+		return TNS_ERR_WRONG_PORT_NUMBER;
+
+	if (port_num < 8) {
+		tbl_entry->prt_bmap7_0 = tbl_entry->prt_bmap7_0 &
+					 ~(0x1 << port_num);
+	} else if (port_num == 8) {
+		tbl_entry->prt_bmap_8 = 0;
+	} else if (port_num <= 72) {
+		port_base = port_num - 9;
+		tbl_entry->prt_bmap_72_9 = tbl_entry->prt_bmap_72_9 &
+						~(0x1ull << port_base);
+	} else if (port_num <= TNS_MAC_FILTER_MAX_SYS_PORTS) {
+		port_base = port_num - 73;
+		tbl_entry->prt_bmap_136_73 = tbl_entry->prt_bmap_136_73 &
+						~(0x1ull << port_base);
+	}
+
+	return TNS_NO_ERR;
+}
+
+int disable_all_ports(struct evif_entry *tbl_entry)
+{
+	tbl_entry->prt_bmap_136_73 = 0x0ull;
+	tbl_entry->prt_bmap_72_9 = 0x0ull;
+	tbl_entry->prt_bmap_8 = 0x0;
+	tbl_entry->prt_bmap7_0 = 0x0;
+
+	return TNS_NO_ERR;
+}
+
+int is_vlan_port_enabled(int vf, vlan_port_bitmap_t vlan_vif)
+{
+	int port_base = (vf / 8), port_offset = (vf % 8);
+
+	if (validate_port(vf))
+		return TNS_ERR_WRONG_PORT_NUMBER;
+
+	if (vlan_vif[port_base] & (1 << port_offset))
+		return 1;
+
+	return 0;
+}
+
+int enable_vlan_port(int port_num, vlan_port_bitmap_t vlan_vif)
+{
+	int port_base = (port_num / 8), port_offset = (port_num % 8);
+
+	if (validate_port(port_num))
+		return TNS_ERR_WRONG_PORT_NUMBER;
+
+	vlan_vif[port_base] = vlan_vif[port_base] | (1 << port_offset);
+
+	return TNS_NO_ERR;
+}
+
+int disable_vlan_port(int port_num, vlan_port_bitmap_t vlan_vif)
+{
+	int port_base = (port_num / 8), port_offset = (port_num % 8);
+
+	if (validate_port(port_num))
+		return TNS_ERR_WRONG_PORT_NUMBER;
+
+	vlan_vif[port_base] = vlan_vif[port_base] & ~(1 << port_offset);
+
+	return TNS_NO_ERR;
+}
+
+int disable_vlan_vif_ports(vlan_port_bitmap_t vlan_vif)
+{
+	memset((void *)(&vlan_vif[0]), 0x0, sizeof(vlan_port_bitmap_t));
+
+	return TNS_NO_ERR;
+}
+
+int dump_vlan_vif_portss(vlan_port_bitmap_t vlan_vif)
+{
+	int i;
+
+	filter_dbg(FINFO, "Port Bitmap (0...135) 0x ");
+	for (i = 0; i < (TNS_MAC_FILTER_MAX_SYS_PORTS / 8); i++)
+		filter_dbg(FINFO, "%x ", vlan_vif[i]);
+	filter_dbg(FINFO, "\n");
+
+	return TNS_NO_ERR;
+}
+
+static inline int getingress_grp(int node, int vf)
+{
+	int i;
+
+	for (i = 0; i < TNS_MAX_INGRESS_GROUP; i++) {
+		if (ingressgrp_gblvif[node][i].is_valid &&
+		    (ingressgrp_gblvif[node][i].ingress_grp ==
+		     intr_to_ingressgrp[node][vf]))
+			return i;
+	}
+	return -1;
+}
+
+inline int vf_bcast_vif(int node, int vf, int *bcast_vif)
+{
+	int ing_grp = getingress_grp(node, vf);
+
+	if (ing_grp == -1)
+		return TNS_ERR_ENTRY_NOT_FOUND;
+
+	*bcast_vif = ingressgrp_gblvif[node][ing_grp].bcast_vif;
+
+	return TNS_NO_ERR;
+}
+
+inline int vf_mcast_vif(int node, int vf, int *mcast_vif)
+{
+	int ing_grp = getingress_grp(node, vf);
+
+	if (ing_grp == -1)
+		return TNS_ERR_ENTRY_NOT_FOUND;
+
+	*mcast_vif = ingressgrp_gblvif[node][ing_grp].mcast_vif;
+
+	return TNS_NO_ERR;
+}
+
+inline int vf_pfvf_id(int node, int vf, int *pfvf)
+{
+	int ing_grp = getingress_grp(node, vf);
+
+	if (ing_grp == -1)
+		return TNS_ERR_ENTRY_NOT_FOUND;
+
+	*pfvf = ingressgrp_gblvif[node][ing_grp].pf_vf;
+
+	return TNS_NO_ERR;
+}
+
+bool is_vf_registered_entry(int node, int vf, int index)
+{
+	int i;
+
+	for (i = 0; i < vf_reg_data[node][vf].filter_count; i++) {
+		if (vf_reg_data[node][vf].filter_index[i] == index)
+			return true;
+	}
+
+	return false;
+}
+
+bool is_vlan_registered(int node, int vf, int vlan)
+{
+	int i;
+
+	for (i = 0; i < vf_reg_data[node][vf].vlan_count; i++) {
+		if (vf_reg_data[node][vf].vlan[i] == vlan)
+			return true;
+	}
+
+	return false;
+}
+
+int is_empty_vif(int node, int vf, struct evif_entry *evif_dat)
+{
+	int i;
+
+	for (i = 0; i < TNS_MAX_VF; i++)
+		if (intr_to_ingressgrp[node][vf] ==
+		    intr_to_ingressgrp[node][i] &&
+		    (vf_reg_data[node][i].vf_in_promis ||
+		     vf_reg_data[node][i].vf_in_mcast_promis))
+			disable_port(i, evif_dat);
+	disable_port(intr_to_ingressgrp[node][vf], evif_dat);
+
+	if (evif_dat->prt_bmap7_0 || evif_dat->prt_bmap_8 ||
+	    evif_dat->prt_bmap_72_9 || evif_dat->prt_bmap_136_73)
+		return 0;
+
+	return 1;
+}
+
+int is_empty_vlan(int node, int vf, int vlan, vlan_port_bitmap_t vlan_vif)
+{
+	int i, pf_vf;
+	int ret;
+
+	ret = vf_pfvf_id(node, vf, &pf_vf);
+	if (ret)
+		return ret;
+
+	if (vf_reg_data[node][pf_vf].vf_in_promis &&
+	    !is_vlan_registered(node, pf_vf, vlan))
+		disable_vlan_port(pf_vf, vlan_vif);
+
+	disable_vlan_port(intr_to_ingressgrp[node][vf], vlan_vif);
+	for (i = 0; i < sizeof(vlan_port_bitmap_t); i++)
+		if (vlan_vif[i])
+			break;
+
+	if (i == sizeof(vlan_port_bitmap_t))
+		return 1;
+
+	return 0;
+}
+
+int filter_tbl_lookup(int node, int table_id, void *entry, int *index)
+{
+	switch (table_id) {
+	case MAC_FILTER_TABLE:
+	{
+		struct mac_filter_entry tbl_entry;
+		struct mac_filter_entry *inp = (struct mac_filter_entry *)entry;
+		int i;
+		int ret;
+
+		for (i = 0; i < TNS_MAC_FILTER_MAX_INDEX; i++) {
+			ret = tbl_read(node, MAC_FILTER_TABLE, i,
+				       &tbl_entry.key, &tbl_entry.mask,
+				       &tbl_entry.data);
+
+			if (ret && (ret != TNS_ERR_MAC_FILTER_INVALID_ENTRY))
+				return ret;
+			else if (ret == TNS_ERR_MAC_FILTER_INVALID_ENTRY)
+				continue;
+
+			if ((tbl_entry.key.key_type.key_value ==
+			     inp->key.key_type.key_value) &&
+			    (tbl_entry.mask.key_type.key_value ==
+			     inp->mask.key_type.key_value)) {
+				//Found an Entry
+				*index = i;
+				inp->data.data = tbl_entry.data.data;
+				return TNS_NO_ERR;
+			}
+			//Unable to find entry
+			*index = -1;
+		}
+		break;
+	}
+	case VLAN_FILTER_TABLE:
+	{
+		struct vlan_filter_entry tbl_entry;
+		struct vlan_filter_entry *inp_entry;
+		int i;
+		int ret;
+
+		inp_entry = (struct vlan_filter_entry *)entry;
+		for (i = 1; i < TNS_VLAN_FILTER_MAX_INDEX; i++) {
+			ret = tbl_read(node, VLAN_FILTER_TABLE, i,
+				       &tbl_entry.key, &tbl_entry.mask,
+					&tbl_entry.data);
+			if (ret && (ret != TNS_ERR_MAC_FILTER_INVALID_ENTRY))
+				return ret;
+			else if (ret == TNS_ERR_MAC_FILTER_INVALID_ENTRY)
+				continue;
+
+			if ((tbl_entry.key.key_type.key_value ==
+			     inp_entry->key.key_type.key_value) &&
+			    (tbl_entry.mask.key_type.key_value ==
+			     inp_entry->mask.key_type.key_value)) {
+				//Found an Entry
+				*index = i;
+				inp_entry->data.data = tbl_entry.data.data;
+				return TNS_NO_ERR;
+			}
+		}
+		//Unable to find entry
+		*index = -1;
+		break;
+	}
+	default:
+		filter_dbg(FERR, "Wrong Table ID: %d\n", table_id);
+		return TNS_ERR_INVALID_TBL_ID;
+	}
+
+	return TNS_NO_ERR;
+}
+
+int tns_enable_mcast_promis(int node, int vf)
+{
+	int mcast_vif;
+	int j;
+	int ret;
+	struct evif_entry evif_dat;
+	int ing_grp = getingress_grp(node, vf);
+	int pports;
+
+	if (ing_grp == -1)
+		return TNS_ERROR_INVALID_ARG;
+
+	ret = vf_mcast_vif(node, vf, &mcast_vif);
+	if (ret) {
+		filter_dbg(FERR, "Error: Unable to get multicast VIF\n");
+		return ret;
+	}
+
+	ret = tbl_read(node, MAC_EVIF_TABLE, mcast_vif, NULL, NULL, &evif_dat);
+	if (ret)
+		return ret;
+
+	enable_port(vf, &evif_dat);
+	dump_evif_entry(&evif_dat);
+	ret = tbl_write(node, MAC_EVIF_TABLE, mcast_vif, NULL, NULL,
+			(void *)&evif_dat);
+	if (ret)
+		return ret;
+
+	pports = ingressgrp_gblvif[node][ing_grp].valid_mcast_promis_ports;
+	//Enable VF in multicast MAC promiscuous group
+	for (j = 0; j < pports; j++) {
+		if (MCAST_PROMIS(node, ing_grp, j) == vf) {
+			filter_dbg(FDEBUG, "VF found in MCAST promis group\n");
+			return TNS_NO_ERR;
+		}
+	}
+	MCAST_PROMIS(node, ing_grp, pports) = vf;
+	ingressgrp_gblvif[node][ing_grp].valid_mcast_promis_ports += 1;
+	filter_dbg(FINFO, "VF %d permanently entered into MCAST promisc mode\n",
+		   vf);
+
+	return TNS_NO_ERR;
+}
+
+int remove_vf_from_regi_mcast_vif(int node, int vf)
+{
+	int ret;
+	int mcast_vif;
+	struct evif_entry evif_dat;
+
+	ret = vf_mcast_vif(node, vf, &mcast_vif);
+	if (ret) {
+		filter_dbg(FERR, "Error: Unable to get multicast VIF\n");
+		return ret;
+	}
+
+	ret = tbl_read(node, MAC_EVIF_TABLE, mcast_vif, NULL, NULL, &evif_dat);
+	if (ret)
+		return ret;
+	disable_port(vf, &evif_dat);
+	dump_evif_entry(&evif_dat);
+	ret = tbl_write(node, MAC_EVIF_TABLE, mcast_vif, NULL, NULL,
+			(void *)&evif_dat);
+	if (ret)
+		return ret;
+
+	return TNS_NO_ERR;
+}
+
+int remove_vf_from_mcast_promis_grp(int node, int vf)
+{
+	int j, k;
+	int ing_grp = getingress_grp(node, vf);
+	int pports;
+
+	if (ing_grp == -1)
+		return TNS_ERROR_INVALID_ARG;
+
+	pports = ingressgrp_gblvif[node][ing_grp].valid_mcast_promis_ports;
+	for (j = 0; j < pports; j++) {
+		if (MCAST_PROMIS(node, ing_grp, j) != vf)
+			continue;
+
+		filter_dbg(FDEBUG, "VF found in MCAST promis group %d\n",
+			   intr_to_ingressgrp[node][vf]);
+		for (k = j; k < (pports - 1); k++)
+			MCAST_PROMIS(node, ing_grp, k) =
+			 MCAST_PROMIS(node, ing_grp, (k + 1));
+		VALID_MCAST_PROMIS(node, ing_grp) -= 1;
+		remove_vf_from_regi_mcast_vif(node, vf);
+		return TNS_NO_ERR;
+	}
+	filter_dbg(FDEBUG, "VF %d not found in multicast promiscuous group\n",
+		   vf);
+
+	return TNS_ERR_ENTRY_NOT_FOUND;
+}
+
+int registered_vf_filter_index(int node, int vf, int mac_idx, int action)
+{
+	int f_count = vf_reg_data[node][vf].filter_count, j;
+
+	if (!action) {
+		for (j = 0; j < f_count; j++) {
+			if (vf_reg_data[node][vf].filter_index[j] == mac_idx) {
+				int i, k = j + 1;
+
+				for (i = j; i < f_count - 1; i++, k++)
+					vf_reg_data[node][vf].filter_index[i] =
+					 vf_reg_data[node][vf].filter_index[k];
+				break;
+			}
+		}
+		if (j == vf_reg_data[node][vf].filter_count)
+			filter_dbg(FDEBUG, "VF not in registered filtr list\n");
+		else
+			vf_reg_data[node][vf].filter_count -= 1;
+	} else {
+		vf_reg_data[node][vf].filter_index[f_count] = mac_idx;
+		vf_reg_data[node][vf].filter_count += 1;
+		filter_dbg(FINFO, "%s Added at Filter count %d Index %d\n",
+			   __func__, vf_reg_data[node][vf].filter_count,
+			   mac_idx);
+	}
+
+	/* We are restricting each VF to register atmost 11 filter entries
+	 * (including unicast & multicast)
+	 */
+	if (vf_reg_data[node][vf].filter_count <= TNS_MAX_MAC_PER_VF) {
+		vf_reg_data[node][vf].vf_in_mcast_promis = 0;
+		if (!vf_reg_data[node][vf].vf_in_promis)
+			remove_vf_from_mcast_promis_grp(node, vf);
+		filter_dbg(FINFO, "VF %d removed from MCAST promis mode\n", vf);
+	}
+
+	return TNS_NO_ERR;
+}
+
+int add_mac_filter_mcast_entry(int node, int table_id, int vf, int mac_idx,
+			       void *mac_DA)
+{
+	int ret;
+	struct mac_filter_entry tbl_entry;
+	struct mac_filter_keymask_s key, mask;
+	union mac_filter_data_s data;
+	int vif = -1, k, j;
+	struct evif_entry evif_dat;
+	int ing_grp = getingress_grp(node, vf);
+
+	if (ing_grp == -1)
+		return TNS_ERROR_INVALID_ARG;
+
+	if (vf_reg_data[node][vf].filter_count >= TNS_MAX_MAC_PER_VF) {
+		if (!vf_reg_data[node][vf].vf_in_mcast_promis) {
+			tns_enable_mcast_promis(node, vf);
+			vf_reg_data[node][vf].vf_in_mcast_promis = 1;
+		}
+		return TNS_ERR_MAX_LIMIT;
+	}
+
+	tbl_entry.key.is_valid = 1;
+	tbl_entry.key.key_type.s.ingress_grp = intr_to_ingressgrp[node][vf];
+	tbl_entry.mask.key_type.s.ingress_grp = 0x0;
+	for (j = 5, k = 0; j >= 0; j--, k++) {
+		tbl_entry.key.key_type.s.mac_DA[k] = ((u8 *)mac_DA)[j];
+		tbl_entry.mask.key_type.s.mac_DA[k] = 0x0;
+	}
+	ret = filter_tbl_lookup(node, MAC_FILTER_TABLE, &tbl_entry, &mac_idx);
+	if (ret)
+		return ret;
+	if (mac_idx != -1 &&
+	    !(mac_idx >= (TNS_MAC_FILTER_MAX_INDEX - TNS_MAX_INGRESS_GROUP) &&
+	      mac_idx < TNS_MAC_FILTER_MAX_INDEX)) {
+		int evif = tbl_entry.data.s.evif;
+
+		filter_dbg(FINFO, "Multicast MAC found at %d evif: %d\n",
+			   mac_idx, evif);
+		ret = tbl_read(node, MAC_EVIF_TABLE, evif, NULL, NULL,
+			       &evif_dat);
+		if (ret)
+			return ret;
+		if (is_vf_registered_entry(node, vf, mac_idx)) {
+			//No Need to register again
+			return TNS_NO_ERR;
+		}
+		enable_port(vf, &evif_dat);
+		ret = tbl_write(node, MAC_EVIF_TABLE, evif, NULL, NULL,
+				(void *)&evif_dat);
+		if (ret)
+			return ret;
+		registered_vf_filter_index(node, vf, mac_idx, 1);
+		dump_evif_entry(&evif_dat);
+		return TNS_NO_ERR;
+	}
+
+	//New multicast MAC registration
+	if (alloc_table_index(node, MAC_FILTER_TABLE, &mac_idx)) {
+		filter_dbg(FERR, "%s Filter Table Full\n", __func__);
+		return TNS_ERR_MAX_LIMIT;
+	}
+	key.is_valid = 1;
+	mask.is_valid = 1;
+	key.key_type.s.ingress_grp = intr_to_ingressgrp[node][vf];
+	mask.key_type.s.ingress_grp = 0;
+	for (j = 5, k = 0; j >= 0; j--, k++) {
+		key.key_type.s.mac_DA[k] = ((u8 *)mac_DA)[j];
+		mask.key_type.s.mac_DA[k] = 0x0;
+	}
+	if (alloc_table_index(node, MAC_EVIF_TABLE, &vif)) {
+		filter_dbg(FERR, "%s EVIF Table Full\n", __func__);
+		return TNS_ERR_MAX_LIMIT;
+	}
+	evif_dat.insert_ptr0 = 0xFFFF;
+	evif_dat.insert_ptr1 = 0xFFFF;
+	evif_dat.insert_ptr2 = 0xFFFF;
+	evif_dat.mre_ptr = 0x7FFF;
+	evif_dat.rewrite_ptr0 = 0xFF;
+	evif_dat.rewrite_ptr1 = 0xFF;
+	evif_dat.data31_0 = 0x0;
+	evif_dat.q_mirror_en = 0x0;
+	evif_dat.mirror_en = 0x0;
+	evif_dat.mtu_prf = 0x0;
+	evif_dat.truncate = 0x0;
+	evif_dat.rsp_type = 0x3;
+	disable_all_ports(&evif_dat);
+	for (j = 0; j < VALID_MCAST_PROMIS(node, ing_grp); j++)
+		enable_port(MCAST_PROMIS(node, ing_grp, j), &evif_dat);
+	enable_port(vf, &evif_dat);
+	ret = tbl_write(node, MAC_EVIF_TABLE, vif, NULL, NULL, &evif_dat);
+	if (ret)
+		return ret;
+	data.data = 0x0ull;
+	data.s.evif = vif;
+	ret = tbl_write(node, MAC_FILTER_TABLE, mac_idx, &key, &mask, &data);
+	if (ret)
+		return ret;
+	macfilter_freeindex[node] += 1;
+	registered_vf_filter_index(node, vf, mac_idx, 1);
+
+	return TNS_NO_ERR;
+}
+
+int del_mac_filter_entry(int node, int table_id, int vf, int mac_idx,
+			 void *mac_DA, int addr_type)
+{
+	int ret;
+	struct mac_filter_entry tbl_entry;
+	int old_mac_idx = -1, vif;
+	int j, k;
+
+	tbl_entry.key.is_valid = 1;
+	tbl_entry.key.key_type.s.ingress_grp = intr_to_ingressgrp[node][vf];
+	tbl_entry.mask.key_type.s.ingress_grp = 0x0;
+
+	for (j = 5, k = 0; j >= 0; j--, k++) {
+		tbl_entry.key.key_type.s.mac_DA[k] = ((u8 *)mac_DA)[j];
+		tbl_entry.mask.key_type.s.mac_DA[k] = 0x0;
+	}
+
+	ret = filter_tbl_lookup(node, MAC_FILTER_TABLE, (void *)&tbl_entry,
+				&old_mac_idx);
+	if (ret)
+		return ret;
+
+	if (old_mac_idx == -1) {
+		filter_dbg(FDEBUG, "Invalid Delete, entry not found\n");
+		return TNS_ERR_ENTRY_NOT_FOUND;
+	}
+	if (mac_idx != -1 && mac_idx != old_mac_idx) {
+		filter_dbg(FDEBUG, "Found and requested are mismatched\n");
+		return TNS_ERR_ENTRY_NOT_FOUND;
+	}
+	if (old_mac_idx == vf) {
+		filter_dbg(FDEBUG, "Primary Unicast MAC delete not allowed\n");
+		return TNS_ERR_MAC_FILTER_INVALID_ENTRY;
+	}
+
+	//Remove MAC Filter entry from VF register MAC filter list
+	registered_vf_filter_index(node, vf, old_mac_idx, 0);
+
+	//Remove VIF entry (output portmask) related to this filter entry
+	vif = tbl_entry.data.s.evif;
+	if (addr_type) {
+		struct evif_entry evif_dat;
+
+		ret = tbl_read(node, MAC_EVIF_TABLE, vif, NULL, NULL,
+			       &evif_dat);
+		if (ret)
+			return ret;
+
+		disable_port(vf, &evif_dat);
+		ret = tbl_write(node, MAC_EVIF_TABLE, vif, NULL, NULL,
+				&evif_dat);
+		if (ret)
+			return ret;
+
+		dump_evif_entry(&evif_dat);
+		//In case of multicast MAC check for empty portmask
+		if (!is_empty_vif(node, vf, &evif_dat))
+			return TNS_NO_ERR;
+	}
+	invalidate_table_entry(node, MAC_FILTER_TABLE, old_mac_idx);
+	free_table_index(node, MAC_FILTER_TABLE, old_mac_idx);
+	free_table_index(node, MAC_EVIF_TABLE, vif);
+	macfilter_freeindex[node] -= 1;
+
+	return TNS_NO_ERR;
+}
+
+int add_mac_filter_entry(int node, int table_id, int vf, int mac_idx,
+			 void *mac_DA)
+{
+	int ret;
+	struct mac_filter_entry tbl_entry;
+	int old_mac_idx = -1;
+	int j, k;
+	struct mac_filter_keymask_s key, mask;
+	union mac_filter_data_s data;
+
+	/* We are restricting each VF to register atmost 11 filter entries
+	 * (including unicast & multicast)
+	 */
+	if (mac_idx != vf &&
+	    vf_reg_data[node][vf].filter_count >= TNS_MAX_MAC_PER_VF) {
+		if (!vf_reg_data[node][vf].vf_in_mcast_promis) {
+			tns_enable_mcast_promis(node, vf);
+			vf_reg_data[node][vf].vf_in_mcast_promis = 1;
+		}
+		return TNS_ERR_MAX_LIMIT;
+	}
+
+	//Adding Multicast MAC will be handled differently
+	if ((((u8 *)mac_DA)[0]) & 0x1) {
+		filter_dbg(FDEBUG, "%s It is multicast MAC entry\n", __func__);
+		return add_mac_filter_mcast_entry(node, table_id, vf, mac_idx,
+						  mac_DA);
+	}
+
+	tbl_entry.key.is_valid = 1;
+	tbl_entry.key.key_type.s.ingress_grp = intr_to_ingressgrp[node][vf];
+	tbl_entry.mask.key_type.s.ingress_grp = 0x0;
+	for (j = 5, k = 0; j >= 0; j--, k++) {
+		tbl_entry.key.key_type.s.mac_DA[k] = ((u8 *)mac_DA)[j];
+		tbl_entry.mask.key_type.s.mac_DA[k] = 0x0;
+	}
+	ret = filter_tbl_lookup(node, MAC_FILTER_TABLE, (void *)&tbl_entry,
+				&old_mac_idx);
+	if (ret)
+		return ret;
+	if (old_mac_idx != -1) {
+		filter_dbg(FINFO, "Duplicate entry found at %d\n", old_mac_idx);
+		if (tbl_entry.data.s.evif != vf) {
+			filter_dbg(FDEBUG, "Registered VF %d Requested VF %d\n",
+				   (int)tbl_entry.data.s.evif, (int)vf);
+			return TNS_ERR_DUPLICATE_MAC;
+		}
+		return TNS_NO_ERR;
+	}
+	if (alloc_table_index(node, MAC_FILTER_TABLE, &mac_idx)) {
+		filter_dbg(FERR, "(%s) Filter Table Full\n", __func__);
+		return TNS_ERR_MAX_LIMIT;
+	}
+	if (mac_idx == -1) {
+		filter_dbg(FERR, "!!!ERROR!!! reached maximum limit\n");
+		return TNS_ERR_MAX_LIMIT;
+	}
+	key.is_valid = 1;
+	mask.is_valid = 1;
+	key.key_type.s.ingress_grp = intr_to_ingressgrp[node][vf];
+	mask.key_type.s.ingress_grp = 0;
+	for (j = 5, k = 0; j >= 0; j--, k++) {
+		key.key_type.s.mac_DA[k] = ((u8 *)mac_DA)[j];
+		mask.key_type.s.mac_DA[k] = 0x0;
+	}
+	filter_dbg(FINFO, "VF id: %d with ingress_grp: %d ", vf,
+		   key.key_type.s.ingress_grp);
+	filter_dbg(FINFO, "MAC: %x: %x: %x %x: %x %x Added at Index: %d\n",
+		   ((u8 *)mac_DA)[0], ((u8 *)mac_DA)[1],
+		   ((u8 *)mac_DA)[2], ((u8 *)mac_DA)[3],
+		   ((u8 *)mac_DA)[4], ((u8 *)mac_DA)[5], mac_idx);
+
+	data.data = 0x0ull;
+	data.s.evif = vf;
+	ret = tbl_write(node, MAC_FILTER_TABLE, mac_idx, &key, &mask, &data);
+	if (ret)
+		return ret;
+
+	if (mac_idx != vf) {
+		registered_vf_filter_index(node, vf, mac_idx, 1);
+		macfilter_freeindex[node] += 1;
+	}
+
+	return TNS_NO_ERR;
+}
+
+int vf_interface_up(int node, int tbl_id, int vf, void *mac_DA)
+{
+	int ret;
+
+	//Enable unicast MAC entry for this VF
+	ret = add_mac_filter_entry(node, tbl_id, vf, vf, mac_DA);
+	if (ret)
+		return ret;
+
+	return TNS_NO_ERR;
+}
+
+int del_vlan_entry(int node, int vf, int vlan, int vlanx)
+{
+	int ret;
+	struct vlan_filter_entry tbl_entry;
+	int vlan_tbl_idx = -1, i;
+	vlan_port_bitmap_t vlan_vif;
+	int vlan_cnt = vf_reg_data[node][vf].vlan_count;
+
+	tbl_entry.key.is_valid = 1;
+	tbl_entry.key.key_type.key_value  = 0x0ull;
+	tbl_entry.mask.key_type.key_value = 0xFFFFFFFFFFFFFFFFull;
+	tbl_entry.key.key_type.s.ingress_grp = intr_to_ingressgrp[node][vf];
+	tbl_entry.mask.key_type.s.ingress_grp = 0x0;
+	tbl_entry.key.key_type.s.vlan = vlan;
+	tbl_entry.mask.key_type.s.vlan = 0x0;
+
+	filter_dbg(FINFO, "%s VF %d with ingress_grp %d VLANID %d\n",
+		   __func__, vf, tbl_entry.key.key_type.s.ingress_grp,
+		   tbl_entry.key.key_type.s.vlan);
+
+	ret = filter_tbl_lookup(node, VLAN_FILTER_TABLE, &tbl_entry,
+				&vlan_tbl_idx);
+	if (ret)
+		return ret;
+
+	if (vlan_tbl_idx == -1) {
+		filter_dbg(FINFO, "VF %d VLAN %d filter not registered\n",
+			   vf, vlan);
+		return TNS_NO_ERR;
+	}
+
+	if (vlan_tbl_idx < 1 && vlan_tbl_idx >= TNS_VLAN_FILTER_MAX_INDEX) {
+		filter_dbg(FERR, "Invalid VLAN Idx: %d\n", vlan_tbl_idx);
+		return TNS_ERR_VLAN_FILTER_INVLAID_ENTRY;
+	}
+
+	vlanx = tbl_entry.data.s.filter_idx;
+	ret = tbl_read(node, VLAN_EVIF_TABLE, vlanx, NULL, NULL,
+		       (void *)(&vlan_vif[0]));
+	if (ret)
+		return ret;
+
+	disable_vlan_port(vf, vlan_vif);
+	ret = tbl_write(node, VLAN_EVIF_TABLE, vlanx, NULL, NULL,
+			(void *)(&vlan_vif[0]));
+	if (ret)
+		return ret;
+
+	for (i = 0; i < vlan_cnt; i++) {
+		if (vf_reg_data[node][vf].vlan[i] == vlan) {
+			int j;
+
+			for (j = i; j < vlan_cnt - 1; j++)
+				vf_reg_data[node][vf].vlan[j] =
+				 vf_reg_data[node][vf].vlan[j + 1];
+			vf_reg_data[node][vf].vlan_count -= 1;
+			break;
+		}
+	}
+	if (is_empty_vlan(node, vf, vlan, vlan_vif)) {
+		free_table_index(node, VLAN_FILTER_TABLE, vlanx);
+		vlanfilter_freeindex[node] -= 1;
+		invalidate_table_entry(node, VLAN_FILTER_TABLE, vlanx);
+	}
+
+	return TNS_NO_ERR;
+}
+
+int add_vlan_entry(int node, int vf, int vlan, int vlanx)
+{
+	int ret;
+	int pf_vf;
+	struct vlan_filter_entry tbl_entry;
+	int vlan_tbl_idx = -1;
+	vlan_port_bitmap_t vlan_vif;
+
+	if (vf_reg_data[node][vf].vlan_count >= TNS_MAX_VLAN_PER_VF) {
+		filter_dbg(FDEBUG, "Reached maximum limit per VF count: %d\n",
+			   vf_reg_data[node][vf].vlan_count);
+		return TNS_ERR_MAX_LIMIT;
+	}
+
+	tbl_entry.key.is_valid = 1;
+	tbl_entry.key.key_type.key_value  = 0x0ull;
+	tbl_entry.mask.key_type.key_value = 0xFFFFFFFFFFFFFFFFull;
+	tbl_entry.key.key_type.s.ingress_grp = intr_to_ingressgrp[node][vf];
+	tbl_entry.mask.key_type.s.ingress_grp = 0x0;
+	tbl_entry.key.key_type.s.vlan = vlan;
+	tbl_entry.mask.key_type.s.vlan = 0x0;
+
+	ret = filter_tbl_lookup(node, VLAN_FILTER_TABLE, &tbl_entry,
+				&vlan_tbl_idx);
+	if (ret)
+		return ret;
+
+	if (vlan_tbl_idx != -1) {
+		filter_dbg(FINFO, "Duplicate entry found at %d\n",
+			   vlan_tbl_idx);
+		if (vlan_tbl_idx < 1 &&
+		    vlan_tbl_idx >= TNS_VLAN_FILTER_MAX_INDEX) {
+			filter_dbg(FDEBUG, "Invalid VLAN Idx %d\n",
+				   vlan_tbl_idx);
+			return TNS_ERR_VLAN_FILTER_INVLAID_ENTRY;
+		}
+
+		vlanx = tbl_entry.data.s.filter_idx;
+		ret = tbl_read(node, VLAN_EVIF_TABLE, vlanx, NULL, NULL,
+			       (void *)(&vlan_vif[0]));
+		if (ret)
+			return ret;
+
+		enable_vlan_port(vf, vlan_vif);
+		ret = tbl_write(node, VLAN_EVIF_TABLE, vlanx, NULL, NULL,
+				(void *)(&vlan_vif[0]));
+		if (ret)
+			return ret;
+
+		vf_reg_data[node][vf].vlan[vf_reg_data[node][vf].vlan_count] =
+		 vlan;
+		vf_reg_data[node][vf].vlan_count += 1;
+
+		return TNS_NO_ERR;
+	}
+
+	if (alloc_table_index(node, VLAN_FILTER_TABLE, &vlanx)) {
+		filter_dbg(FDEBUG, "%s VLAN Filter Table Full\n", __func__);
+		return TNS_ERR_MAX_LIMIT;
+	}
+	disable_vlan_vif_ports(vlan_vif);
+	enable_vlan_port(vf, vlan_vif);
+	enable_vlan_port(intr_to_ingressgrp[node][vf], vlan_vif);
+	ret = vf_pfvf_id(node, vf, &pf_vf);
+
+	if (ret)
+		return ret;
+
+	if (vf_reg_data[node][pf_vf].vf_in_promis)
+		enable_vlan_port(pf_vf, vlan_vif);
+
+	dump_vlan_vif_portss(vlan_vif);
+	ret = tbl_write(node, VLAN_EVIF_TABLE, vlanx, NULL, NULL,
+			(void *)(&vlan_vif[0]));
+	if (ret)
+		return ret;
+
+	tbl_entry.key.is_valid = 1;
+	tbl_entry.key.key_type.s.ingress_grp = intr_to_ingressgrp[node][vf];
+	tbl_entry.key.key_type.s.vlan = vlan;
+	tbl_entry.key.key_type.s.reserved = 0x0;
+	tbl_entry.key.key_type.s.reserved1 = 0x0;
+	tbl_entry.mask.is_valid = 1;
+	tbl_entry.mask.key_type.s.ingress_grp = 0x0;
+	tbl_entry.mask.key_type.s.vlan = 0x0;
+	tbl_entry.mask.key_type.s.reserved = 0xF;
+	tbl_entry.mask.key_type.s.reserved1 = 0xFFFFFFFF;
+	tbl_entry.data.data = 0x0ull;
+	tbl_entry.data.s.filter_idx = vlanx;
+	ret = tbl_write(node, VLAN_FILTER_TABLE, vlanx, &tbl_entry.key,
+			&tbl_entry.mask, &tbl_entry.data);
+	if (ret)
+		return ret;
+
+	filter_dbg(FINFO, "VF %d with ingress_grp %d VLAN %d Added at %d\n",
+		   vf, tbl_entry.key.key_type.s.ingress_grp,
+		   tbl_entry.key.key_type.s.vlan, vlanx);
+
+	vlanfilter_freeindex[node] += 1;
+	vf_reg_data[node][vf].vlan[vf_reg_data[node][vf].vlan_count] = vlan;
+	vf_reg_data[node][vf].vlan_count += 1;
+
+	return TNS_NO_ERR;
+}
+
+int enable_promiscuous_mode(int node, int vf)
+{
+	int ret = tns_enable_mcast_promis(node, vf);
+	int pf_vf;
+
+	if (ret)
+		return ret;
+
+	vf_reg_data[node][vf].vf_in_promis = 1;
+	ret = vf_pfvf_id(node, vf, &pf_vf);
+	if (ret)
+		return ret;
+
+	if (vf == pf_vf) {
+		//PFVF interface, enable full promiscuous mode
+		int i;
+		int vif = intr_to_ingressgrp[node][vf];
+		struct evif_entry evif_dat;
+		struct itt_entry_s port_cfg_entry;
+
+		for (i = 0; i < macfilter_freeindex[node]; i++) {
+			struct mac_filter_entry tbl_entry;
+
+			ret = tbl_read(node, MAC_FILTER_TABLE, i,
+				       &tbl_entry.key, &tbl_entry.mask,
+				       &tbl_entry.data);
+			if (ret && (ret != TNS_ERR_MAC_FILTER_INVALID_ENTRY))
+				return ret;
+			else if (ret == TNS_ERR_MAC_FILTER_INVALID_ENTRY)
+				continue;
+
+			if (tbl_entry.key.key_type.s.ingress_grp ==
+			    intr_to_ingressgrp[node][vf]) {
+				int vif = tbl_entry.data.s.evif;
+				struct evif_entry evif_dat;
+
+				ret = tbl_read(node, MAC_EVIF_TABLE, vif, NULL,
+					       NULL, &evif_dat);
+				if (ret)
+					return ret;
+
+				enable_port(vf, &evif_dat);
+				dump_evif_entry(&evif_dat);
+				ret = tbl_write(node, MAC_EVIF_TABLE, vif, NULL,
+						NULL, (void *)&evif_dat);
+				if (ret)
+					return ret;
+			}
+		}
+		/*If pfVf interface enters in promiscuous mode we will forward
+		 * packets destined to corresponding LMAC
+		 */
+
+		ret = tbl_read(node, MAC_EVIF_TABLE, vif, NULL, NULL,
+			       &evif_dat);
+		if (ret)
+			return ret;
+		enable_port(vf, &evif_dat);
+		dump_evif_entry(&evif_dat);
+		ret = tbl_write(node, MAC_EVIF_TABLE, vif, NULL, NULL,
+				(void *)&evif_dat);
+		if (ret)
+			return ret;
+
+		/* Update default_evif of LMAC from NULLVif to pfVf interface,
+		 * so that pfVf will shows all dropped packets as well
+		 */
+		ret = tbl_read(node, PORT_CONFIG_TABLE,
+			       intr_to_ingressgrp[node][vf], NULL, NULL,
+			       &port_cfg_entry);
+		if (ret)
+			return ret;
+
+		port_cfg_entry.default_evif = vf;
+		ret = tbl_write(node, PORT_CONFIG_TABLE,
+				intr_to_ingressgrp[node][vf], NULL, NULL,
+				(void *)&port_cfg_entry);
+		if (ret)
+			return ret;
+
+		filter_dbg(FINFO, "%s Port %d pkt_dir %d defaultVif %d",
+			   __func__, vf, port_cfg_entry.pkt_dir,
+			   port_cfg_entry.default_evif);
+		filter_dbg(FINFO, " adminVlan %d %s\n",
+			   port_cfg_entry.admin_vlan,
+			   port_cfg_entry.is_admin_vlan_enabled ? "Enable" :
+				"Disable");
+
+		for (i = 1; i < vlanfilter_freeindex[node]; i++) {
+			struct vlan_filter_entry tbl_entry;
+
+			ret = tbl_read(node, VLAN_FILTER_TABLE, i,
+				       &tbl_entry.key, &tbl_entry.mask,
+				       &tbl_entry.data);
+			if (ret && (ret != TNS_ERR_MAC_FILTER_INVALID_ENTRY))
+				return ret;
+			else if (ret == TNS_ERR_MAC_FILTER_INVALID_ENTRY)
+				continue;
+
+			if (tbl_entry.key.key_type.s.ingress_grp ==
+			    intr_to_ingressgrp[node][vf]) {
+				int vlanx = tbl_entry.data.s.filter_idx;
+				vlan_port_bitmap_t vlan_vif;
+
+				ret = tbl_read(node, VLAN_EVIF_TABLE, vlanx,
+					       NULL, NULL,
+					       (void *)(&vlan_vif[0]));
+				if (ret)
+					return ret;
+				enable_vlan_port(vf, vlan_vif);
+				ret = tbl_write(node, VLAN_EVIF_TABLE, vlanx,
+						NULL, NULL,
+						(void *)(&vlan_vif[0]));
+				if (ret)
+					return ret;
+			}
+		}
+	} else {
+		//VF interface enable multicast promiscuous mode
+		int i;
+		int ret;
+
+		for (i = TNS_MAX_VF; i < macfilter_freeindex[node]; i++) {
+			struct mac_filter_entry tbl_entry;
+
+			ret = tbl_read(node, MAC_FILTER_TABLE, i,
+				       &tbl_entry.key, &tbl_entry.mask,
+				       &tbl_entry.data);
+			if (ret && (ret != TNS_ERR_MAC_FILTER_INVALID_ENTRY))
+				return ret;
+			else if (ret == TNS_ERR_MAC_FILTER_INVALID_ENTRY)
+				continue;
+
+			/* We found filter entry, lets verify either this is
+			 * unicast or multicast
+			 */
+			if (((((u8 *)tbl_entry.key.key_type.s.mac_DA)[5]) &
+			       0x1) && (tbl_entry.key.key_type.s.ingress_grp ==
+					intr_to_ingressgrp[node][vf])) {
+				int vif = tbl_entry.data.s.evif;
+				struct evif_entry evif_dat;
+
+				ret = tbl_read(node, MAC_EVIF_TABLE, vif, NULL,
+					       NULL, &evif_dat);
+				if (ret)
+					return ret;
+				enable_port(vf, &evif_dat);
+				dump_evif_entry(&evif_dat);
+				ret = tbl_write(node, MAC_EVIF_TABLE, vif, NULL,
+						NULL, (void *)&evif_dat);
+				if (ret)
+					return ret;
+			}
+		}
+	}
+
+	return TNS_NO_ERR;
+}
+
+int disable_promiscuous_mode(int node, int vf)
+{
+	int i, pf_vf;
+	int ret;
+
+	vf_reg_data[node][vf].vf_in_promis = 0;
+	ret = vf_pfvf_id(node, vf, &pf_vf);
+	if (ret)
+		return ret;
+
+	for (i = TNS_MAX_VF; i < macfilter_freeindex[node]; i++) {
+		struct mac_filter_entry tbl_entry;
+
+		ret = tbl_read(node, MAC_FILTER_TABLE, i, &tbl_entry.key,
+			       &tbl_entry.mask, &tbl_entry.data);
+		if (ret && (ret != TNS_ERR_MAC_FILTER_INVALID_ENTRY))
+			return ret;
+		else if (ret == TNS_ERR_MAC_FILTER_INVALID_ENTRY)
+			continue;
+
+		//We found an entry belongs to this group
+		if (tbl_entry.key.key_type.s.ingress_grp ==
+		    intr_to_ingressgrp[node][vf]) {
+			int vif = tbl_entry.data.s.evif;
+			struct evif_entry evif_dat;
+
+			if (is_vf_registered_entry(node, vf, i))
+				continue;
+
+			//Is this multicast entry
+			if (((((u8 *)tbl_entry.key.key_type.s.mac_DA)[5]) &
+			       0x1) && vf_reg_data[node][vf].vf_in_mcast_promis)
+				continue;
+
+			//Disable port bitmap in EVIF entry
+			ret = tbl_read(node, MAC_EVIF_TABLE, vif, NULL,
+				       NULL, &evif_dat);
+			if (ret)
+				return ret;
+			disable_port(vf, &evif_dat);
+			dump_evif_entry(&evif_dat);
+			ret = tbl_write(node, MAC_EVIF_TABLE, vif, NULL, NULL,
+					(void *)&evif_dat);
+			if (ret)
+				return ret;
+		}
+	}
+	/* If pfVf interface exit from promiscuous mode, then  we will change
+	 * portbitmap corresponding to LMAC
+	 */
+	if (vf == pf_vf) {
+		int vif = intr_to_ingressgrp[node][vf];
+		struct evif_entry evif_dat;
+		struct itt_entry_s port_cfg_entry;
+
+		ret = tbl_read(node, MAC_EVIF_TABLE, vif, NULL, NULL,
+			       &evif_dat);
+		if (ret)
+			return ret;
+
+		disable_port(vf, &evif_dat);
+		dump_evif_entry(&evif_dat);
+		ret = tbl_write(node, MAC_EVIF_TABLE, vif, NULL, NULL,
+				(void *)&evif_dat);
+		if (ret)
+			return ret;
+
+		for (i = 1; i < vlanfilter_freeindex[node]; i++) {
+			struct vlan_filter_entry tbl_entry;
+
+			ret = tbl_read(node, VLAN_FILTER_TABLE, i,
+				       &tbl_entry.key, &tbl_entry.mask,
+				       &tbl_entry.data);
+			if (ret && (ret != TNS_ERR_MAC_FILTER_INVALID_ENTRY))
+				return ret;
+			else if (ret == TNS_ERR_MAC_FILTER_INVALID_ENTRY)
+				continue;
+
+			if (tbl_entry.key.key_type.s.ingress_grp ==
+			    intr_to_ingressgrp[node][vf]) {
+				int vlanx = tbl_entry.data.s.filter_idx;
+				vlan_port_bitmap_t vlan_vif;
+				int vlan = tbl_entry.key.key_type.s.vlan;
+
+				if (!is_vlan_registered(node, vf, vlan)) {
+					ret = tbl_read(node, VLAN_EVIF_TABLE,
+						       vlanx, NULL, NULL,
+						       (void *)(&vlan_vif[0]));
+					if (ret)
+						return ret;
+					disable_vlan_port(vf, vlan_vif);
+					ret = tbl_write(node, VLAN_EVIF_TABLE,
+							vlanx, NULL, NULL,
+							(void *)(&vlan_vif[0]));
+					if (ret)
+						return ret;
+				}
+			}
+		}
+		//Update default_evif of LMAC to NULLVif
+		ret = tbl_read(node, PORT_CONFIG_TABLE,
+			       intr_to_ingressgrp[node][vf], NULL, NULL,
+			       &port_cfg_entry);
+		if (ret)
+			return ret;
+
+		port_cfg_entry.default_evif = TNS_NULL_VIF;
+		ret = tbl_write(node, PORT_CONFIG_TABLE,
+				intr_to_ingressgrp[node][vf], NULL, NULL,
+				(void *)&port_cfg_entry);
+		if (ret)
+			return ret;
+		filter_dbg(FINFO, "%s Port %d pkt_dir %d defaultVif %d ",
+			   __func__, vf, port_cfg_entry.pkt_dir,
+			   port_cfg_entry.default_evif);
+		filter_dbg(FINFO, "adminVlan %d %s\n",
+			   port_cfg_entry.admin_vlan,
+			   port_cfg_entry.is_admin_vlan_enabled ? "Enable" :
+			   "Disable");
+	}
+	if (!vf_reg_data[node][vf].vf_in_mcast_promis)
+		remove_vf_from_mcast_promis_grp(node, vf);
+
+	return TNS_NO_ERR;
+}
+
+/* CRB-1S configuration
+ * Valid LMAC's - 3 (128, 132, & 133)
+ * PFVF - 3 (0, 64, & 96)
+ * bcast_vif - 3 (136, 140, & 141)
+ * mcast_vif - 3 (144, 148, & 149)
+ * null_vif - 1 (152)
+ */
+int mac_filter_config(void)
+{
+	int node, j;
+
+	for (node = 0; node < nr_node_ids; node++) {
+		int lmac;
+
+		//Reset inerface to Ingress Group
+		for (j = 0; j < TNS_MAC_FILTER_MAX_SYS_PORTS; j++)
+			intr_to_ingressgrp[node][j] = j;
+
+		if (!pf_vf_map_data[node].valid)
+			continue;
+
+		for (j = 0; j < TNS_MAX_INGRESS_GROUP; j++)
+			ingressgrp_gblvif[node][j].is_valid = 0;
+
+		for (lmac = 0; lmac < pf_vf_map_data[node].lmac_cnt; lmac++) {
+			int slm = pf_vf_map_data[node].pf_vf[lmac].sys_lmac;
+			int valid_pf = pf_vf_map_data[node].pf_vf[lmac].pf_id;
+			int num_vfs = pf_vf_map_data[node].pf_vf[lmac].num_vfs;
+			struct evif_entry evif_dat;
+			int bvif, mvif;
+			int ret;
+
+			bvif = TNS_BASE_BCAST_VIF + slm;
+			mvif = TNS_BASE_MCAST_VIF + slm;
+
+			//Map inerface to Ingress Group
+			for (j = valid_pf; j < (valid_pf + num_vfs); j++) {
+				struct itt_entry_s port_cfg_entry;
+				int ret;
+
+				intr_to_ingressgrp[node][j] = TNS_MAX_VF + slm;
+
+				ret = tbl_read(node, PORT_CONFIG_TABLE, j, NULL,
+					       NULL, (void *)&port_cfg_entry);
+				if (ret)
+					return ret;
+				port_cfg_entry.default_evif =
+					intr_to_ingressgrp[node][j];
+				ret = tbl_write(node, PORT_CONFIG_TABLE, j,
+						NULL, NULL,
+						(void *)&port_cfg_entry);
+				if (ret)
+					return ret;
+			}
+
+			//LMAC Configuration
+			ingressgrp_gblvif[node][slm].is_valid = 1;
+			ingressgrp_gblvif[node][slm].ingress_grp = TNS_MAX_VF +
+								     slm;
+			ingressgrp_gblvif[node][slm].pf_vf = valid_pf;
+			ingressgrp_gblvif[node][slm].bcast_vif = bvif;
+			ingressgrp_gblvif[node][slm].mcast_vif = mvif;
+			ingressgrp_gblvif[node][slm].null_vif = TNS_NULL_VIF;
+			MCAST_PROMIS(node, slm, 0) = TNS_MAX_VF + slm;
+			VALID_MCAST_PROMIS(node, slm) = 1;
+
+			filter_dbg(FINFO, "lmac %d syslm %d num_vfs %d ",
+				   lmac, slm,
+				   pf_vf_map_data[node].pf_vf[lmac].num_vfs);
+			filter_dbg(FINFO, "ingress_grp %d pfVf %d bCast %d ",
+				   ingressgrp_gblvif[node][slm].ingress_grp,
+				   ingressgrp_gblvif[node][slm].pf_vf,
+				   ingressgrp_gblvif[node][slm].bcast_vif);
+			filter_dbg(FINFO, "mCast: %d\n",
+				   ingressgrp_gblvif[node][slm].mcast_vif);
+
+			ret = tbl_read(node, MAC_EVIF_TABLE, bvif, NULL, NULL,
+				       &evif_dat);
+			if (ret)
+				return ret;
+
+			evif_dat.rewrite_ptr0 = 0xFF;
+			evif_dat.rewrite_ptr1 = 0xFF;
+			enable_port(ingressgrp_gblvif[node][slm].ingress_grp,
+				    &evif_dat);
+
+			ret = tbl_write(node, MAC_EVIF_TABLE, bvif, NULL, NULL,
+					(void *)&evif_dat);
+			if (ret)
+				return ret;
+
+			ret = tbl_read(node, MAC_EVIF_TABLE, mvif, NULL, NULL,
+				       &evif_dat);
+			if (ret)
+				return ret;
+
+			evif_dat.rewrite_ptr0 = 0xFF;
+			evif_dat.rewrite_ptr1 = 0xFF;
+			enable_port(ingressgrp_gblvif[node][slm].ingress_grp,
+				    &evif_dat);
+
+			ret = tbl_write(node, MAC_EVIF_TABLE, mvif, NULL, NULL,
+					(void *)&evif_dat);
+			if (ret)
+				return ret;
+
+			ret = tbl_read(node, MAC_EVIF_TABLE, TNS_NULL_VIF, NULL,
+				       NULL, &evif_dat);
+			if (ret)
+				return ret;
+
+			evif_dat.rewrite_ptr0 = 0xFF;
+			evif_dat.rewrite_ptr1 = 0xFF;
+
+			ret = tbl_write(node, MAC_EVIF_TABLE, TNS_NULL_VIF,
+					NULL, NULL, (void *)&evif_dat);
+			if (ret)
+				return ret;
+		}
+		j = 0;
+		alloc_table_index(node, VLAN_FILTER_TABLE, &j);
+
+		for (j = 0; j < TNS_MAX_VF; j++) {
+			vf_reg_data[node][j].vf_in_mcast_promis = 0;
+			vf_reg_data[node][j].filter_count = 1;
+			vf_reg_data[node][j].filter_index[0] = j;
+			vf_reg_data[node][j].vlan_count = 0;
+			alloc_table_index(node, MAC_FILTER_TABLE, &j);
+		}
+		for (j = 0; j <= TNS_NULL_VIF; j++)
+			alloc_table_index(node, MAC_EVIF_TABLE, &j);
+		macfilter_freeindex[node] = TNS_MAX_VF;
+		vlanfilter_freeindex[node] = 1;
+	}
+
+	return TNS_NO_ERR;
+}
+
+int add_admin_vlan(int node, int vf, int vlan)
+{
+	int index = -1;
+	int ret;
+	struct itt_entry_s port_cfg_entry;
+
+	ret = add_vlan_entry(node, vf, vlan, index);
+	if (ret) {
+		filter_dbg(FERR, "Add admin VLAN for VF: %d Failed %d\n",
+			   vf, ret);
+		return ret;
+	}
+
+	ret = tbl_read(node, PORT_CONFIG_TABLE, vf, NULL, NULL,
+		       (void *)&port_cfg_entry);
+	if (ret)
+		return ret;
+	port_cfg_entry.is_admin_vlan_enabled = 1;
+	port_cfg_entry.admin_vlan = vlan;
+	ret = tbl_write(node, PORT_CONFIG_TABLE, vf, NULL, NULL,
+			(void *)&port_cfg_entry);
+	if (ret)
+		return ret;
+	filter_dbg(FINFO, "%s Port %d dir %d defaultVif %d adminVlan %d %s\n",
+		   __func__, vf, port_cfg_entry.pkt_dir,
+		   port_cfg_entry.default_evif, port_cfg_entry.admin_vlan,
+		   port_cfg_entry.is_admin_vlan_enabled ? "Enable" : "Disable");
+
+	return TNS_NO_ERR;
+}
+
+int del_admin_vlan(int node, int vf, int vlan)
+{
+	int index = -1;
+	int ret;
+	struct itt_entry_s port_cfg_entry;
+
+	ret = del_vlan_entry(node, vf, vlan, index);
+	if (ret) {
+		filter_dbg(FERR, "Delete admin VLAN: %d for VF %d failed %d\n",
+			   vlan, vf, ret);
+		return ret;
+	}
+
+	ret = tbl_read(node, PORT_CONFIG_TABLE, vf, NULL, NULL,
+		       (void *)&port_cfg_entry);
+	if (ret)
+		return ret;
+	port_cfg_entry.is_admin_vlan_enabled = 0;
+	port_cfg_entry.admin_vlan = 0x0;
+	ret = tbl_write(node, PORT_CONFIG_TABLE, vf, NULL, NULL,
+			(void *)&port_cfg_entry);
+	if (ret)
+		return ret;
+	filter_dbg(FINFO, "%s Port %d dir %d defaultVif %d adminVlan %d %s\n",
+		   __func__, vf, port_cfg_entry.pkt_dir,
+		   port_cfg_entry.default_evif, port_cfg_entry.admin_vlan,
+		   port_cfg_entry.is_admin_vlan_enabled ? "Enable" : "Disable");
+
+	return TNS_NO_ERR;
+}
+
+void link_status_notification(int node, int vf, void *arg)
+{
+	int status =  *((int *)arg);
+	int bcast_vif;
+	int ret;
+	struct evif_entry evif_dat;
+
+	filter_dbg(FINFO, "VF %d Link %s\n", vf, status ? "up " : "down");
+	if (status) {
+		ret = vf_bcast_vif(node, vf, &bcast_vif);
+		if (ret)
+			return;
+
+		ret = tbl_read(node, MAC_EVIF_TABLE, bcast_vif, NULL, NULL,
+			       &evif_dat);
+		if (ret)
+			return;
+
+		enable_port(vf, &evif_dat);
+		dump_evif_entry(&evif_dat);
+		ret = tbl_write(node, MAC_EVIF_TABLE, bcast_vif, NULL, NULL,
+				(void *)&evif_dat);
+		if (ret)
+			return;
+	} else {
+		ret = vf_bcast_vif(node, vf, &bcast_vif);
+		if (ret)
+			return;
+
+		ret = tbl_read(node, MAC_EVIF_TABLE, bcast_vif, NULL, NULL,
+			       &evif_dat);
+		if (ret)
+			return;
+
+		disable_port(vf, &evif_dat);
+		dump_evif_entry(&evif_dat);
+		ret = tbl_write(node, MAC_EVIF_TABLE, bcast_vif, NULL, NULL,
+				(void *)&evif_dat);
+		if (ret)
+			return;
+	}
+}
+
+void mac_update_notification(int node, int vf_id, void *arg)
+{
+	u8 *mac = (u8 *)arg;
+
+	filter_dbg(FINFO, "VF:%d MAC %02x:%02x:%02x:%02x:%02x:%02x Updated\n",
+		   vf_id, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+	vf_interface_up(node, MAC_FILTER_TABLE, vf_id, arg);
+}
+
+void promisc_update_notification(int node, int vf_id, void *arg)
+{
+	int on = *(int *)arg;
+
+	filter_dbg(FERR, "VF %d %s promiscuous mode\n", vf_id,
+		   on ? "enter" : "left");
+	if (on)
+		enable_promiscuous_mode(node, vf_id);
+	else
+		disable_promiscuous_mode(node, vf_id);
+}
+
+void uc_mc_update_notification(int node, int vf_id, void *arg)
+{
+	struct uc_mc_msg *uc_mc_cfg = (struct uc_mc_msg *)arg;
+	u8 *mac;
+
+	mac = (u8 *)uc_mc_cfg->mac_addr;
+	if (uc_mc_cfg->is_flush) {
+		filter_dbg(FINFO, "\nNOTIFICATION VF:%d %s %s\n", vf_id,
+			   uc_mc_cfg->addr_type ? "mc" : "uc", "flush");
+	} else {
+		filter_dbg(FINFO, "\nNOTIFICATION VF:%d %s %s ", vf_id,
+			   uc_mc_cfg->addr_type ? "mc" : "uc",
+			   uc_mc_cfg->is_add ? "add" : "del");
+		filter_dbg(FINFO, "MAC ADDRESS %02x:%02x:%02x:%02x:%02x:%02x\n",
+			   mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+		if (uc_mc_cfg->is_add) {
+			if (uc_mc_cfg->addr_type)
+				add_mac_filter_mcast_entry(node,
+							   MAC_FILTER_TABLE,
+							   vf_id, -1, mac);
+			else
+				add_mac_filter_entry(node, MAC_FILTER_TABLE,
+						     vf_id, -1, mac);
+		} else {
+			del_mac_filter_entry(node, MAC_FILTER_TABLE, vf_id, -1,
+					     mac, uc_mc_cfg->addr_type);
+		}
+	}
+}
+
+void admin_vlan_update_notification(int node, int vf_id, void *arg)
+{
+	struct vlan_msg *vlan_cfg = (struct vlan_msg *)arg;
+
+	filter_dbg(FINFO, "\nNOTIFICATION ADMIN VF %d VLAN id %d %s\n", vf_id,
+		   vlan_cfg->vlan_id, (vlan_cfg->vlan_add) ? "add" : "del");
+	if (vlan_cfg->vlan_add)
+		add_admin_vlan(node, vf_id, vlan_cfg->vlan_id);
+	else
+		del_admin_vlan(node, vf_id, vlan_cfg->vlan_id);
+}
+
+void vlan_update_notification(int node, int vf_id, void *arg)
+{
+	struct vlan_msg *vlan_cfg = (struct vlan_msg *)arg;
+
+	filter_dbg(FINFO, "\nNOTIFICATION VF %d VLAN id %d %s\n", vf_id,
+		   vlan_cfg->vlan_id, (vlan_cfg->vlan_add) ? "add" : "del");
+	if (vlan_cfg->vlan_add && vlan_cfg->vlan_id) {
+		int index = -1;
+		int ret = add_vlan_entry(node, vf_id, vlan_cfg->vlan_id,
+					      index);
+
+		if (ret)
+			filter_dbg(FERR, "Adding VLAN failed: %d\n", ret);
+		else
+			filter_dbg(FINFO, "VF: %d with VLAN: %d added\n",
+				   vf_id, vlan_cfg->vlan_id);
+	} else if (!vlan_cfg->vlan_add && vlan_cfg->vlan_id) {
+		int index = -1;
+		int ret = del_vlan_entry(node, vf_id, vlan_cfg->vlan_id,
+						index);
+
+		if (ret)
+			filter_dbg(FERR, "Deleting VLAN failed: %d\n", ret);
+		else
+			filter_dbg(FINFO, "VF: %d with VLAN: %d deleted\n",
+				   vf_id, vlan_cfg->vlan_id);
+	}
+}
+
+void pf_notify_msg_handler(int node, void *arg)
+{
+	union nic_mbx *mbx = (union nic_mbx *)arg;
+	int status;
+
+	switch (mbx->msg.msg) {
+	case NIC_MBOX_MSG_ADMIN_VLAN:
+		admin_vlan_update_notification(node, mbx->vlan_cfg.vf_id,
+					       &mbx->vlan_cfg);
+		break;
+	case NIC_MBOX_MSG_VLAN:
+		vlan_update_notification(node, mbx->vlan_cfg.vf_id,
+					 &mbx->vlan_cfg);
+		break;
+	case NIC_MBOX_MSG_UC_MC:
+		uc_mc_update_notification(node, mbx->vlan_cfg.vf_id,
+					  &mbx->uc_mc_cfg);
+		break;
+	case NIC_MBOX_MSG_SET_MAC:
+		mac_update_notification(node, mbx->mac.vf_id,
+					(void *)mbx->mac.mac_addr);
+		break;
+	case NIC_MBOX_MSG_CFG_DONE:
+	case NIC_MBOX_MSG_OP_UP:
+		status = true;
+		link_status_notification(node, mbx->mac.vf_id, (void *)&status);
+		break;
+	case NIC_MBOX_MSG_SHUTDOWN:
+	case NIC_MBOX_MSG_OP_DOWN:
+		status = false;
+		link_status_notification(node, mbx->mac.vf_id, (void *)&status);
+		break;
+	case NIC_MBOX_MSG_PROMISC:
+		status = mbx->promisc_cfg.on;
+		promisc_update_notification(node, mbx->promisc_cfg.vf_id,
+					    (void *)&status);
+		break;
+	}
+}
+
+int pf_filter_init(void)
+{
+	mac_filter_config();
+
+	return 0;
+}
-- 
1.8.3.1

^ permalink raw reply related

* [RFC PATCH 6/7] HW Filter Table access API's
From: Satha Koteswara Rao @ 2016-12-21  8:46 UTC (permalink / raw)
  To: linux-kernel
  Cc: rvatsavayi, rric, david.daney, netdev, satha.rao, derek.chickles,
	sgoutham, davem, linux-arm-kernel, philip.romanov
In-Reply-To: <1482310011-1862-1-git-send-email-satha.rao@caviumnetworks.com>

---
 drivers/net/ethernet/cavium/thunder/tbl_access.c | 262 +++++++++++++++++++++++
 drivers/net/ethernet/cavium/thunder/tbl_access.h |  61 ++++++
 2 files changed, 323 insertions(+)
 create mode 100644 drivers/net/ethernet/cavium/thunder/tbl_access.c
 create mode 100644 drivers/net/ethernet/cavium/thunder/tbl_access.h

diff --git a/drivers/net/ethernet/cavium/thunder/tbl_access.c b/drivers/net/ethernet/cavium/thunder/tbl_access.c
new file mode 100644
index 0000000..6be31eb
--- /dev/null
+++ b/drivers/net/ethernet/cavium/thunder/tbl_access.c
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2015 Cavium, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ */
+
+#include <linux/uaccess.h>
+#include "pf_globals.h"
+#include "pf_locals.h"
+#include "tbl_access.h"
+
+struct tns_table_s *get_table_information(int table_id)
+{
+	int i;
+
+	for (i = 0; i < TNS_MAX_TABLE; i++) {
+		if (!tbl_info[i].sdata.valid)
+			continue;
+
+		if (tbl_info[i].sdata.tbl_id == table_id)
+			return &tbl_info[i];
+	}
+
+	return NULL;
+}
+
+int tbl_write(int node, int table_id, int tbl_index, void *key, void *mask,
+	      void *data)
+{
+	int i;
+	struct tns_table_s *tbl = get_table_information(table_id);
+	int bck_cnt, data_index, data_offset;
+	u64 data_entry[4];
+
+	if (!tbl) {
+		filter_dbg(FERR, "Invalid Table ID: %d\n", table_id);
+		return TNS_ERR_INVALID_TBL_ID;
+	}
+
+	bck_cnt = tbl->sdata.data_width / tbl->sdata.data_size;
+	data_index = (tbl_index / bck_cnt);
+	data_offset = (tbl_index % bck_cnt);
+	//TCAM Table, we need to parse key & mask into single array
+	if (tbl->sdata.tbl_type == TNS_TBL_TYPE_TT) {
+		struct filter_keymask_s *tk = (struct filter_keymask_s *)key;
+		struct filter_keymask_s *tm = (struct filter_keymask_s *)mask;
+		u8 km[32];
+		u64 mod_key, mod_mask, temp_mask;
+		int index = 0, offset = 0;
+
+		memset(km, 0x0, 32);
+
+/* TCAM truth table data creation. Translation from data/mask to following
+ * truth table:
+ *
+ *         Mask   Data     Content
+ *          0     0         X
+ *          0     1         1
+ *          1     0         0
+ *          1     1         Always Mismatch
+ *
+ */
+		mod_mask = ~tk->key_value;
+		temp_mask = tm->key_value;
+		mod_key = tk->key_value;
+		mod_key = mod_key & (~temp_mask);
+		mod_mask = mod_mask & (~temp_mask);
+
+		for (i = 0; i < 64; i++) {
+			km[index] = km[index] | (((mod_mask >> i) & 0x1) <<
+						 offset);
+			km[index] = km[index] | (((mod_key >> i) & 0x1) <<
+						 (offset + 1));
+			offset += 2;
+			if (offset == 8) {
+				offset = 0;
+				index += 1;
+			}
+		}
+		km[index] = 0x2;
+		if (tns_write_register_indirect(node,
+						(tbl->sdata.key_base_addr +
+						 (tbl_index * 32)), 32,
+						(void *)&km[0])) {
+			filter_dbg(FERR, "key write failed node %d tbl ID %d",
+				   node, table_id);
+			filter_dbg(FERR, " index %d\n", tbl_index);
+			return TNS_ERR_DRIVER_WRITE;
+		}
+	}
+
+	/* Data Writes are ReadModifyWrite */
+	if (tns_read_register_indirect(node, (tbl->sdata.data_base_addr +
+					      (data_index * 32)), 32,
+				       (void *)&data_entry[0])) {
+		filter_dbg(FERR, "data read failed node %d tbl ID %d idx %d\n",
+			   node, table_id, tbl_index);
+		return TNS_ERR_DRIVER_READ;
+	}
+	memcpy(&data_entry[data_offset], data, tbl->sdata.data_size / 8);
+	if (tns_write_register_indirect(node, (tbl->sdata.data_base_addr +
+					       (data_index * 32)), 32,
+					(void *)&data_entry[0])) {
+		filter_dbg(FERR, "data write failed node %d tbl ID %d idx %d\n",
+			   node, table_id, tbl_index);
+		return TNS_ERR_DRIVER_WRITE;
+	}
+
+	return TNS_NO_ERR;
+}
+
+int tbl_read(int node, int table_id, int tbl_index, void *key, void *mask,
+	     void *data)
+{
+	struct tns_table_s *tbl = get_table_information(table_id);
+	int i, bck_cnt, data_index, data_offset;
+	u64 data_entry[4];
+	u8 km[32];
+
+	if (!tbl) {
+		filter_dbg(FERR, "Invalid Table ID: %d\n", table_id);
+		return TNS_ERR_INVALID_TBL_ID;
+	}
+
+	bck_cnt = tbl->sdata.data_width / tbl->sdata.data_size;
+	data_index = (tbl_index / bck_cnt);
+	data_offset = (tbl_index % bck_cnt);
+
+	//TCAM Table, we need to parse key & mask into single array
+	if (tbl->sdata.tbl_type == TNS_TBL_TYPE_TT) {
+		memset(km, 0x0, 32);
+
+		if (tns_read_register_indirect(node, (tbl->sdata.key_base_addr +
+						      (tbl_index * 32)), 32,
+					       (void *)&km[0])) {
+			filter_dbg(FERR, "key read failed node %d tbl ID %d",
+				   node, table_id);
+			filter_dbg(FERR, " idx %d\n", tbl_index);
+			return TNS_ERR_DRIVER_READ;
+		}
+		if (!(km[((tbl->sdata.key_size * 2) / 8)] == 0x2))
+			return TNS_ERR_MAC_FILTER_INVALID_ENTRY;
+	}
+
+	if (tns_read_register_indirect(node, (tbl->sdata.data_base_addr +
+					      (data_index * 32)), 32,
+				       (void *)&data_entry[0])) {
+		filter_dbg(FERR, "data read failed node %d tbl ID %d idx %d\n",
+			   node, table_id, tbl_index);
+		return TNS_ERR_DRIVER_READ;
+	}
+	memcpy(data, (void *)(&data_entry[data_offset]),
+	       (tbl->sdata.data_size / 8));
+
+	if (tbl->sdata.tbl_type == TNS_TBL_TYPE_TT) {
+		struct filter_keymask_s *tk = (struct filter_keymask_s *)key;
+		struct filter_keymask_s *tm = (struct filter_keymask_s *)mask;
+		u8 temp_km;
+		int index = 0, offset = 0;
+
+		tk->key_value = 0x0ull;
+		tm->key_value = 0x0ull;
+		temp_km = km[0];
+		for (i = 0; i < 64; i++) {
+			tm->key_value = tm->key_value |
+					 ((temp_km & 0x1ull) << i);
+			temp_km >>= 1;
+			tk->key_value = tk->key_value |
+					 ((temp_km & 0x1ull) << i);
+			temp_km >>= 1;
+			offset += 2;
+			if (offset == 8) {
+				offset = 0;
+				index += 1;
+				temp_km = km[index];
+			}
+		}
+		tm->key_value = ~tm->key_value & ~tk->key_value;
+		tk->is_valid = 1;
+		tm->is_valid = 0;
+	}
+
+	return TNS_NO_ERR;
+}
+
+int invalidate_table_entry(int node, int table_id, int tbl_idx)
+{
+	struct tns_table_s *tbl = get_table_information(table_id);
+
+	if (!tbl) {
+		filter_dbg(FERR, "Invalid Table ID: %d\n", table_id);
+		return TNS_ERR_INVALID_TBL_ID;
+	}
+
+	if (tbl->sdata.tbl_type == TNS_TBL_TYPE_TT) {
+		u8 km[32];
+
+		memset(km, 0x0, 32);
+		km[((tbl->sdata.key_size * 2) / 8)] = 0x1;
+
+		if (tns_write_register_indirect(node,
+						(tbl->sdata.key_base_addr +
+						 (tbl_idx * 32)), 32,
+						(void *)&km[0])) {
+			filter_dbg(FERR, "%s failed node %d tbl ID %d idx %d\n",
+				   __func__, node, table_id, tbl_idx);
+			return TNS_ERR_DRIVER_WRITE;
+		}
+	}
+
+	return TNS_NO_ERR;
+}
+
+int alloc_table_index(int node, int table_id, int *index)
+{
+	int err = 0;
+	struct tns_table_s *tbl = get_table_information(table_id);
+
+	if (!tbl) {
+		filter_dbg(FERR, "%s Invalid TableID %d\n", __func__, table_id);
+		return TNS_ERR_INVALID_TBL_ID;
+	}
+
+	if (*index == -1) {
+		*index = find_first_zero_bit(tbl->ddata[node].bitmap,
+					     tbl->sdata.depth);
+
+		if (*index < 0 || *index >= tbl->sdata.depth)
+			err = -ENOSPC;
+		else
+			__set_bit(*index, tbl->ddata[node].bitmap);
+
+		return err;
+	} else if (*index < 0 || *index >= tbl->sdata.depth) {
+		filter_dbg(FERR, "%s Out of bound index %d requested[0...%d]\n",
+			   __func__, *index, tbl->sdata.depth);
+		return TNS_ERR_MAC_FILTER_INVALID_ENTRY;
+	}
+	if (test_and_set_bit(*index, tbl->ddata[node].bitmap))
+		filter_dbg(FDEBUG, "%s Entry Already exists\n", __func__);
+
+	return err;
+}
+
+void free_table_index(int node, int table_id, int index)
+{
+	struct tns_table_s *tbl = get_table_information(table_id);
+
+	if (!tbl) {
+		filter_dbg(FERR, "%s Invalid TableID %d\n", __func__, table_id);
+		return;
+	}
+	if (index < 0 || index >= tbl->sdata.depth) {
+		filter_dbg(FERR, "%s Invalid Index %d Max Limit %d\n",
+			   __func__, index, tbl->sdata.depth);
+		return;
+	}
+
+	__clear_bit(index, tbl->ddata[node].bitmap);
+}
diff --git a/drivers/net/ethernet/cavium/thunder/tbl_access.h b/drivers/net/ethernet/cavium/thunder/tbl_access.h
new file mode 100644
index 0000000..c098410
--- /dev/null
+++ b/drivers/net/ethernet/cavium/thunder/tbl_access.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2015 Cavium, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ */
+
+#ifndef __TBL_ACCESS_H__
+#define __TBL_ACCESS_H__
+
+#define TNS_MAX_TABLE	8
+
+enum {
+	TNS_TBL_TYPE_DT,
+	TNS_TBL_TYPE_HT,
+	TNS_TBL_TYPE_TT,
+	TNS_TBL_TYPE_MAX
+};
+
+struct table_static_s {
+	u8 tbl_type;
+	u8 tbl_id;
+	u8 valid;
+	u8 rsvd;
+	u16 key_size;
+	u16 data_size;
+	u16 data_width;
+	u16 key_width;
+	u32 depth;
+	u64 key_base_addr;
+	u64 data_base_addr;
+	u8 tbl_name[32];
+};
+
+struct table_dynamic_s {
+	unsigned long *bitmap;
+};
+
+struct tns_table_s {
+	struct table_static_s sdata;
+	struct table_dynamic_s ddata[MAX_NUMNODES];
+};
+
+enum {
+	MAC_FILTER_TABLE = 102,
+	VLAN_FILTER_TABLE = 103,
+	MAC_EVIF_TABLE = 140,
+	VLAN_EVIF_TABLE = 201,
+	PORT_CONFIG_TABLE = 202,
+	TABLE_ID_END
+};
+
+extern struct tns_table_s	tbl_info[TNS_MAX_TABLE];
+
+struct filter_keymask_s {
+	u8 is_valid;
+	u64 key_value;
+};
+
+#endif /* __TBL_ACCESS_H__ */
-- 
1.8.3.1

^ permalink raw reply related

* [RFC PATCH 5/7] Multiple VF's grouped together under single physical port called PF group PF Group maintainance API's
From: Satha Koteswara Rao @ 2016-12-21  8:46 UTC (permalink / raw)
  To: linux-kernel
  Cc: rvatsavayi, rric, david.daney, netdev, satha.rao, derek.chickles,
	sgoutham, davem, linux-arm-kernel, philip.romanov
In-Reply-To: <1482310011-1862-1-git-send-email-satha.rao@caviumnetworks.com>

---
 drivers/net/ethernet/cavium/thunder/pf_globals.h |  78 +++++
 drivers/net/ethernet/cavium/thunder/pf_locals.h  | 365 +++++++++++++++++++++++
 drivers/net/ethernet/cavium/thunder/pf_vf.c      | 207 +++++++++++++
 3 files changed, 650 insertions(+)
 create mode 100644 drivers/net/ethernet/cavium/thunder/pf_globals.h
 create mode 100644 drivers/net/ethernet/cavium/thunder/pf_locals.h
 create mode 100644 drivers/net/ethernet/cavium/thunder/pf_vf.c

diff --git a/drivers/net/ethernet/cavium/thunder/pf_globals.h b/drivers/net/ethernet/cavium/thunder/pf_globals.h
new file mode 100644
index 0000000..79fab86
--- /dev/null
+++ b/drivers/net/ethernet/cavium/thunder/pf_globals.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2015 Cavium, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ */
+
+#ifndef NIC_PF_H
+#define	NIC_PF_H
+
+#include <linux/netdevice.h>
+#include <linux/interrupt.h>
+#include <linux/firmware.h>
+#include "thunder_bgx.h"
+#include "tbl_access.h"
+
+#define TNS_MAX_LMAC	8
+#define TNS_MIN_LMAC    0
+
+struct tns_global_st {
+	u64 magic;
+	char     version[16];
+	u64 reg_cnt;
+	struct table_static_s tbl_info[TNS_MAX_TABLE];
+};
+
+#define PF_COUNT 3
+#define PF_1	0
+#define PF_2	64
+#define PF_3	96
+#define PF_END	128
+
+int is_pf(int node_id, int vf);
+int get_pf(int node_id, int vf);
+void get_vf_group(int node_id, int lmac, int *start_vf, int *end_vf);
+int vf_to_pport(int node_id, int vf);
+int pf_filter_init(void);
+int tns_init(const struct firmware *fw, struct device *dev);
+void tns_exit(void);
+void pf_notify_msg_handler(int node_id, void *arg);
+void nic_init_pf_vf_mapping(void);
+int nic_set_pf_vf_mapping(int node_id);
+int get_bgx_id(int node_id, int vf_id, int *bgx_id, int *lmac);
+int phy_port_to_bgx_lmac(int node, int port, int *bgx, int *lmac);
+int tns_filter_valid_entry(int node, int req_type, int vf, int vlan);
+void nic_enable_valid_vf(int max_vf_cnt);
+
+union nic_pf_qsx_rqx_bp_cfg {
+	u64 u;
+	struct nic_pf_qsx_rqx_bp_cfg_s {
+		u64 bpid		: 8;
+		u64 cq_bp		: 8;
+		u64 rbdr_bp		: 8;
+		u64 reserved_24_61	: 38;
+		u64 cq_bp_ena		: 1;
+		u64 rbdr_bp_ena		: 1;
+	} s;
+};
+
+#define NIC_PF_QSX_RQX_BP_CFG	0x20010500ul
+#define RBDR_CQ_BP		129
+
+union nic_pf_intfx_bp_cfg {
+	u64 u;
+	struct bdk_nic_pf_intfx_bp_cfg_s {
+		u64 bp_id		: 4;
+		u64 bp_type		: 1;
+		u64 reserved_5_62	: 58;
+		u64 bp_ena		: 1;
+	} s;
+};
+
+#define NIC_PF_INTFX_BP_CFG	0x208ull
+
+#define FW_NAME	"tns_firmware.bin"
+
+#endif
diff --git a/drivers/net/ethernet/cavium/thunder/pf_locals.h b/drivers/net/ethernet/cavium/thunder/pf_locals.h
new file mode 100644
index 0000000..f7e74bb
--- /dev/null
+++ b/drivers/net/ethernet/cavium/thunder/pf_locals.h
@@ -0,0 +1,365 @@
+/*
+ * Copyright (C) 2015 Cavium, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ */
+
+#ifndef __PF_LOCALS__
+#define __PF_LOCALS__
+
+#include <linux/printk.h>
+
+#define XP_TOTAL_PORTS	(137)
+#define MAX_SYS_PORTS	XP_TOTAL_PORTS
+//Loopback port was invalid in MAC filter design
+#define TNS_MAC_FILTER_MAX_SYS_PORTS	(MAX_SYS_PORTS - 1)
+//Maximum LMAC available
+#define TNS_MAX_INGRESS_GROUP	8
+#define TNS_MAX_VF	(TNS_MAC_FILTER_MAX_SYS_PORTS - TNS_MAX_INGRESS_GROUP)
+#define TNS_VLAN_FILTER_MAX_INDEX	256
+#define TNS_MAC_FILTER_MAX_INDEX	1536
+#define TNS_MAX_VLAN_PER_VF	16
+
+#define TNS_NULL_VIF		152
+#define TNS_BASE_BCAST_VIF	136
+#define TNS_BASE_MCAST_VIF	144
+#define TNS_FW_MAX_SIZE         1048576
+
+/* We are restricting each VF to register atmost 11 filter entries
+ * (including unicast & multicast)
+ */
+#define TNS_MAX_MAC_PER_VF	11
+
+#define FERR		0
+#define FDEBUG		1
+#define FINFO		2
+
+#define FILTER_DBG_GBL		FERR
+#define filter_dbg(dbg_lvl, fmt, args...) \
+	({ \
+	if ((dbg_lvl) <= FILTER_DBG_GBL) \
+		pr_info(fmt, ##args); \
+	})
+
+typedef u8 mac_addr_t[6];		///< User define type for Mac Address
+typedef u8 vlan_port_bitmap_t[32];
+
+enum {
+	TNS_NO_ERR = 0,
+
+	/* Error in indirect read watch out the status */
+	TNS_ERROR_INDIRECT_READ = 4,
+	/* Error in indirect write watch out the status */
+	TNS_ERROR_INDIRECT_WRITE = 5,
+	/* Data too large for Read/Write */
+	TNS_ERROR_DATA_TOO_LARGE = 6,
+	/* Invalid arguments supplied to the IOCTL */
+	TNS_ERROR_INVALID_ARG = 7,
+
+	TNS_ERR_MAC_FILTER_INVALID_ENTRY,
+	TNS_ERR_MAC_FILTER_TBL_READ,
+	TNS_ERR_MAC_FILTER_TBL_WRITE,
+	TNS_ERR_MAC_EVIF_TBL_READ,
+	TNS_ERR_MAC_EVIF_TBL_WRITE,
+
+	TNS_ERR_VLAN_FILTER_INVLAID_ENTRY,
+	TNS_ERR_VLAN_FILTER_TBL_READ,
+	TNS_ERR_VLAN_FILTER_TBL_WRITE,
+	TNS_ERR_VLAN_EVIF_TBL_READ,
+	TNS_ERR_VLAN_EVIF_TBL_WRITE,
+
+	TNS_ERR_PORT_CONFIG_TBL_READ,
+	TNS_ERR_PORT_CONFIG_TBL_WRITE,
+	TNS_ERR_PORT_CONFIG_INVALID_ENTRY,
+
+	TNS_ERR_DRIVER_READ,
+	TNS_ERR_DRIVER_WRITE,
+
+	TNS_ERR_WRONG_PORT_NUMBER,
+	TNS_ERR_INVALID_TBL_ID,
+	TNS_ERR_ENTRY_NOT_FOUND,
+	TNS_ERR_DUPLICATE_MAC,
+	TNS_ERR_MAX_LIMIT,
+
+	TNS_STATUS_NUM_ENTRIES
+};
+
+struct ing_grp_gblvif {
+	u32 ingress_grp;
+	u32 pf_vf;
+	u32 bcast_vif;
+	u32 mcast_vif;
+	u32 null_vif;
+	u32 is_valid; //Is this Ingress Group or LMAC is valid
+	u8 mcast_promis_grp[TNS_MAC_FILTER_MAX_SYS_PORTS];
+	u8 valid_mcast_promis_ports;
+};
+
+struct vf_register_s {
+	int filter_index[16];
+	u32 filter_count;
+	int vf_in_mcast_promis;
+	int vf_in_promis;
+	int vlan[TNS_MAX_VLAN_PER_VF];
+	u32 vlan_count;
+};
+
+union mac_filter_keymask_type_s {
+	u64 key_value;
+
+	struct {
+		u32	ingress_grp: 16;
+		mac_addr_t	mac_DA;
+	} s;
+};
+
+struct mac_filter_keymask_s {
+	u8 is_valid;
+	union mac_filter_keymask_type_s key_type;
+};
+
+union mac_filter_data_s {
+	u64 data;
+	struct {
+		u64 evif: 16;
+		u64 Reserved0 : 48;
+	} s;
+};
+
+struct mac_filter_entry {
+	struct mac_filter_keymask_s key;
+	struct mac_filter_keymask_s mask;
+	union mac_filter_data_s data;
+};
+
+union vlan_filter_keymask_type_s {
+	u64 key_value;
+
+	struct {
+		u32	ingress_grp: 16;
+		u32	vlan: 12;
+		u32	reserved: 4;
+		u32	reserved1;
+	} s;
+};
+
+struct vlan_filter_keymask_s {
+	u8 is_valid;
+	union vlan_filter_keymask_type_s key_type;
+};
+
+union vlan_filter_data_s {
+	u64 data;
+	struct {
+		u64 filter_idx: 16;
+		u64 Reserved0 : 48;
+	} s;
+};
+
+struct vlan_filter_entry {
+	struct vlan_filter_keymask_s key;
+	struct vlan_filter_keymask_s mask;
+	union vlan_filter_data_s data;
+};
+
+struct evif_entry {
+	u64	rsp_type: 2;
+	u64	truncate: 1;
+	u64	mtu_prf: 3;
+	u64	mirror_en: 1;
+	u64	q_mirror_en: 1;
+	u64	prt_bmap7_0: 8;
+	u64	rewrite_ptr0: 8;
+	u64	rewrite_ptr1: 8;
+	/* Byte 0 is data31_0[7:0] and byte 3 is data31_0[31:24] */
+	u64	data31_0: 32;
+	u64	insert_ptr0: 16;
+	u64	insert_ptr1: 16;
+	u64	insert_ptr2: 16;
+	u64	mre_ptr: 15;
+	u64	prt_bmap_8: 1;
+	u64	prt_bmap_72_9;
+	u64	prt_bmap_136_73;
+};
+
+struct itt_entry_s {
+	u32 rsvd0 : 30;
+	u32 pkt_dir : 1;
+	u32 is_admin_vlan_enabled : 1;
+	u32 reserved0 : 6;
+	u32 default_evif : 8;
+	u32 admin_vlan : 12;
+	u32 Reserved1 : 6;
+	u32 Reserved2[6];
+};
+
+static inline u64 TNS_TDMA_SST_ACC_RDATX(unsigned long param1)
+{
+	return 0x00000480ull + (param1 & 7) * 0x10ull;
+}
+
+static inline u64 TNS_TDMA_SST_ACC_WDATX(unsigned long param1)
+{
+	return 0x00000280ull + (param1 & 7) * 0x10ull;
+}
+
+union tns_tdma_sst_acc_cmd {
+	u64 u;
+	struct  tns_tdma_sst_acc_cmd_s {
+		u64 reserved_0_1	: 2;
+		u64 addr		: 30;
+		u64 size		: 4;
+		u64 op			: 1;
+		u64 go			: 1;
+		u64 reserved_38_63	: 26;
+	} s;
+};
+
+#define TDMA_SST_ACC_CMD 0x00000270ull
+
+union tns_tdma_sst_acc_stat_t {
+	u64 u;
+	struct  tns_tdma_sst_acc_stat_s {
+		u64 cmd_done		: 1;
+		u64 error		: 1;
+		u64 reserved_2_63	: 62;
+	} s;
+};
+
+#define TDMA_SST_ACC_STAT 0x00000470ull
+#define TDMA_NB_INT_STAT 0x01000110ull
+
+union tns_acc_data {
+	u64 u;
+	struct tns_acc_data_s {
+		u64 lower32 : 32;
+		u64 upper32 : 32;
+	} s;
+};
+
+union tns_tdma_config {
+	u64 u;
+	struct  tns_tdma_config_s {
+		u64 clk_ena		: 1;
+		u64 clk_2x_ena		: 1;
+		u64 reserved_2_3	: 2;
+		u64 csr_access_ena	: 1;
+		u64 reserved_5_7	: 3;
+		u64 bypass0_ena		: 1;
+		u64 bypass1_ena		: 1;
+		u64 reserved_10_63	: 54;
+	} s;
+};
+
+#define TNS_TDMA_CONFIG_OFFSET  0x00000200ull
+
+union tns_tdma_cap {
+	u64 u;
+	struct tns_tdma_cap_s {
+		u64 switch_capable	: 1;
+		u64 reserved_1_63	: 63;
+	} s;
+};
+
+#define TNS_TDMA_CAP_OFFSET 0x00000400ull
+#define TNS_RDMA_CONFIG_OFFSET 0x00001200ull
+
+union tns_tdma_lmacx_config {
+	u64 u;
+	struct tns_tdma_lmacx_config_s {
+		u64 fifo_cdts		: 14;
+		u64 reserved_14_63	: 50;
+	} s;
+};
+
+union _tns_sst_config {
+	u64 data;
+	struct {
+#ifdef __BIG_ENDIAN
+		u64 powerof2stride	: 1;
+		u64 run			: 11;
+		u64 reserved		: 14;
+		u64 req_type		: 2;
+		u64 word_cnt		: 4;
+		u64 byte_addr		: 32;
+#else
+		u64 byte_addr		: 32;
+		u64 word_cnt		: 4;
+		u64 req_type		: 2;
+		u64 reserved		: 14;
+		u64 run			: 11;
+		u64 powerof2stride	: 1;
+#endif
+	} cmd;
+	struct {
+#ifdef __BIG_ENDIAN
+		u64 do_not_copy		: 26;
+		u64 do_copy		: 38;
+#else
+		u64 do_copy		: 38;
+		u64 do_not_copy		: 26;
+#endif
+	} copy;
+	struct {
+#ifdef __BIG_ENDIAN
+		u64 magic		: 48;
+		u64 major_version_BCD	: 8;
+		u64 minor_version_BCD	: 8;
+#else
+		u64 minor_version_BCD	: 8;
+		u64 major_version_BCD	: 8;
+		u64 magic		: 48;
+#endif
+	} header;
+};
+
+static inline u64 TNS_TDMA_LMACX_CONFIG_OFFSET(unsigned long param1)
+			 __attribute__ ((pure, always_inline));
+static inline u64 TNS_TDMA_LMACX_CONFIG_OFFSET(unsigned long param1)
+{
+	return 0x00000300ull + (param1 & 7) * 0x10ull;
+}
+
+#define TNS_TDMA_RESET_CTL_OFFSET 0x00000210ull
+
+int read_register_indirect(u64 address, u8 size, u8 *kern_buffer);
+int write_register_indirect(u64 address, u8 size, u8 *kern_buffer);
+int tns_write_register_indirect(int node, u64 address, u8 size,
+				u8 *kern_buffer);
+int tns_read_register_indirect(int node, u64 address, u8 size,
+			       u8 *kern_buffer);
+u64 tns_read_register(u64 start, u64 offset);
+void tns_write_register(u64 start, u64 offset, u64 data);
+int tbl_write(int node, int tbl_id, int tbl_index, void *key, void *mask,
+	      void *data);
+int tbl_read(int node, int tbl_id, int tbl_index, void *key, void *mask,
+	     void *data);
+int invalidate_table_entry(int node, int tbl_id, int tbl_idx);
+int alloc_table_index(int node, int table_id, int *index);
+void free_table_index(int node, int table_id, int index);
+
+struct pf_vf_data {
+	int pf_id;
+	int num_vfs;
+	int lmac;
+	int sys_lmac;
+	int bgx_idx;
+};
+
+struct pf_vf_map_s {
+	bool valid;
+	int lmac_cnt;
+	struct pf_vf_data pf_vf[TNS_MAX_LMAC];
+};
+
+extern struct pf_vf_map_s pf_vf_map_data[MAX_NUMNODES];
+int tns_enable_mcast_promis(int node, int vf);
+int filter_tbl_lookup(int node, int tblid, void *entry, int *idx);
+
+#define MCAST_PROMIS(a, b, c) ingressgrp_gblvif[(a)][(b)].mcast_promis_grp[(c)]
+#define VALID_MCAST_PROMIS(a, b) \
+	ingressgrp_gblvif[(a)][(b)].valid_mcast_promis_ports
+
+#endif /*__PF_LOCALS__*/
diff --git a/drivers/net/ethernet/cavium/thunder/pf_vf.c b/drivers/net/ethernet/cavium/thunder/pf_vf.c
new file mode 100644
index 0000000..bc4f923
--- /dev/null
+++ b/drivers/net/ethernet/cavium/thunder/pf_vf.c
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2015 Cavium, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ */
+
+#include "nic_reg.h"
+#include "nic.h"
+#include "pf_globals.h"
+#include "pf_locals.h"
+
+#define PFVF_DAT(gidx, lidx) \
+	pf_vf_map_data[gidx].pf_vf[lidx]
+
+struct pf_vf_map_s pf_vf_map_data[MAX_NUMNODES];
+
+void nic_init_pf_vf_mapping(void)
+{
+	int i;
+
+	for (i = 0 ; i < MAX_NUMNODES; i++) {
+		pf_vf_map_data[i].lmac_cnt = 0;
+		pf_vf_map_data[i].valid = false;
+	}
+}
+
+/* Based on available LMAC's we create physical group called ingress group
+ * Designate first VF as acted PF of this group, called PfVf interface.
+ */
+static inline void set_pf_vf_global_data(int node, int valid_vf_cnt)
+{
+	unsigned int bgx_map;
+	int bgx;
+	int lmac, lmac_cnt = 0;
+
+	if (pf_vf_map_data[node].valid)
+		return;
+
+	bgx_map = bgx_get_map(node);
+	for (bgx = 0; bgx < MAX_BGX_PER_CN88XX; bgx++)	{
+		if (!(bgx_map & (1 << bgx)))
+			continue;
+		pf_vf_map_data[node].valid = true;
+		lmac_cnt = bgx_get_lmac_count(node, bgx);
+
+		for (lmac = 0; lmac < lmac_cnt; lmac++)	{
+			int slc = lmac + pf_vf_map_data[node].lmac_cnt;
+
+			PFVF_DAT(node, slc).pf_id = (bgx * 64) + (lmac *
+								 valid_vf_cnt);
+			PFVF_DAT(node, slc).num_vfs = valid_vf_cnt;
+			PFVF_DAT(node, slc).lmac = lmac;
+			PFVF_DAT(node, slc).bgx_idx = bgx;
+			PFVF_DAT(node, slc).sys_lmac = bgx * MAX_LMAC_PER_BGX +
+						      lmac;
+		}
+		pf_vf_map_data[node].lmac_cnt += lmac_cnt;
+	}
+}
+
+/* We have 2 NIC pipes in each node.Each NIC pipe associated with BGX interface
+ * Each BGX contains atmost 4 LMACs (or PHY's) and supports 64 VF's
+ * Hardware doesn't have any physical PF, one of VF acts as PF.
+ */
+int nic_set_pf_vf_mapping(int node_id)
+{
+	unsigned int bgx_map;
+	int node = 0;
+	int bgx;
+	int lmac_cnt = 0, valid_vf_cnt = 64;
+
+	do {
+		bgx_map = bgx_get_map(node);
+		/* Calculate Maximum VF's in each physical port group */
+		for (bgx = 0; bgx < MAX_BGX_PER_CN88XX; bgx++) {
+			if (!(bgx_map & (1 << bgx)))
+				continue;
+			lmac_cnt = bgx_get_lmac_count(node, bgx);
+			//Maximum 64 VF's for each BGX
+			if (valid_vf_cnt > (64 / lmac_cnt))
+				valid_vf_cnt = (64 / lmac_cnt);
+		}
+	} while (++node < nr_node_ids);
+
+	nic_enable_valid_vf(valid_vf_cnt);
+	node = 0;
+	do {
+		set_pf_vf_global_data(node, valid_vf_cnt);
+	} while (++node < nr_node_ids);
+
+	return 0;
+}
+
+/* Find if VF is a acted PF */
+int is_pf(int node, int vf)
+{
+	int i;
+
+	/* Invalid Request, Init not done properly */
+	if (!pf_vf_map_data[node].valid)
+		return 0;
+
+	for (i = 0; i < pf_vf_map_data[node].lmac_cnt; i++)
+		if (vf == PFVF_DAT(node, i).pf_id)
+			return 1;
+
+	return 0;
+}
+
+/* Get the acted PF corresponding to this VF */
+int get_pf(int node, int vf)
+{
+	int i;
+
+	/* Invalid Request, Init not done properly */
+	if (!pf_vf_map_data[node].valid)
+		return 0;
+
+	for (i = 0; i < pf_vf_map_data[node].lmac_cnt; i++)
+		if ((vf >= PFVF_DAT(node, i).pf_id) &&
+		    (vf < (PFVF_DAT(node, i).pf_id +
+			   PFVF_DAT(node, i).num_vfs)))
+			return pf_vf_map_data[node].pf_vf[i].pf_id;
+
+	return -1;
+}
+
+/* Get the starting vf and ending vf number of the LMAC group */
+void get_vf_group(int node, int lmac, int *start_vf, int *end_vf)
+{
+	int i;
+
+	/* Invalid Request, Init not done properly */
+	if (!pf_vf_map_data[node].valid)
+		return;
+
+	for (i = 0; i < pf_vf_map_data[node].lmac_cnt; i++) {
+		if (lmac == (PFVF_DAT(node, i).sys_lmac)) {
+			*start_vf = PFVF_DAT(node, i).pf_id;
+			*end_vf = PFVF_DAT(node, i).pf_id +
+				  PFVF_DAT(node, i).num_vfs;
+			return;
+		}
+	}
+}
+
+/* Get the physical port # of the given vf */
+int vf_to_pport(int node, int vf)
+{
+	int i;
+
+	/* Invalid Request, Init not done properly */
+	if (!pf_vf_map_data[node].valid)
+		return 0;
+
+	for (i = 0; i < pf_vf_map_data[node].lmac_cnt; i++)
+		if ((vf >= PFVF_DAT(node, i).pf_id) &&
+		    (vf < (PFVF_DAT(node, i).pf_id +
+		     PFVF_DAT(node, i).num_vfs)))
+			return PFVF_DAT(node, i).sys_lmac;
+
+	return -1;
+}
+
+/* Get BGX # and LMAC # corresponding to VF */
+int get_bgx_id(int node, int vf, int *bgx_idx, int *lmac)
+{
+	int i;
+
+	/* Invalid Request, Init not done properly */
+	if (!pf_vf_map_data[node].valid)
+		return 1;
+
+	for (i = 0; i < pf_vf_map_data[node].lmac_cnt; i++) {
+		if ((vf >= PFVF_DAT(node, i).pf_id) &&
+		    (vf < (PFVF_DAT(node, i).pf_id +
+			   PFVF_DAT(node, i).num_vfs))) {
+			*bgx_idx = pf_vf_map_data[node].pf_vf[i].bgx_idx;
+			*lmac = pf_vf_map_data[node].pf_vf[i].lmac;
+			return 0;
+		}
+	}
+
+	return 1;
+}
+
+/* Get BGX # and LMAC # corresponding to physical port */
+int phy_port_to_bgx_lmac(int node, int port, int *bgx, int *lmac)
+{
+	int i;
+
+	/* Invalid Request, Init not done properly */
+	if (!pf_vf_map_data[node].valid)
+		return 1;
+
+	for (i = 0; i < pf_vf_map_data[node].lmac_cnt; i++) {
+		if (port == (PFVF_DAT(node, i).sys_lmac)) {
+			*bgx = pf_vf_map_data[node].pf_vf[i].bgx_idx;
+			*lmac = pf_vf_map_data[node].pf_vf[i].lmac;
+			return 0;
+		}
+	}
+
+	return 1;
+}
-- 
1.8.3.1

^ permalink raw reply related

* [RFC PATCH 4/7] HW Filter Initialization code and register access APIs
From: Satha Koteswara Rao @ 2016-12-21  8:46 UTC (permalink / raw)
  To: linux-kernel
  Cc: rvatsavayi, rric, david.daney, netdev, satha.rao, derek.chickles,
	sgoutham, davem, linux-arm-kernel, philip.romanov
In-Reply-To: <1482310011-1862-1-git-send-email-satha.rao@caviumnetworks.com>

---
 drivers/net/ethernet/cavium/thunder/pf_reg.c | 660 +++++++++++++++++++++++++++
 1 file changed, 660 insertions(+)
 create mode 100644 drivers/net/ethernet/cavium/thunder/pf_reg.c

diff --git a/drivers/net/ethernet/cavium/thunder/pf_reg.c b/drivers/net/ethernet/cavium/thunder/pf_reg.c
new file mode 100644
index 0000000..1f95c7f
--- /dev/null
+++ b/drivers/net/ethernet/cavium/thunder/pf_reg.c
@@ -0,0 +1,660 @@
+/*
+ * Copyright (C) 2015 Cavium, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/version.h>
+#include <linux/proc_fs.h>
+#include <linux/device.h>
+#include <linux/mman.h>
+#include <linux/uaccess.h>
+#include <linux/delay.h>
+#include <linux/cdev.h>
+#include <linux/err.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/firmware.h>
+#include "pf_globals.h"
+#include "pf_locals.h"
+#include "tbl_access.h"
+#include "linux/lz4.h"
+
+struct tns_table_s tbl_info[TNS_MAX_TABLE];
+
+#define TNS_TDMA_SST_ACC_CMD_ADDR	0x0000842000000270ull
+
+#define BAR0_START 0x842000000000
+#define BAR0_END   0x84200000FFFF
+#define BAR0_SIZE  (64 * 1024)
+#define BAR2_START 0x842040000000
+#define BAR2_END   0x84207FFFFFFF
+#define BAR2_SIZE  (1024 * 1024 * 1024)
+
+#define NODE1_BAR0_START 0x942000000000
+#define NODE1_BAR0_END   0x94200000FFFF
+#define NODE1_BAR0_SIZE  (64 * 1024)
+#define NODE1_BAR2_START 0x942040000000
+#define NODE1_BAR2_END   0x94207FFFFFFF
+#define NODE1_BAR2_SIZE  (1024 * 1024 * 1024)
+/* Allow a max of 4 chunks for the Indirect Read/Write */
+#define MAX_SIZE (64 * 4)
+#define CHUNK_SIZE (64)
+/* To protect register access */
+spinlock_t pf_reg_lock;
+
+u64 iomem0;
+u64 iomem2;
+u8 tns_enabled;
+u64 node1_iomem0;
+u64 node1_iomem2;
+u8 node1_tns;
+int n1_tns;
+
+int tns_write_register_indirect(int node_id, u64 address, u8 size,
+				u8 *kern_buffer)
+{
+	union tns_tdma_sst_acc_cmd acccmd;
+	union tns_tdma_sst_acc_stat_t accstat;
+	union tns_acc_data data;
+	int i, j, w = 0;
+	int cnt = 0;
+	u32 *dataw = NULL;
+	int temp = 0;
+	int k = 0;
+	int chunks = 0;
+	u64 acccmd_address;
+	u64 lmem2 = 0, lmem0 = 0;
+
+	if (size == 0 || !kern_buffer) {
+		filter_dbg(FERR, "%s data size cannot be zero\n", __func__);
+		return TNS_ERROR_INVALID_ARG;
+	}
+	if (size > MAX_SIZE) {
+		filter_dbg(FERR, "%s Max allowed size exceeded\n", __func__);
+		return TNS_ERROR_DATA_TOO_LARGE;
+	}
+	if (node_id) {
+		lmem0 = node1_iomem0;
+		lmem2 = node1_iomem2;
+	} else {
+		lmem0 = iomem0;
+		lmem2 = iomem2;
+	}
+
+	chunks = ((size + (CHUNK_SIZE - 1)) / CHUNK_SIZE);
+	acccmd_address = (address & 0x00000000ffffffff);
+	spin_lock_bh(&pf_reg_lock);
+
+	for (k = 0; k < chunks; k++) {
+		/* Should never happen */
+		if (size < 0) {
+			filter_dbg(FERR, "%s size mismatch [CHUNK %d]\n",
+				   __func__, k);
+			break;
+		}
+		temp = (size > CHUNK_SIZE) ? CHUNK_SIZE : size;
+		dataw = (u32 *)(kern_buffer + (k * CHUNK_SIZE));
+		cnt = ((temp + 3) / 4);
+		data.u = 0ULL;
+		for (j = 0, i = 0; i < cnt; i++) {
+			/* Odd words go in the upper 32 bits of the data
+			 * register
+			 */
+			if (i & 1) {
+				data.s.upper32 = dataw[i];
+				writeq_relaxed(data.u, (void *)(lmem0 +
+					       TNS_TDMA_SST_ACC_WDATX(j)));
+				data.u = 0ULL;
+				j++; /* Advance to the next data word */
+				w = 0;
+			} else {
+				/* Lower 32 bits contain words 0, 2, 4, etc. */
+				data.s.lower32 = dataw[i];
+				w = 1;
+			}
+		}
+
+		/* If the last word was a partial (< 64 bits) then
+		 * see if we need to write it.
+		 */
+		if (w)
+			writeq_relaxed(data.u, (void *)(lmem0 +
+				       TNS_TDMA_SST_ACC_WDATX(j)));
+
+		acccmd.u = 0ULL;
+		acccmd.s.go = 1; /* Cleared once the request is serviced */
+		acccmd.s.size = cnt;
+		acccmd.s.addr = (acccmd_address >> 2);
+		writeq_relaxed(acccmd.u, (void *)(lmem0 +
+			       TDMA_SST_ACC_CMD));
+		accstat.u = 0ULL;
+
+		while (!accstat.s.cmd_done && !accstat.s.error)
+			accstat.u = readq_relaxed((void *)(lmem0 +
+					  TDMA_SST_ACC_STAT));
+
+		if (accstat.s.error) {
+			data.u = readq_relaxed((void *)(lmem2 +
+					       TDMA_NB_INT_STAT));
+			filter_dbg(FERR, "%s Reading data from ", __func__);
+			filter_dbg(FERR, "0x%0lx chunk %d failed 0x%0lx",
+				   (unsigned long)address, k,
+				   (unsigned long)data.u);
+			spin_unlock_bh(&pf_reg_lock);
+			kfree(kern_buffer);
+			return TNS_ERROR_INDIRECT_WRITE;
+		}
+		/* Calculate the next offset to write */
+		acccmd_address = acccmd_address + CHUNK_SIZE;
+		size -= CHUNK_SIZE;
+	}
+	spin_unlock_bh(&pf_reg_lock);
+
+	return 0;
+}
+
+int tns_read_register_indirect(int node_id, u64 address, u8 size,
+			       u8 *kern_buffer)
+{
+	union tns_tdma_sst_acc_cmd acccmd;
+	union tns_tdma_sst_acc_stat_t accstat;
+	union tns_acc_data data;
+	int i, j, dcnt;
+	int cnt = 0;
+	u32 *dataw = NULL;
+	int temp = 0;
+	int k = 0;
+	int chunks = 0;
+	u64 acccmd_address;
+	u64 lmem2 = 0, lmem0 = 0;
+
+	if (size == 0 || !kern_buffer) {
+		filter_dbg(FERR, "%s data size cannot be zero\n", __func__);
+		return TNS_ERROR_INVALID_ARG;
+	}
+	if (size > MAX_SIZE) {
+		filter_dbg(FERR, "%s Max allowed size exceeded\n", __func__);
+		return TNS_ERROR_DATA_TOO_LARGE;
+	}
+	if (node_id) {
+		lmem0 = node1_iomem0;
+		lmem2 = node1_iomem2;
+	} else {
+		lmem0 = iomem0;
+		lmem2 = iomem2;
+	}
+
+	chunks = ((size + (CHUNK_SIZE - 1)) / CHUNK_SIZE);
+	acccmd_address = (address & 0x00000000ffffffff);
+	spin_lock_bh(&pf_reg_lock);
+	for (k = 0; k < chunks; k++) {
+		/* This should never happen */
+		if (size < 0) {
+			filter_dbg(FERR, "%s size mismatch [CHUNK:%d]\n",
+				   __func__, k);
+			break;
+		}
+		temp = (size > CHUNK_SIZE) ? CHUNK_SIZE : size;
+		dataw = (u32 *)(kern_buffer + (k * CHUNK_SIZE));
+		cnt = ((temp + 3) / 4);
+		acccmd.u = 0ULL;
+		acccmd.s.op = 1; /* Read operation */
+		acccmd.s.size = cnt;
+		acccmd.s.addr = (acccmd_address >> 2);
+		acccmd.s.go = 1; /* Execute */
+		writeq_relaxed(acccmd.u, (void *)(lmem0 +
+			       TDMA_SST_ACC_CMD));
+		accstat.u = 0ULL;
+
+		while (!accstat.s.cmd_done && !accstat.s.error)
+			accstat.u = readq_relaxed((void *)(lmem0 +
+						  TDMA_SST_ACC_STAT));
+
+		if (accstat.s.error) {
+			data.u = readq_relaxed((void *)(lmem2 +
+					       TDMA_NB_INT_STAT));
+			filter_dbg(FERR, "%s Reading data from", __func__);
+			filter_dbg(FERR, "0x%0lx chunk %d failed 0x%0lx",
+				   (unsigned long)address, k,
+				   (unsigned long)data.u);
+			spin_unlock_bh(&pf_reg_lock);
+			kfree(kern_buffer);
+			return TNS_ERROR_INDIRECT_READ;
+		}
+
+		dcnt = cnt / 2;
+		if (cnt & 1)
+			dcnt++;
+		for (i = 0, j = 0; (j < dcnt) && (i < cnt); j++) {
+			data.u = readq_relaxed((void *)(lmem0 +
+					       TNS_TDMA_SST_ACC_RDATX(j)));
+			dataw[i++] = data.s.lower32;
+			if (i < cnt)
+				dataw[i++] = data.s.upper32;
+		}
+		/* Calculate the next offset to read */
+		acccmd_address = acccmd_address + CHUNK_SIZE;
+		size -= CHUNK_SIZE;
+	}
+	spin_unlock_bh(&pf_reg_lock);
+	return 0;
+}
+
+u64 tns_read_register(u64 start, u64 offset)
+{
+	return readq_relaxed((void *)(start + offset));
+}
+
+void tns_write_register(u64 start, u64 offset, u64 data)
+{
+	writeq_relaxed(data, (void *)(start + offset));
+}
+
+/* Check if TNS is available. If yes return 0 else 1 */
+int is_tns_available(void)
+{
+	union tns_tdma_cap tdma_cap;
+
+	tdma_cap.u = tns_read_register(iomem0, TNS_TDMA_CAP_OFFSET);
+	tns_enabled = tdma_cap.s.switch_capable;
+	/* In multi-node systems, make sure TNS should be there in both nodes */
+	if (nr_node_ids > 1) {
+		tdma_cap.u = tns_read_register(node1_iomem0,
+					       TNS_TDMA_CAP_OFFSET);
+		if (tdma_cap.s.switch_capable)
+			n1_tns = 1;
+	}
+	tns_enabled &= tdma_cap.s.switch_capable;
+	return (!tns_enabled);
+}
+
+int bist_error_check(void)
+{
+	int fail = 0, i;
+	u64 bist_stat = 0;
+
+	for (i = 0; i < 12; i++) {
+		bist_stat = tns_read_register(iomem0, (i * 16));
+		if (bist_stat) {
+			filter_dbg(FERR, "TNS BIST%d fail 0x%llx\n",
+				   i, bist_stat);
+			fail = 1;
+		}
+		if (!n1_tns)
+			continue;
+		bist_stat = tns_read_register(node1_iomem0, (i * 16));
+		if (bist_stat) {
+			filter_dbg(FERR, "TNS(N1) BIST%d fail 0x%llx\n",
+				   i, bist_stat);
+			fail = 1;
+		}
+	}
+
+	return fail;
+}
+
+int replay_indirect_trace(int node, u64 *buf_ptr, int idx)
+{
+	union _tns_sst_config cmd = (union _tns_sst_config)(buf_ptr[idx]);
+	int remaining = cmd.cmd.run;
+	u64 io_addr;
+	int word_cnt = cmd.cmd.word_cnt;
+	int size = (word_cnt + 1) / 2;
+	u64 stride = word_cnt;
+	u64 acc_cmd = cmd.copy.do_copy;
+	u64 lmem2 = 0, lmem0 = 0;
+	union tns_tdma_sst_acc_stat_t accstat;
+	union tns_acc_data data;
+
+	if (node) {
+		lmem0 = node1_iomem0;
+		lmem2 = node1_iomem2;
+	} else {
+		lmem0 = iomem0;
+		lmem2 = iomem2;
+	}
+
+	if (word_cnt == 0) {
+		word_cnt = 16;
+		stride = 16;
+		size = 8;
+	} else {
+		// make stride next power of 2
+		if (cmd.cmd.powerof2stride)
+			while ((stride & (stride - 1)) != 0)
+				stride++;
+	}
+	stride *= 4; //convert stride from 32-bit words to bytes
+
+	do {
+		int addr_p = 1;
+		/* extract (big endian) data from the config
+		 * into the data array
+		 */
+		while (size > 0) {
+			io_addr = lmem0 + TDMA_SST_ACC_CMD + addr_p * 16;
+			tns_write_register(io_addr, 0, buf_ptr[idx + size]);
+			addr_p += 1;
+			size--;
+		}
+		tns_write_register((lmem0 + TDMA_SST_ACC_CMD), 0, acc_cmd);
+		/* TNS Block access registers indirectly, ran memory barrier
+		 * between two writes
+		 */
+		wmb();
+		/* Check for completion */
+		accstat.u = 0ULL;
+		while (!accstat.s.cmd_done && !accstat.s.error)
+			accstat.u = readq_relaxed((void *)(lmem0 +
+							   TDMA_SST_ACC_STAT));
+
+		/* Check for error, and report it */
+		if (accstat.s.error) {
+			filter_dbg(FERR, "%s data from 0x%0llx failed 0x%llx\n",
+				   __func__, acc_cmd, accstat.u);
+			data.u = readq_relaxed((void *)(lmem2 +
+							TDMA_NB_INT_STAT));
+			filter_dbg(FERR, "Status 0x%llx\n", data.u);
+		}
+		/* update the address */
+		acc_cmd += stride;
+		size = (word_cnt + 1) / 2;
+		usleep_range(20, 30);
+	} while (remaining-- > 0);
+
+	return size;
+}
+
+void replay_tns_node(int node, u64 *buf_ptr, int reg_cnt)
+{
+	int counter = 0;
+	u64 offset = 0;
+	u64 io_address;
+	int datapathmode = 1;
+	u64 lmem2 = 0, lmem0 = 0;
+
+	if (node) {
+		lmem0 = node1_iomem0;
+		lmem2 = node1_iomem2;
+	} else {
+		lmem0 = iomem0;
+		lmem2 = iomem2;
+	}
+	for (counter = 0; counter < reg_cnt; counter++) {
+		if (buf_ptr[counter] == 0xDADADADADADADADAull) {
+			datapathmode = 1;
+			continue;
+		} else if (buf_ptr[counter] == 0xDEDEDEDEDEDEDEDEull) {
+			datapathmode = 0;
+			continue;
+		}
+		if (datapathmode == 1) {
+			if (buf_ptr[counter] >= BAR0_START &&
+			    buf_ptr[counter] <= BAR0_END) {
+				offset = buf_ptr[counter] - BAR0_START;
+				io_address = lmem0 + offset;
+			} else if (buf_ptr[counter] >= BAR2_START &&
+				   buf_ptr[counter] <= BAR2_END) {
+				offset = buf_ptr[counter] - BAR2_START;
+				io_address = lmem2 + offset;
+			} else {
+				filter_dbg(FERR, "%s Address 0x%llx invalid\n",
+					   __func__, buf_ptr[counter]);
+				return;
+			}
+
+			tns_write_register(io_address, 0, buf_ptr[counter + 1]);
+			/* TNS Block access registers indirectly, ran memory
+			 * barrier between two writes
+			 */
+			wmb();
+			counter += 1;
+			usleep_range(20, 30);
+		} else if (datapathmode == 0) {
+			int sz = replay_indirect_trace(node, buf_ptr, counter);
+
+			counter += sz;
+		}
+	}
+}
+
+int alloc_table_info(int i, struct table_static_s tbl_sdata[])
+{
+	tbl_info[i].ddata[0].bitmap = kcalloc(BITS_TO_LONGS(tbl_sdata[i].depth),
+					      sizeof(uintptr_t), GFP_KERNEL);
+	if (!tbl_info[i].ddata[0].bitmap)
+		return 1;
+
+	if (!n1_tns)
+		return 0;
+
+	tbl_info[i].ddata[1].bitmap = kcalloc(BITS_TO_LONGS(tbl_sdata[i].depth),
+					      sizeof(uintptr_t), GFP_KERNEL);
+	if (!tbl_info[i].ddata[1].bitmap) {
+		kfree(tbl_info[i].ddata[0].bitmap);
+		return 1;
+	}
+
+	return 0;
+}
+
+void tns_replay_register_trace(const struct firmware *fw, struct device *dev)
+{
+	int i;
+	int node = 0;
+	u8 *buffer = NULL;
+	u64 *buf_ptr = NULL;
+	struct tns_global_st *fw_header = NULL;
+	struct table_static_s tbl_sdata[TNS_MAX_TABLE];
+	size_t src_len;
+	size_t dest_len = TNS_FW_MAX_SIZE;
+	int rc;
+	u8 *fw2_buf = NULL;
+	unsigned char *decomp_dest = NULL;
+
+	fw2_buf = (u8 *)fw->data;
+	src_len = fw->size - 8;
+
+	decomp_dest = kcalloc((dest_len * 2), sizeof(char), GFP_KERNEL);
+	if (!decomp_dest)
+		return;
+
+	memset(decomp_dest, 0, (dest_len * 2));
+	rc = lz4_decompress_unknownoutputsize(&fw2_buf[8], src_len, decomp_dest,
+					      &dest_len);
+	if (rc) {
+		filter_dbg(FERR, "Decompress Error %d\n", rc);
+		pr_info("Uncompressed destination length %ld\n", dest_len);
+		kfree(decomp_dest);
+		return;
+	}
+	fw_header = (struct tns_global_st *)decomp_dest;
+	buffer = (u8 *)decomp_dest;
+
+	filter_dbg(FINFO, "TNS Firmware version: %s Loading...\n",
+		   fw_header->version);
+
+	memset(tbl_info, 0x0, sizeof(tbl_info));
+	buf_ptr = (u64 *)(buffer + sizeof(struct tns_global_st));
+	memcpy(tbl_sdata, fw_header->tbl_info, sizeof(fw_header->tbl_info));
+
+	for (i = 0; i < TNS_MAX_TABLE; i++) {
+		if (!tbl_sdata[i].valid)
+			continue;
+		memcpy(&tbl_info[i].sdata, &tbl_sdata[i],
+		       sizeof(struct table_static_s));
+		if (alloc_table_info(i, tbl_sdata)) {
+			kfree(decomp_dest);
+			return;
+		}
+	}
+
+	for (node = 0; node < nr_node_ids; node++)
+		replay_tns_node(node, buf_ptr, fw_header->reg_cnt);
+
+	kfree(decomp_dest);
+	release_firmware(fw);
+}
+
+int tns_init(const struct firmware *fw, struct device *dev)
+{
+	int result = 0;
+	int i = 0;
+	int temp;
+	union tns_tdma_config tdma_config;
+	union tns_tdma_lmacx_config tdma_lmac_cfg;
+	u64 reg_init_val;
+
+	spin_lock_init(&pf_reg_lock);
+
+	/* use two regions insted of a single big mapping to save
+	 * the kernel virtual space
+	 */
+	iomem0 = (u64)ioremap(BAR0_START, BAR0_SIZE);
+	if (iomem0 == 0ULL) {
+		filter_dbg(FERR, "Node0 ioremap failed for BAR0\n");
+		result = -EAGAIN;
+		goto error;
+	} else {
+		filter_dbg(FINFO, "ioremap success for BAR0\n");
+	}
+
+	if (nr_node_ids > 1) {
+		node1_iomem0 = (u64)ioremap(NODE1_BAR0_START, NODE1_BAR0_SIZE);
+		if (node1_iomem0 == 0ULL) {
+			filter_dbg(FERR, "Node1 ioremap failed for BAR0\n");
+			result = -EAGAIN;
+			goto error;
+		} else {
+			filter_dbg(FINFO, "ioremap success for BAR0\n");
+		}
+	}
+
+	if (is_tns_available()) {
+		filter_dbg(FERR, "TNS NOT AVAILABLE\n");
+		goto error;
+	}
+
+	if (bist_error_check()) {
+		filter_dbg(FERR, "BIST ERROR CHECK FAILED");
+		goto error;
+	}
+
+	/* NIC0-BGX0 is TNS, NIC1-BGX1 is TNS, DISABLE BACKPRESSURE */
+	reg_init_val = 0ULL;
+	pr_info("NIC Block configured in TNS/TNS mode");
+	tns_write_register(iomem0, TNS_RDMA_CONFIG_OFFSET, reg_init_val);
+	usleep_range(10, 20);
+	if (n1_tns) {
+		tns_write_register(node1_iomem0, TNS_RDMA_CONFIG_OFFSET,
+				   reg_init_val);
+		usleep_range(10, 20);
+	}
+
+	// Configure each LMAC with 512 credits in BYPASS mode
+	for (i = TNS_MIN_LMAC; i < (TNS_MIN_LMAC + TNS_MAX_LMAC); i++) {
+		tdma_lmac_cfg.u = 0ULL;
+		tdma_lmac_cfg.s.fifo_cdts = 0x200;
+		tns_write_register(iomem0, TNS_TDMA_LMACX_CONFIG_OFFSET(i),
+				   tdma_lmac_cfg.u);
+		usleep_range(10, 20);
+		if (n1_tns) {
+			tns_write_register(node1_iomem0,
+					   TNS_TDMA_LMACX_CONFIG_OFFSET(i),
+					   tdma_lmac_cfg.u);
+			usleep_range(10, 20);
+		}
+	}
+
+	//ENABLE TNS CLOCK AND CSR READS
+	temp = tns_read_register(iomem0, TNS_TDMA_CONFIG_OFFSET);
+	tdma_config.u = temp;
+	tdma_config.s.clk_2x_ena = 1;
+	tdma_config.s.clk_ena = 1;
+	tns_write_register(iomem0, TNS_TDMA_CONFIG_OFFSET, tdma_config.u);
+	if (n1_tns)
+		tns_write_register(node1_iomem0, TNS_TDMA_CONFIG_OFFSET,
+				   tdma_config.u);
+
+	temp = tns_read_register(iomem0, TNS_TDMA_CONFIG_OFFSET);
+	tdma_config.u = temp;
+	tdma_config.s.csr_access_ena = 1;
+	tns_write_register(iomem0, TNS_TDMA_CONFIG_OFFSET, tdma_config.u);
+	if (n1_tns)
+		tns_write_register(node1_iomem0, TNS_TDMA_CONFIG_OFFSET,
+				   tdma_config.u);
+
+	reg_init_val = 0ULL;
+	tns_write_register(iomem0, TNS_TDMA_RESET_CTL_OFFSET, reg_init_val);
+	if (n1_tns)
+		tns_write_register(node1_iomem0, TNS_TDMA_RESET_CTL_OFFSET,
+				   reg_init_val);
+
+	iomem2 = (u64)ioremap(BAR2_START, BAR2_SIZE);
+	if (iomem2 == 0ULL) {
+		filter_dbg(FERR, "ioremap failed for BAR2\n");
+		result = -EAGAIN;
+		goto error;
+	} else {
+		filter_dbg(FINFO, "ioremap success for BAR2\n");
+	}
+
+	if (n1_tns) {
+		node1_iomem2 = (u64)ioremap(NODE1_BAR2_START, NODE1_BAR2_SIZE);
+		if (node1_iomem2 == 0ULL) {
+			filter_dbg(FERR, "Node1 ioremap failed for BAR2\n");
+			result = -EAGAIN;
+			goto error;
+		} else {
+			filter_dbg(FINFO, "Node1 ioremap success for BAR2\n");
+		}
+	}
+	msleep(1000);
+	//We will replay register trace to initialize TNS block
+	tns_replay_register_trace(fw, dev);
+
+	return 0;
+error:
+	if (iomem0 != 0)
+		iounmap((void *)iomem0);
+	if (iomem2 != 0)
+		iounmap((void *)iomem2);
+
+	if (node1_iomem0 != 0)
+		iounmap((void *)node1_iomem0);
+	if (node1_iomem2 != 0)
+		iounmap((void *)node1_iomem2);
+
+	return result;
+}
+
+void tns_exit(void)
+{
+	int i;
+
+	if (iomem0 != 0)
+		iounmap((void *)iomem0);
+	if (iomem2 != 0)
+		iounmap((void *)iomem2);
+
+	if (node1_iomem0 != 0)
+		iounmap((void *)node1_iomem0);
+	if (node1_iomem2 != 0)
+		iounmap((void *)node1_iomem2);
+
+	for (i = 0; i < TNS_MAX_TABLE; i++) {
+		if (!tbl_info[i].sdata.valid)
+			continue;
+		kfree(tbl_info[i].ddata[0].bitmap);
+		kfree(tbl_info[i].ddata[n1_tns].bitmap);
+	}
+}
-- 
1.8.3.1

^ permalink raw reply related

* [RFC PATCH 3/7] Enable pause frame support
From: Satha Koteswara Rao @ 2016-12-21  8:46 UTC (permalink / raw)
  To: linux-kernel
  Cc: rvatsavayi, rric, david.daney, netdev, satha.rao, derek.chickles,
	sgoutham, davem, linux-arm-kernel, philip.romanov
In-Reply-To: <1482310011-1862-1-git-send-email-satha.rao@caviumnetworks.com>

---
 drivers/net/ethernet/cavium/thunder/thunder_bgx.c | 25 +++++++++++++++++++++++
 drivers/net/ethernet/cavium/thunder/thunder_bgx.h |  7 +++++++
 2 files changed, 32 insertions(+)

diff --git a/drivers/net/ethernet/cavium/thunder/thunder_bgx.c b/drivers/net/ethernet/cavium/thunder/thunder_bgx.c
index 050e21f..92d7e04 100644
--- a/drivers/net/ethernet/cavium/thunder/thunder_bgx.c
+++ b/drivers/net/ethernet/cavium/thunder/thunder_bgx.c
@@ -121,6 +121,31 @@ static int bgx_poll_reg(struct bgx *bgx, u8 lmac, u64 reg, u64 mask, bool zero)
 	return 1;
 }
 
+void enable_pause_frames(int node, int bgx_idx, int lmac)
+{
+	u64 reg_value = 0;
+	struct bgx *bgx = bgx_vnic[(node * MAX_BGX_PER_NODE) + bgx_idx];
+
+	reg_value =  bgx_reg_read(bgx, lmac, BGX_SMUX_TX_CTL);
+	/* Enable BGX()_SMU()_TX_CTL */
+	if (!(reg_value & L2P_BP_CONV))
+		bgx_reg_write(bgx, lmac, BGX_SMUX_TX_CTL,
+			      (reg_value | (L2P_BP_CONV)));
+
+	reg_value =  bgx_reg_read(bgx, lmac, BGX_SMUX_HG2_CTL);
+	/* Clear if BGX()_SMU()_HG2_CONTROL[HG2TX_EN] is set */
+	if (reg_value & SMUX_HG2_CTL_HG2TX_EN)
+		bgx_reg_write(bgx, lmac, BGX_SMUX_HG2_CTL,
+			      (reg_value & (~SMUX_HG2_CTL_HG2TX_EN)));
+
+	reg_value =  bgx_reg_read(bgx, lmac, BGX_SMUX_CBFC_CTL);
+	/* Clear if BGX()_SMU()_CBFC_CTL[TX_EN] is set */
+	if (reg_value & CBFC_CTL_TX_EN)
+		bgx_reg_write(bgx, lmac, BGX_SMUX_CBFC_CTL,
+			      (reg_value & (~CBFC_CTL_TX_EN)));
+}
+EXPORT_SYMBOL(enable_pause_frames);
+
 /* Return number of BGX present in HW */
 unsigned bgx_get_map(int node)
 {
diff --git a/drivers/net/ethernet/cavium/thunder/thunder_bgx.h b/drivers/net/ethernet/cavium/thunder/thunder_bgx.h
index 01cc7c8..5b57bd1 100644
--- a/drivers/net/ethernet/cavium/thunder/thunder_bgx.h
+++ b/drivers/net/ethernet/cavium/thunder/thunder_bgx.h
@@ -131,6 +131,11 @@
 #define BGX_SMUX_TX_CTL			0x20178
 #define  SMU_TX_CTL_DIC_EN			BIT_ULL(0)
 #define  SMU_TX_CTL_UNI_EN			BIT_ULL(1)
+#define  L2P_BP_CONV				BIT_ULL(7)
+#define  BGX_SMUX_CBFC_CTL		0x20218
+#define  CBFC_CTL_TX_EN				BIT_ULL(1)
+#define  BGX_SMUX_HG2_CTL		0x20210
+#define SMUX_HG2_CTL_HG2TX_EN			BIT_ULL(18)
 #define  SMU_TX_CTL_LNK_STATUS			(3ull << 4)
 #define BGX_SMUX_TX_THRESH		0x20180
 #define BGX_SMUX_CTL			0x20200
@@ -212,6 +217,8 @@ void bgx_lmac_internal_loopback(int node, int bgx_idx,
 
 u64 bgx_get_rx_stats(int node, int bgx_idx, int lmac, int idx);
 u64 bgx_get_tx_stats(int node, int bgx_idx, int lmac, int idx);
+void enable_pause_frames(int node, int bgx_idx, int lmac);
+
 #define BGX_RX_STATS_COUNT 11
 #define BGX_TX_STATS_COUNT 18
 
-- 
1.8.3.1

^ 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