Skip to content

Commit

Permalink
Merge branch 'sfc-next'
Browse files Browse the repository at this point in the history
Shradha Shah says:

====================
sfc: Get/Set MAC address and ndo_[set/get]_vf_* entrypoint functions

This is the second installment of patches towards supporting EF10 SRIOV.

This patch series implements the ndo_get_vf_config, ndo_set_vf_mac,
ndo_set_vf_vlan and ndo_set_vf_spoofcheck function callbacks for EF10.

This patch series also introduces privileges for the MCDI commands
based on which functions are allowed to call them, i.e. Link control
or primary function.

The patch series has been tested with and without CONFIG_SFC_SRIOV.

The ndo function callbacks are tested using ip link.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed May 21, 2015
2 parents 4ec49a3 + 910c878 commit 4e7b3be
Show file tree
Hide file tree
Showing 19 changed files with 1,055 additions and 98 deletions.
279 changes: 243 additions & 36 deletions drivers/net/ethernet/sfc/ef10.c

Large diffs are not rendered by default.

320 changes: 314 additions & 6 deletions drivers/net/ethernet/sfc/ef10_sriov.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,29 @@ static int efx_ef10_vswitch_alloc(struct efx_nic *efx, unsigned int port_id,
unsigned int vswitch_type)
{
MCDI_DECLARE_BUF(inbuf, MC_CMD_VSWITCH_ALLOC_IN_LEN);
int rc;

MCDI_SET_DWORD(inbuf, VSWITCH_ALLOC_IN_UPSTREAM_PORT_ID, port_id);
MCDI_SET_DWORD(inbuf, VSWITCH_ALLOC_IN_TYPE, vswitch_type);
MCDI_SET_DWORD(inbuf, VSWITCH_ALLOC_IN_NUM_VLAN_TAGS, 0);
MCDI_SET_DWORD(inbuf, VSWITCH_ALLOC_IN_NUM_VLAN_TAGS, 2);
MCDI_POPULATE_DWORD_1(inbuf, VSWITCH_ALLOC_IN_FLAGS,
VSWITCH_ALLOC_IN_FLAG_AUTO_PORT, 0);

return efx_mcdi_rpc(efx, MC_CMD_VSWITCH_ALLOC, inbuf, sizeof(inbuf),
NULL, 0, NULL);
/* Quietly try to allocate 2 VLAN tags */
rc = efx_mcdi_rpc_quiet(efx, MC_CMD_VSWITCH_ALLOC, inbuf, sizeof(inbuf),
NULL, 0, NULL);

/* If 2 VLAN tags is too many, revert to trying with 1 VLAN tags */
if (rc == -EPROTO) {
MCDI_SET_DWORD(inbuf, VSWITCH_ALLOC_IN_NUM_VLAN_TAGS, 1);
rc = efx_mcdi_rpc(efx, MC_CMD_VSWITCH_ALLOC, inbuf,
sizeof(inbuf), NULL, 0, NULL);
} else if (rc) {
efx_mcdi_display_error(efx, MC_CMD_VSWITCH_ALLOC,
MC_CMD_VSWITCH_ALLOC_IN_LEN,
NULL, 0, rc);
}
return rc;
}

static int efx_ef10_vswitch_free(struct efx_nic *efx, unsigned int port_id)
Expand All @@ -81,6 +95,7 @@ static int efx_ef10_vswitch_free(struct efx_nic *efx, unsigned int port_id)
static int efx_ef10_vport_alloc(struct efx_nic *efx,
unsigned int port_id_in,
unsigned int vport_type,
u16 vlan,
unsigned int *port_id_out)
{
MCDI_DECLARE_BUF(inbuf, MC_CMD_VPORT_ALLOC_IN_LEN);
Expand All @@ -92,9 +107,13 @@ static int efx_ef10_vport_alloc(struct efx_nic *efx,

MCDI_SET_DWORD(inbuf, VPORT_ALLOC_IN_UPSTREAM_PORT_ID, port_id_in);
MCDI_SET_DWORD(inbuf, VPORT_ALLOC_IN_TYPE, vport_type);
MCDI_SET_DWORD(inbuf, VPORT_ALLOC_IN_NUM_VLAN_TAGS, 0);
MCDI_SET_DWORD(inbuf, VPORT_ALLOC_IN_NUM_VLAN_TAGS,
(vlan != EFX_EF10_NO_VLAN));
MCDI_POPULATE_DWORD_1(inbuf, VPORT_ALLOC_IN_FLAGS,
VPORT_ALLOC_IN_FLAG_AUTO_PORT, 0);
if (vlan != EFX_EF10_NO_VLAN)
MCDI_POPULATE_DWORD_1(inbuf, VPORT_ALLOC_IN_VLAN_TAGS,
VPORT_ALLOC_IN_VLAN_TAG_0, vlan);

rc = efx_mcdi_rpc(efx, MC_CMD_VPORT_ALLOC, inbuf, sizeof(inbuf),
outbuf, sizeof(outbuf), &outlen);
Expand Down Expand Up @@ -160,6 +179,8 @@ static void efx_ef10_sriov_free_vf_vports(struct efx_nic *efx)
efx_ef10_vport_free(efx, vf->vport_id);
vf->vport_id = 0;
}

vf->efx = NULL;
}
}

