ice: Implement ice_bridge_getlink and ice_bridge_setlink
authorMd Fahad Iqbal Polash <md.fahad.iqbal.polash@intel.com>
Thu, 9 Aug 2018 13:29:54 +0000 (06:29 -0700)
committerJeff Kirsher <jeffrey.t.kirsher@intel.com>
Tue, 28 Aug 2018 18:01:06 +0000 (11:01 -0700)
ice_bridge_getlink returns the current bridge mode using
ndo_dflt_bridge_getlink and the mode parameter available in
first_switch->bridge_mode.

ice_bridge_setlink is invoked when the bridge mode needs to
changed. The value to be changed to is available as a netlink
message which is parsed in this function. If the mode has to
be changed, switch_flags is set appropriately (set ALLOW_LB
for VEB mode and clear it for VEPA mode) and ice_aq_update_vsi
is called. Also change the unicast switch filter rules.

Signed-off-by: Md Fahad Iqbal Polash <md.fahad.iqbal.polash@intel.com>
Signed-off-by: Anirudh Venkataramanan <anirudh.venkataramanan@intel.com>
Tested-by: Tony Brelinski <tonyx.brelinski@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
drivers/net/ethernet/intel/ice/ice_main.c
drivers/net/ethernet/intel/ice/ice_switch.c
drivers/net/ethernet/intel/ice/ice_switch.h

index fccecb6fa61865fa60425640fd45c86e00484ff1..cbeae1355593f18e6e2ab31b047dbe0e336413ea 100644 (file)
@@ -3599,7 +3599,11 @@ static int ice_probe(struct pci_dev *pdev,
                goto err_msix_misc_unroll;
        }
 
-       pf->first_sw->bridge_mode = BRIDGE_MODE_VEB;
+       if (hw->evb_veb)
+               pf->first_sw->bridge_mode = BRIDGE_MODE_VEB;
+       else
+               pf->first_sw->bridge_mode = BRIDGE_MODE_VEPA;
+
        pf->first_sw->pf = pf;
 
        /* record the sw_id available for later use */
@@ -5695,6 +5699,138 @@ int ice_get_rss(struct ice_vsi *vsi, u8 *seed, u8 *lut, u16 lut_size)
        return 0;
 }
 
