qed* : use trust mode to allow VF to override forced MAC

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>
This commit is contained in:
Shahed Shaikh 2018-04-19 05:50:11 -07:00 committed by David S. Miller
parent 2ed021255b
commit 7425d8220f
2 changed files with 195 additions and 18 deletions

View 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 @@ out:
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)

View 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;
}