Expand All @@ -184,7 +205,7 @@ static int efx_ef10_sriov_assign_vf_vport(struct efx_nic *efx,

rc = efx_ef10_vport_alloc(efx, EVB_PORT_ID_ASSIGNED,
MC_CMD_VPORT_ALLOC_IN_VPORT_TYPE_NORMAL,
&vf->vport_id);
vf->vlan, &vf->vport_id);
if (rc)
return rc;

Expand Down Expand Up @@ -215,6 +236,8 @@ static int efx_ef10_sriov_alloc_vf_vswitching(struct efx_nic *efx)

for (i = 0; i < efx->vf_count; i++) {
random_ether_addr(nic_data->vf[i].mac);
nic_data->vf[i].efx = NULL;
nic_data->vf[i].vlan = EFX_EF10_NO_VLAN;

rc = efx_ef10_sriov_assign_vf_vport(efx, i);
if (rc)
Expand Down Expand Up @@ -268,7 +291,7 @@ int efx_ef10_vswitching_probe_pf(struct efx_nic *efx)

rc = efx_ef10_vport_alloc(efx, EVB_PORT_ID_ASSIGNED,
MC_CMD_VPORT_ALLOC_IN_VPORT_TYPE_NORMAL,
&nic_data->vport_id);
EFX_EF10_NO_VLAN, &nic_data->vport_id);
if (rc)
goto fail2;

Expand Down Expand Up @@ -428,3 +451,288 @@ void efx_ef10_sriov_fini(struct efx_nic *efx)
else
netif_dbg(efx, drv, efx->net_dev, "SRIOV disabled\n");
}

static int efx_ef10_vport_del_vf_mac(struct efx_nic *efx, unsigned int port_id,
u8 *mac)
{
MCDI_DECLARE_BUF(inbuf, MC_CMD_VPORT_DEL_MAC_ADDRESS_IN_LEN);
MCDI_DECLARE_BUF_ERR(outbuf);
size_t outlen;
int rc;

MCDI_SET_DWORD(inbuf, VPORT_DEL_MAC_ADDRESS_IN_VPORT_ID, port_id);
ether_addr_copy(MCDI_PTR(inbuf, VPORT_DEL_MAC_ADDRESS_IN_MACADDR), mac);

rc = efx_mcdi_rpc(efx, MC_CMD_VPORT_DEL_MAC_ADDRESS, inbuf,
sizeof(inbuf), outbuf, sizeof(outbuf), &outlen);

return rc;
}

