From: Florian Fainelli <f.faine...@gmail.com>

[ Upstream commit 6344dbde6a27d10d16246d734b968f84887841e2 ]

When asking the ARL to read a MAC address, we will get a number of bins
returned in a single read. Out of those bins, there can essentially be 3
states:

- all bins are full, we have no space left, and we can either replace an
  existing address or return that full condition

- the MAC address was found, then we need to return its bin index and
  modify that one, and only that one

- the MAC address was not found and we have a least one bin free, we use
  that bin index location then

The code would unfortunately fail on all counts.

Fixes: 1da6df85c6fb ("net: dsa: b53: Implement ARL add/del/dump operations")
Signed-off-by: Florian Fainelli <f.faine...@gmail.com>
Signed-off-by: David S. Miller <da...@davemloft.net>
Signed-off-by: Greg Kroah-Hartman <gre...@linuxfoundation.org>
---
 drivers/net/dsa/b53/b53_common.c |   30 ++++++++++++++++++++++++++----
 drivers/net/dsa/b53/b53_regs.h   |    3 +++
 2 files changed, 29 insertions(+), 4 deletions(-)

--- a/drivers/net/dsa/b53/b53_common.c
+++ b/drivers/net/dsa/b53/b53_common.c
@@ -1262,6 +1262,7 @@ static int b53_arl_read(struct b53_devic
                        u16 vid, struct b53_arl_entry *ent, u8 *idx,
                        bool is_valid)
 {
+       DECLARE_BITMAP(free_bins, B53_ARLTBL_MAX_BIN_ENTRIES);
        unsigned int i;
        int ret;
 
@@ -1269,6 +1270,8 @@ static int b53_arl_read(struct b53_devic
        if (ret)
                return ret;
 
+       bitmap_zero(free_bins, dev->num_arl_entries);
+
        /* Read the bins */
        for (i = 0; i < dev->num_arl_entries; i++) {
                u64 mac_vid;
@@ -1280,16 +1283,24 @@ static int b53_arl_read(struct b53_devic
                           B53_ARLTBL_DATA_ENTRY(i), &fwd_entry);
                b53_arl_to_entry(ent, mac_vid, fwd_entry);
 
-               if (!(fwd_entry & ARLTBL_VALID))
+               if (!(fwd_entry & ARLTBL_VALID)) {
+                       set_bit(i, free_bins);
                        continue;
+               }
                if ((mac_vid & ARLTBL_MAC_MASK) != mac)
                        continue;
                if (dev->vlan_enabled &&
                    ((mac_vid >> ARLTBL_VID_S) & ARLTBL_VID_MASK) != vid)
                        continue;
                *idx = i;
+               return 0;
        }
 
+       if (bitmap_weight(free_bins, dev->num_arl_entries) == 0)
+               return -ENOSPC;
+
+       *idx = find_first_bit(free_bins, dev->num_arl_entries);
+
        return -ENOENT;
 }
 
@@ -1319,10 +1330,21 @@ static int b53_arl_op(struct b53_device
        if (op)
                return ret;
 
-       /* We could not find a matching MAC, so reset to a new entry */
-       if (ret) {
+       switch (ret) {
+       case -ENOSPC:
+               dev_dbg(dev->dev, "{%pM,%.4d} no space left in ARL\n",
+                       addr, vid);
+               return is_valid ? ret : 0;
+       case -ENOENT:
+               /* We could not find a matching MAC, so reset to a new entry */
+               dev_dbg(dev->dev, "{%pM,%.4d} not found, using idx: %d\n",
+                       addr, vid, idx);
                fwd_entry = 0;
-               idx = 1;
+               break;
+       default:
+               dev_dbg(dev->dev, "{%pM,%.4d} found, using idx: %d\n",
+                       addr, vid, idx);
+               break;
        }
 
        memset(&ent, 0, sizeof(ent));
--- a/drivers/net/dsa/b53/b53_regs.h
+++ b/drivers/net/dsa/b53/b53_regs.h
@@ -323,6 +323,9 @@
 #define   ARLTBL_STATIC                        BIT(15)
 #define   ARLTBL_VALID                 BIT(16)
 
+/* Maximum number of bin entries in the ARL for all switches */
+#define B53_ARLTBL_MAX_BIN_ENTRIES     4
+
 /* ARL Search Control Register (8 bit) */
 #define B53_ARL_SRCH_CTL               0x50
 #define B53_ARL_SRCH_CTL_25            0x20


Reply via email to