Commit f5a7b21b authored by Jan Sokolowski's avatar Jan Sokolowski Committed by Jeff Kirsher

i40e: Protect access to VF control methods

A scenario has been found in which simultaneous
addition/removal and modification of VF's might cause
unstable behaviour, up to and including kernel panics.

Protect the methods that create/modify/destroy VF's
by locking them behind an atomically set bit in PF status
bitfield.
Signed-off-by: default avatarJan Sokolowski <jan.sokolowski@intel.com>
Tested-by: default avatarAndrew Bowers <andrewx.bowers@intel.com>
Signed-off-by: default avatarJeff Kirsher <jeffrey.t.kirsher@intel.com>
parent 4ff2d854
...@@ -146,6 +146,7 @@ enum i40e_state_t { ...@@ -146,6 +146,7 @@ enum i40e_state_t {
__I40E_CLIENT_SERVICE_REQUESTED, __I40E_CLIENT_SERVICE_REQUESTED,
__I40E_CLIENT_L2_CHANGE, __I40E_CLIENT_L2_CHANGE,
__I40E_CLIENT_RESET, __I40E_CLIENT_RESET,
__I40E_VIRTCHNL_OP_PENDING,
/* This must be last as it determines the size of the BITMAP */ /* This must be last as it determines the size of the BITMAP */
__I40E_STATE_SIZE__, __I40E_STATE_SIZE__,
}; };
......
...@@ -1675,13 +1675,20 @@ static int i40e_pci_sriov_enable(struct pci_dev *pdev, int num_vfs) ...@@ -1675,13 +1675,20 @@ static int i40e_pci_sriov_enable(struct pci_dev *pdev, int num_vfs)
int i40e_pci_sriov_configure(struct pci_dev *pdev, int num_vfs) int i40e_pci_sriov_configure(struct pci_dev *pdev, int num_vfs)
{ {
struct i40e_pf *pf = pci_get_drvdata(pdev); struct i40e_pf *pf = pci_get_drvdata(pdev);
int ret = 0;
if (test_and_set_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state)) {
dev_warn(&pdev->dev, "Unable to configure VFs, other operation is pending.\n");
return -EAGAIN;
}
if (num_vfs) { if (num_vfs) {
if (!(pf->flags & I40E_FLAG_VEB_MODE_ENABLED)) { if (!(pf->flags & I40E_FLAG_VEB_MODE_ENABLED)) {
pf->flags |= I40E_FLAG_VEB_MODE_ENABLED; pf->flags |= I40E_FLAG_VEB_MODE_ENABLED;
i40e_do_reset_safe(pf, I40E_PF_RESET_FLAG); i40e_do_reset_safe(pf, I40E_PF_RESET_FLAG);
} }
return i40e_pci_sriov_enable(pdev, num_vfs); ret = i40e_pci_sriov_enable(pdev, num_vfs);
goto sriov_configure_out;
} }
if (!pci_vfs_assigned(pf->pdev)) { if (!pci_vfs_assigned(pf->pdev)) {
...@@ -1690,9 +1697,12 @@ int i40e_pci_sriov_configure(struct pci_dev *pdev, int num_vfs) ...@@ -1690,9 +1697,12 @@ int i40e_pci_sriov_configure(struct pci_dev *pdev, int num_vfs)
i40e_do_reset_safe(pf, I40E_PF_RESET_FLAG); i40e_do_reset_safe(pf, I40E_PF_RESET_FLAG);
} else { } else {
dev_warn(&pdev->dev, "Unable to free VFs because some are assigned to VMs.\n"); dev_warn(&pdev->dev, "Unable to free VFs because some are assigned to VMs.\n");
return -EINVAL; ret = -EINVAL;
goto sriov_configure_out;
} }
return 0; sriov_configure_out:
clear_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state);
return ret;
} }
/***********************virtual channel routines******************/ /***********************virtual channel routines******************/
...@@ -3893,6 +3903,11 @@ int i40e_ndo_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac) ...@@ -3893,6 +3903,11 @@ int i40e_ndo_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac)
goto error_param; goto error_param;
} }
if (test_and_set_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state)) {
dev_warn(&pf->pdev->dev, "Unable to configure VFs, other operation is pending.\n");
return -EAGAIN;
}
if (is_multicast_ether_addr(mac)) { if (is_multicast_ether_addr(mac)) {
dev_err(&pf->pdev->dev, dev_err(&pf->pdev->dev,
"Invalid Ethernet address %pM for VF %d\n", mac, vf_id); "Invalid Ethernet address %pM for VF %d\n", mac, vf_id);
...@@ -3941,6 +3956,7 @@ int i40e_ndo_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac) ...@@ -3941,6 +3956,7 @@ int i40e_ndo_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac)
dev_info(&pf->pdev->dev, "Bring down and up the VF interface to make this change effective.\n"); dev_info(&pf->pdev->dev, "Bring down and up the VF interface to make this change effective.\n");
error_param: error_param:
clear_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state);
return ret; return ret;
} }
...@@ -3992,6 +4008,11 @@ int i40e_ndo_set_vf_port_vlan(struct net_device *netdev, int vf_id, ...@@ -3992,6 +4008,11 @@ int i40e_ndo_set_vf_port_vlan(struct net_device *netdev, int vf_id,
struct i40e_vf *vf; struct i40e_vf *vf;
int ret = 0; int ret = 0;
if (test_and_set_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state)) {
dev_warn(&pf->pdev->dev, "Unable to configure VFs, other operation is pending.\n");
return -EAGAIN;
}
/* validate the request */ /* validate the request */
ret = i40e_validate_vf(pf, vf_id); ret = i40e_validate_vf(pf, vf_id);
if (ret) if (ret)
...@@ -4107,6 +4128,7 @@ int i40e_ndo_set_vf_port_vlan(struct net_device *netdev, int vf_id, ...@@ -4107,6 +4128,7 @@ int i40e_ndo_set_vf_port_vlan(struct net_device *netdev, int vf_id,
ret = 0; ret = 0;
error_pvid: error_pvid:
clear_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state);
return ret; return ret;
} }
...@@ -4128,6 +4150,11 @@ int i40e_ndo_set_vf_bw(struct net_device *netdev, int vf_id, int min_tx_rate, ...@@ -4128,6 +4150,11 @@ int i40e_ndo_set_vf_bw(struct net_device *netdev, int vf_id, int min_tx_rate,
struct i40e_vf *vf; struct i40e_vf *vf;
int ret = 0; int ret = 0;
if (test_and_set_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state)) {
dev_warn(&pf->pdev->dev, "Unable to configure VFs, other operation is pending.\n");
return -EAGAIN;
}
/* validate the request */ /* validate the request */
ret = i40e_validate_vf(pf, vf_id); ret = i40e_validate_vf(pf, vf_id);
if (ret) if (ret)
...@@ -4154,6 +4181,7 @@ int i40e_ndo_set_vf_bw(struct net_device *netdev, int vf_id, int min_tx_rate, ...@@ -4154,6 +4181,7 @@ int i40e_ndo_set_vf_bw(struct net_device *netdev, int vf_id, int min_tx_rate,
vf->tx_rate = max_tx_rate; vf->tx_rate = max_tx_rate;
error: error:
clear_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state);
return ret; return ret;
} }
...@@ -4174,6 +4202,11 @@ int i40e_ndo_get_vf_config(struct net_device *netdev, ...@@ -4174,6 +4202,11 @@ int i40e_ndo_get_vf_config(struct net_device *netdev,
struct i40e_vf *vf; struct i40e_vf *vf;
int ret = 0; int ret = 0;
if (test_and_set_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state)) {
dev_warn(&pf->pdev->dev, "Unable to configure VFs, other operation is pending.\n");
return -EAGAIN;
}
/* validate the request */ /* validate the request */
ret = i40e_validate_vf(pf, vf_id); ret = i40e_validate_vf(pf, vf_id);
if (ret) if (ret)
...@@ -4209,6 +4242,7 @@ int i40e_ndo_get_vf_config(struct net_device *netdev, ...@@ -4209,6 +4242,7 @@ int i40e_ndo_get_vf_config(struct net_device *netdev,
ret = 0; ret = 0;
error_param: error_param:
clear_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state);
return ret; return ret;
} }
...@@ -4230,6 +4264,11 @@ int i40e_ndo_set_vf_link_state(struct net_device *netdev, int vf_id, int link) ...@@ -4230,6 +4264,11 @@ int i40e_ndo_set_vf_link_state(struct net_device *netdev, int vf_id, int link)
int abs_vf_id; int abs_vf_id;
int ret = 0; int ret = 0;
if (test_and_set_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state)) {
dev_warn(&pf->pdev->dev, "Unable to configure VFs, other operation is pending.\n");
return -EAGAIN;
}
/* validate the request */ /* validate the request */
if (vf_id >= pf->num_alloc_vfs) { if (vf_id >= pf->num_alloc_vfs) {
dev_err(&pf->pdev->dev, "Invalid VF Identifier %d\n", vf_id); dev_err(&pf->pdev->dev, "Invalid VF Identifier %d\n", vf_id);
...@@ -4273,6 +4312,7 @@ int i40e_ndo_set_vf_link_state(struct net_device *netdev, int vf_id, int link) ...@@ -4273,6 +4312,7 @@ int i40e_ndo_set_vf_link_state(struct net_device *netdev, int vf_id, int link)
0, (u8 *)&pfe, sizeof(pfe), NULL); 0, (u8 *)&pfe, sizeof(pfe), NULL);
error_out: error_out:
clear_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state);
return ret; return ret;
} }
...@@ -4294,6 +4334,11 @@ int i40e_ndo_set_vf_spoofchk(struct net_device *netdev, int vf_id, bool enable) ...@@ -4294,6 +4334,11 @@ int i40e_ndo_set_vf_spoofchk(struct net_device *netdev, int vf_id, bool enable)
struct i40e_vf *vf; struct i40e_vf *vf;
int ret = 0; int ret = 0;
if (test_and_set_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state)) {
dev_warn(&pf->pdev->dev, "Unable to configure VFs, other operation is pending.\n");
return -EAGAIN;
}
/* validate the request */ /* validate the request */
if (vf_id >= pf->num_alloc_vfs) { if (vf_id >= pf->num_alloc_vfs) {
dev_err(&pf->pdev->dev, "Invalid VF Identifier %d\n", vf_id); dev_err(&pf->pdev->dev, "Invalid VF Identifier %d\n", vf_id);
...@@ -4327,6 +4372,7 @@ int i40e_ndo_set_vf_spoofchk(struct net_device *netdev, int vf_id, bool enable) ...@@ -4327,6 +4372,7 @@ int i40e_ndo_set_vf_spoofchk(struct net_device *netdev, int vf_id, bool enable)
ret = -EIO; ret = -EIO;
} }
out: out:
clear_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state);
return ret; return ret;
} }
...@@ -4345,15 +4391,22 @@ int i40e_ndo_set_vf_trust(struct net_device *netdev, int vf_id, bool setting) ...@@ -4345,15 +4391,22 @@ int i40e_ndo_set_vf_trust(struct net_device *netdev, int vf_id, bool setting)
struct i40e_vf *vf; struct i40e_vf *vf;
int ret = 0; int ret = 0;
if (test_and_set_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state)) {
dev_warn(&pf->pdev->dev, "Unable to configure VFs, other operation is pending.\n");
return -EAGAIN;
}
/* validate the request */ /* validate the request */
if (vf_id >= pf->num_alloc_vfs) { if (vf_id >= pf->num_alloc_vfs) {
dev_err(&pf->pdev->dev, "Invalid VF Identifier %d\n", vf_id); dev_err(&pf->pdev->dev, "Invalid VF Identifier %d\n", vf_id);
return -EINVAL; ret = -EINVAL;
goto out;
} }
if (pf->flags & I40E_FLAG_MFP_ENABLED) { if (pf->flags & I40E_FLAG_MFP_ENABLED) {
dev_err(&pf->pdev->dev, "Trusted VF not supported in MFP mode.\n"); dev_err(&pf->pdev->dev, "Trusted VF not supported in MFP mode.\n");
return -EINVAL; ret = -EINVAL;
goto out;
} }
vf = &pf->vf[vf_id]; vf = &pf->vf[vf_id];
...@@ -4376,5 +4429,6 @@ int i40e_ndo_set_vf_trust(struct net_device *netdev, int vf_id, bool setting) ...@@ -4376,5 +4429,6 @@ int i40e_ndo_set_vf_trust(struct net_device *netdev, int vf_id, bool setting)
} }
out: out:
clear_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state);
return ret; return ret;
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment