// SPDX-License-Identifier: GPL-2.0-or-later /* * Handling of a single switch port * * Copyright (c) 2017 Savoir-faire Linux Inc. * Vivien Didelot <vivien.didelot@savoirfairelinux.com>
*/
/** * dsa_port_notify - Notify the switching fabric of changes to a port * @dp: port on which change occurred * @e: event, must be of type DSA_NOTIFIER_* * @v: event-specific value. * * Notify all switches in the DSA tree that this port's switch belongs to, * including this switch itself, of an event. Allows the other switches to * reconfigure themselves for cross-chip operations. Can also be used to * reconfigure ports without net_devices (CPU ports, DSA links) whenever * a user port's state changes.
*/ staticint dsa_port_notify(conststruct dsa_port *dp, unsignedlong e, void *v)
{ return dsa_tree_notify(dp->ds->dst, e, v);
}
if (!ds->ops->port_hwtstamp_get || !ds->ops->port_hwtstamp_set) returnfalse;
/* "See through" shim implementations of the "get" method. */
err = ds->ops->port_hwtstamp_get(ds, dp->index, &config); return err != -EOPNOTSUPP;
}
int dsa_port_set_state(struct dsa_port *dp, u8 state, bool do_fast_age)
{ struct dsa_switch *ds = dp->ds; int port = dp->index;
if (!ds->ops->port_stp_state_set) return -EOPNOTSUPP;
ds->ops->port_stp_state_set(ds, port, state);
if (!dsa_port_can_configure_learning(dp) ||
(do_fast_age && dp->learning)) { /* Fast age FDB entries or flush appropriate forwarding database * for the given port, if we are moving it from Learning or * Forwarding state, to Disabled or Blocking or Listening state. * Ports that were standalone before the STP state change don't * need to fast age the FDB, since address learning is off in * standalone mode.
*/
if ((dp->stp_state == BR_STATE_LEARNING ||
dp->stp_state == BR_STATE_FORWARDING) &&
(state == BR_STATE_DISABLED ||
state == BR_STATE_BLOCKING ||
state == BR_STATE_LISTENING))
dsa_port_fast_age(dp);
}
/* If the bridge was vlan_filtering, the bridge core doesn't trigger an * event for changing vlan_filtering setting upon user ports leaving * it. That is a good thing, because that lets us handle it and also * handle the case where the switch's vlan_filtering setting is global * (not per port). When that happens, the correct moment to trigger the * vlan_filtering callback is only when the last port leaves the last * VLAN-aware bridge.
*/ if (change_vlan_filtering && ds->vlan_filtering_is_global) {
dsa_switch_for_each_port(other_dp, ds) { struct net_device *br = dsa_port_bridge_dev_get(other_dp);
staticvoid dsa_port_switchdev_unsync_attrs(struct dsa_port *dp, struct dsa_bridge bridge)
{ /* Configure the port for standalone mode (no address learning, * flood everything). * The bridge only emits SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS events * when the user requests it through netlink or sysfs, but not * automatically at port join or leave, so we need to handle resetting * the brport flags ourselves. But we even prefer it that way, because * otherwise, some setups might never get the notification they need, * for example, when a port leaves a LAG that offloads the bridge, * it becomes standalone, but as far as the bridge is concerned, no * port ever left.
*/
dsa_port_clear_brport_flags(dp);
/* Port left the bridge, put in BR_STATE_DISABLED by the bridge layer, * so allow it to be in BR_STATE_FORWARDING to be kept functional
*/
dsa_port_set_state_now(dp, BR_STATE_FORWARDING, true);
dsa_port_reset_vlan_filtering(dp, bridge);
/* Ageing time may be global to the switch chip, so don't change it * here because we have no good reason (or value) to change it to.
*/
}
if (br_mst_enabled(br) && !dsa_port_supports_mst(dp)) return -EOPNOTSUPP;
/* Here the interface is already bridged. Reflect the current * configuration so that drivers can program their chips accordingly.
*/
err = dsa_port_bridge_create(dp, br, extack); if (err) return err;
void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br)
{ struct dsa_notifier_bridge_info info = {
.dp = dp,
}; int err;
/* If the port could not be offloaded to begin with, then * there is nothing to do.
*/ if (!dp->bridge) return;
info.bridge = *dp->bridge;
/* Here the port is already unbridged. Reflect the current configuration * so that drivers can program their chips accordingly.
*/
dsa_port_bridge_destroy(dp, br);
err = dsa_broadcast(DSA_NOTIFIER_BRIDGE_LEAVE, &info); if (err)
dev_err(dp->ds->dev, "port %d failed to notify DSA_NOTIFIER_BRIDGE_LEAVE: %pe\n",
dp->index, ERR_PTR(err));
int dsa_port_lag_change(struct dsa_port *dp, struct netdev_lag_lower_state_info *linfo)
{ struct dsa_notifier_lag_info info = {
.dp = dp,
}; bool tx_enabled;
if (!dp->lag) return 0;
/* On statically configured aggregates (e.g. loadbalance * without LACP) ports will always be tx_enabled, even if the * link is down. Thus we require both link_up and tx_enabled * in order to include it in the tx set.
*/
tx_enabled = linfo->link_up && linfo->tx_enabled;
/* Port might have been part of a LAG that in turn was * attached to a bridge.
*/ if (br)
dsa_port_bridge_leave(dp, br);
info.lag = *dp->lag;
dsa_port_lag_destroy(dp);
err = dsa_port_notify(dp, DSA_NOTIFIER_LAG_LEAVE, &info); if (err)
dev_err(dp->ds->dev, "port %d failed to notify DSA_NOTIFIER_LAG_LEAVE: %pe\n",
dp->index, ERR_PTR(err));
}
/* Must be called under rcu_read_lock() */ staticbool dsa_port_can_apply_vlan_filtering(struct dsa_port *dp, bool vlan_filtering, struct netlink_ext_ack *extack)
{ struct dsa_switch *ds = dp->ds; struct dsa_port *other_dp; int err;
/* VLAN awareness was off, so the question is "can we turn it on". * We may have had 8021q uppers, those need to go. Make sure we don't * enter an inconsistent state: deny changing the VLAN awareness state * as long as we have 8021q uppers.
*/ if (vlan_filtering && dsa_port_is_user(dp)) { struct net_device *br = dsa_port_bridge_dev_get(dp); struct net_device *upper_dev, *user = dp->user; struct list_head *iter;
/* br_vlan_get_info() returns -EINVAL or -ENOENT if the * device, respectively the VID is not found, returning * 0 means success, which is a failure for us here.
*/
err = br_vlan_get_info(br, vid, &br_info); if (err == 0) {
NL_SET_ERR_MSG_MOD(extack, "Must first remove VLAN uppers having VIDs also present in bridge"); returnfalse;
}
}
}
if (!ds->vlan_filtering_is_global) returntrue;
/* For cases where enabling/disabling VLAN awareness is global to the * switch, we need to handle the case where multiple bridges span * different ports of the same switch device and one of them has a * different setting than what is being requested.
*/
dsa_switch_for_each_port(other_dp, ds) { struct net_device *other_br = dsa_port_bridge_dev_get(other_dp);
/* If it's the same bridge, it also has same * vlan_filtering setting => no need to check
*/ if (!other_br || other_br == dsa_port_bridge_dev_get(dp)) continue;
if (br_vlan_enabled(other_br) != vlan_filtering) {
NL_SET_ERR_MSG_MOD(extack, "VLAN filtering is a global setting"); returnfalse;
}
} returntrue;
}
if (!ds->ops->port_vlan_filtering) return -EOPNOTSUPP;
/* We are called from dsa_user_switchdev_blocking_event(), * which is not under rcu_read_lock(), unlike * dsa_user_switchdev_event().
*/
rcu_read_lock();
apply = dsa_port_can_apply_vlan_filtering(dp, vlan_filtering, extack);
rcu_read_unlock(); if (!apply) return -EINVAL;
if (dsa_port_is_vlan_filtering(dp) == vlan_filtering) return 0;
err = ds->ops->port_vlan_filtering(ds, dp->index, vlan_filtering,
extack); if (err) return err;
if (ds->vlan_filtering_is_global) { struct dsa_port *other_dp;
int dsa_port_ageing_time(struct dsa_port *dp, clock_t ageing_clock)
{ unsignedlong ageing_jiffies = clock_t_to_jiffies(ageing_clock); unsignedint ageing_time = jiffies_to_msecs(ageing_jiffies); struct dsa_notifier_ageing_time_info info; int err;
info.ageing_time = ageing_time;
err = dsa_port_notify(dp, DSA_NOTIFIER_AGEING_TIME, &info); if (err) return err;
dp->ageing_time = ageing_time;
return 0;
}
int dsa_port_mst_enable(struct dsa_port *dp, bool on, struct netlink_ext_ack *extack)
{ if (on && !dsa_port_supports_mst(dp)) {
NL_SET_ERR_MSG_MOD(extack, "Hardware does not support MST"); return -EINVAL;
}
/* Refcounting takes bridge.num as a key, and should be global for all * bridges in the absence of FDB isolation, and per bridge otherwise. * Force the bridge.num to zero here in the absence of FDB isolation.
*/ if (!dp->ds->fdb_isolation)
info.db.bridge.num = 0;
int dsa_port_bridge_host_fdb_add(struct dsa_port *dp, constunsignedchar *addr, u16 vid)
{ struct net_device *conduit = dsa_port_to_conduit(dp); struct dsa_db db = {
.type = DSA_DB_BRIDGE,
.bridge = *dp->bridge,
}; int err;
if (!dp->ds->fdb_isolation)
db.bridge.num = 0;
/* Avoid a call to __dev_set_promiscuity() on the conduit, which * requires rtnl_lock(), since we can't guarantee that is held here, * and we can't take it either.
*/ if (conduit->priv_flags & IFF_UNICAST_FLT) {
err = dev_uc_add(conduit, addr); if (err) return err;
}
/* Change the dp->cpu_dp affinity for a user port. Note that both cross-chip * notifiers and drivers have implicit assumptions about user-to-CPU-port * mappings, so we unfortunately cannot delay the deletion of the objects * (switchdev, standalone addresses, standalone VLANs) on the old CPU port * until the new CPU port has been set up. So we need to completely tear down * the old CPU port before changing it, and restore it on errors during the * bringup of the new one.
*/ int dsa_port_change_conduit(struct dsa_port *dp, struct net_device *conduit, struct netlink_ext_ack *extack)
{ struct net_device *bridge_dev = dsa_port_bridge_dev_get(dp); struct net_device *old_conduit = dsa_port_to_conduit(dp); struct net_device *dev = dp->user; struct dsa_switch *ds = dp->ds; bool vlan_filtering; int err, tmp;
/* Bridges may hold host FDB, MDB and VLAN objects. These need to be * migrated, so dynamically unoffload and later reoffload the bridge * port.
*/ if (bridge_dev) {
dsa_port_pre_bridge_leave(dp, bridge_dev);
dsa_port_bridge_leave(dp, bridge_dev);
}
/* The port might still be VLAN filtering even if it's no longer * under a bridge, either due to ds->vlan_filtering_is_global or * ds->needs_standalone_vlan_filtering. In turn this means VLANs * on the CPU port.
*/
vlan_filtering = dsa_port_is_vlan_filtering(dp); if (vlan_filtering) {
err = dsa_user_manage_vlan_filtering(dev, false); if (err) {
NL_SET_ERR_MSG_MOD(extack, "Failed to remove standalone VLANs"); goto rewind_old_bridge;
}
}
/* Standalone addresses, and addresses of upper interfaces like * VLAN, LAG, HSR need to be migrated.
*/
dsa_user_unsync_ha(dev);
/* If live-changing, we also need to uninstall the user device address * from the port FDB and the conduit interface.
*/ if (dev->flags & IFF_UP)
dsa_user_host_uc_uninstall(dev);
err = dsa_port_assign_conduit(dp, conduit, extack, true); if (err) goto rewind_old_addrs;
/* If the port doesn't have its own MAC address and relies on the DSA * conduit's one, inherit it again from the new DSA conduit.
*/ if (is_zero_ether_addr(dp->mac))
eth_hw_addr_inherit(dev, conduit);
/* If live-changing, we need to install the user device address to the * port FDB and the conduit interface.
*/ if (dev->flags & IFF_UP) {
err = dsa_user_host_uc_install(dev, dev->dev_addr); if (err) {
NL_SET_ERR_MSG_MOD(extack, "Failed to install host UC address"); goto rewind_addr_inherit;
}
}
dsa_user_sync_ha(dev);
if (vlan_filtering) {
err = dsa_user_manage_vlan_filtering(dev, true); if (err) {
NL_SET_ERR_MSG_MOD(extack, "Failed to restore standalone VLANs"); goto rewind_new_addrs;
}
}
if (bridge_dev) {
err = dsa_port_bridge_join(dp, bridge_dev, extack); if (err && err == -EOPNOTSUPP) {
NL_SET_ERR_MSG_MOD(extack, "Failed to reoffload bridge"); goto rewind_new_vlan;
}
}
return 0;
rewind_new_vlan: if (vlan_filtering)
dsa_user_manage_vlan_filtering(dev, false);
rewind_new_addrs:
dsa_user_unsync_ha(dev);
if (dev->flags & IFF_UP)
dsa_user_host_uc_uninstall(dev);
rewind_addr_inherit: if (is_zero_ether_addr(dp->mac))
eth_hw_addr_inherit(dev, old_conduit);
/* Restore the objects on the old CPU port */
rewind_old_addrs: if (dev->flags & IFF_UP) {
tmp = dsa_user_host_uc_install(dev, dev->dev_addr); if (tmp) {
dev_err(ds->dev, "port %d failed to restore host UC address: %pe\n",
dp->index, ERR_PTR(tmp));
}
}
dsa_user_sync_ha(dev);
if (vlan_filtering) {
tmp = dsa_user_manage_vlan_filtering(dev, true); if (tmp) {
dev_err(ds->dev, "port %d failed to restore standalone VLANs: %pe\n",
dp->index, ERR_PTR(tmp));
}
}
rewind_old_bridge: if (bridge_dev) {
tmp = dsa_port_bridge_join(dp, bridge_dev, extack); if (tmp) {
dev_err(ds->dev, "port %d failed to rejoin bridge %s: %pe\n",
dp->index, bridge_dev->name, ERR_PTR(tmp));
}
}
/* dsa_supports_eee - indicate that EEE is supported * @ds: pointer to &struct dsa_switch * @port: port index * * A default implementation for the .support_eee() DSA operations member, * which drivers can use to indicate that they support EEE on all of their * user ports. * * Returns: true
*/ bool dsa_supports_eee(struct dsa_switch *ds, int port)
{ returntrue;
}
EXPORT_SYMBOL_GPL(dsa_supports_eee);
/* During the initial DSA driver migration to OF, port nodes were sometimes * added to device trees with no indication of how they should operate from a * link management perspective (phy-handle, fixed-link, etc). Additionally, the * phy-mode may be absent. The interpretation of these port OF nodes depends on * their type. * * User ports with no phy-handle or fixed-link are expected to connect to an * internal PHY located on the ds->user_mii_bus at an MDIO address equal to * the port number. This description is still actively supported. * * Shared (CPU and DSA) ports with no phy-handle or fixed-link are expected to * operate at the maximum speed that their phy-mode is capable of. If the * phy-mode is absent, they are expected to operate using the phy-mode * supported by the port that gives the highest link speed. It is unspecified * if the port should use flow control or not, half duplex or full duplex, or * if the phy-mode is a SERDES link, whether in-band autoneg is expected to be * enabled or not. * * In the latter case of shared ports, omitting the link management description * from the firmware node is deprecated and strongly discouraged. DSA uses * phylink, which rejects the firmware nodes of these ports for lacking * required properties. * * For switches in this table, DSA will skip enforcing validation and will * later omit registering a phylink instance for the shared ports, if they lack * a fixed-link, a phy-handle, or a managed = "in-band-status" property. * It becomes the responsibility of the driver to ensure that these ports * operate at the maximum speed (whatever this means) and will interoperate * with the DSA conduit or other cascade port, since phylink methods will not be * invoked for them. * * If you are considering expanding this table for newly introduced switches, * think again. It is OK to remove switches from this table if there aren't DT * blobs in circulation which rely on defaulting the shared ports.
*/ staticconstchar * const dsa_switches_apply_workarounds[] = { #if IS_ENABLED(CONFIG_NET_DSA_XRS700X) "arrow,xrs7003e", "arrow,xrs7003f", "arrow,xrs7004e", "arrow,xrs7004f", #endif #if IS_ENABLED(CONFIG_B53) "brcm,bcm5325", "brcm,bcm53115", "brcm,bcm53125", "brcm,bcm53128", "brcm,bcm5365", "brcm,bcm5389", "brcm,bcm5395", "brcm,bcm5397", "brcm,bcm5398", "brcm,bcm53010-srab", "brcm,bcm53011-srab", "brcm,bcm53012-srab", "brcm,bcm53018-srab", "brcm,bcm53019-srab", "brcm,bcm5301x-srab", "brcm,bcm11360-srab", "brcm,bcm58522-srab", "brcm,bcm58525-srab", "brcm,bcm58535-srab", "brcm,bcm58622-srab", "brcm,bcm58623-srab", "brcm,bcm58625-srab", "brcm,bcm88312-srab", "brcm,cygnus-srab", "brcm,nsp-srab", "brcm,omega-srab", "brcm,bcm3384-switch", "brcm,bcm6328-switch", "brcm,bcm6368-switch", "brcm,bcm63xx-switch", #endif #if IS_ENABLED(CONFIG_NET_DSA_BCM_SF2) "brcm,bcm7445-switch-v4.0", "brcm,bcm7278-switch-v4.0", "brcm,bcm7278-switch-v4.8", #endif #if IS_ENABLED(CONFIG_NET_DSA_LANTIQ_GSWIP) "lantiq,xrx200-gswip", "lantiq,xrx300-gswip", "lantiq,xrx330-gswip", #endif #if IS_ENABLED(CONFIG_NET_DSA_MV88E6060) "marvell,mv88e6060", #endif #if IS_ENABLED(CONFIG_NET_DSA_MV88E6XXX) "marvell,mv88e6085", "marvell,mv88e6190", "marvell,mv88e6250", #endif #if IS_ENABLED(CONFIG_NET_DSA_MICROCHIP_KSZ_COMMON) "microchip,ksz8765", "microchip,ksz8794", "microchip,ksz8795", "microchip,ksz8863", "microchip,ksz8873", "microchip,ksz9477", "microchip,ksz9897", "microchip,ksz9893", "microchip,ksz9563", "microchip,ksz8563", "microchip,ksz9567", #endif #if IS_ENABLED(CONFIG_NET_DSA_SMSC_LAN9303_MDIO) "smsc,lan9303-mdio", #endif #if IS_ENABLED(CONFIG_NET_DSA_SMSC_LAN9303_I2C) "smsc,lan9303-i2c", #endif
NULL,
};
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.