Skip to content

Commit

Permalink
ixgbe: Add support for Double VLAN mode
Browse files Browse the repository at this point in the history
There is no practical limit on number of stacked vlans in Linux which
means multiple VLAN headers could be present in Ethernet frames.

However it is less common to have stacking depth more than two which
is known as 802.1ad or QinQ protocols.

By default driver provides hardware offloading only for one VLAN header:
it supports filtering by vlan id (vid), strip on receive, insert on
transmit; as well as skipping header to provide offload for next
protocol.

In case of stacked vlans next protocol is also VLAN which is in turn
isn't known to hardware without additional configuration. That means
hardware does not provide any additional offload like receive hash
calculation, direction to specific receive queue etc. Last one means
that RSS does not work for packets with multiple VLAN headers.

Hardware supports double vlans when configured in specific mode called
Global Double VLAN in "Intel(R) 82599 10 GbE Controller Datasheet".

With this change we implement support for this mode in ixgbe driver.

Signed-off-by: Serhey Popovych <[email protected]>
  • Loading branch information
serhepopovych committed Feb 15, 2024
1 parent 0b42877 commit 85831b0
Show file tree
Hide file tree
Showing 9 changed files with 444 additions and 45 deletions.
14 changes: 14 additions & 0 deletions ixgbe.7
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,20 @@ This parameter is only relevant for devices operating in SR-IOV mode.
When this parameter is set, the driver detects malicious VF driver and
disables its Tx/Rx queues until a VF driver reset occurs.
.LP
.B DV (Double VLAN)
.IP
.B Valid Range: 0-1
.IP
0 = Disabled
.IP
1 = Enabled
.IP
Control Double VLAN mode on device. If enabled hardware assumes that at least
single vlan header present in packet buffer and second header is skipped to
enable rest of L2/L3 offloading processing (e.g. RSS). It is disabled by default
since VID filtering, if enabled, is done in software by driver and not compatible
with VMDq.
.LP
.B Jumbo Frames
.IP
Jumbo Frames support is enabled by changing the Maximum Transmission Unit (MTU) to a value larger than the default value of 1500.
Expand Down
11 changes: 10 additions & 1 deletion src/ixgbe.h
Original file line number Diff line number Diff line change
Expand Up @@ -906,7 +906,9 @@ struct ixgbe_adapter {
#define IXGBE_FLAG2_PHY_INTERRUPT (u32)(1 << 17)
#define IXGBE_FLAG2_VLAN_PROMISC (u32)(1 << 18)
#define IXGBE_FLAG2_RX_LEGACY (u32)(1 << 19)
#define IXGBE_FLAG2_AUTO_DISABLE_VF BIT(20)
#define IXGBE_FLAG2_AUTO_DISABLE_VF (u32)(1 << 20)
#define IXGBE_FLAG2_VLAN_STAG_RX (u32)(1 << 21)
#define IXGBE_FLAG2_VLAN_STAG_FILTER (u32)(1 << 22)

/* Tx fast path data */
int num_tx_queues;
Expand Down Expand Up @@ -1275,6 +1277,13 @@ void ixgbe_disable_tx_queue(struct ixgbe_adapter *adapter);
#ifdef ETHTOOL_OPS_COMPAT
int ethtool_ioctl(struct ifreq *ifr);
#endif
#ifdef HAVE_RHEL6_NET_DEVICE_OPS_EXT
u32 ixgbe_vlan_double_fix_features(struct net_device *netdev, u32 features);
#else
netdev_features_t ixgbe_vlan_double_fix_features(struct net_device *netdev,
netdev_features_t features);
#endif


#if IS_ENABLED(CONFIG_FCOE)
void ixgbe_configure_fcoe(struct ixgbe_adapter *adapter);
Expand Down
88 changes: 86 additions & 2 deletions src/ixgbe_ethtool.c
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,10 @@ static const char ixgbe_priv_flags_strings[][ETH_GSTRING_LEN] = {
#endif
#define IXGBE_PRIV_FLAGS_AUTO_DISABLE_VF BIT(2)
"mdd-disable-vf",
#define IXGBE_PRIV_FLAGS_VLAN_STAG_RX BIT(3)
"vlan-stag-rx",
#define IXGBE_PRIV_FLAGS_VLAN_STAG_FILTER BIT(4)
"vlan-stag-filter",
};

#define IXGBE_PRIV_FLAGS_STR_LEN ARRAY_SIZE(ixgbe_priv_flags_strings)
Expand Down Expand Up @@ -3390,7 +3394,7 @@ static int ixgbe_set_tso(struct net_device *netdev, u32 data)
static int ixgbe_set_flags(struct net_device *netdev, u32 data)
{
struct ixgbe_adapter *adapter = netdev_priv(netdev);
u32 supported_flags = ETH_FLAG_RXVLAN | ETH_FLAG_TXVLAN;
u32 supported_flags = ETH_FLAG_RXVLAN;
u32 changed = netdev->features ^ data;
bool need_reset = false;
int rc;
Expand All @@ -3402,6 +3406,8 @@ static int ixgbe_set_flags(struct net_device *netdev, u32 data)
#endif
if (adapter->flags2 & IXGBE_FLAG2_RSC_CAPABLE)
supported_flags |= ETH_FLAG_LRO;
if (!(adapter->flags & IXGBE_FLAG2_VLAN_STAG_RX))
supported_flags |= ETH_FLAG_TXVLAN;

#ifdef ETHTOOL_GRXRINGS
switch (adapter->hw.mac.type) {
Expand Down Expand Up @@ -4678,6 +4684,12 @@ static u32 ixgbe_get_priv_flags(struct net_device *netdev)
if (adapter->flags2 & IXGBE_FLAG2_AUTO_DISABLE_VF)
priv_flags |= IXGBE_PRIV_FLAGS_AUTO_DISABLE_VF;

if (adapter->flags2 & IXGBE_FLAG2_VLAN_STAG_RX)
priv_flags |= IXGBE_PRIV_FLAGS_VLAN_STAG_RX;

if (adapter->flags2 & IXGBE_FLAG2_VLAN_STAG_FILTER)
priv_flags |= IXGBE_PRIV_FLAGS_VLAN_STAG_FILTER;

return priv_flags;
}

Expand All @@ -4694,6 +4706,8 @@ static int ixgbe_set_priv_flags(struct net_device *netdev, u32 priv_flags)
IXGBE_SPF_NONE = 0,
IXGBE_SPF_REINIT_LOCKED = (1 << 0),
IXGBE_SPF_RESET = (1 << 1),
IXGBE_SPF_SET_RX_MODE = (1 << 2),
IXGBE_SPF_SET_FEATURES = (1 << 3),
} do_reset = IXGBE_SPF_NONE;

if (!changed)
Expand Down Expand Up @@ -4749,14 +4763,84 @@ static int ixgbe_set_priv_flags(struct net_device *netdev, u32 priv_flags)
do_reset |= IXGBE_SPF_REINIT_LOCKED;
}

