]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
ice: Add support for PF/VF promiscuous mode
authorAkeem G Abodunrin <akeem.g.abodunrin@intel.com>
Wed, 27 Feb 2019 00:35:14 +0000 (16:35 -0800)
committerJeff Kirsher <jeffrey.t.kirsher@intel.com>
Fri, 22 Mar 2019 15:19:17 +0000 (08:19 -0700)
Implement support for VF promiscuous mode, MAC/VLAN/MAC_VLAN and PF
multicast MAC/VLAN/MAC_VLAN promiscuous mode.

Signed-off-by: Akeem G Abodunrin <akeem.g.abodunrin@intel.com>
Signed-off-by: Anirudh Venkataramanan <anirudh.venkataramanan@intel.com>
Tested-by: Andrew Bowers <andrewx.bowers@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
drivers/net/ethernet/intel/ice/ice.h
drivers/net/ethernet/intel/ice/ice_lib.c
drivers/net/ethernet/intel/ice/ice_lib.h
drivers/net/ethernet/intel/ice/ice_main.c
drivers/net/ethernet/intel/ice/ice_switch.c
drivers/net/ethernet/intel/ice/ice_switch.h
drivers/net/ethernet/intel/ice/ice_virtchnl_pf.c

index 2bb2898efb82ebb1222ca0586cb1d7f820a59cc6..0843868d1bbd27f51d936241d9be4b7aaeb22865 100644 (file)
@@ -125,6 +125,23 @@ extern const char ice_drv_ver[];
 #define ice_for_each_q_vector(vsi, i) \
        for ((i) = 0; (i) < (vsi)->num_q_vectors; (i)++)
 
