diff mbox series

[v3,09/11] net/igc: implement feature of VLAN

Message ID 20200413063037.13728-10-alvinx.zhang@intel.com (mailing list archive)
State Superseded, archived
Delegated to: Ferruh Yigit
Headers show
Series igc pmd | expand

Checks

Context Check Description
ci/Intel-compilation success Compilation OK
ci/checkpatch success coding style OK

Commit Message

Alvin Zhang April 13, 2020, 6:30 a.m. UTC
From: Alvin Zhang <alvinx.zhang@intel.com>

Below ops ware added:
vlan_filter_set
vlan_offload_set
vlan_tpid_set
vlan_strip_queue_set

Signed-off-by: Alvin Zhang <alvinx.zhang@intel.com>

v2: fix max packet length fault when extend vlan is enabled or disabled
v3: fix queue vlan offload fault, update docs.
---
 doc/guides/nics/features/igc.ini |   2 +
 doc/guides/nics/igc.rst          |  35 +++++++
 drivers/net/igc/igc_ethdev.c     | 207 +++++++++++++++++++++++++++++++++++++++
 drivers/net/igc/igc_ethdev.h     |  16 +++
 drivers/net/igc/igc_txrx.c       |  38 +++++++
 drivers/net/igc/igc_txrx.h       |   3 +-
 6 files changed, 300 insertions(+), 1 deletion(-)
diff mbox series

Patch

diff --git a/doc/guides/nics/features/igc.ini b/doc/guides/nics/features/igc.ini
index 23434af..5bc901f 100644
--- a/doc/guides/nics/features/igc.ini
+++ b/doc/guides/nics/features/igc.ini
@@ -30,6 +30,8 @@  Rx interrupt         = Y
 Flow control         = Y
 RSS key update       = Y
 RSS reta update      = Y
+VLAN filter          = Y
+VLAN offload         = Y
 Linux UIO            = Y
 Linux VFIO           = Y
 x86-64               = Y
diff --git a/doc/guides/nics/igc.rst b/doc/guides/nics/igc.rst
index 8dcbf2c..7ea6536 100644
--- a/doc/guides/nics/igc.rst
+++ b/doc/guides/nics/igc.rst
@@ -40,3 +40,38 @@  Foxville LM (I225 LM): Client 2.5G LAN vPro Corporate
 Foxville V (I225 V): Client 2.5G LAN Consumer
 Foxville I (I225 I): Client 2.5G Industrial Temp
 Foxville V (I225 K): Client 2.5G LAN Consumer
+
+
+Sample Application Notes
+------------------------
+
+Vlan filter
+~~~~~~~~~~~
+
+VLAN stripping off only works with inner vlan.
+Only the outer VLAN TPID can be set to a vlan other than 0x8100.
+
+If extend VLAN is enabled:
+
+- The VLAN header in a packet that carries a single VLAN header is treated as the external VLAN.
+
+- Foxville expects that any transmitted packet to have at least the external VLAN added by the
+  software. For those packets where an external VLAN is not present, any offload that relates to
+  inner fields to the EtherType might not be provided.
+
+- If VLAN TX-OFFLOAD is enabled and the packet does not contain an external VLAN, the packet is
+  dropped, and if configured, the queue from which the packet was sent is disabled.
+
+To start ``testpmd``, add vlan 10 to port, set vlan stripping off on, set extend on, set TPID of
+outer VLAN to 0x9100:
+
+.. code-block:: console
+
+   ./app/testpmd -l 4-8 -- -i
+   ...
+
+   testpmd> vlan set filter on 0
+   testpmd> rx_vlan add 10 0
+   testpmd> vlan set strip off 0
+   testpmd> vlan set extend on 0
+   testpmd> vlan set outer tpid 0x9100 0
diff --git a/drivers/net/igc/igc_ethdev.c b/drivers/net/igc/igc_ethdev.c
index 554c809..b176b6e 100644
--- a/drivers/net/igc/igc_ethdev.c
+++ b/drivers/net/igc/igc_ethdev.c
@@ -50,6 +50,10 @@ 
 /* External VLAN Enable bit mask */
 #define IGC_CTRL_EXT_EXT_VLAN		(1u << 26)
 
+/* External VLAN Ether Type bit mask and shift */
+#define IGC_VET_EXT			0xFFFF0000
+#define IGC_VET_EXT_SHIFT		16
+
 /* Per Queue Good Packets Received Count */
 #define IGC_PQGPRC(idx)		(0x10010 + 0x100 * (idx))
 /* Per Queue Good Octets Received Count */
