summaryrefslogtreecommitdiff
path: root/drivers/net/ethernet/pensando/ionic/ionic_lif.c
diff options
context:
space:
mode:
authorShannon Nelson <snelson@pensando.io>2021-08-25 18:24:48 -0700
committerDavid S. Miller <davem@davemloft.net>2021-08-26 09:41:50 +0100
commit969f843946041a8ac10a5af06127a68ab7880ad5 (patch)
tree7d1921d57f5803d98d7054f075f0c6f702c9a5b9 /drivers/net/ethernet/pensando/ionic/ionic_lif.c
parentb941ea057177daf7dd661959803f351808754e6e (diff)
ionic: sync the filters in the work task
In order to separate the atomic needs of __dev_uc_sync() and __dev_mc_sync() from the safe rx_mode handling, we need to have the ndo handler manipulate the driver's filter list, and later have the driver sync the filters to the firmware, outside of the atomic context. Here we put __dev_mc_sync() and __dev_uc_sync() back into the ndo callback to give them their netif_addr_lock context and have them update the driver's filter list, flagging changes that should be made to the device filter list. Later, in the rx_mode handler, we read those hints and sync up the device's list as needed. It is possible for multiple add/delete requests to come from the stack before the rx_mode task processes the list, but the handling of the sync status flag should keep everything sorted correctly. For example, if a delete of an existing filter is followed by another add before the rx_mode task is run, as can happen when going in and out of a bond, the add will cancel the delete and no actual changes will be sent to the device. We also add a check in the watchdog to see if there are any stray unsync'd filters, possibly left over from a filter overflow and waiting to get sync'd after some other filter gets removed to make room. Signed-off-by: Shannon Nelson <snelson@pensando.io> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/ethernet/pensando/ionic/ionic_lif.c')
-rw-r--r--drivers/net/ethernet/pensando/ionic/ionic_lif.c108
1 files changed, 81 insertions, 27 deletions
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.c b/drivers/net/ethernet/pensando/ionic/ionic_lif.c
index 60bc1251d995..7812991f4736 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_lif.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.c
@@ -30,9 +30,6 @@ static const u8 ionic_qtype_versions[IONIC_QTYPE_MAX] = {
*/
};
-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);
static void ionic_lif_handle_fw_down(struct ionic_lif *lif);
static void ionic_lif_handle_fw_up(struct ionic_lif *lif);
@@ -1072,7 +1069,11 @@ static int ionic_lif_add_hwstamp_rxfilt(struct ionic_lif *lif, u64 pkt_class)
if (err && err != -EEXIST)
return err;
- return ionic_rx_filter_save(lif, 0, qid, 0, &ctx);
+ spin_lock_bh(&lif->rx_filters.lock);
+ err = ionic_rx_filter_save(lif, 0, qid, 0, &ctx, IONIC_FILTER_STATE_SYNCED);
+ spin_unlock_bh(&lif->rx_filters.lock);
+
+ return err;
}
int ionic_lif_set_hwstamp_rxfilt(struct ionic_lif *lif, u64 pkt_class)
@@ -1245,7 +1246,7 @@ void ionic_get_stats64(struct net_device *netdev,
ns->tx_errors = ns->tx_aborted_errors;
}
-static int ionic_lif_addr_add(struct ionic_lif *lif, const u8 *addr)
+int ionic_lif_addr_add(struct ionic_lif *lif, const u8 *addr)
{
struct ionic_admin_ctx ctx = {
.work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
@@ -1256,26 +1257,64 @@ static int ionic_lif_addr_add(struct ionic_lif *lif, const u8 *addr)
},
};
struct ionic_rx_filter *f;
- int err;
+ int err = 0;
- /* don't bother if we already have it */
spin_lock_bh(&lif->rx_filters.lock);
f = ionic_rx_filter_by_addr(lif, addr);
+ if (f) {
+ /* don't bother if we already have it and it is sync'd */
+ if (f->state == IONIC_FILTER_STATE_SYNCED) {
+ spin_unlock_bh(&lif->rx_filters.lock);
+ return 0;
+ }
+
+ /* mark preemptively as sync'd to block any parallel attempts */
+ f->state = IONIC_FILTER_STATE_SYNCED;
+ } else {
+ /* save as SYNCED to catch any DEL requests while processing */
+ memcpy(ctx.cmd.rx_filter_add.mac.addr, addr, ETH_ALEN);
+ err = ionic_rx_filter_save(lif, 0, IONIC_RXQ_INDEX_ANY, 0, &ctx,
+ IONIC_FILTER_STATE_SYNCED);
+ }
spin_unlock_bh(&lif->rx_filters.lock);
- if (f)
- return 0;
+ if (err)
+ return err;
netdev_dbg(lif->netdev, "rx_filter add ADDR %pM\n", addr);
- memcpy(ctx.cmd.rx_filter_add.mac.addr, addr, ETH_ALEN);
err = ionic_adminq_post_wait(lif, &ctx);
- if (err && err != -EEXIST)
+
+ spin_lock_bh(&lif->rx_filters.lock);
+ if (err && err != -EEXIST) {
+ /* set the state back to NEW so we can try again later */
+ f = ionic_rx_filter_by_addr(lif, addr);
+ if (f && f->state == IONIC_FILTER_STATE_SYNCED)
+ f->state = IONIC_FILTER_STATE_NEW;
+
+ spin_unlock_bh(&lif->rx_filters.lock);
return err;
+ }
+
+ f = ionic_rx_filter_by_addr(lif, addr);
+ if (f && f->state == IONIC_FILTER_STATE_OLD) {
+ /* Someone requested a delete while we were adding
+ * so update the filter info with the results from the add
+ * and the data will be there for the delete on the next
+ * sync cycle.
+ */
+ err = ionic_rx_filter_save(lif, 0, IONIC_RXQ_INDEX_ANY, 0, &ctx,
+ IONIC_FILTER_STATE_OLD);
+ } else {
+ err = ionic_rx_filter_save(lif, 0, IONIC_RXQ_INDEX_ANY, 0, &ctx,
+ IONIC_FILTER_STATE_SYNCED);
+ }
- return ionic_rx_filter_save(lif, 0, IONIC_RXQ_INDEX_ANY, 0, &ctx);
+ spin_unlock_bh(&lif->rx_filters.lock);
+
+ return err;
}
-static int ionic_lif_addr_del(struct ionic_lif *lif, const u8 *addr)
+int ionic_lif_addr_del(struct ionic_lif *lif, const u8 *addr)
{
struct ionic_admin_ctx ctx = {
.work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
@@ -1285,6 +1324,7 @@ static int ionic_lif_addr_del(struct ionic_lif *lif, const u8 *addr)
},
};
struct ionic_rx_filter *f;
+ int state;
int err;
spin_lock_bh(&lif->rx_filters.lock);
@@ -1297,13 +1337,16 @@ static int ionic_lif_addr_del(struct ionic_lif *lif, const u8 *addr)
netdev_dbg(lif->netdev, "rx_filter del ADDR %pM (id %d)\n",
addr, f->filter_id);
+ state = f->state;
ctx.cmd.rx_filter_del.filter_id = cpu_to_le32(f->filter_id);
ionic_rx_filter_free(lif, f);
spin_unlock_bh(&lif->rx_filters.lock);
- err = ionic_adminq_post_wait(lif, &ctx);
- if (err && err != -EEXIST)
- return err;
+ if (state != IONIC_FILTER_STATE_NEW) {
+ err = ionic_adminq_post_wait(lif, &ctx);
+ if (err && err != -EEXIST)
+ return err;
+ }
return 0;
}
@@ -1347,15 +1390,15 @@ static int ionic_lif_addr(struct ionic_lif *lif, const u8 *addr, bool add)
static int ionic_addr_add(struct net_device *netdev, const u8 *addr)
{
- return ionic_lif_addr(netdev_priv(netdev), addr, ADD_ADDR);
+ return ionic_lif_list_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);
+ return ionic_lif_list_addr(netdev_priv(netdev), addr, DEL_ADDR);
}
-static void ionic_lif_rx_mode(struct ionic_lif *lif)
+void ionic_lif_rx_mode(struct ionic_lif *lif)
{
struct net_device *netdev = lif->netdev;
unsigned int nfilters;
@@ -1376,14 +1419,15 @@ static void ionic_lif_rx_mode(struct ionic_lif *lif)
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
+ /* sync the mac filters */
+ ionic_rx_filter_sync(lif);
+
+ /* check for 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;
@@ -1394,8 +1438,6 @@ static void ionic_lif_rx_mode(struct ionic_lif *lif)
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;
@@ -1449,6 +1491,13 @@ static void ionic_ndo_set_rx_mode(struct net_device *netdev)
struct ionic_lif *lif = netdev_priv(netdev);
struct ionic_deferred_work *work;
+ /* Sync the kernel filter list with the driver filter list */
+ __dev_uc_sync(netdev, ionic_addr_add, ionic_addr_del);
+ __dev_mc_sync(netdev, ionic_addr_add, ionic_addr_del);
+
+ /* Shove off the rest of the rxmode work to the work task
+ * which will include syncing the filters to the firmware.
+ */
work = kzalloc(sizeof(*work), GFP_ATOMIC);
if (!work) {
netdev_err(lif->netdev, "rxmode change dropped\n");
@@ -1677,13 +1726,13 @@ static int ionic_set_mac_address(struct net_device *netdev, void *sa)
if (!is_zero_ether_addr(netdev->dev_addr)) {
netdev_info(netdev, "deleting mac addr %pM\n",
netdev->dev_addr);
- ionic_addr_del(netdev, netdev->dev_addr);
+ ionic_lif_addr_del(netdev_priv(netdev), netdev->dev_addr);
}
eth_commit_mac_addr_change(netdev, addr);
netdev_info(netdev, "updating mac addr %pM\n", mac);
- return ionic_addr_add(netdev, mac);
+ return ionic_lif_addr_add(netdev_priv(netdev), mac);
}
static void ionic_stop_queues_reconfig(struct ionic_lif *lif)
@@ -1789,7 +1838,12 @@ static int ionic_vlan_rx_add_vid(struct net_device *netdev, __be16 proto,
if (err)
return err;
- return ionic_rx_filter_save(lif, 0, IONIC_RXQ_INDEX_ANY, 0, &ctx);
+ spin_lock_bh(&lif->rx_filters.lock);
+ err = ionic_rx_filter_save(lif, 0, IONIC_RXQ_INDEX_ANY, 0, &ctx,
+ IONIC_FILTER_STATE_SYNCED);
+ spin_unlock_bh(&lif->rx_filters.lock);
+
+ return err;
}
static int ionic_vlan_rx_kill_vid(struct net_device *netdev, __be16 proto,