+/**
+ * ice_bridge_getlink - Get the hardware bridge mode
+ * @skb: skb buff
+ * @pid: process id
+ * @seq: RTNL message seq
+ * @dev: the netdev being configured
+ * @filter_mask: filter mask passed in
+ * @nlflags: netlink flags passed in
+ *
+ * Return the bridge mode (VEB/VEPA)
+ */
+static int
+ice_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
+                  struct net_device *dev, u32 filter_mask, int nlflags)
+{
+       struct ice_netdev_priv *np = netdev_priv(dev);
+       struct ice_vsi *vsi = np->vsi;
+       struct ice_pf *pf = vsi->back;
+       u16 bmode;
+
+       bmode = pf->first_sw->bridge_mode;
+
+       return ndo_dflt_bridge_getlink(skb, pid, seq, dev, bmode, 0, 0, nlflags,
+                                      filter_mask, NULL);
+}
+
+/**
+ * ice_vsi_update_bridge_mode - Update VSI for switching bridge mode (VEB/VEPA)
+ * @vsi: Pointer to VSI structure
+ * @bmode: Hardware bridge mode (VEB/VEPA)
+ *
+ * Returns 0 on success, negative on failure
+ */
+static int ice_vsi_update_bridge_mode(struct ice_vsi *vsi, u16 bmode)
+{
+       struct device *dev = &vsi->back->pdev->dev;
+       struct ice_aqc_vsi_props *vsi_props;
+       struct ice_hw *hw = &vsi->back->hw;
+       struct ice_vsi_ctx ctxt = { 0 };
+       enum ice_status status;
+
+       vsi_props = &vsi->info;
+       ctxt.info = vsi->info;
+
+       if (bmode == BRIDGE_MODE_VEB)
+               /* change from VEPA to VEB mode */
+               ctxt.info.sw_flags |= ICE_AQ_VSI_SW_FLAG_ALLOW_LB;
+       else
+               /* change from VEB to VEPA mode */
+               ctxt.info.sw_flags &= ~ICE_AQ_VSI_SW_FLAG_ALLOW_LB;
+       ctxt.vsi_num = vsi->vsi_num;
+       ctxt.info.valid_sections = cpu_to_le16(ICE_AQ_VSI_PROP_SW_VALID);
+       status = ice_aq_update_vsi(hw, &ctxt, NULL);
+       if (status) {
+               dev_err(dev, "update VSI for bridge mode failed, bmode = %d err %d aq_err %d\n",
+                       bmode, status, hw->adminq.sq_last_status);
+               return -EIO;
+       }
+       /* Update sw flags for book keeping */
+       vsi_props->sw_flags = ctxt.info.sw_flags;
+
+       return 0;
+}
+
+/**
+ * ice_bridge_setlink - Set the hardware bridge mode
+ * @dev: the netdev being configured
+ * @nlh: RTNL message
+ * @flags: bridge setlink flags
+ *
+ * Sets the bridge mode (VEB/VEPA) of the switch to which the netdev (VSI) is
+ * hooked up to. Iterates through the PF VSI list and sets the loopback mode (if
+ * not already set for all VSIs connected to this switch. And also update the
+ * unicast switch filter rules for the corresponding switch of the netdev.
+ */
+static int
+ice_bridge_setlink(struct net_device *dev, struct nlmsghdr *nlh,
+                  u16 __always_unused flags)
+{
+       struct ice_netdev_priv *np = netdev_priv(dev);
+       struct ice_pf *pf = np->vsi->back;
+       struct nlattr *attr, *br_spec;
+       struct ice_hw *hw = &pf->hw;
+       enum ice_status status;
+       struct ice_sw *pf_sw;
+       int rem, v, err = 0;
+
+       pf_sw = pf->first_sw;
+       /* find the attribute in the netlink message */
+       br_spec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), IFLA_AF_SPEC);
+
+       nla_for_each_nested(attr, br_spec, rem) {
+               __u16 mode;
+
+               if (nla_type(attr) != IFLA_BRIDGE_MODE)
+                       continue;
+               mode = nla_get_u16(attr);
+               if (mode != BRIDGE_MODE_VEPA && mode != BRIDGE_MODE_VEB)
+                       return -EINVAL;
+               /* Continue  if bridge mode is not being flipped */
+               if (mode == pf_sw->bridge_mode)
+                       continue;
+               /* Iterates through the PF VSI list and update the loopback
+                * mode of the VSI
+                */
+               ice_for_each_vsi(pf, v) {
+                       if (!pf->vsi[v])
+                               continue;
+                       err = ice_vsi_update_bridge_mode(pf->vsi[v], mode);
+                       if (err)
+                               return err;
+               }
+
+               hw->evb_veb = (mode == BRIDGE_MODE_VEB);
+               /* Update the unicast switch filter rules for the corresponding
+                * switch of the netdev
+                */
+               status = ice_update_sw_rule_bridge_mode(hw);
+               if (status) {
+                       netdev_err(dev, "update SW_RULE for bridge mode failed,  = %d err %d aq_err %d\n",
+                                  mode, status, hw->adminq.sq_last_status);
+                       /* revert hw->evb_veb */
+                       hw->evb_veb = (pf_sw->bridge_mode == BRIDGE_MODE_VEB);
+                       return -EIO;
+               }
+
+               pf_sw->bridge_mode = mode;
+       }
+
+       return 0;
+}
+
 /**
  * ice_tx_timeout - Respond to a Tx Hang
  * @netdev: network interface device structure
@@ -5907,6 +6043,8 @@ static const struct net_device_ops ice_netdev_ops = {
        .ndo_vlan_rx_add_vid = ice_vlan_rx_add_vid,
        .ndo_vlan_rx_kill_vid = ice_vlan_rx_kill_vid,
        .ndo_set_features = ice_set_features,
+       .ndo_bridge_getlink = ice_bridge_getlink,
+       .ndo_bridge_setlink = ice_bridge_setlink,
        .ndo_fdb_add = ice_fdb_add,
        .ndo_fdb_del = ice_fdb_del,
        .ndo_tx_timeout = ice_tx_timeout,
index ac66c96a123dfcb148d85cf8833f1f5511b10fc2..65b4e1cca6bef412d15f1d1cf6985ce776c5aae6 100644 (file)
@@ -1130,6 +1130,47 @@ ice_update_pkt_fwd_rule(struct ice_hw *hw, struct ice_fltr_info *f_info)
        return status;
 }
 
+/**
+ * ice_update_sw_rule_bridge_mode
+ * @hw: pointer to the hw struct
+ *
+ * Updates unicast switch filter rules based on VEB/VEPA mode
+ */
+enum ice_status ice_update_sw_rule_bridge_mode(struct ice_hw *hw)
+{
+       struct ice_switch_info *sw = hw->switch_info;
+       struct ice_fltr_mgmt_list_entry *fm_entry;
+       enum ice_status status = 0;
+       struct list_head *rule_head;
+       struct mutex *rule_lock; /* Lock to protect filter rule list */
+
+       rule_lock = &sw->recp_list[ICE_SW_LKUP_MAC].filt_rule_lock;
+       rule_head = &sw->recp_list[ICE_SW_LKUP_MAC].filt_rules;
+
+       mutex_lock(rule_lock);
+       list_for_each_entry(fm_entry, rule_head, list_entry) {
+               struct ice_fltr_info *fi = &fm_entry->fltr_info;
+               u8 *addr = fi->l_data.mac.mac_addr;
+
+               /* Update unicast Tx rules to reflect the selected
+                * VEB/VEPA mode
+                */
+               if ((fi->flag & ICE_FLTR_TX) && is_unicast_ether_addr(addr) &&
+                   (fi->fltr_act == ICE_FWD_TO_VSI ||
+                    fi->fltr_act == ICE_FWD_TO_VSI_LIST ||
+                    fi->fltr_act == ICE_FWD_TO_Q ||
+                    fi->fltr_act == ICE_FWD_TO_QGRP)) {
+                       status = ice_update_pkt_fwd_rule(hw, fi);
+                       if (status)
+                               break;
+               }
+       }
+
+       mutex_unlock(rule_lock);
+
+       return status;
+}
+
 /**
  * ice_add_update_vsi_list
  * @hw: pointer to the hardware structure
index 11a481644dac863a047fb95af73922e4c56b8732..646389ca1238e85453b404fe5dbf96d29f8c898c 100644 (file)
@@ -169,6 +169,7 @@ ice_free_vsi(struct ice_hw *hw, u16 vsi_handle, struct ice_vsi_ctx *vsi_ctx,
 enum ice_status ice_get_initial_sw_cfg(struct ice_hw *hw);
 
 /* Switch/bridge related commands */
+enum ice_status ice_update_sw_rule_bridge_mode(struct ice_hw *hw);
 enum ice_status ice_add_mac(struct ice_hw *hw, struct list_head *m_lst);
 enum ice_status ice_remove_mac(struct ice_hw *hw, struct list_head *m_lst);
 void ice_remove_vsi_fltr(struct ice_hw *hw, u16 vsi_id);