/* Global Double VLAN features handling */
if (changed & IXGBE_PRIV_FLAGS_VLAN_STAG_RX) {
struct ixgbe_hw *hw = &adapter->hw;

switch (hw->mac.type) {
case ixgbe_mac_82599EB:
case ixgbe_mac_X540:
case ixgbe_mac_X550:
case ixgbe_mac_X550EM_x:
case ixgbe_mac_X550EM_a:
/* VMDq requires vlan filtering to be enbled */
if (!(adapter->flags & IXGBE_FLAG_VMDQ_ENABLED) &&
(priv_flags & IXGBE_PRIV_FLAGS_VLAN_STAG_RX)) {
/* Turn on STAG filter by default: user might
* turn it off later if required.
*/
if (!(adapter->flags2 & IXGBE_FLAG2_VLAN_STAG_RX)) {
#ifndef HAVE_VLAN_RX_REGISTER
adapter->flags2 |=
(IXGBE_FLAG2_VLAN_STAG_RX|
IXGBE_FLAG2_VLAN_STAG_FILTER);
#else
/* No filtering by driver by default: there
* are setups with old kernels where filtering
* might be broken (e.g. vlan on top of macvlan)
*/
adapter->flags2 |=
(IXGBE_FLAG2_VLAN_STAG_RX);
#endif
}
break;
}
/* fall thru */
default:
/* No filtering by outer tag without outer VLAN
* header acceleration on receive.
*/
adapter->flags2 &= ~(IXGBE_FLAG2_VLAN_STAG_RX |
IXGBE_FLAG2_VLAN_STAG_FILTER);
}

changed &= ~IXGBE_PRIV_FLAGS_VLAN_STAG_FILTER;
do_reset |= IXGBE_SPF_SET_FEATURES;
}

if (changed & IXGBE_PRIV_FLAGS_VLAN_STAG_FILTER) {
if (adapter->flags2 & IXGBE_FLAG2_VLAN_STAG_RX) {
adapter->flags2 ^= IXGBE_FLAG2_VLAN_STAG_FILTER;
do_reset |= IXGBE_SPF_SET_FEATURES;
}
}

if (do_reset & IXGBE_SPF_SET_FEATURES) {
typeof(netdev->features) features = netdev->features;

features = ixgbe_vlan_double_fix_features(netdev, features);
if (features != netdev->features) {
netdev->features = features;
netdev_features_change(netdev);
}

do_reset |= IXGBE_SPF_SET_RX_MODE;
}

if (do_reset & IXGBE_SPF_RESET) {
ixgbe_do_reset(netdev);
do_reset &= ~IXGBE_SPF_SET_RX_MODE;
} else if (do_reset & IXGBE_SPF_REINIT_LOCKED) {
/* reset interface to repopulate queues */
if (netif_running(netdev))
if (netif_running(netdev)) {
ixgbe_reinit_locked(adapter);
do_reset &= ~IXGBE_SPF_SET_RX_MODE;
}
}

if (do_reset & IXGBE_SPF_SET_RX_MODE)
ixgbe_set_rx_mode(netdev);

return 0;
}

Expand Down
Loading

0 comments on commit 85831b0

Please sign in to comment.