@@ -8208,8 +8208,16 @@ static int ice_vsi_update_bridge_mode(struct ice_vsi *vsi, u16 bmode)
*
* 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
+ * 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.
+ *
+ * Return:
+ * * %0 if mode was set, propagated to VSIs, and changes to filters were all
+ * successful,
+ * * %-EINVAL if requested netlink attributes or bridge mode were invalid,
+ * * otherwise an error from VSI update, filter rollback, or filter update is
+ * forwarded. This may include %-EINVAL. See ice_vsi_update_bridge_mode() and
+ * ice_update_sw_rule_bridge_mode().
*/
static int
ice_bridge_setlink(struct net_device *dev, struct nlmsghdr *nlh,
@@ -8219,8 +8227,8 @@ ice_bridge_setlink(struct net_device *dev, struct nlmsghdr *nlh,
struct ice_pf *pf = ice_netdev_to_pf(dev);
struct nlattr *attr, *br_spec;
struct ice_hw *hw = &pf->hw;
+ int rem, v, rb_err, err = 0;
struct ice_sw *pf_sw;
- int rem, v, err = 0;
pf_sw = pf->first_sw;
/* find the attribute in the netlink message */
@@ -8230,6 +8238,7 @@ ice_bridge_setlink(struct net_device *dev, struct nlmsghdr *nlh,
nla_for_each_nested_type(attr, IFLA_BRIDGE_MODE, br_spec, rem) {
__u16 mode = nla_get_u16(attr);
+ u8 old_evb_veb = hw->evb_veb;
if (mode != BRIDGE_MODE_VEPA && mode != BRIDGE_MODE_VEB)
return -EINVAL;
@@ -8251,17 +8260,38 @@ ice_bridge_setlink(struct net_device *dev, struct nlmsghdr *nlh,
/* Update the unicast switch filter rules for the corresponding
* switch of the netdev
*/
- err = ice_update_sw_rule_bridge_mode(hw);
+ err = ice_update_sw_rule_bridge_mode(hw, ICE_SW_LKUP_MAC);
+ if (err) {
+ /* evb_veb is expected to be already reverted in error
+ * path because of the potential rollback.
+ */
+ hw->evb_veb = old_evb_veb;
+ goto err_without_rollback;
+ }
+ err = ice_update_sw_rule_bridge_mode(hw, ICE_SW_LKUP_MAC_VLAN);
if (err) {
- netdev_err(dev, "switch rule update failed, mode = %d err %d aq_err %s\n",
- mode, err,
+ /* ice_update_sw_rule_bridge_mode looks this up, so we
+ * must revert it before attempting a rollback.
+ */
+ hw->evb_veb = old_evb_veb;
+ goto err_rollback_mac;
+ }
+ pf_sw->bridge_mode = mode;
+ continue;
+
+err_rollback_mac:
+ rb_err = ice_update_sw_rule_bridge_mode(hw, ICE_SW_LKUP_MAC);
+ if (rb_err) {
+ netdev_err(dev, "switch rule update failed, mode = %d err %d; rollback failed, err %d aq_err %s\n",
+ mode, err, rb_err,
libie_aq_str(hw->adminq.sq_last_status));
- /* revert hw->evb_veb */
- hw->evb_veb = (pf_sw->bridge_mode == BRIDGE_MODE_VEB);
- return err;
+ return rb_err;
}
- pf_sw->bridge_mode = mode;
+err_without_rollback:
+ netdev_err(dev, "switch rule update failed, mode = %d err %d aq_err %s\n",
+ mode, err, libie_aq_str(hw->adminq.sq_last_status));
+ return err;
}
return 0;
@@ -3067,10 +3067,12 @@ ice_update_pkt_fwd_rule(struct ice_hw *hw, struct ice_fltr_info *f_info)
/**
* ice_update_sw_rule_bridge_mode
* @hw: pointer to the HW struct
+ * @lkup: recipe/lookup type to update
*
* Updates unicast switch filter rules based on VEB/VEPA mode
*/
-int ice_update_sw_rule_bridge_mode(struct ice_hw *hw)
+int ice_update_sw_rule_bridge_mode(struct ice_hw *hw,
+ enum ice_sw_lkup_type lkup)
{
struct ice_switch_info *sw = hw->switch_info;
struct ice_fltr_mgmt_list_entry *fm_entry;
@@ -3078,8 +3080,8 @@ int ice_update_sw_rule_bridge_mode(struct ice_hw *hw)
struct mutex *rule_lock; /* Lock to protect filter rule list */
int status = 0;
- rule_lock = &sw->recp_list[ICE_SW_LKUP_MAC].filt_rule_lock;
- rule_head = &sw->recp_list[ICE_SW_LKUP_MAC].filt_rules;
+ rule_lock = &sw->recp_list[lkup].filt_rule_lock;
+ rule_head = &sw->recp_list[lkup].filt_rules;
mutex_lock(rule_lock);
list_for_each_entry(fm_entry, rule_head, list_entry) {
@@ -366,7 +366,8 @@ int
ice_add_adv_rule(struct ice_hw *hw, struct ice_adv_lkup_elem *lkups,
u16 lkups_cnt, struct ice_adv_rule_info *rinfo,
struct ice_rule_query_data *added_entry);
-int ice_update_sw_rule_bridge_mode(struct ice_hw *hw);
+int ice_update_sw_rule_bridge_mode(struct ice_hw *hw,
+ enum ice_sw_lkup_type lkup);
int ice_add_vlan(struct ice_hw *hw, struct list_head *m_list);
int ice_remove_vlan(struct ice_hw *hw, struct list_head *v_list);
int ice_add_mac(struct ice_hw *hw, struct list_head *m_lst);