net: bcmgenet: use promisc for unsupported filters [Linux 4.4.187]

This Linux kernel change "net: bcmgenet: use promisc for unsupported filters" is included in the Linux 4.4.187 release. This change is authored by Justin Chen <justinpopo6 [at] gmail.com> on Wed Jul 17 14:58:53 2019 -0700. The commit for this change in Linux stable tree is 7165159 (patch) which is from upstream commit 35cbef9. The same Linux upstream change may have been applied to various maintained Linux releases and you can find all Linux releases containing changes from upstream 35cbef9.

net: bcmgenet: use promisc for unsupported filters

[ Upstream commit 35cbef9863640f06107144687bd13151bc2e8ce3 ]

Currently we silently ignore filters if we cannot meet the filter
requirements. This will lead to the MAC dropping packets that are
expected to pass. A better solution would be to set the NIC to promisc
mode when the required filters cannot be met.

Also correct the number of MDF filters supported. It should be 17,
not 16.

Signed-off-by: Justin Chen <[email protected]>
Reviewed-by: Florian Fainelli <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>

There are 57 lines of Linux source code added/deleted in this change. Code changes to Linux kernel are as follows.

 drivers/net/ethernet/broadcom/genet/bcmgenet.c | 57 ++++++++++++--------------
 1 file changed, 26 insertions(+), 31 deletions(-)

diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c
index 74dd48f..04fe570 100644
--- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c
+++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c
@@ -3090,39 +3090,42 @@ static void bcmgenet_timeout(struct net_device *dev)
    netif_tx_wake_all_queues(dev);
 }

-#define MAX_MC_COUNT   16
+#define MAX_MDF_FILTER 17

 static inline void bcmgenet_set_mdf_addr(struct bcmgenet_priv *priv,
                     unsigned char *addr,
-                    int *i,
-                    int *mc)
+                    int *i)
 {
-   u32 reg;
-
    bcmgenet_umac_writel(priv, addr[0] << 8 | addr[1],
                 UMAC_MDF_ADDR + (*i * 4));
    bcmgenet_umac_writel(priv, addr[2] << 24 | addr[3] << 16 |
                 addr[4] << 8 | addr[5],
                 UMAC_MDF_ADDR + ((*i + 1) * 4));
-   reg = bcmgenet_umac_readl(priv, UMAC_MDF_CTRL);
-   reg |= (1 << (MAX_MC_COUNT - *mc));
-   bcmgenet_umac_writel(priv, reg, UMAC_MDF_CTRL);
    *i += 2;
-   (*mc)++;
 }

 static void bcmgenet_set_rx_mode(struct net_device *dev)
 {
    struct bcmgenet_priv *priv = netdev_priv(dev);
    struct netdev_hw_addr *ha;
-   int i, mc;
+   int i, nfilter;
    u32 reg;

    netif_dbg(priv, hw, dev, "%s: %08X\n", __func__, dev->flags);

-   /* Promiscuous mode */
+   /* Number of filters needed */
+   nfilter = netdev_uc_count(dev) + netdev_mc_count(dev) + 2;
+
+   /*
+    * Turn on promicuous mode for three scenarios
+    * 1. IFF_PROMISC flag is set
+    * 2. IFF_ALLMULTI flag is set
+    * 3. The number of filters needed exceeds the number filters
+    *    supported by the hardware.
+   */
    reg = bcmgenet_umac_readl(priv, UMAC_CMD);
-   if (dev->flags & IFF_PROMISC) {
+   if ((dev->flags & (IFF_PROMISC | IFF_ALLMULTI)) ||
+       (nfilter > MAX_MDF_FILTER)) {
        reg |= CMD_PROMISC;
        bcmgenet_umac_writel(priv, reg, UMAC_CMD);
        bcmgenet_umac_writel(priv, 0, UMAC_MDF_CTRL);
@@ -3132,32 +3135,24 @@ static void bcmgenet_set_rx_mode(struct net_device *dev)
        bcmgenet_umac_writel(priv, reg, UMAC_CMD);
    }

-   /* UniMac doesn't support ALLMULTI */
-   if (dev->flags & IFF_ALLMULTI) {
-       netdev_warn(dev, "ALLMULTI is not supported\n");
-       return;
-   }
-
    /* update MDF filter */
    i = 0;
-   mc = 0;
    /* Broadcast */
-   bcmgenet_set_mdf_addr(priv, dev->broadcast, &i, &mc);
+   bcmgenet_set_mdf_addr(priv, dev->broadcast, &i);
    /* my own address.*/
-   bcmgenet_set_mdf_addr(priv, dev->dev_addr, &i, &mc);
-   /* Unicast list*/
-   if (netdev_uc_count(dev) > (MAX_MC_COUNT - mc))
-       return;
+   bcmgenet_set_mdf_addr(priv, dev->dev_addr, &i);

-   if (!netdev_uc_empty(dev))
-       netdev_for_each_uc_addr(ha, dev)
-           bcmgenet_set_mdf_addr(priv, ha->addr, &i, &mc);
-   /* Multicast */
-   if (netdev_mc_empty(dev) || netdev_mc_count(dev) >= (MAX_MC_COUNT - mc))
-       return;
+   /* Unicast */
+   netdev_for_each_uc_addr(ha, dev)
+       bcmgenet_set_mdf_addr(priv, ha->addr, &i);

+   /* Multicast */
    netdev_for_each_mc_addr(ha, dev)
-       bcmgenet_set_mdf_addr(priv, ha->addr, &i, &mc);
+       bcmgenet_set_mdf_addr(priv, ha->addr, &i);
+
+   /* Enable filters */
+   reg = GENMASK(MAX_MDF_FILTER - 1, MAX_MDF_FILTER - nfilter);
+   bcmgenet_umac_writel(priv, reg, UMAC_MDF_CTRL);
 }

 /* Set the hardware MAC address. */

Leave a Reply

Your email address will not be published. Required fields are marked *