]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
qed* : use trust mode to allow VF to override forced MAC
authorShahed Shaikh <shahed.shaikh@cavium.com>
Thu, 19 Apr 2018 12:50:11 +0000 (05:50 -0700)
committerDavid S. Miller <davem@davemloft.net>
Fri, 20 Apr 2018 15:26:36 +0000 (11:26 -0400)
As per existing behavior, when PF sets a MAC address for a VF
(also called as forced MAC), VF is not allowed to change its
MAC address afterwards.
This puts the limitation on few use cases such as bonding of VFs,
where bonding driver asks VF to change its MAC address.

This patch uses a VF trust mode to allow VF to change its MAC address
in spite PF has set a forced MAC for that VF.

Signed-off-by: Shahed Shaikh <shahed.shaikh@cavium.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/qlogic/qed/qed_sriov.c
drivers/net/ethernet/qlogic/qede/qede_filter.c

index 5acb91b3564c43b96ae4d5c40e7d2a0e412b1118..77376fda8eaea56ae5fd267c5a2625cee37ca7a9 100644 (file)
@@ -48,7 +48,7 @@ static int qed_sriov_eqe_event(struct qed_hwfn *p_hwfn,
                               u8 opcode,
                               __le16 echo,
                               union event_ring_data *data, u8 fw_return_code);
