ionic: handle mac filter overflow
authorShannon Nelson <snelson@pensando.io>
Thu, 26 Aug 2021 01:24:50 +0000 (18:24 -0700)
committerDavid S. Miller <davem@davemloft.net>
Thu, 26 Aug 2021 08:41:50 +0000 (09:41 +0100)
Make sure we go into PROMISC mode when we have too many
filters by specifically counting the filters that successfully
get saved to the firmware.

The device advertises max_ucast_filters and max_mcast_filters,
but really only has max_ucast_filters slots available for
uc and mc filters combined.

Signed-off-by: Shannon Nelson <snelson@pensando.io>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/pensando/ionic/ionic_lif.c

index b248c2e..e494d6b 100644 (file)
@@ -1256,6 +1256,8 @@ int ionic_lif_addr_add(struct ionic_lif *lif, const u8 *addr)
                        .match = cpu_to_le16(IONIC_RX_FILTER_MATCH_MAC),
                },
        };
+       int nfilters = le32_to_cpu(lif->identity->eth.max_ucast_filters);
+       bool mc = is_multicast_ether_addr(addr);
        struct ionic_rx_filter *f;
        int err = 0;
 
@@ -1282,7 +1284,13 @@ int ionic_lif_addr_add(struct ionic_lif *lif, const u8 *addr)
 
        netdev_dbg(lif->netdev, "rx_filter add ADDR %pM\n", addr);
 
-       err = ionic_adminq_post_wait(lif, &ctx);
+       /* Don't bother with the write to FW if we know there's no room,
+        * we can try again on the next sync attempt.
+        */
+       if ((lif->nucast + lif->nmcast) >= nfilters)
+               err = -ENOSPC;
+       else
+               err = ionic_adminq_post_wait(lif, &ctx);
 
        spin_lock_bh(&lif->rx_filters.lock);
        if (err && err != -EEXIST) {
@@ -1292,9 +1300,18 @@ int ionic_lif_addr_add(struct ionic_lif *lif, const u8 *addr)
                        f->state = IONIC_FILTER_STATE_NEW;
 
                spin_unlock_bh(&lif->rx_filters.lock);
-               return err;
+
+               if (err == -ENOSPC)
+                       return 0;
+               else
+                       return err;
        }
 
+       if (mc)
+               lif->nmcast++;
+       else
+               lif->nucast++;
+
        f = ionic_rx_filter_by_addr(lif, addr);
        if (f && f->state == IONIC_FILTER_STATE_OLD) {
                /* Someone requested a delete while we were adding
@@ -1340,6 +1357,12 @@ int ionic_lif_addr_del(struct ionic_lif *lif, const u8 *addr)
        state = f->state;
        ctx.cmd.rx_filter_del.filter_id = cpu_to_le32(f->filter_id);
        ionic_rx_filter_free(lif, f);
+
+       if (is_multicast_ether_addr(addr) && lif->nmcast)
+               lif->nmcast--;
+       else if (!is_multicast_ether_addr(addr) && lif->nucast)
+               lif->nucast--;
+
        spin_unlock_bh(&lif->rx_filters.lock);
 
        if (state != IONIC_FILTER_STATE_NEW) {
@@ -1392,21 +1415,16 @@ void ionic_lif_rx_mode(struct ionic_lif *lif)
         *       to see if we can disable NIC PROMISC
         */
        nfilters = le32_to_cpu(lif->identity->eth.max_ucast_filters);
-       if (netdev_uc_count(netdev) + 1 > nfilters) {
+       if ((lif->nucast + lif->nmcast) >= nfilters) {
                rx_mode |= IONIC_RX_MODE_F_PROMISC;
+               rx_mode |= IONIC_RX_MODE_F_ALLMULTI;
                lif->uc_overflow = true;
+               lif->mc_overflow = true;
        } else if (lif->uc_overflow) {
                lif->uc_overflow = false;
+               lif->mc_overflow = false;
                if (!(nd_flags & IFF_PROMISC))
                        rx_mode &= ~IONIC_RX_MODE_F_PROMISC;
-       }
-
-       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;
        }