ionic: make all rx_mode work threadsafe
Move the bulk of the code from ionic_set_rx_mode(), which
can be called from atomic context, into ionic_lif_rx_mode()
which is a safe context.
A call from the stack will get pushed off into a work thread,
but it is also possible to simultaneously have a call driven
by a queue reconfig request from an ethtool command or fw
recovery event. We add a mutex around the rx_mode work to be
sure they don't collide.
Fixes: 81dbc24147
("ionic: change set_rx_mode from_ndo to can_sleep")
Signed-off-by: Shannon Nelson <snelson@pensando.io>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
0506c93fba
commit
6840e17b8e
drivers/net/ethernet/pensando/ionic
@ -29,7 +29,7 @@ static const u8 ionic_qtype_versions[IONIC_QTYPE_MAX] = {
|
||||
*/
|
||||
};
|
||||
|
||||
static void ionic_lif_rx_mode(struct ionic_lif *lif, unsigned int rx_mode);
|
||||
static void ionic_lif_rx_mode(struct ionic_lif *lif);
|
||||
static int ionic_lif_addr_add(struct ionic_lif *lif, const u8 *addr);
|
||||
static int ionic_lif_addr_del(struct ionic_lif *lif, const u8 *addr);
|
||||
static void ionic_link_status_check(struct ionic_lif *lif);
|
||||
@ -77,7 +77,7 @@ static void ionic_lif_deferred_work(struct work_struct *work)
|
||||
|
||||
switch (w->type) {
|
||||
case IONIC_DW_TYPE_RX_MODE:
|
||||
ionic_lif_rx_mode(lif, w->rx_mode);
|
||||
ionic_lif_rx_mode(lif);
|
||||
break;
|
||||
case IONIC_DW_TYPE_RX_ADDR_ADD:
|
||||
ionic_lif_addr_add(lif, w->addr);
|
||||
@ -1301,10 +1301,8 @@ static int ionic_lif_addr_del(struct ionic_lif *lif, const u8 *addr)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ionic_lif_addr(struct ionic_lif *lif, const u8 *addr, bool add,
|
||||
bool can_sleep)
|
||||
static int ionic_lif_addr(struct ionic_lif *lif, const u8 *addr, bool add)
|
||||
{
|
||||
struct ionic_deferred_work *work;
|
||||
unsigned int nmfilters;
|
||||
unsigned int nufilters;
|
||||
|
||||
@ -1330,63 +1328,77 @@ static int ionic_lif_addr(struct ionic_lif *lif, const u8 *addr, bool add,
|
||||
lif->nucast--;
|
||||
}
|
||||
|
||||
if (!can_sleep) {
|
||||
work = kzalloc(sizeof(*work), GFP_ATOMIC);
|
||||
if (!work)
|
||||
return -ENOMEM;
|
||||
work->type = add ? IONIC_DW_TYPE_RX_ADDR_ADD :
|
||||
IONIC_DW_TYPE_RX_ADDR_DEL;
|
||||
memcpy(work->addr, addr, ETH_ALEN);
|
||||
netdev_dbg(lif->netdev, "deferred: rx_filter %s %pM\n",
|
||||
add ? "add" : "del", addr);
|
||||
ionic_lif_deferred_enqueue(&lif->deferred, work);
|
||||
} else {
|
||||
netdev_dbg(lif->netdev, "rx_filter %s %pM\n",
|
||||
add ? "add" : "del", addr);
|
||||
if (add)
|
||||
return ionic_lif_addr_add(lif, addr);
|
||||
else
|
||||
return ionic_lif_addr_del(lif, addr);
|
||||
}
|
||||
netdev_dbg(lif->netdev, "rx_filter %s %pM\n",
|
||||
add ? "add" : "del", addr);
|
||||
if (add)
|
||||
return ionic_lif_addr_add(lif, addr);
|
||||
else
|
||||
return ionic_lif_addr_del(lif, addr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ionic_addr_add(struct net_device *netdev, const u8 *addr)
|
||||
{
|
||||
return ionic_lif_addr(netdev_priv(netdev), addr, ADD_ADDR, CAN_SLEEP);
|
||||
}
|
||||
|
||||
static int ionic_ndo_addr_add(struct net_device *netdev, const u8 *addr)
|
||||
{
|
||||
return ionic_lif_addr(netdev_priv(netdev), addr, ADD_ADDR, CAN_NOT_SLEEP);
|
||||
return ionic_lif_addr(netdev_priv(netdev), addr, ADD_ADDR);
|
||||
}
|
||||
|
||||
static int ionic_addr_del(struct net_device *netdev, const u8 *addr)
|
||||
{
|
||||
return ionic_lif_addr(netdev_priv(netdev), addr, DEL_ADDR, CAN_SLEEP);
|
||||
return ionic_lif_addr(netdev_priv(netdev), addr, DEL_ADDR);
|
||||
}
|
||||
|
||||
static int ionic_ndo_addr_del(struct net_device *netdev, const u8 *addr)
|
||||
static void ionic_lif_rx_mode(struct ionic_lif *lif)
|
||||
{
|
||||
return ionic_lif_addr(netdev_priv(netdev), addr, DEL_ADDR, CAN_NOT_SLEEP);
|
||||
}
|
||||
|
||||
static void ionic_lif_rx_mode(struct ionic_lif *lif, unsigned int rx_mode)
|
||||
{
|
||||
struct ionic_admin_ctx ctx = {
|
||||
.work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
|
||||
.cmd.rx_mode_set = {
|
||||
.opcode = IONIC_CMD_RX_MODE_SET,
|
||||
.lif_index = cpu_to_le16(lif->index),
|
||||
.rx_mode = cpu_to_le16(rx_mode),
|
||||
},
|
||||
};
|
||||
struct net_device *netdev = lif->netdev;
|
||||
unsigned int nfilters;
|
||||
unsigned int nd_flags;
|
||||
char buf[128];
|
||||
int err;
|
||||
u16 rx_mode;
|
||||
int i;
|
||||
#define REMAIN(__x) (sizeof(buf) - (__x))
|
||||
|
||||
mutex_lock(&lif->config_lock);
|
||||
|
||||
/* grab the flags once for local use */
|
||||
nd_flags = netdev->flags;
|
||||
|
||||
rx_mode = IONIC_RX_MODE_F_UNICAST;
|
||||
rx_mode |= (nd_flags & IFF_MULTICAST) ? IONIC_RX_MODE_F_MULTICAST : 0;
|
||||
rx_mode |= (nd_flags & IFF_BROADCAST) ? IONIC_RX_MODE_F_BROADCAST : 0;
|
||||
rx_mode |= (nd_flags & IFF_PROMISC) ? IONIC_RX_MODE_F_PROMISC : 0;
|
||||
rx_mode |= (nd_flags & IFF_ALLMULTI) ? IONIC_RX_MODE_F_ALLMULTI : 0;
|
||||
|
||||
/* sync unicast addresses
|
||||
* next check to see if we're in an overflow state
|
||||
* if so, we track that we overflowed and enable NIC PROMISC
|
||||
* else if the overflow is set and not needed
|
||||
* we remove our overflow flag and check the netdev flags
|
||||
* to see if we can disable NIC PROMISC
|
||||
*/
|
||||
__dev_uc_sync(netdev, ionic_addr_add, ionic_addr_del);
|
||||
nfilters = le32_to_cpu(lif->identity->eth.max_ucast_filters);
|
||||
if (netdev_uc_count(netdev) + 1 > nfilters) {
|
||||
rx_mode |= IONIC_RX_MODE_F_PROMISC;
|
||||
lif->uc_overflow = true;
|
||||
} else if (lif->uc_overflow) {
|
||||
lif->uc_overflow = false;
|
||||
if (!(nd_flags & IFF_PROMISC))
|
||||
rx_mode &= ~IONIC_RX_MODE_F_PROMISC;
|
||||
}
|
||||
|
||||
/* same for multicast */
|
||||
__dev_mc_sync(netdev, ionic_addr_add, ionic_addr_del);
|
||||
nfilters = le32_to_cpu(lif->identity->eth.max_mcast_filters);
|
||||
if (netdev_mc_count(netdev) > nfilters) {
|
||||
rx_mode |= IONIC_RX_MODE_F_ALLMULTI;
|
||||
lif->mc_overflow = true;
|
||||
} else if (lif->mc_overflow) {
|
||||
lif->mc_overflow = false;
|
||||
if (!(nd_flags & IFF_ALLMULTI))
|
||||
rx_mode &= ~IONIC_RX_MODE_F_ALLMULTI;
|
||||
}
|
||||
|
||||
i = scnprintf(buf, sizeof(buf), "rx_mode 0x%04x -> 0x%04x:",
|
||||
lif->rx_mode, rx_mode);
|
||||
if (rx_mode & IONIC_RX_MODE_F_UNICAST)
|
||||
@ -1399,79 +1411,48 @@ static void ionic_lif_rx_mode(struct ionic_lif *lif, unsigned int rx_mode)
|
||||
i += scnprintf(&buf[i], REMAIN(i), " RX_MODE_F_PROMISC");
|
||||
if (rx_mode & IONIC_RX_MODE_F_ALLMULTI)
|
||||
i += scnprintf(&buf[i], REMAIN(i), " RX_MODE_F_ALLMULTI");
|
||||
netdev_dbg(lif->netdev, "lif%d %s\n", lif->index, buf);
|
||||
if (rx_mode & IONIC_RX_MODE_F_RDMA_SNIFFER)
|
||||
i += scnprintf(&buf[i], REMAIN(i), " RX_MODE_F_RDMA_SNIFFER");
|
||||
netdev_dbg(netdev, "lif%d %s\n", lif->index, buf);
|
||||
|
||||
err = ionic_adminq_post_wait(lif, &ctx);
|
||||
if (err)
|
||||
netdev_warn(lif->netdev, "set rx_mode 0x%04x failed: %d\n",
|
||||
rx_mode, err);
|
||||
else
|
||||
lif->rx_mode = rx_mode;
|
||||
if (lif->rx_mode != rx_mode) {
|
||||
struct ionic_admin_ctx ctx = {
|
||||
.work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
|
||||
.cmd.rx_mode_set = {
|
||||
.opcode = IONIC_CMD_RX_MODE_SET,
|
||||
.lif_index = cpu_to_le16(lif->index),
|
||||
},
|
||||
};
|
||||
int err;
|
||||
|
||||
ctx.cmd.rx_mode_set.rx_mode = cpu_to_le16(rx_mode);
|
||||
err = ionic_adminq_post_wait(lif, &ctx);
|
||||
if (err)
|
||||
netdev_warn(netdev, "set rx_mode 0x%04x failed: %d\n",
|
||||
rx_mode, err);
|
||||
else
|
||||
lif->rx_mode = rx_mode;
|
||||
}
|
||||
|
||||
mutex_unlock(&lif->config_lock);
|
||||
}
|
||||
|
||||
static void ionic_set_rx_mode(struct net_device *netdev, bool can_sleep)
|
||||
{
|
||||
struct ionic_lif *lif = netdev_priv(netdev);
|
||||
struct ionic_deferred_work *work;
|
||||
unsigned int nfilters;
|
||||
unsigned int rx_mode;
|
||||
|
||||
rx_mode = IONIC_RX_MODE_F_UNICAST;
|
||||
rx_mode |= (netdev->flags & IFF_MULTICAST) ? IONIC_RX_MODE_F_MULTICAST : 0;
|
||||
rx_mode |= (netdev->flags & IFF_BROADCAST) ? IONIC_RX_MODE_F_BROADCAST : 0;
|
||||
rx_mode |= (netdev->flags & IFF_PROMISC) ? IONIC_RX_MODE_F_PROMISC : 0;
|
||||
rx_mode |= (netdev->flags & IFF_ALLMULTI) ? IONIC_RX_MODE_F_ALLMULTI : 0;
|
||||
|
||||
/* sync unicast addresses
|
||||
* next check to see if we're in an overflow state
|
||||
* if so, we track that we overflowed and enable NIC PROMISC
|
||||
* else if the overflow is set and not needed
|
||||
* we remove our overflow flag and check the netdev flags
|
||||
* to see if we can disable NIC PROMISC
|
||||
*/
|
||||
if (can_sleep)
|
||||
__dev_uc_sync(netdev, ionic_addr_add, ionic_addr_del);
|
||||
else
|
||||
__dev_uc_sync(netdev, ionic_ndo_addr_add, ionic_ndo_addr_del);
|
||||
nfilters = le32_to_cpu(lif->identity->eth.max_ucast_filters);
|
||||
if (netdev_uc_count(netdev) + 1 > nfilters) {
|
||||
rx_mode |= IONIC_RX_MODE_F_PROMISC;
|
||||
lif->uc_overflow = true;
|
||||
} else if (lif->uc_overflow) {
|
||||
lif->uc_overflow = false;
|
||||
if (!(netdev->flags & IFF_PROMISC))
|
||||
rx_mode &= ~IONIC_RX_MODE_F_PROMISC;
|
||||
}
|
||||
|
||||
/* same for multicast */
|
||||
if (can_sleep)
|
||||
__dev_mc_sync(netdev, ionic_addr_add, ionic_addr_del);
|
||||
else
|
||||
__dev_mc_sync(netdev, ionic_ndo_addr_add, ionic_ndo_addr_del);
|
||||
nfilters = le32_to_cpu(lif->identity->eth.max_mcast_filters);
|
||||
if (netdev_mc_count(netdev) > nfilters) {
|
||||
rx_mode |= IONIC_RX_MODE_F_ALLMULTI;
|
||||
lif->mc_overflow = true;
|
||||
} else if (lif->mc_overflow) {
|
||||
lif->mc_overflow = false;
|
||||
if (!(netdev->flags & IFF_ALLMULTI))
|
||||
rx_mode &= ~IONIC_RX_MODE_F_ALLMULTI;
|
||||
}
|
||||
|
||||
if (lif->rx_mode != rx_mode) {
|
||||
if (!can_sleep) {
|
||||
work = kzalloc(sizeof(*work), GFP_ATOMIC);
|
||||
if (!work) {
|
||||
netdev_err(lif->netdev, "rxmode change dropped\n");
|
||||
return;
|
||||
}
|
||||
work->type = IONIC_DW_TYPE_RX_MODE;
|
||||
work->rx_mode = rx_mode;
|
||||
netdev_dbg(lif->netdev, "deferred: rx_mode\n");
|
||||
ionic_lif_deferred_enqueue(&lif->deferred, work);
|
||||
} else {
|
||||
ionic_lif_rx_mode(lif, rx_mode);
|
||||
if (!can_sleep) {
|
||||
work = kzalloc(sizeof(*work), GFP_ATOMIC);
|
||||
if (!work) {
|
||||
netdev_err(lif->netdev, "rxmode change dropped\n");
|
||||
return;
|
||||
}
|
||||
work->type = IONIC_DW_TYPE_RX_MODE;
|
||||
netdev_dbg(lif->netdev, "deferred: rx_mode\n");
|
||||
ionic_lif_deferred_enqueue(&lif->deferred, work);
|
||||
} else {
|
||||
ionic_lif_rx_mode(lif);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3058,6 +3039,7 @@ void ionic_lif_deinit(struct ionic_lif *lif)
|
||||
ionic_lif_qcq_deinit(lif, lif->notifyqcq);
|
||||
ionic_lif_qcq_deinit(lif, lif->adminqcq);
|
||||
|
||||
mutex_destroy(&lif->config_lock);
|
||||
mutex_destroy(&lif->queue_lock);
|
||||
ionic_lif_reset(lif);
|
||||
}
|
||||
@ -3185,7 +3167,7 @@ static int ionic_station_set(struct ionic_lif *lif)
|
||||
*/
|
||||
if (!ether_addr_equal(ctx.comp.lif_getattr.mac,
|
||||
netdev->dev_addr))
|
||||
ionic_lif_addr(lif, netdev->dev_addr, ADD_ADDR, CAN_SLEEP);
|
||||
ionic_lif_addr(lif, netdev->dev_addr, ADD_ADDR);
|
||||
} else {
|
||||
/* Update the netdev mac with the device's mac */
|
||||
memcpy(addr.sa_data, ctx.comp.lif_getattr.mac, netdev->addr_len);
|
||||
@ -3202,7 +3184,7 @@ static int ionic_station_set(struct ionic_lif *lif)
|
||||
|
||||
netdev_dbg(lif->netdev, "adding station MAC addr %pM\n",
|
||||
netdev->dev_addr);
|
||||
ionic_lif_addr(lif, netdev->dev_addr, ADD_ADDR, CAN_SLEEP);
|
||||
ionic_lif_addr(lif, netdev->dev_addr, ADD_ADDR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -3225,6 +3207,7 @@ int ionic_lif_init(struct ionic_lif *lif)
|
||||
|
||||
lif->hw_index = le16_to_cpu(comp.hw_index);
|
||||
mutex_init(&lif->queue_lock);
|
||||
mutex_init(&lif->config_lock);
|
||||
|
||||
/* now that we have the hw_index we can figure out our doorbell page */
|
||||
lif->dbid_count = le32_to_cpu(lif->ionic->ident.dev.ndbpgs_per_lif);
|
||||
|
@ -108,7 +108,6 @@ struct ionic_deferred_work {
|
||||
struct list_head list;
|
||||
enum ionic_deferred_work_type type;
|
||||
union {
|
||||
unsigned int rx_mode;
|
||||
u8 addr[ETH_ALEN];
|
||||
u8 fw_status;
|
||||
};
|
||||
@ -179,6 +178,7 @@ struct ionic_lif {
|
||||
unsigned int index;
|
||||
unsigned int hw_index;
|
||||
struct mutex queue_lock; /* lock for queue structures */
|
||||
struct mutex config_lock; /* lock for config actions */
|
||||
spinlock_t adminq_lock; /* lock for AdminQ operations */
|
||||
struct ionic_qcq *adminqcq;
|
||||
struct ionic_qcq *notifyqcq;
|
||||
@ -199,7 +199,7 @@ struct ionic_lif {
|
||||
unsigned int nrxq_descs;
|
||||
u32 rx_copybreak;
|
||||
u64 rxq_features;
|
||||
unsigned int rx_mode;
|
||||
u16 rx_mode;
|
||||
u64 hw_features;
|
||||
bool registered;
|
||||
bool mc_overflow;
|
||||
|
Loading…
Reference in New Issue
Block a user