-
+static int qed_iov_bulletin_set_mac(struct qed_hwfn *p_hwfn, u8 *mac, int vfid);
 
 static u8 qed_vf_calculate_legacy(struct qed_vf_info *p_vf)
 {
@@ -1790,7 +1790,8 @@ static int qed_iov_configure_vport_forced(struct qed_hwfn *p_hwfn,
        if (!p_vf->vport_instance)
                return -EINVAL;
 
-       if (events & BIT(MAC_ADDR_FORCED)) {
+       if ((events & BIT(MAC_ADDR_FORCED)) ||
+           p_vf->p_vf_info.is_trusted_configured) {
                /* Since there's no way [currently] of removing the MAC,
                 * we can always assume this means we need to force it.
                 */
@@ -1809,8 +1810,12 @@ static int qed_iov_configure_vport_forced(struct qed_hwfn *p_hwfn,
                                  "PF failed to configure MAC for VF\n");
                        return rc;
                }
-
-               p_vf->configured_features |= 1 << MAC_ADDR_FORCED;
+               if (p_vf->p_vf_info.is_trusted_configured)
+                       p_vf->configured_features |=
+                               BIT(VFPF_BULLETIN_MAC_ADDR);
+               else
+                       p_vf->configured_features |=
+                               BIT(MAC_ADDR_FORCED);
        }
 
        if (events & BIT(VLAN_ADDR_FORCED)) {
@@ -3170,6 +3175,10 @@ static int qed_iov_vf_update_mac_shadow(struct qed_hwfn *p_hwfn,
        if (p_vf->bulletin.p_virt->valid_bitmap & BIT(MAC_ADDR_FORCED))
                return 0;
 
+       /* Don't keep track of shadow copy since we don't intend to restore. */
+       if (p_vf->p_vf_info.is_trusted_configured)
+               return 0;
+
        /* First remove entries and then add new ones */
        if (p_params->opcode == QED_FILTER_REMOVE) {
                for (i = 0; i < QED_ETH_VF_NUM_MAC_FILTERS; i++) {
@@ -3244,9 +3253,17 @@ static int qed_iov_chk_ucast(struct qed_hwfn *hwfn,
 
        /* No real decision to make; Store the configured MAC */
        if (params->type == QED_FILTER_MAC ||
-           params->type == QED_FILTER_MAC_VLAN)
+           params->type == QED_FILTER_MAC_VLAN) {
                ether_addr_copy(vf->mac, params->mac);
 
+               if (vf->is_trusted_configured) {
+                       qed_iov_bulletin_set_mac(hwfn, vf->mac, vfid);
+
+                       /* Update and post bulleitin again */
+                       qed_schedule_iov(hwfn, QED_IOV_WQ_BULLETIN_UPDATE_FLAG);
+               }
+       }
+
        return 0;
 }
 
@@ -4081,16 +4098,60 @@ static void qed_iov_bulletin_set_forced_mac(struct qed_hwfn *p_hwfn,
                return;
        }
 
-       feature = 1 << MAC_ADDR_FORCED;
+       if (vf_info->p_vf_info.is_trusted_configured) {
+               feature = BIT(VFPF_BULLETIN_MAC_ADDR);
+               /* Trust mode will disable Forced MAC */
+               vf_info->bulletin.p_virt->valid_bitmap &=
+                       ~BIT(MAC_ADDR_FORCED);
+       } else {
+               feature = BIT(MAC_ADDR_FORCED);
+               /* Forced MAC will disable MAC_ADDR */
+               vf_info->bulletin.p_virt->valid_bitmap &=
+                       ~BIT(VFPF_BULLETIN_MAC_ADDR);
+       }
+
        memcpy(vf_info->bulletin.p_virt->mac, mac, ETH_ALEN);
 
        vf_info->bulletin.p_virt->valid_bitmap |= feature;
-       /* Forced MAC will disable MAC_ADDR */
-       vf_info->bulletin.p_virt->valid_bitmap &= ~BIT(VFPF_BULLETIN_MAC_ADDR);
 
        qed_iov_configure_vport_forced(p_hwfn, vf_info, feature);
 }
 
+static int qed_iov_bulletin_set_mac(struct qed_hwfn *p_hwfn, u8 *mac, int vfid)
+{
+       struct qed_vf_info *vf_info;
+       u64 feature;
+
+       vf_info = qed_iov_get_vf_info(p_hwfn, (u16)vfid, true);
+       if (!vf_info) {
+               DP_NOTICE(p_hwfn->cdev, "Can not set MAC, invalid vfid [%d]\n",
+                         vfid);
+               return -EINVAL;
+       }
+
+       if (vf_info->b_malicious) {
+               DP_NOTICE(p_hwfn->cdev, "Can't set MAC to malicious VF [%d]\n",
+                         vfid);
+               return -EINVAL;
+       }
+
+       if (vf_info->bulletin.p_virt->valid_bitmap & BIT(MAC_ADDR_FORCED)) {
+               DP_VERBOSE(p_hwfn, QED_MSG_IOV,
+                          "Can not set MAC, Forced MAC is configured\n");
+               return -EINVAL;
+       }
+
+       feature = BIT(VFPF_BULLETIN_MAC_ADDR);
+       ether_addr_copy(vf_info->bulletin.p_virt->mac, mac);
+
+       vf_info->bulletin.p_virt->valid_bitmap |= feature;
+
+       if (vf_info->p_vf_info.is_trusted_configured)
+               qed_iov_configure_vport_forced(p_hwfn, vf_info, feature);
+
+       return 0;
+}
+
 static void qed_iov_bulletin_set_forced_vlan(struct qed_hwfn *p_hwfn,
                                             u16 pvid, int vfid)
 {
@@ -4204,6 +4265,21 @@ static int qed_iov_spoofchk_set(struct qed_hwfn *p_hwfn, int vfid, bool val)
        return rc;
 }
 
+static u8 *qed_iov_bulletin_get_mac(struct qed_hwfn *p_hwfn, u16 rel_vf_id)
+{
+       struct qed_vf_info *p_vf;
+
+       p_vf = qed_iov_get_vf_info(p_hwfn, rel_vf_id, true);
+       if (!p_vf || !p_vf->bulletin.p_virt)
+               return NULL;
+
+       if (!(p_vf->bulletin.p_virt->valid_bitmap &
+             BIT(VFPF_BULLETIN_MAC_ADDR)))
+               return NULL;
+
+       return p_vf->bulletin.p_virt->mac;
+}
+
 static u8 *qed_iov_bulletin_get_forced_mac(struct qed_hwfn *p_hwfn,
                                           u16 rel_vf_id)
 {
@@ -4493,8 +4569,12 @@ static int qed_sriov_pf_set_mac(struct qed_dev *cdev, u8 *mac, int vfid)
                if (!vf_info)
                        continue;
 
-               /* Set the forced MAC, and schedule the IOV task */
-               ether_addr_copy(vf_info->forced_mac, mac);
+               /* Set the MAC, and schedule the IOV task */
+               if (vf_info->is_trusted_configured)
+                       ether_addr_copy(vf_info->mac, mac);
+               else
+                       ether_addr_copy(vf_info->forced_mac, mac);
+
                qed_schedule_iov(hwfn, QED_IOV_WQ_SET_UNICAST_FILTER_FLAG);
        }
 
@@ -4802,6 +4882,33 @@ static void qed_handle_vf_msg(struct qed_hwfn *hwfn)
        qed_ptt_release(hwfn, ptt);
 }
 
+static bool qed_pf_validate_req_vf_mac(struct qed_hwfn *hwfn,
+                                      u8 *mac,
+                                      struct qed_public_vf_info *info)
+{
+       if (info->is_trusted_configured) {
+               if (is_valid_ether_addr(info->mac) &&
+                   (!mac || !ether_addr_equal(mac, info->mac)))
+                       return true;
+       } else {
+               if (is_valid_ether_addr(info->forced_mac) &&
+                   (!mac || !ether_addr_equal(mac, info->forced_mac)))
+                       return true;
+       }
+
+       return false;
+}
+
+static void qed_set_bulletin_mac(struct qed_hwfn *hwfn,
+                                struct qed_public_vf_info *info,
+                                int vfid)
+{
+       if (info->is_trusted_configured)
+               qed_iov_bulletin_set_mac(hwfn, info->mac, vfid);
+       else
+               qed_iov_bulletin_set_forced_mac(hwfn, info->forced_mac, vfid);
+}
+
 static void qed_handle_pf_set_vf_unicast(struct qed_hwfn *hwfn)
 {
        int i;
@@ -4816,18 +4923,20 @@ static void qed_handle_pf_set_vf_unicast(struct qed_hwfn *hwfn)
                        continue;
 
                /* Update data on bulletin board */
-               mac = qed_iov_bulletin_get_forced_mac(hwfn, i);
-               if (is_valid_ether_addr(info->forced_mac) &&
-                   (!mac || !ether_addr_equal(mac, info->forced_mac))) {
+               if (info->is_trusted_configured)
+                       mac = qed_iov_bulletin_get_mac(hwfn, i);
+               else
+                       mac = qed_iov_bulletin_get_forced_mac(hwfn, i);
+
+               if (qed_pf_validate_req_vf_mac(hwfn, mac, info)) {
                        DP_VERBOSE(hwfn,
                                   QED_MSG_IOV,
                                   "Handling PF setting of VF MAC to VF 0x%02x [Abs 0x%02x]\n",
                                   i,
                                   hwfn->cdev->p_iov_info->first_vf_in_pf + i);
 
-                       /* Update bulletin board with forced MAC */
-                       qed_iov_bulletin_set_forced_mac(hwfn,
-                                                       info->forced_mac, i);
+                       /* Update bulletin board with MAC */
+                       qed_set_bulletin_mac(hwfn, info, i);
                        update = true;
                }
 
@@ -4867,6 +4976,72 @@ static void qed_handle_bulletin_post(struct qed_hwfn *hwfn)
        qed_ptt_release(hwfn, ptt);
 }
 
+static void qed_update_mac_for_vf_trust_change(struct qed_hwfn *hwfn, int vf_id)
+{
+       struct qed_public_vf_info *vf_info;
+       struct qed_vf_info *vf;
+       u8 *force_mac;
+       int i;
+
+       vf_info = qed_iov_get_public_vf_info(hwfn, vf_id, true);
+       vf = qed_iov_get_vf_info(hwfn, vf_id, true);
+
+       if (!vf_info || !vf)
+               return;
+
+       /* Force MAC converted to generic MAC in case of VF trust on */
+       if (vf_info->is_trusted_configured &&
+           (vf->bulletin.p_virt->valid_bitmap & BIT(MAC_ADDR_FORCED))) {
+               force_mac = qed_iov_bulletin_get_forced_mac(hwfn, vf_id);
+
+               if (force_mac) {
+                       /* Clear existing shadow copy of MAC to have a clean
+                        * slate.
+                        */
+                       for (i = 0; i < QED_ETH_VF_NUM_MAC_FILTERS; i++) {
+                               if (ether_addr_equal(vf->shadow_config.macs[i],
+                                                    vf_info->mac)) {
+                                       memset(vf->shadow_config.macs[i], 0,
+                                              ETH_ALEN);
+                                       DP_VERBOSE(hwfn, QED_MSG_IOV,
+                                                  "Shadow MAC %pM removed for VF 0x%02x, VF trust mode is ON\n",
+                                                   vf_info->mac, vf_id);
+                                       break;
+                               }
+                       }
+
+                       ether_addr_copy(vf_info->mac, force_mac);
+                       memset(vf_info->forced_mac, 0, ETH_ALEN);
+                       vf->bulletin.p_virt->valid_bitmap &=
+                                       ~BIT(MAC_ADDR_FORCED);
+                       qed_schedule_iov(hwfn, QED_IOV_WQ_BULLETIN_UPDATE_FLAG);
+               }
+       }
+
+       /* Update shadow copy with VF MAC when trust mode is turned off */
+       if (!vf_info->is_trusted_configured) {
+               u8 empty_mac[ETH_ALEN];
+
+               memset(empty_mac, 0, ETH_ALEN);
+               for (i = 0; i < QED_ETH_VF_NUM_MAC_FILTERS; i++) {
+                       if (ether_addr_equal(vf->shadow_config.macs[i],
+                                            empty_mac)) {
+                               ether_addr_copy(vf->shadow_config.macs[i],
+                                               vf_info->mac);
+                               DP_VERBOSE(hwfn, QED_MSG_IOV,
+                                          "Shadow is updated with %pM for VF 0x%02x, VF trust mode is OFF\n",
+                                           vf_info->mac, vf_id);
+                               break;
+                       }
+               }
+               /* Clear bulletin when trust mode is turned off,
+                * to have a clean slate for next (normal) operations.
+                */
+               qed_iov_bulletin_set_mac(hwfn, empty_mac, vf_id);
+               qed_schedule_iov(hwfn, QED_IOV_WQ_BULLETIN_UPDATE_FLAG);
+       }
+}
+
 static void qed_iov_handle_trust_change(struct qed_hwfn *hwfn)
 {
        struct qed_sp_vport_update_params params;
@@ -4890,6 +5065,9 @@ static void qed_iov_handle_trust_change(struct qed_hwfn *hwfn)
                        continue;
                vf_info->is_trusted_configured = vf_info->is_trusted_request;
 
+               /* Handle forced MAC mode */
+               qed_update_mac_for_vf_trust_change(hwfn, i);
+
                /* Validate that the VF has a configured vport */
                vf = qed_iov_get_vf_info(hwfn, i, true);
                if (!vf->vport_instance)
index 6687e04d15583b225e161a93aaab07bb70674b93..8094f035b705fabae424ded237057df66798e403 100644 (file)
@@ -550,8 +550,7 @@ void qede_force_mac(void *dev, u8 *mac, bool forced)
 
        __qede_lock(edev);
 
-       /* MAC hints take effect only if we haven't set one already */
-       if (is_valid_ether_addr(edev->ndev->dev_addr) && !forced) {
+       if (!is_valid_ether_addr(mac)) {
                __qede_unlock(edev);
                return;
        }