@@ -227,6 +231,11 @@  static int eth_igc_rss_hash_update(struct rte_eth_dev *dev,
 			struct rte_eth_rss_conf *rss_conf);
 static int eth_igc_rss_hash_conf_get(struct rte_eth_dev *dev,
 			struct rte_eth_rss_conf *rss_conf);
+static int
+eth_igc_vlan_filter_set(struct rte_eth_dev *dev, uint16_t vlan_id, int on);
+static int eth_igc_vlan_offload_set(struct rte_eth_dev *dev, int mask);
+static int eth_igc_vlan_tpid_set(struct rte_eth_dev *dev,
+		      enum rte_vlan_type vlan_type, uint16_t tpid);
 
 static const struct eth_dev_ops eth_igc_ops = {
 	.dev_configure		= eth_igc_configure,
@@ -279,6 +288,10 @@  static int eth_igc_rss_hash_conf_get(struct rte_eth_dev *dev,
 	.reta_query		= eth_igc_rss_reta_query,
 	.rss_hash_update	= eth_igc_rss_hash_update,
 	.rss_hash_conf_get	= eth_igc_rss_hash_conf_get,
+	.vlan_filter_set	= eth_igc_vlan_filter_set,
+	.vlan_offload_set	= eth_igc_vlan_offload_set,
+	.vlan_tpid_set		= eth_igc_vlan_tpid_set,
+	.vlan_strip_queue_set	= eth_igc_vlan_strip_queue_set,
 };
 
 /*
@@ -949,6 +962,11 @@  static int eth_igc_rss_hash_conf_get(struct rte_eth_dev *dev,
 
 	igc_clear_hw_cntrs_base_generic(hw);
 
+	/* VLAN Offload Settings */
+	eth_igc_vlan_offload_set(dev,
+		ETH_VLAN_STRIP_MASK | ETH_VLAN_FILTER_MASK |
+		ETH_VLAN_EXTEND_MASK);
+
 	/* Setup link speed and duplex */
 	speeds = &dev->data->dev_conf.link_speeds;
 	if (*speeds == ETH_LINK_SPEED_AUTONEG) {
@@ -1443,6 +1461,7 @@  static int eth_igc_rss_hash_conf_get(struct rte_eth_dev *dev,
 	dev_info->max_mac_addrs = hw->mac.rar_entry_count;
 	dev_info->rx_offload_capa = IGC_RX_OFFLOAD_ALL;
 	dev_info->tx_offload_capa = IGC_TX_OFFLOAD_ALL;
+	dev_info->rx_queue_offload_capa = DEV_RX_OFFLOAD_VLAN_STRIP;
 
 	dev_info->max_rx_queues = IGC_QUEUE_PAIRS_NUM;
 	dev_info->max_tx_queues = IGC_QUEUE_PAIRS_NUM;
@@ -2359,6 +2378,194 @@  static int eth_igc_rss_hash_conf_get(struct rte_eth_dev *dev,
 }
 
 static int
+eth_igc_vlan_filter_set(struct rte_eth_dev *dev, uint16_t vlan_id, int on)
+{
+	struct igc_hw *hw = IGC_DEV_PRIVATE_HW(dev);
+	struct igc_vfta *shadow_vfta = IGC_DEV_PRIVATE_VFTA(dev);
+	uint32_t vfta;
+	uint32_t vid_idx;
+	uint32_t vid_bit;
+
+	vid_idx = (vlan_id >> IGC_VFTA_ENTRY_SHIFT) & IGC_VFTA_ENTRY_MASK;
+	vid_bit = 1u << (vlan_id & IGC_VFTA_ENTRY_BIT_SHIFT_MASK);
+	vfta = shadow_vfta->vfta[vid_idx];
+	if (on)
+		vfta |= vid_bit;
+	else
+		vfta &= ~vid_bit;
+	IGC_WRITE_REG_ARRAY(hw, IGC_VFTA, vid_idx, vfta);
+
+	/* update local VFTA copy */
+	shadow_vfta->vfta[vid_idx] = vfta;
+
+	return 0;
+}
+
+static void
+igc_vlan_hw_filter_disable(struct rte_eth_dev *dev)
+{
+	struct igc_hw *hw = IGC_DEV_PRIVATE_HW(dev);
+	igc_read_reg_check_clear_bits(hw, IGC_RCTL,
+			IGC_RCTL_CFIEN | IGC_RCTL_VFE);
+}
+
+static void
+igc_vlan_hw_filter_enable(struct rte_eth_dev *dev)
+{
+	struct igc_hw *hw = IGC_DEV_PRIVATE_HW(dev);
+	struct igc_vfta *shadow_vfta = IGC_DEV_PRIVATE_VFTA(dev);
+	uint32_t reg_val;
+	int i;
+
+	/* Filter Table Enable, CFI not used for packet acceptance */
+	reg_val = IGC_READ_REG(hw, IGC_RCTL);
+	reg_val &= ~IGC_RCTL_CFIEN;
+	reg_val |= IGC_RCTL_VFE;
+	IGC_WRITE_REG(hw, IGC_RCTL, reg_val);
+
+	/* restore VFTA table */
+	for (i = 0; i < IGC_VFTA_SIZE; i++)
+		IGC_WRITE_REG_ARRAY(hw, IGC_VFTA, i, shadow_vfta->vfta[i]);
+}
+
+static void
+igc_vlan_hw_strip_disable(struct rte_eth_dev *dev)
+{
+	struct igc_hw *hw = IGC_DEV_PRIVATE_HW(dev);
+
+	igc_read_reg_check_clear_bits(hw, IGC_CTRL, IGC_CTRL_VME);
+}
+
+static void
+igc_vlan_hw_strip_enable(struct rte_eth_dev *dev)
+{
+	struct igc_hw *hw = IGC_DEV_PRIVATE_HW(dev);
+
+	igc_read_reg_check_set_bits(hw, IGC_CTRL, IGC_CTRL_VME);
+}
+
+static int
+igc_vlan_hw_extend_disable(struct rte_eth_dev *dev)
+{
+	struct igc_hw *hw = IGC_DEV_PRIVATE_HW(dev);
+	uint32_t ctrl_ext;
+
+	ctrl_ext = IGC_READ_REG(hw, IGC_CTRL_EXT);
+
+	/* if extend vlan hasn't been enabled */
+	if ((ctrl_ext & IGC_CTRL_EXT_EXT_VLAN) == 0)
+		return 0;
+
+	if ((dev->data->dev_conf.rxmode.offloads &
+			DEV_RX_OFFLOAD_JUMBO_FRAME) == 0)
+		goto write_ext_vlan;
+
+	/* Update maximum packet length */
+	if (dev->data->dev_conf.rxmode.max_rx_pkt_len <
+		RTE_ETHER_MIN_MTU + VLAN_TAG_SIZE) {
+		PMD_DRV_LOG(ERR, "maximum packet length %u error, "
+				"here minimum value should be %u",
+				dev->data->dev_conf.rxmode.max_rx_pkt_len,
+				VLAN_TAG_SIZE + RTE_ETHER_MIN_MTU);
+		return -EINVAL;
+	}
+	dev->data->dev_conf.rxmode.max_rx_pkt_len -= VLAN_TAG_SIZE;
+	IGC_WRITE_REG(hw, IGC_RLPML,
+		dev->data->dev_conf.rxmode.max_rx_pkt_len);
+
+write_ext_vlan:
+	IGC_WRITE_REG(hw, IGC_CTRL_EXT, ctrl_ext & ~IGC_CTRL_EXT_EXT_VLAN);
+	return 0;
+}
+
+static int
+igc_vlan_hw_extend_enable(struct rte_eth_dev *dev)
+{
+	struct igc_hw *hw = IGC_DEV_PRIVATE_HW(dev);
+	uint32_t ctrl_ext;
+
+	ctrl_ext = IGC_READ_REG(hw, IGC_CTRL_EXT);
+
+	/* if extend vlan has been enabled */
+	if (ctrl_ext & IGC_CTRL_EXT_EXT_VLAN)
+		return 0;
+
+	if ((dev->data->dev_conf.rxmode.offloads &
+			DEV_RX_OFFLOAD_JUMBO_FRAME) == 0)
+		goto write_ext_vlan;
+
+	/* Update maximum packet length */
+	if (dev->data->dev_conf.rxmode.max_rx_pkt_len >
+		MAX_RX_JUMBO_FRAME_SIZE - VLAN_TAG_SIZE) {
+		PMD_DRV_LOG(ERR, "maximum packet length %u error, "
+				"maximum value is %u",
+				dev->data->dev_conf.rxmode.max_rx_pkt_len +
+				VLAN_TAG_SIZE, MAX_RX_JUMBO_FRAME_SIZE);
+		return -EINVAL;
+	}
+	dev->data->dev_conf.rxmode.max_rx_pkt_len += VLAN_TAG_SIZE;
+	IGC_WRITE_REG(hw, IGC_RLPML,
+		dev->data->dev_conf.rxmode.max_rx_pkt_len);
+
+write_ext_vlan:
+	IGC_WRITE_REG(hw, IGC_CTRL_EXT, ctrl_ext | IGC_CTRL_EXT_EXT_VLAN);
+	return 0;
+}
+
+static int
+eth_igc_vlan_offload_set(struct rte_eth_dev *dev, int mask)
+{
+	struct rte_eth_rxmode *rxmode;
+
+	rxmode = &dev->data->dev_conf.rxmode;
+	if (mask & ETH_VLAN_STRIP_MASK) {
+		if (rxmode->offloads & DEV_RX_OFFLOAD_VLAN_STRIP)
+			igc_vlan_hw_strip_enable(dev);
+		else
+			igc_vlan_hw_strip_disable(dev);
+	}
+
+	if (mask & ETH_VLAN_FILTER_MASK) {
+		if (rxmode->offloads & DEV_RX_OFFLOAD_VLAN_FILTER)
+			igc_vlan_hw_filter_enable(dev);
+		else
+			igc_vlan_hw_filter_disable(dev);
+	}
+
+	if (mask & ETH_VLAN_EXTEND_MASK) {
+		if (rxmode->offloads & DEV_RX_OFFLOAD_VLAN_EXTEND)
+			return igc_vlan_hw_extend_enable(dev);
+		else
+			return igc_vlan_hw_extend_disable(dev);
+	}
+
+	return 0;
+}
+
+static int
+eth_igc_vlan_tpid_set(struct rte_eth_dev *dev,
+		      enum rte_vlan_type vlan_type,
+		      uint16_t tpid)
+{
+	struct igc_hw *hw = IGC_DEV_PRIVATE_HW(dev);
+	uint32_t reg_val;
+
+	/* only outer TPID of double VLAN can be configured*/
+	if (vlan_type == ETH_VLAN_TYPE_OUTER) {
+		reg_val = IGC_READ_REG(hw, IGC_VET);
+		reg_val = (reg_val & (~IGC_VET_EXT)) |
+			((uint32_t)tpid << IGC_VET_EXT_SHIFT);
+		IGC_WRITE_REG(hw, IGC_VET, reg_val);
+
+		return 0;
+	}
+
+	/* all other TPID values are read-only*/
+	PMD_DRV_LOG(ERR, "Not supported");
+	return -ENOTSUP;
+}
+
+static int
 eth_igc_pci_probe(struct rte_pci_driver *pci_drv __rte_unused,
 	struct rte_pci_device *pci_dev)
 {
diff --git a/drivers/net/igc/igc_ethdev.h b/drivers/net/igc/igc_ethdev.h
index e8fd1b1..4b84127 100644
--- a/drivers/net/igc/igc_ethdev.h
+++ b/drivers/net/igc/igc_ethdev.h
@@ -17,6 +17,10 @@ 
 #endif
 
 #define IGC_RSS_RDT_SIZD		128
+
+/* VLAN filter table size */
+#define IGC_VFTA_SIZE			128
+
 #define IGC_QUEUE_PAIRS_NUM		4
 
 #define IGC_HKEY_MAX_INDEX		10
@@ -54,6 +58,9 @@ 
 #define IGC_TX_MAX_MTU_SEG	UINT8_MAX
 
 #define IGC_RX_OFFLOAD_ALL	(    \
+	DEV_RX_OFFLOAD_VLAN_STRIP  | \
+	DEV_RX_OFFLOAD_VLAN_FILTER | \
+	DEV_RX_OFFLOAD_VLAN_EXTEND | \
 	DEV_RX_OFFLOAD_IPV4_CKSUM  | \
 	DEV_RX_OFFLOAD_UDP_CKSUM   | \
 	DEV_RX_OFFLOAD_TCP_CKSUM   | \
@@ -113,6 +120,11 @@  struct igc_hw_queue_stats {
 	/* per transmit queue drop packet count */
 };
 
+/* local vfta copy */
+struct igc_vfta {
+	uint32_t vfta[IGC_VFTA_SIZE];
+};
+
 /*
  * Structure to store private data for each driver instance (for each port).
  */
@@ -124,6 +136,7 @@  struct igc_adapter {
 	int16_t rxq_stats_map[IGC_QUEUE_PAIRS_NUM];
 
 	struct igc_interrupt	intr;
+	struct igc_vfta	shadow_vfta;
 	bool		stopped;
 };
 
@@ -141,6 +154,9 @@  struct igc_adapter {
 #define IGC_DEV_PRIVATE_INTR(_dev) \
 	(&((struct igc_adapter *)(_dev)->data->dev_private)->intr)
 
+#define IGC_DEV_PRIVATE_VFTA(_dev) \
+	(&((struct igc_adapter *)(_dev)->data->dev_private)->shadow_vfta)
+
 static inline void
 igc_read_reg_check_set_bits(struct igc_hw *hw, uint32_t reg, uint32_t bits)
 {
diff --git a/drivers/net/igc/igc_txrx.c b/drivers/net/igc/igc_txrx.c
index 03998c0..177dd11 100644
--- a/drivers/net/igc/igc_txrx.c
+++ b/drivers/net/igc/igc_txrx.c
@@ -1160,6 +1160,16 @@  int eth_igc_rx_descriptor_status(void *rx_queue, uint16_t offset)
 		IGC_WRITE_REG(hw, IGC_RDH(rxq->reg_idx), 0);
 		IGC_WRITE_REG(hw, IGC_RDT(rxq->reg_idx),
 				rxq->nb_rx_desc - 1);
+
+		/* strip queue vlan offload */
+		if (rxq->offloads & DEV_RX_OFFLOAD_VLAN_STRIP) {
+			uint32_t dvmolr;
+			dvmolr = IGC_READ_REG(hw, IGC_DVMOLR(rxq->queue_id));
+
+			/* If vlan been stripped off, the CRC is meaningless. */
+			dvmolr |= IGC_DVMOLR_STRVLAN | IGC_DVMOLR_STRCRC;
+			IGC_WRITE_REG(hw, IGC_DVMOLR(rxq->reg_idx), dvmolr);
+		}
 	}
 
 	return 0;
@@ -2105,3 +2115,31 @@  int eth_igc_tx_queue_setup(struct rte_eth_dev *dev, uint16_t queue_idx,
 	qinfo->conf.tx_thresh.wthresh = txq->wthresh;
 	qinfo->conf.offloads = txq->offloads;
 }
+
+void
+eth_igc_vlan_strip_queue_set(struct rte_eth_dev *dev,
+			uint16_t rx_queue_id, int on)
+{
+	struct igc_hw *hw = IGC_DEV_PRIVATE_HW(dev);
+	struct igc_rx_queue *rxq = dev->data->rx_queues[rx_queue_id];
+	uint32_t reg_val;
+
+	if (rx_queue_id >= IGC_QUEUE_PAIRS_NUM) {
+		PMD_DRV_LOG(ERR, "Queue index(%u) illegal, max is %u",
+			rx_queue_id, IGC_QUEUE_PAIRS_NUM - 1);
+		return;
+	}
+
+	reg_val = IGC_READ_REG(hw, IGC_DVMOLR(rx_queue_id));
+	if (on) {
+		/* If vlan been stripped off, the CRC is meaningless. */
+		reg_val |= IGC_DVMOLR_STRVLAN | IGC_DVMOLR_STRCRC;
+		rxq->offloads |= DEV_RX_OFFLOAD_VLAN_STRIP;
+	} else {
+		reg_val &= ~(IGC_DVMOLR_STRVLAN | IGC_DVMOLR_HIDVLAN |
+				IGC_DVMOLR_STRCRC);
+		rxq->offloads &= ~DEV_RX_OFFLOAD_VLAN_STRIP;
+	}
+
+	IGC_WRITE_REG(hw, IGC_DVMOLR(rx_queue_id), reg_val);
+}
diff --git a/drivers/net/igc/igc_txrx.h b/drivers/net/igc/igc_txrx.h
index 82ca95e..0cf9cbe 100644
--- a/drivers/net/igc/igc_txrx.h
+++ b/drivers/net/igc/igc_txrx.h
@@ -44,7 +44,8 @@  void eth_igc_rxq_info_get(struct rte_eth_dev *dev, uint16_t queue_id,
 	struct rte_eth_rxq_info *qinfo);
 void eth_igc_txq_info_get(struct rte_eth_dev *dev, uint16_t queue_id,
 	struct rte_eth_txq_info *qinfo);
-
+void eth_igc_vlan_strip_queue_set(struct rte_eth_dev *dev,
+			uint16_t rx_queue_id, int on);
 #ifdef __cplusplus
 }
 #endif