Commit 4f74dcc1 authored by Brett Creeley's avatar Brett Creeley Committed by Jeff Kirsher

ice: Enable VSI Rx/Tx pruning only when VLAN 0 is active

VLAN pruning is not valid when VLAN 0 is not active. If VLAN
pruning is enabled and VLAN 0 is not active (8021q driver not loaded)
then normal, non-VLAN, traffic will not pass.

TX/RX VLAN pruning is enabled when the VLAN 0 is added to the
active_vlan bitmap and it is disabled when VLAN 0 is removed from the
active_vlan bitmap.

So, only enable VLAN pruning when VLAN 0 is active. Setting RX VLAN
pruning causes the switch to drop received VLAN packets when there
are no matching VLAN ids in the associated VSI's switch filters. Setting
TX pruning makes it so the switch will not send out any packets with
VLAN tags that don't match the associated VSI's switch filters.

With this patch, if the VF or PF tries to send a VLAN tagged packet with
a VLAN tag that it does not have a pruning rule for it will trigger an
MDD event. For example, if PF0 has VLAN10 and VLAN11 interfaces and
scapy is used to send a packet with VLAN8 then the MDD is triggered.

Also make ice_vsi_kill_vlan return a value which the caller can check
before updating VLAN related data structures (counts, pruning bits, etc.).
Signed-off-by: default avatarBrett Creeley <brett.creeley@intel.com>
Signed-off-by: default avatarAnirudh Venkataramanan <anirudh.venkataramanan@intel.com>
Tested-by: default avatarTony Brelinski <tonyx.brelinski@intel.com>
Signed-off-by: default avatarJeff Kirsher <jeffrey.t.kirsher@intel.com>
parent 8b97ceb1
...@@ -349,6 +349,63 @@ static bool ice_vsi_fltr_changed(struct ice_vsi *vsi) ...@@ -349,6 +349,63 @@ static bool ice_vsi_fltr_changed(struct ice_vsi *vsi)
test_bit(ICE_VSI_FLAG_VLAN_FLTR_CHANGED, vsi->flags); test_bit(ICE_VSI_FLAG_VLAN_FLTR_CHANGED, vsi->flags);
} }
/**
* ice_cfg_vlan_pruning - enable or disable VLAN pruning on the VSI
* @vsi: VSI to enable or disable VLAN pruning on
* @ena: set to true to enable VLAN pruning and false to disable it
*
* returns 0 if VSI is updated, negative otherwise
*/
static int ice_cfg_vlan_pruning(struct ice_vsi *vsi, bool ena)
{
struct ice_vsi_ctx *ctxt;
struct device *dev;
int status;
if (!vsi)
return -EINVAL;
dev = &vsi->back->pdev->dev;
ctxt = devm_kzalloc(dev, sizeof(*ctxt), GFP_KERNEL);
if (!ctxt)
return -ENOMEM;
ctxt->info = vsi->info;
if (ena) {
ctxt->info.sec_flags |=
ICE_AQ_VSI_SEC_TX_VLAN_PRUNE_ENA <<
ICE_AQ_VSI_SEC_TX_PRUNE_ENA_S;
ctxt->info.sw_flags2 |= ICE_AQ_VSI_SW_FLAG_RX_VLAN_PRUNE_ENA;
} else {
ctxt->info.sec_flags &=
~(ICE_AQ_VSI_SEC_TX_VLAN_PRUNE_ENA <<
ICE_AQ_VSI_SEC_TX_PRUNE_ENA_S);
ctxt->info.sw_flags2 &= ~ICE_AQ_VSI_SW_FLAG_RX_VLAN_PRUNE_ENA;
}
ctxt->info.valid_sections = cpu_to_le16(ICE_AQ_VSI_PROP_SECURITY_VALID |
ICE_AQ_VSI_PROP_SW_VALID);
ctxt->vsi_num = vsi->vsi_num;
status = ice_aq_update_vsi(&vsi->back->hw, ctxt, NULL);
if (status) {
netdev_err(vsi->netdev, "%sabling VLAN pruning on VSI %d failed, err = %d, aq_err = %d\n",
ena ? "Ena" : "Dis", vsi->vsi_num, status,
vsi->back->hw.adminq.sq_last_status);
goto err_out;
}
vsi->info.sec_flags = ctxt->info.sec_flags;
vsi->info.sw_flags2 = ctxt->info.sw_flags2;
devm_kfree(dev, ctxt);
return 0;
err_out:
devm_kfree(dev, ctxt);
return -EIO;
}
/** /**
* ice_vsi_sync_fltr - Update the VSI filter list to the HW * ice_vsi_sync_fltr - Update the VSI filter list to the HW
* @vsi: ptr to the VSI * @vsi: ptr to the VSI
...@@ -3126,7 +3183,7 @@ static int ice_vlan_rx_add_vid(struct net_device *netdev, ...@@ -3126,7 +3183,7 @@ static int ice_vlan_rx_add_vid(struct net_device *netdev,
{ {
struct ice_netdev_priv *np = netdev_priv(netdev); struct ice_netdev_priv *np = netdev_priv(netdev);
struct ice_vsi *vsi = np->vsi; struct ice_vsi *vsi = np->vsi;
int ret = 0; int ret;
if (vid >= VLAN_N_VID) { if (vid >= VLAN_N_VID) {
netdev_err(netdev, "VLAN id requested %d is out of range %d\n", netdev_err(netdev, "VLAN id requested %d is out of range %d\n",
...@@ -3137,6 +3194,13 @@ static int ice_vlan_rx_add_vid(struct net_device *netdev, ...@@ -3137,6 +3194,13 @@ static int ice_vlan_rx_add_vid(struct net_device *netdev,
if (vsi->info.pvid) if (vsi->info.pvid)
return -EINVAL; return -EINVAL;
/* Enable VLAN pruning when VLAN 0 is added */
if (unlikely(!vid)) {
ret = ice_cfg_vlan_pruning(vsi, true);
if (ret)
return ret;
}
/* Add all VLAN ids including 0 to the switch filter. VLAN id 0 is /* Add all VLAN ids including 0 to the switch filter. VLAN id 0 is
* needed to continue allowing all untagged packets since VLAN prune * needed to continue allowing all untagged packets since VLAN prune
* list is applied to all packets by the switch * list is applied to all packets by the switch
...@@ -3153,16 +3217,19 @@ static int ice_vlan_rx_add_vid(struct net_device *netdev, ...@@ -3153,16 +3217,19 @@ static int ice_vlan_rx_add_vid(struct net_device *netdev,
* ice_vsi_kill_vlan - Remove VSI membership for a given VLAN * ice_vsi_kill_vlan - Remove VSI membership for a given VLAN
* @vsi: the VSI being configured * @vsi: the VSI being configured
* @vid: VLAN id to be removed * @vid: VLAN id to be removed
*
* Returns 0 on success and negative on failure
*/ */
static void ice_vsi_kill_vlan(struct ice_vsi *vsi, u16 vid) static int ice_vsi_kill_vlan(struct ice_vsi *vsi, u16 vid)
{ {
struct ice_fltr_list_entry *list; struct ice_fltr_list_entry *list;
struct ice_pf *pf = vsi->back; struct ice_pf *pf = vsi->back;
LIST_HEAD(tmp_add_list); LIST_HEAD(tmp_add_list);
int status = 0;
list = devm_kzalloc(&pf->pdev->dev, sizeof(*list), GFP_KERNEL); list = devm_kzalloc(&pf->pdev->dev, sizeof(*list), GFP_KERNEL);
if (!list) if (!list)
return; return -ENOMEM;
list->fltr_info.lkup_type = ICE_SW_LKUP_VLAN; list->fltr_info.lkup_type = ICE_SW_LKUP_VLAN;
list->fltr_info.fwd_id.vsi_id = vsi->vsi_num; list->fltr_info.fwd_id.vsi_id = vsi->vsi_num;
...@@ -3174,11 +3241,14 @@ static void ice_vsi_kill_vlan(struct ice_vsi *vsi, u16 vid) ...@@ -3174,11 +3241,14 @@ static void ice_vsi_kill_vlan(struct ice_vsi *vsi, u16 vid)
INIT_LIST_HEAD(&list->list_entry); INIT_LIST_HEAD(&list->list_entry);
list_add(&list->list_entry, &tmp_add_list); list_add(&list->list_entry, &tmp_add_list);
if (ice_remove_vlan(&pf->hw, &tmp_add_list)) if (ice_remove_vlan(&pf->hw, &tmp_add_list)) {
dev_err(&pf->pdev->dev, "Error removing VLAN %d on vsi %i\n", dev_err(&pf->pdev->dev, "Error removing VLAN %d on vsi %i\n",
vid, vsi->vsi_num); vid, vsi->vsi_num);
status = -EIO;
}
ice_free_fltr_list(&pf->pdev->dev, &tmp_add_list); ice_free_fltr_list(&pf->pdev->dev, &tmp_add_list);
return status;
} }
/** /**
...@@ -3194,19 +3264,25 @@ static int ice_vlan_rx_kill_vid(struct net_device *netdev, ...@@ -3194,19 +3264,25 @@ static int ice_vlan_rx_kill_vid(struct net_device *netdev,
{ {
struct ice_netdev_priv *np = netdev_priv(netdev); struct ice_netdev_priv *np = netdev_priv(netdev);
struct ice_vsi *vsi = np->vsi; struct ice_vsi *vsi = np->vsi;
int status;
if (vsi->info.pvid) if (vsi->info.pvid)
return -EINVAL; return -EINVAL;
/* return code is ignored as there is nothing a user /* Make sure ice_vsi_kill_vlan is successful before updating VLAN
* can do about failure to remove and a log message was * information
* already printed from the other function
*/ */
ice_vsi_kill_vlan(vsi, vid); status = ice_vsi_kill_vlan(vsi, vid);
if (status)
return status;
clear_bit(vid, vsi->active_vlans); clear_bit(vid, vsi->active_vlans);
return 0; /* Disable VLAN pruning when VLAN 0 is removed */
if (unlikely(!vid))
status = ice_cfg_vlan_pruning(vsi, false);
return status;
} }
/** /**
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment