net/igc: support VLAN

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>
Reviewed-by: Ferruh Yigit <ferruh.yigit@intel.com>
This commit is contained in:
Alvin Zhang 2020-04-15 16:48:08 +08:00 committed by Ferruh Yigit
parent bd3fcf0d0f
commit 5f266d0d8c
6 changed files with 298 additions and 1 deletions

View File

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

View File

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

View File

@ -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 const struct eth_dev_ops eth_igc_ops = {
.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,
};
/*
@ -950,6 +963,11 @@ eth_igc_start(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 @@ eth_igc_infos_get(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info)
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;
@ -2358,6 +2377,192 @@ eth_igc_rss_hash_conf_get(struct rte_eth_dev *dev,
return 0;
}
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, min is %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, max 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)

View File

@ -17,6 +17,10 @@ extern "C" {
#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 @@ extern "C" {
#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)
{

View File

@ -1162,6 +1162,16 @@ igc_rx_init(struct rte_eth_dev *dev)
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;
@ -2107,3 +2117,31 @@ eth_igc_txq_info_get(struct rte_eth_dev *dev, uint16_t queue_id,
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);
}

View File

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