int efx_ef10_sriov_set_vf_mac(struct efx_nic *efx, int vf_i, u8 *mac)
{
struct efx_ef10_nic_data *nic_data = efx->nic_data;
struct ef10_vf *vf;
int rc;

if (!nic_data->vf)
return -EOPNOTSUPP;

if (vf_i >= efx->vf_count)
return -EINVAL;
vf = nic_data->vf + vf_i;

if (vf->efx) {
efx_device_detach_sync(vf->efx);
efx_net_stop(vf->efx->net_dev);

down_write(&vf->efx->filter_sem);
vf->efx->type->filter_table_remove(vf->efx);

rc = efx_ef10_vadaptor_free(vf->efx, EVB_PORT_ID_ASSIGNED);
if (rc) {
up_write(&vf->efx->filter_sem);
return rc;
}
}

rc = efx_ef10_evb_port_assign(efx, EVB_PORT_ID_NULL, vf_i);
if (rc)
return rc;

if (!is_zero_ether_addr(vf->mac)) {
rc = efx_ef10_vport_del_vf_mac(efx, vf->vport_id, vf->mac);
if (rc)
return rc;
}

if (!is_zero_ether_addr(mac)) {
rc = efx_ef10_vport_add_mac(efx, vf->vport_id, mac);
if (rc) {
eth_zero_addr(vf->mac);
goto fail;
}
if (vf->efx)
ether_addr_copy(vf->efx->net_dev->dev_addr, mac);
}

ether_addr_copy(vf->mac, mac);

rc = efx_ef10_evb_port_assign(efx, vf->vport_id, vf_i);
if (rc)
goto fail;

if (vf->efx) {
/* VF cannot use the vport_id that the PF created */
rc = efx_ef10_vadaptor_alloc(vf->efx, EVB_PORT_ID_ASSIGNED);
if (rc) {
up_write(&vf->efx->filter_sem);
return rc;
}
vf->efx->type->filter_table_probe(vf->efx);
up_write(&vf->efx->filter_sem);
efx_net_open(vf->efx->net_dev);
netif_device_attach(vf->efx->net_dev);
}

return 0;

fail:
memset(vf->mac, 0, ETH_ALEN);
return rc;
}

int efx_ef10_sriov_set_vf_vlan(struct efx_nic *efx, int vf_i, u16 vlan,
u8 qos)
{
struct efx_ef10_nic_data *nic_data = efx->nic_data;
struct ef10_vf *vf;
u16 old_vlan, new_vlan;
int rc = 0, rc2 = 0;

if (vf_i >= efx->vf_count)
return -EINVAL;
if (qos != 0)
return -EINVAL;

vf = nic_data->vf + vf_i;

new_vlan = (vlan == 0) ? EFX_EF10_NO_VLAN : vlan;
if (new_vlan == vf->vlan)
return 0;

if (vf->efx) {
efx_device_detach_sync(vf->efx);
efx_net_stop(vf->efx->net_dev);

down_write(&vf->efx->filter_sem);
vf->efx->type->filter_table_remove(vf->efx);

rc = efx_ef10_vadaptor_free(vf->efx, EVB_PORT_ID_ASSIGNED);
if (rc)
goto restore_filters;
}

if (vf->vport_assigned) {
rc = efx_ef10_evb_port_assign(efx, EVB_PORT_ID_NULL, vf_i);
if (rc) {
netif_warn(efx, drv, efx->net_dev,
"Failed to change vlan on VF %d.\n", vf_i);
netif_warn(efx, drv, efx->net_dev,
"This is likely because the VF is bound to a driver in a VM.\n");
netif_warn(efx, drv, efx->net_dev,
"Please unload the driver in the VM.\n");
goto restore_vadaptor;
}
vf->vport_assigned = 0;
}

if (!is_zero_ether_addr(vf->mac)) {
rc = efx_ef10_vport_del_mac(efx, vf->vport_id, vf->mac);
if (rc)
goto restore_evb_port;
}

if (vf->vport_id) {
rc = efx_ef10_vport_free(efx, vf->vport_id);
if (rc)
goto restore_mac;
vf->vport_id = 0;
}

/* Do the actual vlan change */
old_vlan = vf->vlan;
vf->vlan = new_vlan;

/* Restore everything in reverse order */
rc = efx_ef10_vport_alloc(efx, EVB_PORT_ID_ASSIGNED,
MC_CMD_VPORT_ALLOC_IN_VPORT_TYPE_NORMAL,
vf->vlan, &vf->vport_id);
if (rc)
goto reset_nic;

restore_mac:
if (!is_zero_ether_addr(vf->mac)) {
rc2 = efx_ef10_vport_add_mac(efx, vf->vport_id, vf->mac);
if (rc2) {
eth_zero_addr(vf->mac);
goto reset_nic;
}
}

restore_evb_port:
rc2 = efx_ef10_evb_port_assign(efx, vf->vport_id, vf_i);
if (rc2)
goto reset_nic;
else
vf->vport_assigned = 1;

restore_vadaptor:
if (vf->efx) {
rc2 = efx_ef10_vadaptor_alloc(vf->efx, EVB_PORT_ID_ASSIGNED);
if (rc2)
goto reset_nic;
}

restore_filters:
if (vf->efx) {
rc2 = vf->efx->type->filter_table_probe(vf->efx);
if (rc2)
goto reset_nic;

up_write(&vf->efx->filter_sem);

rc2 = efx_net_open(vf->efx->net_dev);
if (rc2)
goto reset_nic;

netif_device_attach(vf->efx->net_dev);
}
return rc;

reset_nic:
if (vf->efx) {
up_write(&vf->efx->filter_sem);
netif_err(efx, drv, efx->net_dev,
"Failed to restore VF - scheduling reset.\n");
efx_schedule_reset(vf->efx, RESET_TYPE_DATAPATH);
} else {
netif_err(efx, drv, efx->net_dev,
"Failed to restore the VF and cannot reset the VF "
"- VF is not functional.\n");
netif_err(efx, drv, efx->net_dev,
"Please reload the driver attached to the VF.\n");
}

return rc ? rc : rc2;
}

