The ocelot switchdev driver does not include the CPU port in the list of
flooding destinations for unknown traffic, instead that traffic is
supposed to match FDB entries to reach the CPU.
The addresses it installs are:
(a) the station MAC address, in ocelot_probe_port() and later during
runtime in ocelot_port_set_mac_address(). These are the VLAN-unaware
addresses. The VLAN-aware addresses are in ocelot_vlan_vid_add().
(b) multicast addresses added with dev_mc_add() (not bridge host MDB
entries) in ocelot_mc_sync()
(c) multicast destination MAC addresses for MRP in ocelot_mrp_save_mac(),
to make sure those are dropped (not forwarded) by the bridging
service, just trapped to the CPU
So we can see that the logic is slightly buggy ever since the initial
commit a556c76adc
("net: mscc: Add initial Ocelot switch support").
This is because, when ocelot_probe_port() runs, the port pvid is 0.
Then we join a VLAN-aware bridge, the pvid becomes 1, we call
ocelot_port_set_mac_address(), this learns the new MAC address in VID 1
(also fails to forget the old one, since it thinks it's in VID 1, but
that's not so important). Then when we leave the VLAN-aware bridge,
outside world is unable to ping our new MAC address because it isn't
learned in VID 0, the VLAN-unaware pvid.
[ note: this is strictly based on static analysis, I don't have hardware
to test. But there are also many more corner cases ]
The basic idea is that we should have a separation of concerns, and the
FDB entries used for standalone operation should be managed by the
driver, and the FDB entries used by the bridging service should be
managed by the bridge. So the standalone and VLAN-unaware bridge FDB
entries should not follow the bridge PVID, because that will only be
active when the bridge is VLAN-aware. So since the port pvid is
coincidentally zero during probe time, just make those entries
statically go to VID 0.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
121 lines
3.5 KiB
C
121 lines
3.5 KiB
C
/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
|
|
/*
|
|
* Microsemi Ocelot Switch driver
|
|
*
|
|
* Copyright (c) 2017 Microsemi Corporation
|
|
*/
|
|
|
|
#ifndef _MSCC_OCELOT_H_
|
|
#define _MSCC_OCELOT_H_
|
|
|
|
#include <linux/bitops.h>
|
|
#include <linux/etherdevice.h>
|
|
#include <linux/if_vlan.h>
|
|
#include <linux/net_tstamp.h>
|
|
#include <linux/phylink.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/regmap.h>
|
|
|
|
#include <soc/mscc/ocelot_qsys.h>
|
|
#include <soc/mscc/ocelot_sys.h>
|
|
#include <soc/mscc/ocelot_dev.h>
|
|
#include <soc/mscc/ocelot_ana.h>
|
|
#include <soc/mscc/ocelot_ptp.h>
|
|
#include <soc/mscc/ocelot.h>
|
|
#include "ocelot_rew.h"
|
|
#include "ocelot_qs.h"
|
|
|
|
#define OCELOT_VLAN_UNAWARE_PVID 0
|
|
#define OCELOT_BUFFER_CELL_SZ 60
|
|
|
|
#define OCELOT_STATS_CHECK_DELAY (2 * HZ)
|
|
|
|
#define OCELOT_PTP_QUEUE_SZ 128
|
|
|
|
struct ocelot_port_tc {
|
|
bool block_shared;
|
|
unsigned long offload_cnt;
|
|
|
|
unsigned long police_id;
|
|
};
|
|
|
|
struct ocelot_port_private {
|
|
struct ocelot_port port;
|
|
struct net_device *dev;
|
|
struct phylink *phylink;
|
|
struct phylink_config phylink_config;
|
|
u8 chip_port;
|
|
struct ocelot_port_tc tc;
|
|
};
|
|
|
|
struct ocelot_dump_ctx {
|
|
struct net_device *dev;
|
|
struct sk_buff *skb;
|
|
struct netlink_callback *cb;
|
|
int idx;
|
|
};
|
|
|
|
/* MAC table entry types.
|
|
* ENTRYTYPE_NORMAL is subject to aging.
|
|
* ENTRYTYPE_LOCKED is not subject to aging.
|
|
* ENTRYTYPE_MACv4 is not subject to aging. For IPv4 multicast.
|
|
* ENTRYTYPE_MACv6 is not subject to aging. For IPv6 multicast.
|
|
*/
|
|
enum macaccess_entry_type {
|
|
ENTRYTYPE_NORMAL = 0,
|
|
ENTRYTYPE_LOCKED,
|
|
ENTRYTYPE_MACv4,
|
|
ENTRYTYPE_MACv6,
|
|
};
|
|
|
|
/* A (PGID) port mask structure, encoding the 2^ocelot->num_phys_ports
|
|
* possibilities of egress port masks for L2 multicast traffic.
|
|
* For a switch with 9 user ports, there are 512 possible port masks, but the
|
|
* hardware only has 46 individual PGIDs that it can forward multicast traffic
|
|
* to. So we need a structure that maps the limited PGID indices to the port
|
|
* destinations requested by the user for L2 multicast.
|
|
*/
|
|
struct ocelot_pgid {
|
|
unsigned long ports;
|
|
int index;
|
|
refcount_t refcount;
|
|
struct list_head list;
|
|
};
|
|
|
|
struct ocelot_multicast {
|
|
struct list_head list;
|
|
enum macaccess_entry_type entry_type;
|
|
unsigned char addr[ETH_ALEN];
|
|
u16 vid;
|
|
u16 ports;
|
|
struct ocelot_pgid *pgid;
|
|
};
|
|
|
|
int ocelot_port_fdb_do_dump(const unsigned char *addr, u16 vid,
|
|
bool is_static, void *data);
|
|
int ocelot_mact_learn(struct ocelot *ocelot, int port,
|
|
const unsigned char mac[ETH_ALEN],
|
|
unsigned int vid, enum macaccess_entry_type type);
|
|
int ocelot_mact_forget(struct ocelot *ocelot,
|
|
const unsigned char mac[ETH_ALEN], unsigned int vid);
|
|
struct net_device *ocelot_port_to_netdev(struct ocelot *ocelot, int port);
|
|
int ocelot_netdev_to_port(struct net_device *dev);
|
|
|
|
u32 ocelot_port_readl(struct ocelot_port *port, u32 reg);
|
|
void ocelot_port_writel(struct ocelot_port *port, u32 val, u32 reg);
|
|
|
|
int ocelot_probe_port(struct ocelot *ocelot, int port, struct regmap *target,
|
|
struct device_node *portnp);
|
|
void ocelot_release_port(struct ocelot_port *ocelot_port);
|
|
int ocelot_devlink_init(struct ocelot *ocelot);
|
|
void ocelot_devlink_teardown(struct ocelot *ocelot);
|
|
int ocelot_port_devlink_init(struct ocelot *ocelot, int port,
|
|
enum devlink_port_flavour flavour);
|
|
void ocelot_port_devlink_teardown(struct ocelot *ocelot, int port);
|
|
|
|
extern struct notifier_block ocelot_netdevice_nb;
|
|
extern struct notifier_block ocelot_switchdev_nb;
|
|
extern struct notifier_block ocelot_switchdev_blocking_nb;
|
|
extern const struct devlink_ops ocelot_devlink_ops;
|
|
|
|
#endif
|