Commit fd36ef60 authored by David S. Miller's avatar David S. Miller

Merge branch 'sfc-filter-chaining'

Edward Cree says:

====================
sfc: support for cascaded multicast filtering

Recent versions of firmware for SFC9100 adapters add support for filter
 chaining, in which packets matching multiple filters are delivered to all
 filters' recipients, rather than only the highest match-priority filter as was
 previously the case.
This patch series enables this feature and redesigns the filter handling code
 to make use of it; in particular, subscribing to a multicast address on one
 function no longer prevents traffic to that address reaching another function
 which is in promiscuous or allmulti mode.
If the firmware does not support filter chaining, the driver will fall back to
 the old behaviour.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 16040894 12fb0da4
......@@ -49,6 +49,12 @@ enum {
*/
#define HUNT_FILTER_TBL_ROWS 8192
#define EFX_EF10_FILTER_ID_INVALID 0xffff
struct efx_ef10_dev_addr {
u8 addr[ETH_ALEN];
u16 id;
};
struct efx_ef10_filter_table {
/* The RX match field masks supported by this fw & hw, in order of priority */
enum efx_filter_match_flags rx_match_flags[
......@@ -69,13 +75,14 @@ struct efx_ef10_filter_table {
/* Shadow of net_device address lists, guarded by mac_lock */
#define EFX_EF10_FILTER_DEV_UC_MAX 32
#define EFX_EF10_FILTER_DEV_MC_MAX 256
struct {
u8 addr[ETH_ALEN];
u16 id;
} dev_uc_list[EFX_EF10_FILTER_DEV_UC_MAX],
dev_mc_list[EFX_EF10_FILTER_DEV_MC_MAX];
int dev_uc_count; /* negative for PROMISC */
int dev_mc_count; /* negative for PROMISC/ALLMULTI */
struct efx_ef10_dev_addr dev_uc_list[EFX_EF10_FILTER_DEV_UC_MAX];
struct efx_ef10_dev_addr dev_mc_list[EFX_EF10_FILTER_DEV_MC_MAX];
int dev_uc_count;
int dev_mc_count;
/* Indices (like efx_ef10_dev_addr.id) for promisc/allmulti filters */
u16 ucdef_id;
u16 bcast_id;
u16 mcdef_id;
};
/* An arbitrary search limit for the software hash table */
......@@ -387,7 +394,7 @@ static int efx_ef10_probe(struct efx_nic *efx)
* First try to enable it, then if we get EPERM, just
* ask if it's already enabled
*/
rc = efx_mcdi_set_workaround(efx, MC_CMD_WORKAROUND_BUG35388, true);
rc = efx_mcdi_set_workaround(efx, MC_CMD_WORKAROUND_BUG35388, true, NULL);
if (rc == 0) {
nic_data->workaround_35388 = true;
} else if (rc == -EPERM) {
......@@ -2197,6 +2204,29 @@ static int efx_ef10_ev_probe(struct efx_channel *channel)
GFP_KERNEL);
}
static void efx_ef10_ev_fini(struct efx_channel *channel)
{
MCDI_DECLARE_BUF(inbuf, MC_CMD_FINI_EVQ_IN_LEN);
MCDI_DECLARE_BUF_ERR(outbuf);
struct efx_nic *efx = channel->efx;
size_t outlen;
int rc;
MCDI_SET_DWORD(inbuf, FINI_EVQ_IN_INSTANCE, channel->channel);
rc = efx_mcdi_rpc_quiet(efx, MC_CMD_FINI_EVQ, inbuf, sizeof(inbuf),
outbuf, sizeof(outbuf), &outlen);
if (rc && rc != -EALREADY)
goto fail;
return;
fail:
efx_mcdi_display_error(efx, MC_CMD_FINI_EVQ, MC_CMD_FINI_EVQ_IN_LEN,
outbuf, outlen, rc);
}
static int efx_ef10_ev_init(struct efx_channel *channel)
{
MCDI_DECLARE_BUF(inbuf,
......@@ -2208,6 +2238,7 @@ static int efx_ef10_ev_init(struct efx_channel *channel)
struct efx_ef10_nic_data *nic_data;
bool supports_rx_merge;
size_t inlen, outlen;
unsigned int enabled, implemented;
dma_addr_t dma_addr;
int rc;
int i;
......@@ -2248,30 +2279,52 @@ static int efx_ef10_ev_init(struct efx_channel *channel)
rc = efx_mcdi_rpc(efx, MC_CMD_INIT_EVQ, inbuf, inlen,
outbuf, sizeof(outbuf), &outlen);
/* IRQ return is ignored */
return rc;
}
static void efx_ef10_ev_fini(struct efx_channel *channel)
{
MCDI_DECLARE_BUF(inbuf, MC_CMD_FINI_EVQ_IN_LEN);
MCDI_DECLARE_BUF_ERR(outbuf);
struct efx_nic *efx = channel->efx;
size_t outlen;
int rc;
MCDI_SET_DWORD(inbuf, FINI_EVQ_IN_INSTANCE, channel->channel);
rc = efx_mcdi_rpc_quiet(efx, MC_CMD_FINI_EVQ, inbuf, sizeof(inbuf),
outbuf, sizeof(outbuf), &outlen);
if (channel->channel || rc)
return rc;
if (rc && rc != -EALREADY)
/* Successfully created event queue on channel 0 */
rc = efx_mcdi_get_workarounds(efx, &implemented, &enabled);
if (rc == -ENOSYS) {
/* GET_WORKAROUNDS was implemented before the bug26807
* workaround, thus the latter must be unavailable in this fw
*/
nic_data->workaround_26807 = false;
rc = 0;
} else if (rc) {
goto fail;
} else {
nic_data->workaround_26807 =
!!(enabled & MC_CMD_GET_WORKAROUNDS_OUT_BUG26807);
if (implemented & MC_CMD_GET_WORKAROUNDS_OUT_BUG26807 &&
!nic_data->workaround_26807) {
unsigned int flags;
rc = efx_mcdi_set_workaround(efx,
MC_CMD_WORKAROUND_BUG26807,
true, &flags);
if (!rc) {
if (flags &
1 << MC_CMD_WORKAROUND_EXT_OUT_FLR_DONE_LBN) {
netif_info(efx, drv, efx->net_dev,
"other functions on NIC have been reset\n");
/* MC's boot count has incremented */
++nic_data->warm_boot_count;
}
nic_data->workaround_26807 = true;
} else if (rc == -EPERM) {
rc = 0;
}
}
}
return;
if (!rc)
return 0;
fail:
efx_mcdi_display_error(efx, MC_CMD_FINI_EVQ, MC_CMD_FINI_EVQ_IN_LEN,
outbuf, outlen, rc);
efx_ef10_ev_fini(channel);
return rc;
}
static void efx_ef10_ev_remove(struct efx_channel *channel)
......@@ -3225,6 +3278,19 @@ static int efx_ef10_filter_remove_safe(struct efx_nic *efx,
filter_id, false);
}
static u32 efx_ef10_filter_get_unsafe_id(struct efx_nic *efx, u32 filter_id)
{
return filter_id % HUNT_FILTER_TBL_ROWS;
}
static int efx_ef10_filter_remove_unsafe(struct efx_nic *efx,
enum efx_filter_priority priority,
u32 filter_id)
{
return efx_ef10_filter_remove_internal(efx, 1U << priority,
filter_id, true);
}
static int efx_ef10_filter_get_safe(struct efx_nic *efx,
enum efx_filter_priority priority,
u32 filter_id, struct efx_filter_spec *spec)
......@@ -3598,6 +3664,10 @@ static int efx_ef10_filter_table_probe(struct efx_nic *efx)
goto fail;
}
table->ucdef_id = EFX_EF10_FILTER_ID_INVALID;
table->bcast_id = EFX_EF10_FILTER_ID_INVALID;
table->mcdef_id = EFX_EF10_FILTER_ID_INVALID;
efx->filter_state = table;
init_waitqueue_head(&table->waitq);
return 0;
......@@ -3700,145 +3770,233 @@ static void efx_ef10_filter_table_remove(struct efx_nic *efx)
kfree(table);
}
/* Caller must hold efx->filter_sem for read if race against
* efx_ef10_filter_table_remove() is possible
*/
static void efx_ef10_filter_sync_rx_mode(struct efx_nic *efx)
#define EFX_EF10_FILTER_DO_MARK_OLD(id) \
if (id != EFX_EF10_FILTER_ID_INVALID) { \
filter_idx = efx_ef10_filter_get_unsafe_id(efx, id); \
WARN_ON(!table->entry[filter_idx].spec); \
table->entry[filter_idx].spec |= EFX_EF10_FILTER_FLAG_AUTO_OLD; \
}
static void efx_ef10_filter_mark_old(struct efx_nic *efx)
{
struct efx_ef10_filter_table *table = efx->filter_state;
struct net_device *net_dev = efx->net_dev;
struct efx_filter_spec spec;
bool remove_failed = false;
struct netdev_hw_addr *uc;
struct netdev_hw_addr *mc;
unsigned int filter_idx;
int i, n, rc;
if (!efx_dev_registered(efx))
return;
unsigned int filter_idx, i;
if (!table)
return;
/* Mark old filters that may need to be removed */
spin_lock_bh(&efx->filter_lock);
n = table->dev_uc_count < 0 ? 1 : table->dev_uc_count;
for (i = 0; i < n; i++) {
filter_idx = table->dev_uc_list[i].id % HUNT_FILTER_TBL_ROWS;
table->entry[filter_idx].spec |= EFX_EF10_FILTER_FLAG_AUTO_OLD;
}
n = table->dev_mc_count < 0 ? 1 : table->dev_mc_count;
for (i = 0; i < n; i++) {
filter_idx = table->dev_mc_list[i].id % HUNT_FILTER_TBL_ROWS;
table->entry[filter_idx].spec |= EFX_EF10_FILTER_FLAG_AUTO_OLD;
}
for (i = 0; i < table->dev_uc_count; i++)
EFX_EF10_FILTER_DO_MARK_OLD(table->dev_uc_list[i].id);
for (i = 0; i < table->dev_mc_count; i++)
EFX_EF10_FILTER_DO_MARK_OLD(table->dev_mc_list[i].id);
EFX_EF10_FILTER_DO_MARK_OLD(table->ucdef_id);
EFX_EF10_FILTER_DO_MARK_OLD(table->bcast_id);
EFX_EF10_FILTER_DO_MARK_OLD(table->mcdef_id);
spin_unlock_bh(&efx->filter_lock);
}
#undef EFX_EF10_FILTER_DO_MARK_OLD
/* Copy/convert the address lists; add the primary station
* address and broadcast address
*/
netif_addr_lock_bh(net_dev);
if (net_dev->flags & IFF_PROMISC ||
netdev_uc_count(net_dev) >= EFX_EF10_FILTER_DEV_UC_MAX) {
table->dev_uc_count = -1;
} else {
table->dev_uc_count = 1 + netdev_uc_count(net_dev);
ether_addr_copy(table->dev_uc_list[0].addr, net_dev->dev_addr);
i = 1;
netdev_for_each_uc_addr(uc, net_dev) {
ether_addr_copy(table->dev_uc_list[i].addr, uc->addr);
i++;
static void efx_ef10_filter_uc_addr_list(struct efx_nic *efx, bool *promisc)
{
struct efx_ef10_filter_table *table = efx->filter_state;
struct net_device *net_dev = efx->net_dev;
struct netdev_hw_addr *uc;
int addr_count;
unsigned int i;
table->ucdef_id = EFX_EF10_FILTER_ID_INVALID;
addr_count = netdev_uc_count(net_dev);
if (net_dev->flags & IFF_PROMISC)
*promisc = true;
table->dev_uc_count = 1 + addr_count;
ether_addr_copy(table->dev_uc_list[0].addr, net_dev->dev_addr);
i = 1;
netdev_for_each_uc_addr(uc, net_dev) {
if (i >= EFX_EF10_FILTER_DEV_UC_MAX) {
*promisc = true;
break;
}
ether_addr_copy(table->dev_uc_list[i].addr, uc->addr);
table->dev_uc_list[i].id = EFX_EF10_FILTER_ID_INVALID;
i++;
}
if (net_dev->flags & (IFF_PROMISC | IFF_ALLMULTI) ||
netdev_mc_count(net_dev) >= EFX_EF10_FILTER_DEV_MC_MAX) {
table->dev_mc_count = -1;
} else {
table->dev_mc_count = 1 + netdev_mc_count(net_dev);
eth_broadcast_addr(table->dev_mc_list[0].addr);
i = 1;
netdev_for_each_mc_addr(mc, net_dev) {
ether_addr_copy(table->dev_mc_list[i].addr, mc->addr);
i++;
}
static void efx_ef10_filter_mc_addr_list(struct efx_nic *efx, bool *promisc)
{
struct efx_ef10_filter_table *table = efx->filter_state;
struct net_device *net_dev = efx->net_dev;
struct netdev_hw_addr *mc;
unsigned int i, addr_count;
table->mcdef_id = EFX_EF10_FILTER_ID_INVALID;
table->bcast_id = EFX_EF10_FILTER_ID_INVALID;
if (net_dev->flags & (IFF_PROMISC | IFF_ALLMULTI))
*promisc = true;
addr_count = netdev_mc_count(net_dev);
i = 0;
netdev_for_each_mc_addr(mc, net_dev) {
if (i >= EFX_EF10_FILTER_DEV_MC_MAX) {
*promisc = true;
break;
}
ether_addr_copy(table->dev_mc_list[i].addr, mc->addr);
table->dev_mc_list[i].id = EFX_EF10_FILTER_ID_INVALID;
i++;
}
netif_addr_unlock_bh(net_dev);
/* Insert/renew unicast filters */
if (table->dev_uc_count >= 0) {
for (i = 0; i < table->dev_uc_count; i++) {
efx_filter_init_rx(&spec, EFX_FILTER_PRI_AUTO,
EFX_FILTER_FLAG_RX_RSS,
0);
efx_filter_set_eth_local(&spec, EFX_FILTER_VID_UNSPEC,
table->dev_uc_list[i].addr);
rc = efx_ef10_filter_insert(efx, &spec, true);
if (rc < 0) {
/* Fall back to unicast-promisc */
while (i--)
efx_ef10_filter_remove_safe(
table->dev_mc_count = i;
}
static int efx_ef10_filter_insert_addr_list(struct efx_nic *efx,
bool multicast, bool rollback)
{
struct efx_ef10_filter_table *table = efx->filter_state;
struct efx_ef10_dev_addr *addr_list;
struct efx_filter_spec spec;
u8 baddr[ETH_ALEN];
unsigned int i, j;
int addr_count;
int rc;
if (multicast) {
addr_list = table->dev_mc_list;
addr_count = table->dev_mc_count;
} else {
addr_list = table->dev_uc_list;
addr_count = table->dev_uc_count;
}
/* Insert/renew filters */
for (i = 0; i < addr_count; i++) {
efx_filter_init_rx(&spec, EFX_FILTER_PRI_AUTO,
EFX_FILTER_FLAG_RX_RSS,
0);
efx_filter_set_eth_local(&spec, EFX_FILTER_VID_UNSPEC,
addr_list[i].addr);
rc = efx_ef10_filter_insert(efx, &spec, true);
if (rc < 0) {
if (rollback) {
netif_info(efx, drv, efx->net_dev,
"efx_ef10_filter_insert failed rc=%d\n",
rc);
/* Fall back to promiscuous */
for (j = 0; j < i; j++) {
if (addr_list[j].id == EFX_EF10_FILTER_ID_INVALID)
continue;
efx_ef10_filter_remove_unsafe(
efx, EFX_FILTER_PRI_AUTO,
table->dev_uc_list[i].id);
table->dev_uc_count = -1;
break;
addr_list[j].id);
addr_list[j].id = EFX_EF10_FILTER_ID_INVALID;
}
return rc;
} else {
/* mark as not inserted, and carry on */
rc = EFX_EF10_FILTER_ID_INVALID;
}
table->dev_uc_list[i].id = rc;
}
addr_list[i].id = efx_ef10_filter_get_unsafe_id(efx, rc);
}
if (table->dev_uc_count < 0) {
if (multicast && rollback) {
/* Also need an Ethernet broadcast filter */
efx_filter_init_rx(&spec, EFX_FILTER_PRI_AUTO,
EFX_FILTER_FLAG_RX_RSS,
0);
efx_filter_set_uc_def(&spec);
eth_broadcast_addr(baddr);
efx_filter_set_eth_local(&spec, EFX_FILTER_VID_UNSPEC, baddr);
rc = efx_ef10_filter_insert(efx, &spec, true);
if (rc < 0) {
WARN_ON(1);
table->dev_uc_count = 0;
netif_warn(efx, drv, efx->net_dev,
"Broadcast filter insert failed rc=%d\n", rc);
/* Fall back to promiscuous */
for (j = 0; j < i; j++) {
if (addr_list[j].id == EFX_EF10_FILTER_ID_INVALID)
continue;
efx_ef10_filter_remove_unsafe(
efx, EFX_FILTER_PRI_AUTO,
addr_list[j].id);
addr_list[j].id = EFX_EF10_FILTER_ID_INVALID;
}
return rc;
} else {
table->dev_uc_list[0].id = rc;
table->bcast_id = efx_ef10_filter_get_unsafe_id(efx, rc);
}
}
/* Insert/renew multicast filters */
if (table->dev_mc_count >= 0) {
for (i = 0; i < table->dev_mc_count; i++) {
return 0;
}
static int efx_ef10_filter_insert_def(struct efx_nic *efx, bool multicast,
bool rollback)
{
struct efx_ef10_filter_table *table = efx->filter_state;
struct efx_ef10_nic_data *nic_data = efx->nic_data;
struct efx_filter_spec spec;
u8 baddr[ETH_ALEN];
int rc;
efx_filter_init_rx(&spec, EFX_FILTER_PRI_AUTO,
EFX_FILTER_FLAG_RX_RSS,
0);
if (multicast)
efx_filter_set_mc_def(&spec);
else
efx_filter_set_uc_def(&spec);
rc = efx_ef10_filter_insert(efx, &spec, true);
if (rc < 0) {
netif_warn(efx, drv, efx->net_dev,
"%scast mismatch filter insert failed rc=%d\n",
multicast ? "Multi" : "Uni", rc);
} else if (multicast) {
table->mcdef_id = efx_ef10_filter_get_unsafe_id(efx, rc);
if (!nic_data->workaround_26807) {
/* Also need an Ethernet broadcast filter */
efx_filter_init_rx(&spec, EFX_FILTER_PRI_AUTO,
EFX_FILTER_FLAG_RX_RSS,
0);
eth_broadcast_addr(baddr);
efx_filter_set_eth_local(&spec, EFX_FILTER_VID_UNSPEC,
table->dev_mc_list[i].addr);
baddr);
rc = efx_ef10_filter_insert(efx, &spec, true);
if (rc < 0) {
/* Fall back to multicast-promisc */
while (i--)
efx_ef10_filter_remove_safe(
efx, EFX_FILTER_PRI_AUTO,
table->dev_mc_list[i].id);
table->dev_mc_count = -1;
break;
netif_warn(efx, drv, efx->net_dev,
"Broadcast filter insert failed rc=%d\n",
rc);
if (rollback) {
/* Roll back the mc_def filter */
efx_ef10_filter_remove_unsafe(
efx, EFX_FILTER_PRI_AUTO,
table->mcdef_id);
table->mcdef_id = EFX_EF10_FILTER_ID_INVALID;
return rc;
}
} else {
table->bcast_id = efx_ef10_filter_get_unsafe_id(efx, rc);
}
table->dev_mc_list[i].id = rc;
}
}
if (table->dev_mc_count < 0) {
efx_filter_init_rx(&spec, EFX_FILTER_PRI_AUTO,
EFX_FILTER_FLAG_RX_RSS,
0);
efx_filter_set_mc_def(&spec);
rc = efx_ef10_filter_insert(efx, &spec, true);
if (rc < 0) {
WARN_ON(1);
table->dev_mc_count = 0;
} else {
table->dev_mc_list[0].id = rc;
}
rc = 0;
} else {
table->ucdef_id = rc;
rc = 0;
}
return rc;
}
/* Remove filters that weren't renewed. Since nothing else changes the AUTO_OLD
* flag or removes these filters, we don't need to hold the filter_lock while
* scanning for these filters.
*/
static void efx_ef10_filter_remove_old(struct efx_nic *efx)
{
struct efx_ef10_filter_table *table = efx->filter_state;
bool remove_failed = false;
int i;
/* Remove filters that weren't renewed. Since nothing else
* changes the AUTO_OLD flag or removes these filters, we
* don't need to hold the filter_lock while scanning for
* these filters.
*/
for (i = 0; i < HUNT_FILTER_TBL_ROWS; i++) {
if (ACCESS_ONCE(table->entry[i].spec) &
EFX_EF10_FILTER_FLAG_AUTO_OLD) {
......@@ -3917,6 +4075,87 @@ static int efx_ef10_vport_set_mac_address(struct efx_nic *efx)
return rc ? rc : rc2;
}
/* Caller must hold efx->filter_sem for read if race against
* efx_ef10_filter_table_remove() is possible
*/
static void efx_ef10_filter_sync_rx_mode(struct efx_nic *efx)
{
struct efx_ef10_filter_table *table = efx->filter_state;
struct efx_ef10_nic_data *nic_data = efx->nic_data;
struct net_device *net_dev = efx->net_dev;
bool uc_promisc = false, mc_promisc = false;
if (!efx_dev_registered(efx))
return;
if (!table)
return;
efx_ef10_filter_mark_old(efx);
/* Copy/convert the address lists; add the primary station
* address and broadcast address
*/
netif_addr_lock_bh(net_dev);
efx_ef10_filter_uc_addr_list(efx, &uc_promisc);
efx_ef10_filter_mc_addr_list(efx, &mc_promisc);
netif_addr_unlock_bh(net_dev);
/* Insert/renew unicast filters */
if (uc_promisc) {
efx_ef10_filter_insert_def(efx, false, false);
efx_ef10_filter_insert_addr_list(efx, false, false);
} else {
/* If any of the filters failed to insert, fall back to
* promiscuous mode - add in the uc_def filter. But keep
* our individual unicast filters.
*/
if (efx_ef10_filter_insert_addr_list(efx, false, false))
efx_ef10_filter_insert_def(efx, false, false);
}
/* Insert/renew multicast filters */
/* If changing promiscuous state with cascaded multicast filters, remove
* old filters first, so that packets are dropped rather than duplicated
*/
if (nic_data->workaround_26807 && efx->mc_promisc != mc_promisc)
efx_ef10_filter_remove_old(efx);
if (mc_promisc) {
if (nic_data->workaround_26807) {
/* If we failed to insert promiscuous filters, rollback
* and fall back to individual multicast filters
*/
if (efx_ef10_filter_insert_def(efx, true, true)) {
/* Changing promisc state, so remove old filters */
efx_ef10_filter_remove_old(efx);
efx_ef10_filter_insert_addr_list(efx, true, false);
}
} else {
/* If we failed to insert promiscuous filters, don't
* rollback. Regardless, also insert the mc_list
*/
efx_ef10_filter_insert_def(efx, true, false);
efx_ef10_filter_insert_addr_list(efx, true, false);
}
} else {
/* If any filters failed to insert, rollback and fall back to
* promiscuous mode - mc_def filter and maybe broadcast. If
* that fails, roll back again and insert as many of our
* individual multicast filters as we can.
*/
if (efx_ef10_filter_insert_addr_list(efx, true, true)) {
/* Changing promisc state, so remove old filters */
if (nic_data->workaround_26807)
efx_ef10_filter_remove_old(efx);
if (efx_ef10_filter_insert_def(efx, true, true))
efx_ef10_filter_insert_addr_list(efx, true, false);
}
}
efx_ef10_filter_remove_old(efx);
efx->mc_promisc = mc_promisc;
}
static int efx_ef10_set_mac_address(struct efx_nic *efx)
{
MCDI_DECLARE_BUF(inbuf, MC_CMD_VADAPTOR_SET_MAC_IN_LEN);
......
......@@ -1779,15 +1779,31 @@ int efx_mcdi_wol_filter_reset(struct efx_nic *efx)
return rc;
}
int efx_mcdi_set_workaround(struct efx_nic *efx, u32 type, bool enabled)
int efx_mcdi_set_workaround(struct efx_nic *efx, u32 type, bool enabled,
unsigned int *flags)
{
MCDI_DECLARE_BUF(inbuf, MC_CMD_WORKAROUND_IN_LEN);
MCDI_DECLARE_BUF(outbuf, MC_CMD_WORKAROUND_EXT_OUT_LEN);
size_t outlen;
int rc;
BUILD_BUG_ON(MC_CMD_WORKAROUND_OUT_LEN != 0);
MCDI_SET_DWORD(inbuf, WORKAROUND_IN_TYPE, type);
MCDI_SET_DWORD(inbuf, WORKAROUND_IN_ENABLED, enabled);
return efx_mcdi_rpc(efx, MC_CMD_WORKAROUND, inbuf, sizeof(inbuf),
NULL, 0, NULL);
rc = efx_mcdi_rpc(efx, MC_CMD_WORKAROUND, inbuf, sizeof(inbuf),
outbuf, sizeof(outbuf), &outlen);
if (rc)
return rc;
if (!flags)
return 0;
if (outlen >= MC_CMD_WORKAROUND_EXT_OUT_LEN)
*flags = MCDI_DWORD(outbuf, WORKAROUND_EXT_OUT_FLAGS);
else
*flags = 0;
return 0;
}
int efx_mcdi_get_workarounds(struct efx_nic *efx, unsigned int *impl_out,
......@@ -1816,7 +1832,11 @@ int efx_mcdi_get_workarounds(struct efx_nic *efx, unsigned int *impl_out,
return 0;
fail:
netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n", __func__, rc);
/* Older firmware lacks GET_WORKAROUNDS and this isn't especially
* terrifying. The call site will have to deal with it though.
*/
netif_printk(efx, hw, rc == -ENOSYS ? KERN_DEBUG : KERN_ERR,
efx->net_dev, "%s: failed rc=%d\n", __func__, rc);
return rc;
}
......
......@@ -346,7 +346,8 @@ void efx_mcdi_mac_pull_stats(struct efx_nic *efx);
bool efx_mcdi_mac_check_fault(struct efx_nic *efx);
enum reset_type efx_mcdi_map_reset_reason(enum reset_type reason);
int efx_mcdi_reset(struct efx_nic *efx, enum reset_type method);
int efx_mcdi_set_workaround(struct efx_nic *efx, u32 type, bool enabled);
int efx_mcdi_set_workaround(struct efx_nic *efx, u32 type, bool enabled,
unsigned int *flags);
int efx_mcdi_get_workarounds(struct efx_nic *efx, unsigned int *impl_out,
unsigned int *enabled_out);
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -925,6 +925,7 @@ struct vfdi_status;
* @stats_lock: Statistics update lock. Must be held when calling
* efx_nic_type::{update,start,stop}_stats.
* @n_rx_noskb_drops: Count of RX packets dropped due to failure to allocate an skb
* @mc_promisc: Whether in multicast promiscuous mode when last changed
*
* This is stored in the private area of the &struct net_device.
*/
......@@ -1072,6 +1073,7 @@ struct efx_nic {
int last_irq_cpu;
spinlock_t stats_lock;
atomic_t n_rx_noskb_drops;
bool mc_promisc;
};
static inline int efx_dev_registered(struct efx_nic *efx)
......
......@@ -506,6 +506,7 @@ enum {
* @rx_rss_context_exclusive: Whether our RSS context is exclusive or shared
* @stats: Hardware statistics
* @workaround_35388: Flag: firmware supports workaround for bug 35388
* @workaround_26807: Flag: firmware supports workaround for bug 26807
* @must_check_datapath_caps: Flag: @datapath_caps needs to be revalidated
* after MC reboot
* @datapath_caps: Capabilities of datapath firmware (FLAGS1 field of
......@@ -535,6 +536,7 @@ struct efx_ef10_nic_data {
bool rx_rss_context_exclusive;
u64 stats[EF10_STAT_COUNT];
bool workaround_35388;
bool workaround_26807;
bool must_check_datapath_caps;
u32 datapath_caps;
unsigned int rx_dpcpu_fw_id;
......
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