int efx_ef10_sriov_set_vf_spoofchk(struct efx_nic *efx, int vf_i,
bool spoofchk)
{
return spoofchk ? -EOPNOTSUPP : 0;
}

int efx_ef10_sriov_set_vf_link_state(struct efx_nic *efx, int vf_i,
int link_state)
{
MCDI_DECLARE_BUF(inbuf, MC_CMD_LINK_STATE_MODE_IN_LEN);
struct efx_ef10_nic_data *nic_data = efx->nic_data;

BUILD_BUG_ON(IFLA_VF_LINK_STATE_AUTO !=
MC_CMD_LINK_STATE_MODE_IN_LINK_STATE_AUTO);
BUILD_BUG_ON(IFLA_VF_LINK_STATE_ENABLE !=
MC_CMD_LINK_STATE_MODE_IN_LINK_STATE_UP);
BUILD_BUG_ON(IFLA_VF_LINK_STATE_DISABLE !=
MC_CMD_LINK_STATE_MODE_IN_LINK_STATE_DOWN);
MCDI_POPULATE_DWORD_2(inbuf, LINK_STATE_MODE_IN_FUNCTION,
LINK_STATE_MODE_IN_FUNCTION_PF,
nic_data->pf_index,
LINK_STATE_MODE_IN_FUNCTION_VF, vf_i);
MCDI_SET_DWORD(inbuf, LINK_STATE_MODE_IN_NEW_MODE, link_state);
return efx_mcdi_rpc(efx, MC_CMD_LINK_STATE_MODE, inbuf, sizeof(inbuf),
NULL, 0, NULL); /* don't care what old mode was */
}

int efx_ef10_sriov_get_vf_config(struct efx_nic *efx, int vf_i,
struct ifla_vf_info *ivf)
{
MCDI_DECLARE_BUF(inbuf, MC_CMD_LINK_STATE_MODE_IN_LEN);
MCDI_DECLARE_BUF(outbuf, MC_CMD_LINK_STATE_MODE_OUT_LEN);

struct efx_ef10_nic_data *nic_data = efx->nic_data;
struct ef10_vf *vf;
size_t outlen;
int rc;

if (vf_i >= efx->vf_count)
return -EINVAL;

if (!nic_data->vf)
return -EOPNOTSUPP;

vf = nic_data->vf + vf_i;

ivf->vf = vf_i;
ivf->min_tx_rate = 0;
ivf->max_tx_rate = 0;
ether_addr_copy(ivf->mac, vf->mac);
ivf->vlan = (vf->vlan == EFX_EF10_NO_VLAN) ? 0 : vf->vlan;
ivf->qos = 0;

MCDI_POPULATE_DWORD_2(inbuf, LINK_STATE_MODE_IN_FUNCTION,
LINK_STATE_MODE_IN_FUNCTION_PF,
nic_data->pf_index,
LINK_STATE_MODE_IN_FUNCTION_VF, vf_i);
MCDI_SET_DWORD(inbuf, LINK_STATE_MODE_IN_NEW_MODE,
MC_CMD_LINK_STATE_MODE_IN_DO_NOT_CHANGE);
rc = efx_mcdi_rpc(efx, MC_CMD_LINK_STATE_MODE, inbuf, sizeof(inbuf),
outbuf, sizeof(outbuf), &outlen);
if (rc)
return rc;
if (outlen < MC_CMD_LINK_STATE_MODE_OUT_LEN)
return -EIO;
ivf->linkstate = MCDI_DWORD(outbuf, LINK_STATE_MODE_OUT_OLD_MODE);

return 0;
}
Loading

0 comments on commit 4e7b3be

Please sign in to comment.