[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20151023192824.GA21943@ketchup.mtl.sfl>
Date: Fri, 23 Oct 2015 15:28:24 -0400
From: Vivien Didelot <vivien.didelot@...oirfairelinux.com>
To: Florian Fainelli <f.fainelli@...il.com>
Cc: netdev@...r.kernel.org, davem@...emloft.net, jiri@...nulli.us,
andrew@...n.ch, linux@...ck-us.net
Subject: Re: [PATCH net-next] net: dsa: bcm_sf2: Implement FDB operations
On Oct. Friday 23 (43) 11:38 AM, Florian Fainelli wrote:
> Add support for the FDB add, delete, and dump operations. The add and
> delete operations are implemented using directed ARL operations using
> the specified MAC address and consist in a read operation, write and
> readback operation.
>
> The dump operation consists in using the ARL search and software
> filtering entries which are not for the desired port.
>
> Signed-off-by: Florian Fainelli <f.fainelli@...il.com>
> ---
> drivers/net/dsa/bcm_sf2.c | 236 +++++++++++++++++++++++++++++++++++++++++
> drivers/net/dsa/bcm_sf2.h | 56 ++++++++++
> drivers/net/dsa/bcm_sf2_regs.h | 43 ++++++++
> 3 files changed, 335 insertions(+)
>
> diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c
> index 9d56515f4c4d..4f32b8a530bf 100644
> --- a/drivers/net/dsa/bcm_sf2.c
> +++ b/drivers/net/dsa/bcm_sf2.c
> @@ -25,6 +25,8 @@
> #include <linux/ethtool.h>
> #include <linux/if_bridge.h>
> #include <linux/brcmphy.h>
> +#include <linux/etherdevice.h>
> +#include <net/switchdev.h>
>
> #include "bcm_sf2.h"
> #include "bcm_sf2_regs.h"
> @@ -555,6 +557,236 @@ static int bcm_sf2_sw_br_set_stp_state(struct dsa_switch *ds, int port,
> return 0;
> }
>
> +/* Address Resolution Logic routines */
> +static int bcm_sf2_arl_op_wait(struct bcm_sf2_priv *priv)
> +{
> + unsigned int timeout = 10;
> + u32 reg;
> +
> + do {
> + reg = core_readl(priv, CORE_ARLA_RWCTL);
> + if (!(reg & ARL_STRTDN))
> + return 0;
> +
> + usleep_range(1000, 2000);
> + } while (timeout--);
> +
> + return -ETIMEDOUT;
> +}
> +
> +static int bcm_sf2_arl_rw_op(struct bcm_sf2_priv *priv, unsigned int op)
> +{
> + u32 cmd;
> +
> + if (op > ARL_RW)
> + return -EINVAL;
> +
> + cmd = core_readl(priv, CORE_ARLA_RWCTL);
> + cmd &= ~IVL_SVL_SELECT;
> + cmd |= ARL_STRTDN;
> + if (op)
> + cmd |= ARL_RW;
> + else
> + cmd &= ~ARL_RW;
> + core_writel(priv, cmd, CORE_ARLA_RWCTL);
> +
> + return bcm_sf2_arl_op_wait(priv);
> +}
> +
> +static int bcm_sf2_arl_read(struct bcm_sf2_priv *priv, u64 mac,
> + u16 vid, struct bcm_sf2_arl_entry *ent, u8 *idx,
> + bool is_valid)
> +{
> + unsigned int i;
> + int ret;
> +
> + ret = bcm_sf2_arl_op_wait(priv);
> + if (ret)
> + return ret;
> +
> + /* Read the 4 bins */
> + for (i = 0; i < 4; i++) {
> + u64 mac_vid;
> + u32 fwd_entry;
> +
> + mac_vid = core_readq(priv, CORE_ARLA_MACVID_ENTRY(i));
> + fwd_entry = core_readl(priv, CORE_ARLA_FWD_ENTRY(i));
> + bcm_sf2_arl_to_entry(ent, mac_vid, fwd_entry);
> +
> + if (ent->is_valid && is_valid) {
> + *idx = i;
> + return 0;
> + }
> +
> + /* This is the MAC we just deleted */
> + if (!is_valid && (mac_vid & mac))
> + return 0;
> + }
> +
> + return -ENOENT;
> +}
What is the purpose of the "vid" parameter in bcm_sf2_arl_read?
> +
> +static int bcm_sf2_arl_op(struct bcm_sf2_priv *priv, int op, int port,
> + const unsigned char *addr, u16 vid, bool is_valid)
> +{
> + struct bcm_sf2_arl_entry ent;
> + u32 fwd_entry;
> + u64 mac, mac_vid = 0;
> + u8 idx = 0;
> + int ret;
> +
> + /* Convert the array into a 64-bit MAC */
> + mac = bcm_sf2_mac_to_u64(addr);
> +
> + /* Perform a read for the given MAC and VID */
> + core_writeq(priv, mac, CORE_ARLA_MAC);
> + core_writel(priv, vid, CORE_ARLA_VID);
> +
> + /* Issue a read operation for this MAC */
> + ret = bcm_sf2_arl_rw_op(priv, 1);
> + if (ret)
> + return ret;
> +
> + ret = bcm_sf2_arl_read(priv, mac, vid, &ent, &idx, is_valid);
> + /* If this is a read, just finish now */
> + if (op)
> + return ret;
> +
> + /* We could not find a matching MAC, so reset to a new entry */
> + if (ret) {
> + fwd_entry = 0;
> + idx = 0;
> + }
> +
> + memset(&ent, 0, sizeof(ent));
> + ent.port = port;
> + ent.is_valid = is_valid;
> + ent.vid = vid;
> + ent.is_static = true;
> + memcpy(ent.mac, addr, ETH_ALEN);
> + bcm_sf2_arl_from_entry(&mac_vid, &fwd_entry, &ent);
> +
> + core_writeq(priv, mac_vid, CORE_ARLA_MACVID_ENTRY(idx));
> + core_writel(priv, fwd_entry, CORE_ARLA_FWD_ENTRY(idx));
> +
> + ret = bcm_sf2_arl_rw_op(priv, 0);
> + if (ret)
> + return ret;
> +
> + /* Re-read the entry to check */
> + return bcm_sf2_arl_read(priv, mac, vid, &ent, &idx, is_valid);
> +}
> +
> +static int bcm_sf2_sw_fdb_prepare(struct dsa_switch *ds, int port,
> + const struct switchdev_obj_port_fdb *fdb,
> + struct switchdev_trans *trans)
> +{
> + /* We do not need to do anything specific here yet */
> + return 0;
> +}
> +
> +static int bcm_sf2_sw_fdb_add(struct dsa_switch *ds, int port,
> + const struct switchdev_obj_port_fdb *fdb,
> + struct switchdev_trans *trans)
> +{
> + struct bcm_sf2_priv *priv = ds_to_priv(ds);
> +
> + return bcm_sf2_arl_op(priv, 0, port, fdb->addr, fdb->vid, true);
> +}
> +
> +static int bcm_sf2_sw_fdb_del(struct dsa_switch *ds, int port,
> + const struct switchdev_obj_port_fdb *fdb)
> +{
> + struct bcm_sf2_priv *priv = ds_to_priv(ds);
> +
> + return bcm_sf2_arl_op(priv, 0, port, fdb->addr, fdb->vid, false);
> +}
I'm wondering if you are populating the FDB of the invalid VLAN 0 here.
Does your ARL consider that fdb->vid == 0 means "this port's FDB" and
not "FDB of VLAN 0"?
> +
> +static int bcm_sf2_arl_search_wait(struct bcm_sf2_priv *priv)
> +{
> + unsigned timeout = 1000;
> + u32 reg;
> +
> + do {
> + reg = core_readl(priv, CORE_ARLA_SRCH_CTL);
> + if (!(reg & ARLA_SRCH_STDN))
> + return 0;
> +
> + if (reg & ARLA_SRCH_VLID)
> + return 0;
> +
> + usleep_range(1000, 2000);
> + } while (timeout--);
> +
> + return -ETIMEDOUT;
> +}
> +
> +static void bcm_sf2_arl_search_rd(struct bcm_sf2_priv *priv, u8 idx,
> + struct bcm_sf2_arl_entry *ent)
> +{
> + u64 mac_vid;
> + u32 fwd_entry;
> +
> + mac_vid = core_readq(priv, CORE_ARLA_SRCH_RSLT_MACVID(idx));
> + fwd_entry = core_readl(priv, CORE_ARLA_SRCH_RSLT(idx));
> + bcm_sf2_arl_to_entry(ent, mac_vid, fwd_entry);
> +}
> +
> +static int bcm_sf2_sw_fdb_copy(struct net_device *dev, int port,
> + const struct bcm_sf2_arl_entry *ent,
> + struct switchdev_obj_port_fdb *fdb,
> + int (*cb)(struct switchdev_obj *obj))
> +{
> + if (!ent->is_valid)
> + return 0;
> +
> + if (port != ent->port)
> + return 0;
> +
> + ether_addr_copy(fdb->addr, ent->mac);
> + fdb->vid = ent->vid;
> + fdb->ndm_state = ent->is_static ? NUD_NOARP : NUD_REACHABLE;
> +
> + return cb(&fdb->obj);
> +}
> +
> +static int bcm_sf2_sw_fdb_dump(struct dsa_switch *ds, int port,
> + struct switchdev_obj_port_fdb *fdb,
> + int (*cb)(struct switchdev_obj *obj))
> +{
> + struct bcm_sf2_priv *priv = ds_to_priv(ds);
> + struct net_device *dev = ds->ports[port];
> + struct bcm_sf2_arl_entry results[2];
> + unsigned int count = 0;
> + int ret;
> +
> + /* Start search operation */
> + core_writel(priv, ARLA_SRCH_STDN, CORE_ARLA_SRCH_CTL);
> +
> + do {
> + ret = bcm_sf2_arl_search_wait(priv);
> + if (ret)
> + return ret;
> +
> + /* Read both entries, then return their values back */
> + bcm_sf2_arl_search_rd(priv, 0, &results[0]);
> + ret = bcm_sf2_sw_fdb_copy(dev, port, &results[0], fdb, cb);
> + if (ret)
> + return ret;
> +
> + bcm_sf2_arl_search_rd(priv, 1, &results[1]);
> + ret = bcm_sf2_sw_fdb_copy(dev, port, &results[1], fdb, cb);
> + if (ret)
> + return ret;
> +
> + if (!results[0].is_valid && !results[1].is_valid)
> + break;
> +
> + } while (count++ < CORE_ARLA_NUM_ENTRIES);
> +
> + return 0;
> +}
> +
> static irqreturn_t bcm_sf2_switch_0_isr(int irq, void *dev_id)
> {
> struct bcm_sf2_priv *priv = dev_id;
> @@ -1076,6 +1308,10 @@ static struct dsa_switch_driver bcm_sf2_switch_driver = {
> .port_join_bridge = bcm_sf2_sw_br_join,
> .port_leave_bridge = bcm_sf2_sw_br_leave,
> .port_stp_update = bcm_sf2_sw_br_set_stp_state,
> + .port_fdb_prepare = bcm_sf2_sw_fdb_prepare,
> + .port_fdb_add = bcm_sf2_sw_fdb_add,
> + .port_fdb_del = bcm_sf2_sw_fdb_del,
> + .port_fdb_dump = bcm_sf2_sw_fdb_dump,
> };
>
> static int __init bcm_sf2_init(void)
> diff --git a/drivers/net/dsa/bcm_sf2.h b/drivers/net/dsa/bcm_sf2.h
> index 789d7b7737da..cc98abc0aaf3 100644
> --- a/drivers/net/dsa/bcm_sf2.h
> +++ b/drivers/net/dsa/bcm_sf2.h
> @@ -19,6 +19,8 @@
> #include <linux/mutex.h>
> #include <linux/mii.h>
> #include <linux/ethtool.h>
> +#include <linux/types.h>
> +#include <linux/bitops.h>
>
> #include <net/dsa.h>
>
> @@ -50,6 +52,60 @@ struct bcm_sf2_port_status {
> u32 vlan_ctl_mask;
> };
>
> +struct bcm_sf2_arl_entry {
> + u8 port;
> + u8 mac[ETH_ALEN];
> + u16 vid;
> + u8 is_valid:1;
> + u8 is_age:1;
> + u8 is_static:1;
> +};
> +
> +static inline void bcm_sf2_mac_from_u64(u64 src, u8 *dst)
> +{
> + unsigned int i;
> +
> + for (i = 0; i < ETH_ALEN; i++)
> + dst[ETH_ALEN - 1 - i] = (src >> (8 * i)) & 0xff;
> +}
> +
> +static inline u64 bcm_sf2_mac_to_u64(const u8 *src)
> +{
> + unsigned int i;
> + u64 dst = 0;
> +
> + for (i = 0; i < ETH_ALEN; i++)
> + dst |= (u64)src[ETH_ALEN - 1 - i] << (8 * i);
> +
> + return dst;
> +}
> +
> +static inline void bcm_sf2_arl_to_entry(struct bcm_sf2_arl_entry *ent,
> + u64 mac_vid, u32 fwd_entry)
> +{
> + memset(ent, 0, sizeof(*ent));
> + ent->port = fwd_entry & PORTID_MASK;
> + ent->is_valid = !!(fwd_entry & ARL_VALID);
> + ent->is_age = !!(fwd_entry & ARL_AGE);
> + ent->is_static = !!(fwd_entry & ARL_STATIC);
> + bcm_sf2_mac_from_u64(mac_vid, ent->mac);
> + ent->vid = mac_vid >> VID_SHIFT;
> +}
> +
> +static inline void bcm_sf2_arl_from_entry(u64 *mac_vid, u32 *fwd_entry,
> + const struct bcm_sf2_arl_entry *ent)
> +{
> + *mac_vid = bcm_sf2_mac_to_u64(ent->mac);
> + *mac_vid |= (u64)(ent->vid & VID_MASK) << VID_SHIFT;
> + *fwd_entry = ent->port & PORTID_MASK;
> + if (ent->is_valid)
> + *fwd_entry |= ARL_VALID;
> + if (ent->is_static)
> + *fwd_entry |= ARL_STATIC;
> + if (ent->is_age)
> + *fwd_entry |= ARL_AGE;
> +}
> +
> struct bcm_sf2_priv {
> /* Base registers, keep those in order with BCM_SF2_REGS_NAME */
> void __iomem *core;
> diff --git a/drivers/net/dsa/bcm_sf2_regs.h b/drivers/net/dsa/bcm_sf2_regs.h
> index fa4e6e78c9ea..97780d43b5c0 100644
> --- a/drivers/net/dsa/bcm_sf2_regs.h
> +++ b/drivers/net/dsa/bcm_sf2_regs.h
> @@ -231,6 +231,49 @@
> #define CORE_BRCM_HDR_RX_DIS 0x0980
> #define CORE_BRCM_HDR_TX_DIS 0x0988
>
> +#define CORE_ARLA_NUM_ENTRIES 1024
> +
> +#define CORE_ARLA_RWCTL 0x1400
> +#define ARL_RW (1 << 0)
> +#define IVL_SVL_SELECT (1 << 6)
> +#define ARL_STRTDN (1 << 7)
> +
> +#define CORE_ARLA_MAC 0x1408
> +#define CORE_ARLA_VID 0x1420
> +#define ARLA_VIDTAB_INDX_MASK 0x1fff
> +
> +#define CORE_ARLA_MACVID0 0x1440
> +#define MAC_MASK 0xffffffffff
> +#define VID_SHIFT 48
> +#define VID_MASK 0xfff
> +
> +#define CORE_ARLA_FWD_ENTRY0 0x1460
> +#define PORTID_MASK 0x1ff
> +#define ARL_CON_SHIFT 9
> +#define ARL_CON_MASK 0x3
> +#define ARL_PRI_SHIFT 11
> +#define ARL_PRI_MASK 0x7
> +#define ARL_AGE (1 << 14)
> +#define ARL_STATIC (1 << 15)
> +#define ARL_VALID (1 << 16)
> +
> +#define CORE_ARLA_MACVID_ENTRY(x) (CORE_ARLA_MACVID0 + ((x) * 0x40))
> +#define CORE_ARLA_FWD_ENTRY(x) (CORE_ARLA_FWD_ENTRY0 + ((x) * 0x40))
> +
> +#define CORE_ARLA_SRCH_CTL 0x1540
> +#define ARLA_SRCH_VLID (1 << 0)
> +#define IVL_SVL_SELECT (1 << 6)
> +#define ARLA_SRCH_STDN (1 << 7)
> +
> +#define CORE_ARLA_SRCH_ADR 0x1544
> +#define ARLA_SRCH_ADR_VALID (1 << 15)
> +
> +#define CORE_ARLA_SRCH_RSLT_0_MACVID 0x1580
> +#define CORE_ARLA_SRCH_RSLT_0 0x15a0
> +
> +#define CORE_ARLA_SRCH_RSLT_MACVID(x) (CORE_ARLA_SRCH_RSLT_0_MACVID + ((x) * 0x40))
> +#define CORE_ARLA_SRCH_RSLT(x) (CORE_ARLA_SRCH_RSLT_0 + ((x) * 0x40))
> +
> #define CORE_MEM_PSM_VDD_CTRL 0x2380
> #define P_TXQ_PSM_VDD_SHIFT 2
> #define P_TXQ_PSM_VDD_MASK 0x3
> --
> 2.1.0
>
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Powered by blists - more mailing lists