#define MODULE_ALIAS_DSA_TAG_DRIVER(__proto) \
MODULE_ALIAS(DSA_TAG_DRIVER_ALIAS __stringify(__proto##_VALUE))
+struct dsa_lag {
+ struct net_device *dev;
+ unsigned int id;
+ struct mutex fdb_lock;
+ struct list_head fdbs;
+ refcount_t refcount;
+};
+
struct dsa_switch_tree {
struct list_head list;
/* Maps offloaded LAG netdevs to a zero-based linear ID for
* drivers that need it.
*/
- struct net_device **lags;
+ struct dsa_lag **lags;
/* Tagging protocol operations */
const struct dsa_device_ops *tag_ops;
unsigned int last_switch;
};
+/* LAG IDs are one-based, the dst->lags array is zero-based */
#define dsa_lags_foreach_id(_id, _dst) \
- for ((_id) = 0; (_id) < (_dst)->lags_len; (_id)++) \
- if ((_dst)->lags[(_id)])
+ for ((_id) = 1; (_id) <= (_dst)->lags_len; (_id)++) \
+ if ((_dst)->lags[(_id) - 1])
#define dsa_lag_foreach_port(_dp, _dst, _lag) \
list_for_each_entry((_dp), &(_dst)->ports, list) \
- if ((_dp)->lag_dev == (_lag))
+ if (dsa_port_offloads_lag((_dp), (_lag)))
#define dsa_hsr_foreach_port(_dp, _ds, _hsr) \
list_for_each_entry((_dp), &(_ds)->dst->ports, list) \
if ((_dp)->ds == (_ds) && (_dp)->hsr_dev == (_hsr))
-static inline struct net_device *dsa_lag_dev(struct dsa_switch_tree *dst,
- unsigned int id)
+static inline struct dsa_lag *dsa_lag_by_id(struct dsa_switch_tree *dst,
+ unsigned int id)
{
- return dst->lags[id];
+ /* DSA LAG IDs are one-based, dst->lags is zero-based */
+ return dst->lags[id - 1];
}
static inline int dsa_lag_id(struct dsa_switch_tree *dst,
- struct net_device *lag)
+ struct net_device *lag_dev)
{
unsigned int id;
dsa_lags_foreach_id(id, dst) {
- if (dsa_lag_dev(dst, id) == lag)
- return id;
+ struct dsa_lag *lag = dsa_lag_by_id(dst, id);
+
+ if (lag->dev == lag_dev)
+ return lag->id;
}
return -ENODEV;
u8 devlink_port_setup:1;
+ /* Master state bits, valid only on CPU ports */
+ u8 master_admin_up:1;
+ u8 master_oper_up:1;
+
u8 setup:1;
struct device_node *dn;
struct devlink_port devlink_port;
struct phylink *pl;
struct phylink_config pl_config;
- struct net_device *lag_dev;
+ struct dsa_lag *lag;
struct net_device *hsr_dev;
struct list_head list;
struct mutex addr_lists_lock;
struct list_head fdbs;
struct list_head mdbs;
+
+ /* List of VLANs that CPU and DSA ports are members of. */
+ struct mutex vlans_lock;
+ struct list_head vlans;
};
/* TODO: ideally DSA ports would have a single dp->link_dp member,
struct list_head list;
};
+enum dsa_db_type {
+ DSA_DB_PORT,
+ DSA_DB_LAG,
+ DSA_DB_BRIDGE,
+};
+
+struct dsa_db {
+ enum dsa_db_type type;
+
+ union {
+ const struct dsa_port *dp;
+ struct dsa_lag lag;
+ struct dsa_bridge bridge;
+ };
+};
+
struct dsa_mac_addr {
unsigned char addr[ETH_ALEN];
u16 vid;
refcount_t refcount;
struct list_head list;
+ struct dsa_db db;
+};
+
+struct dsa_vlan {
+ u16 vid;
+ refcount_t refcount;
+ struct list_head list;
};
struct dsa_switch {
*/
u32 vlan_filtering:1;
- /* MAC PCS does not provide link state change interrupt, and requires
- * polling. Flag passed on to PHYLINK.
- */
- u32 pcs_poll:1;
-
/* For switches that only have the MRU configurable. To ensure the
* configured MTU is not exceeded, normalization of MRU on all bridged
* interfaces is needed.
*/
u32 mtu_enforcement_ingress:1;
+ /* Drivers that isolate the FDBs of multiple bridges must set this
+ * to true to receive the bridge as an argument in .port_fdb_{add,del}
+ * and .port_mdb_{add,del}. Otherwise, the bridge.num will always be
+ * passed as zero.
+ */
+ u32 fdb_isolation:1;
+
/* Listener for switch fabric events */
struct notifier_block nb;
return dp->type == DSA_PORT_TYPE_UNUSED;
}
+static inline bool dsa_port_master_is_operational(struct dsa_port *dp)
+{
+ return dsa_port_is_cpu(dp) && dp->master_admin_up &&
+ dp->master_oper_up;
+}
+
static inline bool dsa_is_unused_port(struct dsa_switch *ds, int p)
{
return dsa_to_port(ds, p)->type == DSA_PORT_TYPE_UNUSED;
return port == dsa_upstream_port(ds, port);
}
+/* Return true if this is a DSA port leading away from the CPU */
+static inline bool dsa_is_downstream_port(struct dsa_switch *ds, int port)
+{
+ return dsa_is_dsa_port(ds, port) && !dsa_is_upstream_port(ds, port);
+}
+
+/* Return the local port used to reach the CPU port */
+static inline unsigned int dsa_switch_upstream_port(struct dsa_switch *ds)
+{
+ struct dsa_port *dp;
+
+ dsa_switch_for_each_available_port(dp, ds) {
+ return dsa_upstream_port(ds, dp->index);
+ }
+
+ return ds->num_ports;
+}
+
/* Return true if @upstream_ds is an upstream switch of @downstream_ds, meaning
* that the routing port from @downstream_ds to @upstream_ds is also the port
* which @downstream_ds uses to reach its dedicated CPU.
return dp->vlan_filtering;
}
+static inline unsigned int dsa_port_lag_id_get(struct dsa_port *dp)
+{
+ return dp->lag ? dp->lag->id : 0;
+}
+
+static inline struct net_device *dsa_port_lag_dev_get(struct dsa_port *dp)
+{
+ return dp->lag ? dp->lag->dev : NULL;
+}
+
+static inline bool dsa_port_offloads_lag(struct dsa_port *dp,
+ const struct dsa_lag *lag)
+{
+ return dsa_port_lag_dev_get(dp) == lag->dev;
+}
+
static inline
struct net_device *dsa_port_to_bridge_port(const struct dsa_port *dp)
{
if (!dp->bridge)
return NULL;
- if (dp->lag_dev)
- return dp->lag_dev;
+ if (dp->lag)
+ return dp->lag->dev;
else if (dp->hsr_dev)
return dp->hsr_dev;
void (*phylink_validate)(struct dsa_switch *ds, int port,
unsigned long *supported,
struct phylink_link_state *state);
+ struct phylink_pcs *(*phylink_mac_select_pcs)(struct dsa_switch *ds,
+ int port,
+ phy_interface_t iface);
int (*phylink_mac_link_state)(struct dsa_switch *ds, int port,
struct phylink_link_state *state);
void (*phylink_mac_config)(struct dsa_switch *ds, int port,
* Forwarding database
*/
int (*port_fdb_add)(struct dsa_switch *ds, int port,
- const unsigned char *addr, u16 vid);
+ const unsigned char *addr, u16 vid,
+ struct dsa_db db);
int (*port_fdb_del)(struct dsa_switch *ds, int port,
- const unsigned char *addr, u16 vid);
+ const unsigned char *addr, u16 vid,
+ struct dsa_db db);
int (*port_fdb_dump)(struct dsa_switch *ds, int port,
dsa_fdb_dump_cb_t *cb, void *data);
+ int (*lag_fdb_add)(struct dsa_switch *ds, struct dsa_lag lag,
+ const unsigned char *addr, u16 vid,
+ struct dsa_db db);
+ int (*lag_fdb_del)(struct dsa_switch *ds, struct dsa_lag lag,
+ const unsigned char *addr, u16 vid,
+ struct dsa_db db);
/*
* Multicast database
*/
int (*port_mdb_add)(struct dsa_switch *ds, int port,
- const struct switchdev_obj_port_mdb *mdb);
+ const struct switchdev_obj_port_mdb *mdb,
+ struct dsa_db db);
int (*port_mdb_del)(struct dsa_switch *ds, int port,
- const struct switchdev_obj_port_mdb *mdb);
+ const struct switchdev_obj_port_mdb *mdb,
+ struct dsa_db db);
/*
* RXNFC
*/
int (*crosschip_lag_change)(struct dsa_switch *ds, int sw_index,
int port);
int (*crosschip_lag_join)(struct dsa_switch *ds, int sw_index,
- int port, struct net_device *lag,
+ int port, struct dsa_lag lag,
struct netdev_lag_upper_info *info);
int (*crosschip_lag_leave)(struct dsa_switch *ds, int sw_index,
- int port, struct net_device *lag);
+ int port, struct dsa_lag lag);
/*
* PTP functionality
*/
int (*port_lag_change)(struct dsa_switch *ds, int port);
int (*port_lag_join)(struct dsa_switch *ds, int port,
- struct net_device *lag,
+ struct dsa_lag lag,
struct netdev_lag_upper_info *info);
int (*port_lag_leave)(struct dsa_switch *ds, int port,
- struct net_device *lag);
+ struct dsa_lag lag);
/*
* HSR integration
int (*tag_8021q_vlan_add)(struct dsa_switch *ds, int port, u16 vid,
u16 flags);
int (*tag_8021q_vlan_del)(struct dsa_switch *ds, int port, u16 vid);
+
+ /*
+ * DSA master tracking operations
+ */
+ void (*master_state_change)(struct dsa_switch *ds,
+ const struct net_device *master,
+ bool operational);
};
#define DSA_DEVLINK_PARAM_DRIVER(_id, _name, _type, _cmodes) \
#endif
netdev_tx_t dsa_enqueue_skb(struct sk_buff *skb, struct net_device *dev);
-int dsa_port_get_phy_strings(struct dsa_port *dp, uint8_t *data);
-int dsa_port_get_ethtool_phy_stats(struct dsa_port *dp, uint64_t *data);
-int dsa_port_get_phy_sset_count(struct dsa_port *dp);
void dsa_port_phylink_mac_change(struct dsa_switch *ds, int port, bool up);
struct dsa_tag_driver {
/**
* module_dsa_tag_drivers() - Helper macro for registering DSA tag
* drivers
- * @__ops_array: Array of tag driver strucutres
+ * @__ops_array: Array of tag driver structures
*
* Helper macro for DSA tag drivers which do not do anything special
* in module init/exit. Each module may only use this macro once, and