+#define ICE_UCAST_PROMISC_BITS (ICE_PROMISC_UCAST_TX | ICE_PROMISC_MCAST_TX | \
+                               ICE_PROMISC_UCAST_RX | ICE_PROMISC_MCAST_RX)
+
+#define ICE_UCAST_VLAN_PROMISC_BITS (ICE_PROMISC_UCAST_TX | \
+                                    ICE_PROMISC_MCAST_TX | \
+                                    ICE_PROMISC_UCAST_RX | \
+                                    ICE_PROMISC_MCAST_RX | \
+                                    ICE_PROMISC_VLAN_TX  | \
+                                    ICE_PROMISC_VLAN_RX)
+
+#define ICE_MCAST_PROMISC_BITS (ICE_PROMISC_MCAST_TX | ICE_PROMISC_MCAST_RX)
+
+#define ICE_MCAST_VLAN_PROMISC_BITS (ICE_PROMISC_MCAST_TX | \
+                                    ICE_PROMISC_MCAST_RX | \
+                                    ICE_PROMISC_VLAN_TX  | \
+                                    ICE_PROMISC_VLAN_RX)
+
 struct ice_tc_info {
        u16 qoffset;
        u16 qcount_tx;
@@ -258,6 +275,7 @@ struct ice_vsi {
        u8 irqs_ready;
        u8 current_isup;                 /* Sync 'link up' logging */
        u8 stat_offsets_loaded;
+       u8 vlan_ena;
 
        /* queue information */
        u8 tx_mapping_mode;              /* ICE_MAP_MODE_[CONTIG|SCATTER] */
index 87e57328a81b9d1b00ea6b8d2cf7bf4f1317d3c8..7a692f80bda48d4839774e6ca85f1ae1e1982937 100644 (file)
@@ -2119,10 +2119,11 @@ ice_vsi_stop_lan_tx_rings(struct ice_vsi *vsi, enum ice_disq_rst_src rst_src,
  * 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
+ * @vlan_promisc: enable valid security flags if not in VLAN promiscuous mode
  *
  * returns 0 if VSI is updated, negative otherwise
  */
-int ice_cfg_vlan_pruning(struct ice_vsi *vsi, bool ena)
+int ice_cfg_vlan_pruning(struct ice_vsi *vsi, bool ena, bool vlan_promisc)
 {
        struct ice_vsi_ctx *ctxt;
        struct device *dev;
@@ -2150,8 +2151,10 @@ int ice_cfg_vlan_pruning(struct ice_vsi *vsi, bool ena)
                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);
+       if (!vlan_promisc)
+               ctxt->info.valid_sections =
+                       cpu_to_le16(ICE_AQ_VSI_PROP_SECURITY_VALID |
+                                   ICE_AQ_VSI_PROP_SW_VALID);
 
        status = ice_update_vsi(&vsi->back->hw, vsi->idx, ctxt, NULL);
        if (status) {
index 7988a53729a92b3fdfc1d4bc82359e744275d723..04461713812262bba89446d8c1efda33b00f4e40 100644 (file)
@@ -35,7 +35,7 @@ int
 ice_vsi_stop_lan_tx_rings(struct ice_vsi *vsi, enum ice_disq_rst_src rst_src,
                          u16 rel_vmvf_num);
 
-int ice_cfg_vlan_pruning(struct ice_vsi *vsi, bool ena);
+int ice_cfg_vlan_pruning(struct ice_vsi *vsi, bool ena, bool vlan_promisc);
 
 void ice_vsi_delete(struct ice_vsi *vsi);
 
index 34712e38121eb4ba282683648da79d407b55cd7d..879c1f176a17c1d7f6e325aceabe3ed2b3970a82 100644 (file)
@@ -167,6 +167,39 @@ static bool ice_vsi_fltr_changed(struct ice_vsi *vsi)
               test_bit(ICE_VSI_FLAG_VLAN_FLTR_CHANGED, vsi->flags);
 }
 
+/**
+ * ice_cfg_promisc - Enable or disable promiscuous mode for a given PF
+ * @vsi: the VSI being configured
+ * @promisc_m: mask of promiscuous config bits
+ * @set_promisc: enable or disable promisc flag request
+ *
+ */
+static int ice_cfg_promisc(struct ice_vsi *vsi, u8 promisc_m, bool set_promisc)
+{
+       struct ice_hw *hw = &vsi->back->hw;
+       enum ice_status status = 0;
+
+       if (vsi->type != ICE_VSI_PF)
+               return 0;
+
+       if (vsi->vlan_ena) {
+               status = ice_set_vlan_vsi_promisc(hw, vsi->idx, promisc_m,
+                                                 set_promisc);
+       } else {
+               if (set_promisc)
+                       status = ice_set_vsi_promisc(hw, vsi->idx, promisc_m,
+                                                    0);
+               else
+                       status = ice_clear_vsi_promisc(hw, vsi->idx, promisc_m,
+                                                      0);
+       }
+
+       if (status)
+               return -EIO;
+
+       return 0;
+}
+
 /**
  * ice_vsi_sync_fltr - Update the VSI filter list to the HW
  * @vsi: ptr to the VSI
@@ -182,6 +215,7 @@ static int ice_vsi_sync_fltr(struct ice_vsi *vsi)
        struct ice_hw *hw = &pf->hw;
        enum ice_status status = 0;
        u32 changed_flags = 0;
+       u8 promisc_m;
        int err = 0;
 
        if (!vsi->netdev)
@@ -245,8 +279,35 @@ static int ice_vsi_sync_fltr(struct ice_vsi *vsi)
                }
        }
        /* check for changes in promiscuous modes */
-       if (changed_flags & IFF_ALLMULTI)
-               netdev_warn(netdev, "Unsupported configuration\n");
+       if (changed_flags & IFF_ALLMULTI) {
+               if (vsi->current_netdev_flags & IFF_ALLMULTI) {
+                       if (vsi->vlan_ena)
+                               promisc_m = ICE_MCAST_VLAN_PROMISC_BITS;
+                       else
+                               promisc_m = ICE_MCAST_PROMISC_BITS;
+
+                       err = ice_cfg_promisc(vsi, promisc_m, true);
+                       if (err) {
+                               netdev_err(netdev, "Error setting Multicast promiscuous mode on VSI %i\n",
+                                          vsi->vsi_num);
+                               vsi->current_netdev_flags &= ~IFF_ALLMULTI;
+                               goto out_promisc;
+                       }
+               } else if (!(vsi->current_netdev_flags & IFF_ALLMULTI)) {
+                       if (vsi->vlan_ena)
+                               promisc_m = ICE_MCAST_VLAN_PROMISC_BITS;
+                       else
+                               promisc_m = ICE_MCAST_PROMISC_BITS;
+
+                       err = ice_cfg_promisc(vsi, promisc_m, false);
+                       if (err) {
+                               netdev_err(netdev, "Error clearing Multicast promiscuous mode on VSI %i\n",
+                                          vsi->vsi_num);
+                               vsi->current_netdev_flags |= IFF_ALLMULTI;
+                               goto out_promisc;
+                       }
+               }
+       }
 
        if (((changed_flags & IFF_PROMISC) || promisc_forced_on) ||
            test_bit(ICE_VSI_FLAG_PROMISC_CHANGED, vsi->flags)) {
@@ -1665,6 +1726,7 @@ ice_vlan_rx_add_vid(struct net_device *netdev, __always_unused __be16 proto,
 {
        struct ice_netdev_priv *np = netdev_priv(netdev);
        struct ice_vsi *vsi = np->vsi;
+       int ret;
 
        if (vid >= VLAN_N_VID) {
                netdev_err(netdev, "VLAN id requested %d is out of range %d\n",
@@ -1677,8 +1739,7 @@ ice_vlan_rx_add_vid(struct net_device *netdev, __always_unused __be16 proto,
 
        /* Enable VLAN pruning when VLAN 0 is added */
        if (unlikely(!vid)) {
-               int ret = ice_cfg_vlan_pruning(vsi, true);
-
+               ret = ice_cfg_vlan_pruning(vsi, true, false);
                if (ret)
                        return ret;
        }
@@ -1687,7 +1748,13 @@ ice_vlan_rx_add_vid(struct net_device *netdev, __always_unused __be16 proto,
         * needed to continue allowing all untagged packets since VLAN prune
         * list is applied to all packets by the switch
         */
-       return ice_vsi_add_vlan(vsi, vid);
+       ret = ice_vsi_add_vlan(vsi, vid);
+       if (!ret) {
+               vsi->vlan_ena = true;
+               set_bit(ICE_VSI_FLAG_VLAN_FLTR_CHANGED, vsi->flags);
+       }
+
+       return ret;
 }
 
 /**
@@ -1704,7 +1771,7 @@ ice_vlan_rx_kill_vid(struct net_device *netdev, __always_unused __be16 proto,
 {
        struct ice_netdev_priv *np = netdev_priv(netdev);
        struct ice_vsi *vsi = np->vsi;
-       int status;
+       int ret;
 
        if (vsi->info.pvid)
                return -EINVAL;
@@ -1712,15 +1779,17 @@ ice_vlan_rx_kill_vid(struct net_device *netdev, __always_unused __be16 proto,
        /* Make sure ice_vsi_kill_vlan is successful before updating VLAN
         * information
         */
-       status = ice_vsi_kill_vlan(vsi, vid);
-       if (status)
-               return status;
+       ret = ice_vsi_kill_vlan(vsi, vid);
+       if (ret)
+               return ret;
 
        /* Disable VLAN pruning when VLAN 0 is removed */
        if (unlikely(!vid))
-               status = ice_cfg_vlan_pruning(vsi, false);
+               ret = ice_cfg_vlan_pruning(vsi, false, false);
 
-       return status;
+       vsi->vlan_ena = false;
+       set_bit(ICE_VSI_FLAG_VLAN_FLTR_CHANGED, vsi->flags);
+       return ret;
 }
 
 /**
index ed87361f611261293d5e836091479b9dd2dd82a8..64e9e0cb61ce2e938d2f852f1403a1095bc015cf 100644 (file)
@@ -2189,6 +2189,291 @@ ice_add_to_vsi_fltr_list(struct ice_hw *hw, u16 vsi_handle,
        return status;
 }
 
+/**
+ * ice_determine_promisc_mask
+ * @fi: filter info to parse
+ *
+ * Helper function to determine which ICE_PROMISC_ mask corresponds
+ * to given filter into.
+ */
+static u8 ice_determine_promisc_mask(struct ice_fltr_info *fi)
+{
+       u16 vid = fi->l_data.mac_vlan.vlan_id;
+       u8 *macaddr = fi->l_data.mac.mac_addr;
+       bool is_tx_fltr = false;
+       u8 promisc_mask = 0;
+
+       if (fi->flag == ICE_FLTR_TX)
+               is_tx_fltr = true;
+
+       if (is_broadcast_ether_addr(macaddr))
+               promisc_mask |= is_tx_fltr ?
+                       ICE_PROMISC_BCAST_TX : ICE_PROMISC_BCAST_RX;
+       else if (is_multicast_ether_addr(macaddr))
+               promisc_mask |= is_tx_fltr ?
+                       ICE_PROMISC_MCAST_TX : ICE_PROMISC_MCAST_RX;
+       else if (is_unicast_ether_addr(macaddr))
+               promisc_mask |= is_tx_fltr ?
+                       ICE_PROMISC_UCAST_TX : ICE_PROMISC_UCAST_RX;
+       if (vid)
+               promisc_mask |= is_tx_fltr ?
+                       ICE_PROMISC_VLAN_TX : ICE_PROMISC_VLAN_RX;
+
+       return promisc_mask;
+}
+
+/**
+ * ice_remove_promisc - Remove promisc based filter rules
+ * @hw: pointer to the hardware structure
+ * @recp_id: recipe id for which the rule needs to removed
+ * @v_list: list of promisc entries
+ */
+static enum ice_status
+ice_remove_promisc(struct ice_hw *hw, u8 recp_id,
+                  struct list_head *v_list)
+{
+       struct ice_fltr_list_entry *v_list_itr, *tmp;
+
+       list_for_each_entry_safe(v_list_itr, tmp, v_list, list_entry) {
+               v_list_itr->status =
+                       ice_remove_rule_internal(hw, recp_id, v_list_itr);
+               if (v_list_itr->status)
+                       return v_list_itr->status;
+       }
+       return 0;
+}
+
+/**
+ * ice_clear_vsi_promisc - clear specified promiscuous mode(s) for given VSI
+ * @hw: pointer to the hardware structure
+ * @vsi_handle: VSI handle to clear mode
+ * @promisc_mask: mask of promiscuous config bits to clear
+ * @vid: VLAN ID to clear VLAN promiscuous
+ */
+enum ice_status
+ice_clear_vsi_promisc(struct ice_hw *hw, u16 vsi_handle, u8 promisc_mask,
+                     u16 vid)
+{
+       struct ice_switch_info *sw = hw->switch_info;
+       struct ice_fltr_list_entry *fm_entry, *tmp;
+       struct list_head remove_list_head;
+       struct ice_fltr_mgmt_list_entry *itr;
+       struct list_head *rule_head;
+       struct mutex *rule_lock;        /* Lock to protect filter rule list */
+       enum ice_status status = 0;
+       u8 recipe_id;
+
+       if (!ice_is_vsi_valid(hw, vsi_handle))
+               return ICE_ERR_PARAM;
+
+       if (vid)
+               recipe_id = ICE_SW_LKUP_PROMISC_VLAN;
+       else
+               recipe_id = ICE_SW_LKUP_PROMISC;
+
+       rule_head = &sw->recp_list[recipe_id].filt_rules;
+       rule_lock = &sw->recp_list[recipe_id].filt_rule_lock;
+
+       INIT_LIST_HEAD(&remove_list_head);
+
+       mutex_lock(rule_lock);
+       list_for_each_entry(itr, rule_head, list_entry) {
+               u8 fltr_promisc_mask = 0;
+
+               if (!ice_vsi_uses_fltr(itr, vsi_handle))
+                       continue;
+
+               fltr_promisc_mask |=
+                       ice_determine_promisc_mask(&itr->fltr_info);
+
+               /* Skip if filter is not completely specified by given mask */
+               if (fltr_promisc_mask & ~promisc_mask)
+                       continue;
+
+               status = ice_add_entry_to_vsi_fltr_list(hw, vsi_handle,
+                                                       &remove_list_head,
+                                                       &itr->fltr_info);
+               if (status) {
+                       mutex_unlock(rule_lock);
+                       goto free_fltr_list;
+               }
+       }
+       mutex_unlock(rule_lock);
+
+       status = ice_remove_promisc(hw, recipe_id, &remove_list_head);
+
+free_fltr_list:
+       list_for_each_entry_safe(fm_entry, tmp, &remove_list_head, list_entry) {
+               list_del(&fm_entry->list_entry);
+               devm_kfree(ice_hw_to_dev(hw), fm_entry);
+       }
+
+       return status;
+}
+
+/**
+ * ice_set_vsi_promisc - set given VSI to given promiscuous mode(s)
+ * @hw: pointer to the hardware structure
+ * @vsi_handle: VSI handle to configure
+ * @promisc_mask: mask of promiscuous config bits
+ * @vid: VLAN ID to set VLAN promiscuous
+ */
+enum ice_status
+ice_set_vsi_promisc(struct ice_hw *hw, u16 vsi_handle, u8 promisc_mask, u16 vid)
+{
+       enum { UCAST_FLTR = 1, MCAST_FLTR, BCAST_FLTR };
+       struct ice_fltr_list_entry f_list_entry;
+       struct ice_fltr_info new_fltr;
+       enum ice_status status = 0;
+       bool is_tx_fltr;
+       u16 hw_vsi_id;
+       int pkt_type;
+       u8 recipe_id;
+
+       if (!ice_is_vsi_valid(hw, vsi_handle))
+               return ICE_ERR_PARAM;
+       hw_vsi_id = ice_get_hw_vsi_num(hw, vsi_handle);
+
+       memset(&new_fltr, 0, sizeof(new_fltr));
+
+       if (promisc_mask & (ICE_PROMISC_VLAN_RX | ICE_PROMISC_VLAN_TX)) {
+               new_fltr.lkup_type = ICE_SW_LKUP_PROMISC_VLAN;
+               new_fltr.l_data.mac_vlan.vlan_id = vid;
+               recipe_id = ICE_SW_LKUP_PROMISC_VLAN;
+       } else {
+               new_fltr.lkup_type = ICE_SW_LKUP_PROMISC;
+               recipe_id = ICE_SW_LKUP_PROMISC;
+       }
+
+       /* Separate filters must be set for each direction/packet type
+        * combination, so we will loop over the mask value, store the
+        * individual type, and clear it out in the input mask as it
+        * is found.
+        */
+       while (promisc_mask) {
+               u8 *mac_addr;
+
+               pkt_type = 0;
+               is_tx_fltr = false;
+
+               if (promisc_mask & ICE_PROMISC_UCAST_RX) {
+                       promisc_mask &= ~ICE_PROMISC_UCAST_RX;
+                       pkt_type = UCAST_FLTR;
+               } else if (promisc_mask & ICE_PROMISC_UCAST_TX) {
+                       promisc_mask &= ~ICE_PROMISC_UCAST_TX;
+                       pkt_type = UCAST_FLTR;
+                       is_tx_fltr = true;
+               } else if (promisc_mask & ICE_PROMISC_MCAST_RX) {
+                       promisc_mask &= ~ICE_PROMISC_MCAST_RX;
+                       pkt_type = MCAST_FLTR;
+               } else if (promisc_mask & ICE_PROMISC_MCAST_TX) {
+                       promisc_mask &= ~ICE_PROMISC_MCAST_TX;
+                       pkt_type = MCAST_FLTR;
+                       is_tx_fltr = true;
+               } else if (promisc_mask & ICE_PROMISC_BCAST_RX) {
+                       promisc_mask &= ~ICE_PROMISC_BCAST_RX;
+                       pkt_type = BCAST_FLTR;
+               } else if (promisc_mask & ICE_PROMISC_BCAST_TX) {
+                       promisc_mask &= ~ICE_PROMISC_BCAST_TX;
+                       pkt_type = BCAST_FLTR;
+                       is_tx_fltr = true;
+               }
+
+               /* Check for VLAN promiscuous flag */
+               if (promisc_mask & ICE_PROMISC_VLAN_RX) {
+                       promisc_mask &= ~ICE_PROMISC_VLAN_RX;
+               } else if (promisc_mask & ICE_PROMISC_VLAN_TX) {
+                       promisc_mask &= ~ICE_PROMISC_VLAN_TX;
+                       is_tx_fltr = true;
+               }
+
+               /* Set filter DA based on packet type */
+               mac_addr = new_fltr.l_data.mac.mac_addr;
+               if (pkt_type == BCAST_FLTR) {
+                       eth_broadcast_addr(mac_addr);
+               } else if (pkt_type == MCAST_FLTR ||
+                          pkt_type == UCAST_FLTR) {
+                       /* Use the dummy ether header DA */
+                       ether_addr_copy(mac_addr, dummy_eth_header);
+                       if (pkt_type == MCAST_FLTR)
+                               mac_addr[0] |= 0x1;     /* Set multicast bit */
+               }
+
+               /* Need to reset this to zero for all iterations */
+               new_fltr.flag = 0;
+               if (is_tx_fltr) {
+                       new_fltr.flag |= ICE_FLTR_TX;
+                       new_fltr.src = hw_vsi_id;
+               } else {
+                       new_fltr.flag |= ICE_FLTR_RX;
+                       new_fltr.src = hw->port_info->lport;
+               }
+
+               new_fltr.fltr_act = ICE_FWD_TO_VSI;
+               new_fltr.vsi_handle = vsi_handle;
+               new_fltr.fwd_id.hw_vsi_id = hw_vsi_id;
+               f_list_entry.fltr_info = new_fltr;
+
+               status = ice_add_rule_internal(hw, recipe_id, &f_list_entry);
+               if (status)
+                       goto set_promisc_exit;
+       }
+
+set_promisc_exit:
+       return status;
+}
+
+/**
+ * ice_set_vlan_vsi_promisc
+ * @hw: pointer to the hardware structure
+ * @vsi_handle: VSI handle to configure
+ * @promisc_mask: mask of promiscuous config bits
+ * @rm_vlan_promisc: Clear VLANs VSI promisc mode
+ *
+ * Configure VSI with all associated VLANs to given promiscuous mode(s)
+ */
+enum ice_status
+ice_set_vlan_vsi_promisc(struct ice_hw *hw, u16 vsi_handle, u8 promisc_mask,
+                        bool rm_vlan_promisc)
+{
+       struct ice_switch_info *sw = hw->switch_info;
+       struct ice_fltr_list_entry *list_itr, *tmp;
+       struct list_head vsi_list_head;
+       struct list_head *vlan_head;
+       struct mutex *vlan_lock; /* Lock to protect filter rule list */
+       enum ice_status status;
+       u16 vlan_id;
+
+       INIT_LIST_HEAD(&vsi_list_head);
+       vlan_lock = &sw->recp_list[ICE_SW_LKUP_VLAN].filt_rule_lock;
+       vlan_head = &sw->recp_list[ICE_SW_LKUP_VLAN].filt_rules;
+       mutex_lock(vlan_lock);
+       status = ice_add_to_vsi_fltr_list(hw, vsi_handle, vlan_head,
+                                         &vsi_list_head);
+       mutex_unlock(vlan_lock);
+       if (status)
+               goto free_fltr_list;
+
+       list_for_each_entry(list_itr, &vsi_list_head, list_entry) {
+               vlan_id = list_itr->fltr_info.l_data.vlan.vlan_id;
+               if (rm_vlan_promisc)
+                       status = ice_clear_vsi_promisc(hw, vsi_handle,
+                                                      promisc_mask, vlan_id);
+               else
+                       status = ice_set_vsi_promisc(hw, vsi_handle,
+                                                    promisc_mask, vlan_id);
+               if (status)
+                       break;
+       }
+
+free_fltr_list:
+       list_for_each_entry_safe(list_itr, tmp, &vsi_list_head, list_entry) {
+               list_del(&list_itr->list_entry);
+               devm_kfree(ice_hw_to_dev(hw), list_itr);
+       }
+       return status;
+}
+
 /**
  * ice_remove_vsi_lkup_fltr - Remove lookup type filters for a VSI
  * @hw: pointer to the hardware structure
@@ -2224,12 +2509,14 @@ ice_remove_vsi_lkup_fltr(struct ice_hw *hw, u16 vsi_handle,
        case ICE_SW_LKUP_VLAN:
                ice_remove_vlan(hw, &remove_list_head);
                break;
+       case ICE_SW_LKUP_PROMISC:
+       case ICE_SW_LKUP_PROMISC_VLAN:
+               ice_remove_promisc(hw, lkup, &remove_list_head);
+               break;
        case ICE_SW_LKUP_MAC_VLAN:
        case ICE_SW_LKUP_ETHERTYPE:
        case ICE_SW_LKUP_ETHERTYPE_MAC:
-       case ICE_SW_LKUP_PROMISC:
        case ICE_SW_LKUP_DFLT:
-       case ICE_SW_LKUP_PROMISC_VLAN:
        case ICE_SW_LKUP_LAST:
        default:
                ice_debug(hw, ICE_DBG_SW, "Unsupported lookup type %d\n", lkup);
index 2d3a2dfcb0de1a42351adcc72e85535ed78d268c..e4ce0720b871390cb3003d06033664e4f149dc42 100644 (file)
@@ -178,6 +178,17 @@ struct ice_fltr_mgmt_list_entry {
        u8 counter_index;
 };
 
+enum ice_promisc_flags {
+       ICE_PROMISC_UCAST_RX = 0x1,
+       ICE_PROMISC_UCAST_TX = 0x2,
+       ICE_PROMISC_MCAST_RX = 0x4,
+       ICE_PROMISC_MCAST_TX = 0x8,
+       ICE_PROMISC_BCAST_RX = 0x10,
+       ICE_PROMISC_BCAST_TX = 0x20,
+       ICE_PROMISC_VLAN_RX = 0x40,
+       ICE_PROMISC_VLAN_TX = 0x80,
+};
+
 /* VSI related commands */
 enum ice_status
 ice_add_vsi(struct ice_hw *hw, u16 vsi_handle, struct ice_vsi_ctx *vsi_ctx,
@@ -202,8 +213,19 @@ void ice_remove_vsi_fltr(struct ice_hw *hw, u16 vsi_handle);
 enum ice_status
 ice_add_vlan(struct ice_hw *hw, struct list_head *m_list);
 enum ice_status ice_remove_vlan(struct ice_hw *hw, struct list_head *v_list);
+
+/* Promisc/defport setup for VSIs */
 enum ice_status
 ice_cfg_dflt_vsi(struct ice_hw *hw, u16 vsi_handle, bool set, u8 direction);
+enum ice_status
+ice_set_vsi_promisc(struct ice_hw *hw, u16 vsi_handle, u8 promisc_mask,
+                   u16 vid);
+enum ice_status
+ice_clear_vsi_promisc(struct ice_hw *hw, u16 vsi_handle, u8 promisc_mask,
+                     u16 vid);
+enum ice_status
+ice_set_vlan_vsi_promisc(struct ice_hw *hw, u16 vsi_handle, u8 promisc_mask,
+                        bool rm_vlan_promisc);
 
 enum ice_status ice_init_def_sw_recp(struct ice_hw *hw);
 u16 ice_get_hw_vsi_num(struct ice_hw *hw, u16 vsi_handle);
index 926720bead48cb747bc103f307f888a15ce1342a..6c0178b4b3abab0b319349e65103b3bbb2353109 100644 (file)
@@ -771,6 +771,47 @@ static void ice_cleanup_and_realloc_vf(struct ice_vf *vf)
        wr32(hw, VFGEN_RSTAT(vf->vf_id), VIRTCHNL_VFR_VFACTIVE);
 }
 
+/**
+ * ice_vf_set_vsi_promisc - set given VF VSI to given promiscuous mode(s)
+ * @vf: pointer to the VF info
+ * @vsi: the VSI being configured
+ * @promisc_m: mask of promiscuous config bits
+ * @rm_promisc: promisc flag request from the VF to remove or add filter
+ *
+ * This function configures VF VSI promiscuous mode, based on the VF requests,
+ * for Unicast, Multicast and VLAN
+ */
+static enum ice_status
+ice_vf_set_vsi_promisc(struct ice_vf *vf, struct ice_vsi *vsi, u8 promisc_m,
+                      bool rm_promisc)
+{
+       struct ice_pf *pf = vf->pf;
+       enum ice_status status = 0;
+       struct ice_hw *hw;
+
+       hw = &pf->hw;
+       if (vf->num_vlan) {
+               status = ice_set_vlan_vsi_promisc(hw, vsi->idx, promisc_m,
+                                                 rm_promisc);
+       } else if (vf->port_vlan_id) {
+               if (rm_promisc)
+                       status = ice_clear_vsi_promisc(hw, vsi->idx, promisc_m,
+                                                      vf->port_vlan_id);
+               else
+                       status = ice_set_vsi_promisc(hw, vsi->idx, promisc_m,
+                                                    vf->port_vlan_id);
+       } else {
+               if (rm_promisc)
+                       status = ice_clear_vsi_promisc(hw, vsi->idx, promisc_m,
+                                                      0);
+               else
+                       status = ice_set_vsi_promisc(hw, vsi->idx, promisc_m,
+                                                    0);
+       }
+
+       return status;
+}
+
 /**
  * ice_reset_all_vfs - reset all allocated VFs in one go
  * @pf: pointer to the PF structure
@@ -892,9 +933,10 @@ bool ice_reset_all_vfs(struct ice_pf *pf, bool is_vflr)
 static bool ice_reset_vf(struct ice_vf *vf, bool is_vflr)
 {
        struct ice_pf *pf = vf->pf;
-       struct ice_hw *hw = &pf->hw;
        struct ice_vsi *vsi;
+       struct ice_hw *hw;
        bool rsd = false;
+       u8 promisc_m;
        u32 reg;
        int i;
 
@@ -920,6 +962,7 @@ static bool ice_reset_vf(struct ice_vf *vf, bool is_vflr)
                                vf->vf_id, NULL);
        }
 
+       hw = &pf->hw;
        /* poll VPGEN_VFRSTAT reg to make sure
         * that reset is complete
         */
@@ -945,6 +988,21 @@ static bool ice_reset_vf(struct ice_vf *vf, bool is_vflr)
 
        usleep_range(10000, 20000);
 
+       /* disable promiscuous modes in case they were enabled
+        * ignore any error if disabling process failed
+        */
+       if (test_bit(ICE_VF_STATE_UC_PROMISC, vf->vf_states) ||
+           test_bit(ICE_VF_STATE_MC_PROMISC, vf->vf_states)) {
+               if (vf->port_vlan_id ||  vf->num_vlan)
+                       promisc_m = ICE_UCAST_VLAN_PROMISC_BITS;
+               else
+                       promisc_m = ICE_UCAST_PROMISC_BITS;
+
+               vsi = pf->vsi[vf->lan_vsi_idx];
+               if (ice_vf_set_vsi_promisc(vf, vsi, promisc_m, true))
+                       dev_err(&pf->pdev->dev, "disabling promiscuous mode failed\n");
+       }
+
        /* free VF resources to begin resetting the VSI state */
        ice_free_vf_res(vf);
 
@@ -2192,7 +2250,11 @@ static int ice_vc_process_vlan_msg(struct ice_vf *vf, u8 *msg, bool add_v)
            (struct virtchnl_vlan_filter_list *)msg;
        enum ice_status aq_ret = 0;
        struct ice_pf *pf = vf->pf;
+       bool vlan_promisc = false;
        struct ice_vsi *vsi;
+       struct ice_hw *hw;
+       int status = 0;
+       u8 promisc_m;
        int i;
 
        if (!test_bit(ICE_VF_STATE_ACTIVE, vf->vf_states)) {
@@ -2209,7 +2271,9 @@ static int ice_vc_process_vlan_msg(struct ice_vf *vf, u8 *msg, bool add_v)
            vf->num_vlan >= ICE_MAX_VLAN_PER_VF) {
                dev_info(&pf->pdev->dev,
                         "VF is not trusted, switch the VF to trusted mode, in order to add more VLAN addresses\n");
-               aq_ret = ICE_ERR_PARAM;
+               /* There is no need to let VF know about being not trusted,
+                * so we can just return success message here
+                */
                goto error_param;
        }
 
@@ -2222,6 +2286,7 @@ static int ice_vc_process_vlan_msg(struct ice_vf *vf, u8 *msg, bool add_v)
                }
        }
 
+       hw = &pf->hw;
        vsi = ice_find_vsi_from_id(vf->pf, vfl->vsi_id);
        if (!vsi) {
                aq_ret = ICE_ERR_PARAM;
@@ -2241,19 +2306,41 @@ static int ice_vc_process_vlan_msg(struct ice_vf *vf, u8 *msg, bool add_v)
                goto error_param;
        }
 
+       if (test_bit(ICE_VF_STATE_UC_PROMISC, vf->vf_states) ||
+           test_bit(ICE_VF_STATE_MC_PROMISC, vf->vf_states))
+               vlan_promisc = true;
+
        if (add_v) {
                for (i = 0; i < vfl->num_elements; i++) {
                        u16 vid = vfl->vlan_id[i];
 
-                       if (!ice_vsi_add_vlan(vsi, vid)) {
-                               vf->num_vlan++;
+                       if (ice_vsi_add_vlan(vsi, vid)) {
+                               aq_ret = ICE_ERR_PARAM;
+                               goto error_param;
+                       }
 
-                               /* Enable VLAN pruning when VLAN 0 is added */
-                               if (unlikely(!vid))
-                                       if (ice_cfg_vlan_pruning(vsi, true))
-                                               aq_ret = ICE_ERR_PARAM;
+                       vf->num_vlan++;
+                       /* Enable VLAN pruning when VLAN is added */
+                       if (!vlan_promisc) {
+                               status = ice_cfg_vlan_pruning(vsi, true, false);
+                               if (status) {
+                                       aq_ret = ICE_ERR_PARAM;
+                                       dev_err(&pf->pdev->dev,
+                                               "Enable VLAN pruning on VLAN ID: %d failed error-%d\n",
+                                               vid, status);
+                                       goto error_param;
+                               }
                        } else {
-                               aq_ret = ICE_ERR_PARAM;
+                               /* Enable Ucast/Mcast VLAN promiscuous mode */
+                               promisc_m = ICE_PROMISC_VLAN_TX |
+                                           ICE_PROMISC_VLAN_RX;
+
+                               status = ice_set_vsi_promisc(hw, vsi->idx,
+                                                            promisc_m, vid);
+                               if (status)
+                                       dev_err(&pf->pdev->dev,
+                                               "Enable Unicast/multicast promiscuous mode on VLAN ID:%d failed error-%d\n",
+                                               vid, status);
                        }
                }
        } else {
@@ -2263,12 +2350,22 @@ static int ice_vc_process_vlan_msg(struct ice_vf *vf, u8 *msg, bool add_v)
                        /* Make sure ice_vsi_kill_vlan is successful before
                         * updating VLAN information
                         */
-                       if (!ice_vsi_kill_vlan(vsi, vid)) {
-                               vf->num_vlan--;
+                       if (ice_vsi_kill_vlan(vsi, vid)) {
+                               aq_ret = ICE_ERR_PARAM;
+                               goto error_param;
+                       }
+
+                       vf->num_vlan--;
+                       /* Disable VLAN pruning when removing VLAN */
+                       ice_cfg_vlan_pruning(vsi, false, false);
+
+                       /* Disable Unicast/Multicast VLAN promiscuous mode */
+                       if (vlan_promisc) {
+                               promisc_m = ICE_PROMISC_VLAN_TX |
+                                           ICE_PROMISC_VLAN_RX;
 
-                               /* Disable VLAN pruning when removing VLAN 0 */
-                               if (unlikely(!vid))
-                                       ice_cfg_vlan_pruning(vsi, false);
+                               ice_clear_vsi_promisc(hw, vsi->idx,
+                                                     promisc_m, vid);
                        }
                }
        }