Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'net-dsa-mxl862xx-add-support-for-bridge-offloading'

Daniel Golle says:

====================
net: dsa: mxl862xx: add support for bridge offloading

As a next step to complete the mxl862xx DSA driver, add support for
offloading forwarding between bridged ports to the switch hardware.

This works pretty much without any big surprises, apart from two
subtleties:
* per-port control over flooding behavior has to be implemented by
(ab)using a 0-rate QoS meters as stopper in lack of any better
option.
* STP state transition unconditionally enables learning on a port
even if it was previously explicitely disabled (a firmware bug)

Note that as the driver is still lacking all VLAN features (which
are going to be added next), at this point some of the
bridge_vlan_aware.sh tests are failing after applying this series.

This is expected and cannot be avoided without implementing
port_vlan_filtering + port_vlan_add/del. And adding both bridge and
VLAN offloading at the same time would be too much for anyone to
review, so VLAN support is going to be submitted in a follow-up
series immediately after this series has been accepted.

All other relevant selftests (including bridge_vlan_unaware.sh) are
still passing.

Inspired by the comments received from Paolo Abeni as reply to v5
the driver now no longer caches bridge port membership in the
driver, but instead imports an existing helper from yt921x.c to dsa.h
in order to allow the driver to easily iterate over bridge members.
The mapping between DSA bridge num and firmware bridge ID is done
using a simple fixed-size array in mxl862xx_priv.
====================

Link: https://patch.msgid.link/cover.1775049897.git.daniel@makrotopia.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

+1045 -75
+224 -1
drivers/net/dsa/mxl862xx/mxl862xx-api.h
··· 3 3 #ifndef __MXL862XX_API_H 4 4 #define __MXL862XX_API_H 5 5 6 + #include <linux/bits.h> 6 7 #include <linux/if_ether.h> 7 8 8 9 /** ··· 33 32 __le16 addr; 34 33 __le16 data; 35 34 __le16 mask; 35 + } __packed; 36 + 37 + /** 38 + * enum mxl862xx_mac_table_filter - Source/Destination MAC address filtering 39 + * 40 + * @MXL862XX_MAC_FILTER_NONE: no filter 41 + * @MXL862XX_MAC_FILTER_SRC: source address filter 42 + * @MXL862XX_MAC_FILTER_DEST: destination address filter 43 + * @MXL862XX_MAC_FILTER_BOTH: both source and destination filter 44 + */ 45 + enum mxl862xx_mac_table_filter { 46 + MXL862XX_MAC_FILTER_NONE = 0, 47 + MXL862XX_MAC_FILTER_SRC = BIT(0), 48 + MXL862XX_MAC_FILTER_DEST = BIT(1), 49 + MXL862XX_MAC_FILTER_BOTH = BIT(0) | BIT(1), 50 + }; 51 + 52 + #define MXL862XX_TCI_VLAN_ID GENMASK(11, 0) 53 + #define MXL862XX_TCI_VLAN_CFI_DEI BIT(12) 54 + #define MXL862XX_TCI_VLAN_PRI GENMASK(15, 13) 55 + 56 + /* Set in port_id to use port_map[] as a portmap bitmap instead of a single 57 + * port ID. When clear, port_id selects one port; when set, the firmware 58 + * ignores the lower bits of port_id and writes port_map[] directly into 59 + * the PCE bridge port map. 60 + */ 61 + #define MXL862XX_PORTMAP_FLAG BIT(31) 62 + 63 + /** 64 + * struct mxl862xx_mac_table_add - MAC Table Entry to be added 65 + * @fid: Filtering Identifier (FID) (not supported by all switches) 66 + * @port_id: Ethernet Port number 67 + * @port_map: Bridge Port Map 68 + * @sub_if_id: Sub-Interface Identifier Destination 69 + * @age_timer: Aging Time in seconds 70 + * @vlan_id: STAG VLAN Id 71 + * @static_entry: Static Entry (value will be aged out if not set to static) 72 + * @traffic_class: Egress queue traffic class 73 + * @mac: MAC Address to add to the table 74 + * @filter_flag: See &enum mxl862xx_mac_table_filter 75 + * @igmp_controlled: Packet is marked as IGMP controlled if destination MAC 76 + * address matches MAC in this entry 77 + * @associated_mac: Associated Mac address 78 + * @tci: TCI for B-Step 79 + * Bit [0:11] - VLAN ID 80 + * Bit [12] - VLAN CFI/DEI 81 + * Bit [13:15] - VLAN PRI 82 + */ 83 + struct mxl862xx_mac_table_add { 84 + __le16 fid; 85 + __le32 port_id; 86 + __le16 port_map[8]; 87 + __le16 sub_if_id; 88 + __le32 age_timer; 89 + __le16 vlan_id; 90 + u8 static_entry; 91 + u8 traffic_class; 92 + u8 mac[ETH_ALEN]; 93 + u8 filter_flag; 94 + u8 igmp_controlled; 95 + u8 associated_mac[ETH_ALEN]; 96 + __le16 tci; 97 + } __packed; 98 + 99 + /** 100 + * struct mxl862xx_mac_table_remove - MAC Table Entry to be removed 101 + * @fid: Filtering Identifier (FID) 102 + * @mac: MAC Address to be removed from the table. 103 + * @filter_flag: See &enum mxl862xx_mac_table_filter 104 + * @tci: TCI for B-Step 105 + * Bit [0:11] - VLAN ID 106 + * Bit [12] - VLAN CFI/DEI 107 + * Bit [13:15] - VLAN PRI 108 + */ 109 + struct mxl862xx_mac_table_remove { 110 + __le16 fid; 111 + u8 mac[ETH_ALEN]; 112 + u8 filter_flag; 113 + __le16 tci; 114 + } __packed; 115 + 116 + /** 117 + * struct mxl862xx_mac_table_read - MAC Table Entry to be read 118 + * @initial: Restart the get operation from the beginning of the table 119 + * @last: Indicates that the read operation returned last entry 120 + * @fid: Get the MAC table entry belonging to the given Filtering Identifier 121 + * @port_id: The Bridge Port ID 122 + * @port_map: Bridge Port Map 123 + * @age_timer: Aging Time 124 + * @vlan_id: STAG VLAN Id 125 + * @static_entry: Indicates if this is a Static Entry 126 + * @sub_if_id: Sub-Interface Identifier Destination 127 + * @mac: MAC Address. Filled out by the switch API implementation. 128 + * @filter_flag: See &enum mxl862xx_mac_table_filter 129 + * @igmp_controlled: Packet is marked as IGMP controlled if destination MAC 130 + * address matches the MAC in this entry 131 + * @entry_changed: Indicate if the Entry has Changed 132 + * @associated_mac: Associated MAC address 133 + * @hit_status: MAC Table Hit Status Update 134 + * @tci: TCI for B-Step 135 + * Bit [0:11] - VLAN ID 136 + * Bit [12] - VLAN CFI/DEI 137 + * Bit [13:15] - VLAN PRI 138 + * @first_bridge_port_id: The port this MAC address has first been learned. 139 + * This is used for loop detection. 140 + */ 141 + struct mxl862xx_mac_table_read { 142 + u8 initial; 143 + u8 last; 144 + __le16 fid; 145 + __le32 port_id; 146 + __le16 port_map[8]; 147 + __le32 age_timer; 148 + __le16 vlan_id; 149 + u8 static_entry; 150 + __le16 sub_if_id; 151 + u8 mac[ETH_ALEN]; 152 + u8 filter_flag; 153 + u8 igmp_controlled; 154 + u8 entry_changed; 155 + u8 associated_mac[ETH_ALEN]; 156 + u8 hit_status; 157 + __le16 tci; 158 + __le16 first_bridge_port_id; 159 + } __packed; 160 + 161 + /** 162 + * struct mxl862xx_mac_table_query - MAC Table Entry key-based lookup 163 + * @mac: MAC Address to search for (input) 164 + * @fid: Filtering Identifier (input) 165 + * @found: Set by firmware: 1 if entry was found, 0 if not 166 + * @port_id: Bridge Port ID (output; MSB set if portmap mode) 167 + * @port_map: Bridge Port Map (output; valid for static entries) 168 + * @sub_if_id: Sub-Interface Identifier Destination 169 + * @age_timer: Aging Time 170 + * @vlan_id: STAG VLAN Id 171 + * @static_entry: Indicates if this is a Static Entry 172 + * @filter_flag: See &enum mxl862xx_mac_table_filter (input+output) 173 + * @igmp_controlled: IGMP controlled flag 174 + * @entry_changed: Entry changed flag 175 + * @associated_mac: Associated MAC address 176 + * @hit_status: MAC Table Hit Status Update 177 + * @tci: TCI (VLAN ID + CFI/DEI + PRI) (input) 178 + * @first_bridge_port_id: First learned bridge port 179 + */ 180 + struct mxl862xx_mac_table_query { 181 + u8 mac[ETH_ALEN]; 182 + __le16 fid; 183 + u8 found; 184 + __le32 port_id; 185 + __le16 port_map[8]; 186 + __le16 sub_if_id; 187 + __le32 age_timer; 188 + __le16 vlan_id; 189 + u8 static_entry; 190 + u8 filter_flag; 191 + u8 igmp_controlled; 192 + u8 entry_changed; 193 + u8 associated_mac[ETH_ALEN]; 194 + u8 hit_status; 195 + __le16 tci; 196 + __le16 first_bridge_port_id; 36 197 } __packed; 37 198 38 199 /** ··· 300 137 MXL862XX_BRIDGE_PORT_EGRESS_METER_OTHERS, 301 138 MXL862XX_BRIDGE_PORT_EGRESS_METER_MAX, 302 139 }; 140 + 141 + /** 142 + * struct mxl862xx_qos_meter_cfg - Rate meter configuration 143 + * @enable: Enable/disable meter 144 + * @meter_id: Meter ID (assigned by firmware on alloc) 145 + * @meter_name: Meter name string 146 + * @meter_type: Meter algorithm type (srTCM = 0, trTCM = 1) 147 + * @cbs: Committed Burst Size (in bytes) 148 + * @res1: Reserved 149 + * @ebs: Excess Burst Size (in bytes) 150 + * @res2: Reserved 151 + * @rate: Committed Information Rate (in kbit/s) 152 + * @pi_rate: Peak Information Rate (in kbit/s) 153 + * @colour_blind_mode: Colour-blind mode enable 154 + * @pkt_mode: Packet mode enable 155 + * @local_overhd: Local overhead accounting enable 156 + * @local_overhd_val: Local overhead accounting value 157 + */ 158 + struct mxl862xx_qos_meter_cfg { 159 + u8 enable; 160 + __le16 meter_id; 161 + char meter_name[32]; 162 + __le32 meter_type; 163 + __le32 cbs; 164 + __le32 res1; 165 + __le32 ebs; 166 + __le32 res2; 167 + __le32 rate; 168 + __le32 pi_rate; 169 + u8 colour_blind_mode; 170 + u8 pkt_mode; 171 + u8 local_overhd; 172 + __le16 local_overhd_val; 173 + } __packed; 303 174 304 175 /** 305 176 * enum mxl862xx_bridge_forward_mode - Bridge forwarding type of packet ··· 653 456 */ 654 457 struct mxl862xx_bridge_port_config { 655 458 __le16 bridge_port_id; 656 - __le32 mask; /* enum mxl862xx_bridge_port_config_mask */ 459 + __le32 mask; /* enum mxl862xx_bridge_port_config_mask */ 657 460 __le16 bridge_id; 658 461 u8 ingress_extended_vlan_enable; 659 462 __le16 ingress_extended_vlan_block_id; ··· 853 656 __le16 number_of_ctp_port; 854 657 __le32 mode; /* enum mxl862xx_logical_port_mode */ 855 658 __le16 bridge_port_id; 659 + } __packed; 660 + 661 + /** 662 + * enum mxl862xx_stp_port_state - Spanning Tree Protocol port states 663 + * @MXL862XX_STP_PORT_STATE_FORWARD: Forwarding state 664 + * @MXL862XX_STP_PORT_STATE_DISABLE: Disabled/Discarding state 665 + * @MXL862XX_STP_PORT_STATE_LEARNING: Learning state 666 + * @MXL862XX_STP_PORT_STATE_BLOCKING: Blocking/Listening 667 + */ 668 + enum mxl862xx_stp_port_state { 669 + MXL862XX_STP_PORT_STATE_FORWARD = 0, 670 + MXL862XX_STP_PORT_STATE_DISABLE, 671 + MXL862XX_STP_PORT_STATE_LEARNING, 672 + MXL862XX_STP_PORT_STATE_BLOCKING, 673 + }; 674 + 675 + /** 676 + * struct mxl862xx_stp_port_cfg - Configures the Spanning Tree Protocol state 677 + * @port_id: Port number 678 + * @fid: Filtering Identifier (FID) 679 + * @port_state: See &enum mxl862xx_stp_port_state 680 + */ 681 + struct mxl862xx_stp_port_cfg { 682 + __le16 port_id; 683 + __le16 fid; 684 + __le32 port_state; /* enum mxl862xx_stp_port_state */ 856 685 } __packed; 857 686 858 687 /**
+16 -4
drivers/net/dsa/mxl862xx/mxl862xx-cmd.h
··· 15 15 #define MXL862XX_BRDG_MAGIC 0x300 16 16 #define MXL862XX_BRDGPORT_MAGIC 0x400 17 17 #define MXL862XX_CTP_MAGIC 0x500 18 + #define MXL862XX_QOS_MAGIC 0x600 18 19 #define MXL862XX_SWMAC_MAGIC 0xa00 20 + #define MXL862XX_STP_MAGIC 0xf00 19 21 #define MXL862XX_SS_MAGIC 0x1600 20 22 #define GPY_GPY2XX_MAGIC 0x1800 21 23 #define SYS_MISC_MAGIC 0x1900 22 24 23 25 #define MXL862XX_COMMON_CFGGET (MXL862XX_COMMON_MAGIC + 0x9) 26 + #define MXL862XX_COMMON_CFGSET (MXL862XX_COMMON_MAGIC + 0xa) 24 27 #define MXL862XX_COMMON_REGISTERMOD (MXL862XX_COMMON_MAGIC + 0x11) 25 28 26 29 #define MXL862XX_BRIDGE_ALLOC (MXL862XX_BRDG_MAGIC + 0x1) ··· 38 35 39 36 #define MXL862XX_CTP_PORTASSIGNMENTSET (MXL862XX_CTP_MAGIC + 0x3) 40 37 38 + #define MXL862XX_QOS_METERCFGSET (MXL862XX_QOS_MAGIC + 0x2) 39 + #define MXL862XX_QOS_METERALLOC (MXL862XX_QOS_MAGIC + 0x2a) 40 + 41 + #define MXL862XX_MAC_TABLEENTRYADD (MXL862XX_SWMAC_MAGIC + 0x2) 42 + #define MXL862XX_MAC_TABLEENTRYREAD (MXL862XX_SWMAC_MAGIC + 0x3) 43 + #define MXL862XX_MAC_TABLEENTRYQUERY (MXL862XX_SWMAC_MAGIC + 0x4) 44 + #define MXL862XX_MAC_TABLEENTRYREMOVE (MXL862XX_SWMAC_MAGIC + 0x5) 41 45 #define MXL862XX_MAC_TABLECLEARCOND (MXL862XX_SWMAC_MAGIC + 0x8) 42 46 43 - #define MXL862XX_SS_SPTAG_SET (MXL862XX_SS_MAGIC + 0x02) 47 + #define MXL862XX_SS_SPTAG_SET (MXL862XX_SS_MAGIC + 0x2) 44 48 45 - #define INT_GPHY_READ (GPY_GPY2XX_MAGIC + 0x01) 46 - #define INT_GPHY_WRITE (GPY_GPY2XX_MAGIC + 0x02) 49 + #define MXL862XX_STP_PORTCFGSET (MXL862XX_STP_MAGIC + 0x2) 47 50 48 - #define SYS_MISC_FW_VERSION (SYS_MISC_MAGIC + 0x02) 51 + #define INT_GPHY_READ (GPY_GPY2XX_MAGIC + 0x1) 52 + #define INT_GPHY_WRITE (GPY_GPY2XX_MAGIC + 0x2) 53 + 54 + #define SYS_MISC_FW_VERSION (SYS_MISC_MAGIC + 0x2) 49 55 50 56 #define MMD_API_MAXIMUM_ID 0x7fff 51 57
+686 -56
drivers/net/dsa/mxl862xx/mxl862xx.c
··· 7 7 * Copyright (C) 2025 Daniel Golle <daniel@makrotopia.org> 8 8 */ 9 9 10 - #include <linux/module.h> 10 + #include <linux/bitfield.h> 11 11 #include <linux/delay.h> 12 + #include <linux/etherdevice.h> 13 + #include <linux/if_bridge.h> 14 + #include <linux/module.h> 12 15 #include <linux/of_device.h> 13 16 #include <linux/of_mdio.h> 14 17 #include <linux/phy.h> ··· 38 35 39 36 #define MXL862XX_READY_TIMEOUT_MS 10000 40 37 #define MXL862XX_READY_POLL_MS 100 38 + 39 + #define MXL862XX_TCM_INST_SEL 0xe00 40 + #define MXL862XX_TCM_CBS 0xe12 41 + #define MXL862XX_TCM_EBS 0xe13 42 + 43 + static const int mxl862xx_flood_meters[] = { 44 + MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_UC, 45 + MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_MC_IP, 46 + MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_MC_NON_IP, 47 + MXL862XX_BRIDGE_PORT_EGRESS_METER_BROADCAST, 48 + }; 41 49 42 50 static enum dsa_tag_protocol mxl862xx_get_tag_protocol(struct dsa_switch *ds, 43 51 int port, ··· 182 168 return ret; 183 169 } 184 170 171 + static int mxl862xx_bridge_config_fwd(struct dsa_switch *ds, u16 bridge_id, 172 + bool ucast_flood, bool mcast_flood, 173 + bool bcast_flood) 174 + { 175 + struct mxl862xx_bridge_config bridge_config = {}; 176 + struct mxl862xx_priv *priv = ds->priv; 177 + int ret; 178 + 179 + bridge_config.mask = cpu_to_le32(MXL862XX_BRIDGE_CONFIG_MASK_FORWARDING_MODE); 180 + bridge_config.bridge_id = cpu_to_le16(bridge_id); 181 + 182 + bridge_config.forward_unknown_unicast = cpu_to_le32(ucast_flood ? 183 + MXL862XX_BRIDGE_FORWARD_FLOOD : MXL862XX_BRIDGE_FORWARD_DISCARD); 184 + 185 + bridge_config.forward_unknown_multicast_ip = cpu_to_le32(mcast_flood ? 186 + MXL862XX_BRIDGE_FORWARD_FLOOD : MXL862XX_BRIDGE_FORWARD_DISCARD); 187 + bridge_config.forward_unknown_multicast_non_ip = 188 + bridge_config.forward_unknown_multicast_ip; 189 + 190 + bridge_config.forward_broadcast = cpu_to_le32(bcast_flood ? 191 + MXL862XX_BRIDGE_FORWARD_FLOOD : MXL862XX_BRIDGE_FORWARD_DISCARD); 192 + 193 + ret = MXL862XX_API_WRITE(priv, MXL862XX_BRIDGE_CONFIGSET, bridge_config); 194 + if (ret) 195 + dev_err(ds->dev, "failed to configure bridge %u forwarding: %d\n", 196 + bridge_id, ret); 197 + 198 + return ret; 199 + } 200 + 201 + /* Allocate a single zero-rate meter shared by all ports and flood types. 202 + * All flood-blocking egress sub-meters point to this one meter so that any 203 + * packet hitting this meter is unconditionally dropped. 204 + * 205 + * The firmware API requires CBS >= 64 (its bs2ls encoder clamps smaller 206 + * values), so the meter is initially configured with CBS=EBS=64. 207 + * A zero-rate bucket starts full at CBS bytes, which would let one packet 208 + * through before the bucket empties. To eliminate this one-packet leak we 209 + * override CBS and EBS to zero via direct register writes after the API call; 210 + * the hardware accepts CBS=0 and immediately flags the bucket as exceeded, 211 + * so no traffic can ever pass. 212 + */ 213 + static int mxl862xx_setup_drop_meter(struct dsa_switch *ds) 214 + { 215 + struct mxl862xx_qos_meter_cfg meter = {}; 216 + struct mxl862xx_priv *priv = ds->priv; 217 + struct mxl862xx_register_mod reg; 218 + int ret; 219 + 220 + /* meter_id=0 means auto-alloc */ 221 + ret = MXL862XX_API_READ(priv, MXL862XX_QOS_METERALLOC, meter); 222 + if (ret) 223 + return ret; 224 + 225 + meter.enable = true; 226 + meter.cbs = cpu_to_le32(64); 227 + meter.ebs = cpu_to_le32(64); 228 + snprintf(meter.meter_name, sizeof(meter.meter_name), "drop"); 229 + 230 + ret = MXL862XX_API_WRITE(priv, MXL862XX_QOS_METERCFGSET, meter); 231 + if (ret) 232 + return ret; 233 + 234 + priv->drop_meter = le16_to_cpu(meter.meter_id); 235 + 236 + /* Select the meter instance for subsequent TCM register access. */ 237 + reg.addr = cpu_to_le16(MXL862XX_TCM_INST_SEL); 238 + reg.data = cpu_to_le16(priv->drop_meter); 239 + reg.mask = cpu_to_le16(0xffff); 240 + ret = MXL862XX_API_WRITE(priv, MXL862XX_COMMON_REGISTERMOD, reg); 241 + if (ret) 242 + return ret; 243 + 244 + /* Zero CBS so the committed bucket starts empty (exceeded). */ 245 + reg.addr = cpu_to_le16(MXL862XX_TCM_CBS); 246 + reg.data = 0; 247 + ret = MXL862XX_API_WRITE(priv, MXL862XX_COMMON_REGISTERMOD, reg); 248 + if (ret) 249 + return ret; 250 + 251 + /* Zero EBS so the excess bucket starts empty (exceeded). */ 252 + reg.addr = cpu_to_le16(MXL862XX_TCM_EBS); 253 + return MXL862XX_API_WRITE(priv, MXL862XX_COMMON_REGISTERMOD, reg); 254 + } 255 + 256 + static int mxl862xx_set_bridge_port(struct dsa_switch *ds, int port) 257 + { 258 + struct mxl862xx_bridge_port_config br_port_cfg = {}; 259 + struct dsa_port *dp = dsa_to_port(ds, port); 260 + struct mxl862xx_priv *priv = ds->priv; 261 + struct mxl862xx_port *p = &priv->ports[port]; 262 + struct dsa_port *member_dp; 263 + u16 bridge_id; 264 + bool enable; 265 + int i, idx; 266 + 267 + if (!p->setup_done) 268 + return 0; 269 + 270 + if (dsa_port_is_cpu(dp)) { 271 + dsa_switch_for_each_user_port(member_dp, ds) { 272 + if (member_dp->cpu_dp->index != port) 273 + continue; 274 + mxl862xx_fw_portmap_set_bit(br_port_cfg.bridge_port_map, 275 + member_dp->index); 276 + } 277 + } else if (dp->bridge) { 278 + dsa_switch_for_each_bridge_member(member_dp, ds, 279 + dp->bridge->dev) { 280 + if (member_dp->index == port) 281 + continue; 282 + mxl862xx_fw_portmap_set_bit(br_port_cfg.bridge_port_map, 283 + member_dp->index); 284 + } 285 + mxl862xx_fw_portmap_set_bit(br_port_cfg.bridge_port_map, 286 + dp->cpu_dp->index); 287 + } else { 288 + mxl862xx_fw_portmap_set_bit(br_port_cfg.bridge_port_map, 289 + dp->cpu_dp->index); 290 + p->flood_block = 0; 291 + p->learning = false; 292 + } 293 + 294 + bridge_id = dp->bridge ? priv->bridges[dp->bridge->num] : p->fid; 295 + 296 + br_port_cfg.bridge_port_id = cpu_to_le16(port); 297 + br_port_cfg.bridge_id = cpu_to_le16(bridge_id); 298 + br_port_cfg.mask = cpu_to_le32(MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_ID | 299 + MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_PORT_MAP | 300 + MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_SRC_MAC_LEARNING | 301 + MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_SUB_METER); 302 + br_port_cfg.src_mac_learning_disable = !p->learning; 303 + 304 + for (i = 0; i < ARRAY_SIZE(mxl862xx_flood_meters); i++) { 305 + idx = mxl862xx_flood_meters[i]; 306 + enable = !!(p->flood_block & BIT(idx)); 307 + 308 + br_port_cfg.egress_traffic_sub_meter_id[idx] = 309 + enable ? cpu_to_le16(priv->drop_meter) : 0; 310 + br_port_cfg.egress_sub_metering_enable[idx] = enable; 311 + } 312 + 313 + return MXL862XX_API_WRITE(priv, MXL862XX_BRIDGEPORT_CONFIGSET, 314 + br_port_cfg); 315 + } 316 + 317 + static int mxl862xx_sync_bridge_members(struct dsa_switch *ds, 318 + const struct dsa_bridge *bridge) 319 + { 320 + struct dsa_port *dp; 321 + int ret = 0, err; 322 + 323 + dsa_switch_for_each_bridge_member(dp, ds, bridge->dev) { 324 + err = mxl862xx_set_bridge_port(ds, dp->index); 325 + if (err) 326 + ret = err; 327 + } 328 + 329 + return ret; 330 + } 331 + 332 + static int mxl862xx_allocate_bridge(struct mxl862xx_priv *priv) 333 + { 334 + struct mxl862xx_bridge_alloc br_alloc = {}; 335 + int ret; 336 + 337 + ret = MXL862XX_API_READ(priv, MXL862XX_BRIDGE_ALLOC, br_alloc); 338 + if (ret) 339 + return ret; 340 + 341 + return le16_to_cpu(br_alloc.bridge_id); 342 + } 343 + 344 + static void mxl862xx_free_bridge(struct dsa_switch *ds, 345 + const struct dsa_bridge *bridge) 346 + { 347 + struct mxl862xx_priv *priv = ds->priv; 348 + u16 fw_id = priv->bridges[bridge->num]; 349 + struct mxl862xx_bridge_alloc br_alloc = { 350 + .bridge_id = cpu_to_le16(fw_id), 351 + }; 352 + int ret; 353 + 354 + ret = MXL862XX_API_WRITE(priv, MXL862XX_BRIDGE_FREE, br_alloc); 355 + if (ret) { 356 + dev_err(ds->dev, "failed to free fw bridge %u: %pe\n", 357 + fw_id, ERR_PTR(ret)); 358 + return; 359 + } 360 + 361 + priv->bridges[bridge->num] = 0; 362 + } 363 + 185 364 static int mxl862xx_setup(struct dsa_switch *ds) 186 365 { 187 366 struct mxl862xx_priv *priv = ds->priv; ··· 385 178 return ret; 386 179 387 180 ret = mxl862xx_wait_ready(ds); 181 + if (ret) 182 + return ret; 183 + 184 + ret = mxl862xx_setup_drop_meter(ds); 388 185 if (ret) 389 186 return ret; 390 187 ··· 471 260 472 261 static int mxl862xx_setup_cpu_bridge(struct dsa_switch *ds, int port) 473 262 { 474 - struct mxl862xx_bridge_port_config br_port_cfg = {}; 475 263 struct mxl862xx_priv *priv = ds->priv; 476 - u16 bridge_port_map = 0; 477 - struct dsa_port *dp; 478 264 479 - /* CPU port bridge setup */ 480 - br_port_cfg.mask = cpu_to_le32(MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_PORT_MAP | 481 - MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_SRC_MAC_LEARNING | 482 - MXL862XX_BRIDGE_PORT_CONFIG_MASK_VLAN_BASED_MAC_LEARNING); 265 + priv->ports[port].fid = MXL862XX_DEFAULT_BRIDGE; 266 + priv->ports[port].learning = true; 483 267 484 - br_port_cfg.bridge_port_id = cpu_to_le16(port); 485 - br_port_cfg.src_mac_learning_disable = false; 486 - br_port_cfg.vlan_src_mac_vid_enable = true; 487 - br_port_cfg.vlan_dst_mac_vid_enable = true; 488 - 489 - /* include all assigned user ports in the CPU portmap */ 490 - dsa_switch_for_each_user_port(dp, ds) { 491 - /* it's safe to rely on cpu_dp being valid for user ports */ 492 - if (dp->cpu_dp->index != port) 493 - continue; 494 - 495 - bridge_port_map |= BIT(dp->index); 496 - } 497 - br_port_cfg.bridge_port_map[0] |= cpu_to_le16(bridge_port_map); 498 - 499 - return MXL862XX_API_WRITE(priv, MXL862XX_BRIDGEPORT_CONFIGSET, br_port_cfg); 268 + return mxl862xx_set_bridge_port(ds, port); 500 269 } 501 270 502 - static int mxl862xx_add_single_port_bridge(struct dsa_switch *ds, int port) 271 + static int mxl862xx_port_bridge_join(struct dsa_switch *ds, int port, 272 + const struct dsa_bridge bridge, 273 + bool *tx_fwd_offload, 274 + struct netlink_ext_ack *extack) 503 275 { 504 - struct mxl862xx_bridge_port_config br_port_cfg = {}; 505 - struct dsa_port *dp = dsa_to_port(ds, port); 506 - struct mxl862xx_bridge_alloc br_alloc = {}; 276 + struct mxl862xx_priv *priv = ds->priv; 507 277 int ret; 508 278 509 - ret = MXL862XX_API_READ(ds->priv, MXL862XX_BRIDGE_ALLOC, br_alloc); 510 - if (ret) { 511 - dev_err(ds->dev, "failed to allocate a bridge for port %d\n", port); 512 - return ret; 279 + if (!priv->bridges[bridge.num]) { 280 + ret = mxl862xx_allocate_bridge(priv); 281 + if (ret < 0) 282 + return ret; 283 + 284 + priv->bridges[bridge.num] = ret; 285 + 286 + /* Free bridge here on error, DSA rollback won't. */ 287 + ret = mxl862xx_sync_bridge_members(ds, &bridge); 288 + if (ret) { 289 + mxl862xx_free_bridge(ds, &bridge); 290 + return ret; 291 + } 292 + 293 + return 0; 513 294 } 514 295 515 - br_port_cfg.bridge_id = br_alloc.bridge_id; 516 - br_port_cfg.bridge_port_id = cpu_to_le16(port); 517 - br_port_cfg.mask = cpu_to_le32(MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_ID | 518 - MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_PORT_MAP | 519 - MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_SRC_MAC_LEARNING | 520 - MXL862XX_BRIDGE_PORT_CONFIG_MASK_VLAN_BASED_MAC_LEARNING); 521 - br_port_cfg.src_mac_learning_disable = true; 522 - br_port_cfg.vlan_src_mac_vid_enable = false; 523 - br_port_cfg.vlan_dst_mac_vid_enable = false; 524 - /* As this function is only called for user ports it is safe to rely on 525 - * cpu_dp being valid 526 - */ 527 - br_port_cfg.bridge_port_map[0] = cpu_to_le16(BIT(dp->cpu_dp->index)); 296 + return mxl862xx_sync_bridge_members(ds, &bridge); 297 + } 528 298 529 - return MXL862XX_API_WRITE(ds->priv, MXL862XX_BRIDGEPORT_CONFIGSET, br_port_cfg); 299 + static void mxl862xx_port_bridge_leave(struct dsa_switch *ds, int port, 300 + const struct dsa_bridge bridge) 301 + { 302 + int err; 303 + 304 + err = mxl862xx_sync_bridge_members(ds, &bridge); 305 + if (err) 306 + dev_err(ds->dev, 307 + "failed to sync bridge members after port %d left: %pe\n", 308 + port, ERR_PTR(err)); 309 + 310 + /* Revert leaving port, omitted by the sync above, to its 311 + * single-port bridge 312 + */ 313 + err = mxl862xx_set_bridge_port(ds, port); 314 + if (err) 315 + dev_err(ds->dev, 316 + "failed to update bridge port %d state: %pe\n", port, 317 + ERR_PTR(err)); 318 + 319 + if (!dsa_bridge_ports(ds, bridge.dev)) 320 + mxl862xx_free_bridge(ds, &bridge); 530 321 } 531 322 532 323 static int mxl862xx_port_setup(struct dsa_switch *ds, int port) 533 324 { 325 + struct mxl862xx_priv *priv = ds->priv; 534 326 struct dsa_port *dp = dsa_to_port(ds, port); 535 327 bool is_cpu_port = dsa_port_is_cpu(dp); 536 328 int ret; 537 329 538 - /* disable port and flush MAC entries */ 539 330 ret = mxl862xx_port_state(ds, port, false); 540 331 if (ret) 541 332 return ret; 542 333 543 334 mxl862xx_port_fast_age(ds, port); 544 335 545 - /* skip setup for unused and DSA ports */ 546 336 if (dsa_port_is_unused(dp) || 547 337 dsa_port_is_dsa(dp)) 548 338 return 0; 549 339 550 - /* configure tag protocol */ 551 340 ret = mxl862xx_configure_sp_tag_proto(ds, port, is_cpu_port); 552 341 if (ret) 553 342 return ret; 554 343 555 - /* assign CTP port IDs */ 556 344 ret = mxl862xx_configure_ctp_port(ds, port, port, 557 345 is_cpu_port ? 32 - port : 1); 558 346 if (ret) 559 347 return ret; 560 348 561 349 if (is_cpu_port) 562 - /* assign user ports to CPU port bridge */ 563 350 return mxl862xx_setup_cpu_bridge(ds, port); 564 351 565 - /* setup single-port bridge for user ports */ 566 - return mxl862xx_add_single_port_bridge(ds, port); 352 + /* setup single-port bridge for user ports. 353 + * If this fails, the FID is leaked -- but the port then transitions 354 + * to unused, and the FID pool is sized to tolerate this. 355 + */ 356 + ret = mxl862xx_allocate_bridge(priv); 357 + if (ret < 0) { 358 + dev_err(ds->dev, "failed to allocate a bridge for port %d\n", port); 359 + return ret; 360 + } 361 + priv->ports[port].fid = ret; 362 + /* Standalone ports should not flood unknown unicast or multicast 363 + * towards the CPU by default; only broadcast is needed initially. 364 + */ 365 + ret = mxl862xx_bridge_config_fwd(ds, priv->ports[port].fid, 366 + false, false, true); 367 + if (ret) 368 + return ret; 369 + ret = mxl862xx_set_bridge_port(ds, port); 370 + if (ret) 371 + return ret; 372 + 373 + priv->ports[port].setup_done = true; 374 + 375 + return 0; 376 + } 377 + 378 + static void mxl862xx_port_teardown(struct dsa_switch *ds, int port) 379 + { 380 + struct mxl862xx_priv *priv = ds->priv; 381 + struct dsa_port *dp = dsa_to_port(ds, port); 382 + 383 + if (dsa_port_is_unused(dp) || dsa_port_is_dsa(dp)) 384 + return; 385 + 386 + /* Prevent deferred host_flood_work from acting on stale state. 387 + * The flag is checked under rtnl_lock() by the worker; since 388 + * teardown also runs under RTNL, this is race-free. 389 + * 390 + * HW EVLAN/VF blocks are not freed here -- the firmware receives 391 + * a full reset on the next probe, which reclaims all resources. 392 + */ 393 + priv->ports[port].setup_done = false; 567 394 } 568 395 569 396 static void mxl862xx_phylink_get_caps(struct dsa_switch *ds, int port, ··· 614 365 config->supported_interfaces); 615 366 } 616 367 368 + static int mxl862xx_get_fid(struct dsa_switch *ds, struct dsa_db db) 369 + { 370 + struct mxl862xx_priv *priv = ds->priv; 371 + 372 + switch (db.type) { 373 + case DSA_DB_PORT: 374 + return priv->ports[db.dp->index].fid; 375 + 376 + case DSA_DB_BRIDGE: 377 + if (!priv->bridges[db.bridge.num]) 378 + return -ENOENT; 379 + return priv->bridges[db.bridge.num]; 380 + 381 + default: 382 + return -EOPNOTSUPP; 383 + } 384 + } 385 + 386 + static int mxl862xx_port_fdb_add(struct dsa_switch *ds, int port, 387 + const unsigned char *addr, u16 vid, struct dsa_db db) 388 + { 389 + struct mxl862xx_mac_table_add param = {}; 390 + int fid = mxl862xx_get_fid(ds, db), ret; 391 + struct mxl862xx_priv *priv = ds->priv; 392 + 393 + if (fid < 0) 394 + return fid; 395 + 396 + param.port_id = cpu_to_le32(port); 397 + param.static_entry = true; 398 + param.fid = cpu_to_le16(fid); 399 + param.tci = cpu_to_le16(FIELD_PREP(MXL862XX_TCI_VLAN_ID, vid)); 400 + ether_addr_copy(param.mac, addr); 401 + 402 + ret = MXL862XX_API_WRITE(priv, MXL862XX_MAC_TABLEENTRYADD, param); 403 + if (ret) 404 + dev_err(ds->dev, "failed to add FDB entry on port %d\n", port); 405 + 406 + return ret; 407 + } 408 + 409 + static int mxl862xx_port_fdb_del(struct dsa_switch *ds, int port, 410 + const unsigned char *addr, u16 vid, const struct dsa_db db) 411 + { 412 + struct mxl862xx_mac_table_remove param = {}; 413 + int fid = mxl862xx_get_fid(ds, db), ret; 414 + struct mxl862xx_priv *priv = ds->priv; 415 + 416 + if (fid < 0) 417 + return fid; 418 + 419 + param.fid = cpu_to_le16(fid); 420 + param.tci = cpu_to_le16(FIELD_PREP(MXL862XX_TCI_VLAN_ID, vid)); 421 + ether_addr_copy(param.mac, addr); 422 + 423 + ret = MXL862XX_API_WRITE(priv, MXL862XX_MAC_TABLEENTRYREMOVE, param); 424 + if (ret) 425 + dev_err(ds->dev, "failed to remove FDB entry on port %d\n", port); 426 + 427 + return ret; 428 + } 429 + 430 + static int mxl862xx_port_fdb_dump(struct dsa_switch *ds, int port, 431 + dsa_fdb_dump_cb_t *cb, void *data) 432 + { 433 + struct mxl862xx_mac_table_read param = { .initial = 1 }; 434 + struct mxl862xx_priv *priv = ds->priv; 435 + u32 entry_port_id; 436 + int ret; 437 + 438 + while (true) { 439 + ret = MXL862XX_API_READ(priv, MXL862XX_MAC_TABLEENTRYREAD, param); 440 + if (ret) 441 + return ret; 442 + 443 + if (param.last) 444 + break; 445 + 446 + entry_port_id = le32_to_cpu(param.port_id); 447 + 448 + if (entry_port_id == port) { 449 + ret = cb(param.mac, FIELD_GET(MXL862XX_TCI_VLAN_ID, 450 + le16_to_cpu(param.tci)), 451 + param.static_entry, data); 452 + if (ret) 453 + return ret; 454 + } 455 + 456 + memset(&param, 0, sizeof(param)); 457 + } 458 + 459 + return 0; 460 + } 461 + 462 + static int mxl862xx_port_mdb_add(struct dsa_switch *ds, int port, 463 + const struct switchdev_obj_port_mdb *mdb, 464 + const struct dsa_db db) 465 + { 466 + struct mxl862xx_mac_table_query qparam = {}; 467 + struct mxl862xx_mac_table_add aparam = {}; 468 + struct mxl862xx_priv *priv = ds->priv; 469 + int fid, ret; 470 + 471 + fid = mxl862xx_get_fid(ds, db); 472 + if (fid < 0) 473 + return fid; 474 + 475 + ether_addr_copy(qparam.mac, mdb->addr); 476 + qparam.fid = cpu_to_le16(fid); 477 + qparam.tci = cpu_to_le16(FIELD_PREP(MXL862XX_TCI_VLAN_ID, mdb->vid)); 478 + 479 + ret = MXL862XX_API_READ(priv, MXL862XX_MAC_TABLEENTRYQUERY, qparam); 480 + if (ret) 481 + return ret; 482 + 483 + /* Build the ADD command using portmap mode */ 484 + ether_addr_copy(aparam.mac, mdb->addr); 485 + aparam.fid = cpu_to_le16(fid); 486 + aparam.tci = cpu_to_le16(FIELD_PREP(MXL862XX_TCI_VLAN_ID, mdb->vid)); 487 + aparam.static_entry = true; 488 + aparam.port_id = cpu_to_le32(MXL862XX_PORTMAP_FLAG); 489 + 490 + if (qparam.found) 491 + memcpy(aparam.port_map, qparam.port_map, 492 + sizeof(aparam.port_map)); 493 + 494 + mxl862xx_fw_portmap_set_bit(aparam.port_map, port); 495 + 496 + return MXL862XX_API_WRITE(priv, MXL862XX_MAC_TABLEENTRYADD, aparam); 497 + } 498 + 499 + static int mxl862xx_port_mdb_del(struct dsa_switch *ds, int port, 500 + const struct switchdev_obj_port_mdb *mdb, 501 + const struct dsa_db db) 502 + { 503 + struct mxl862xx_mac_table_remove rparam = {}; 504 + struct mxl862xx_mac_table_query qparam = {}; 505 + struct mxl862xx_mac_table_add aparam = {}; 506 + int fid = mxl862xx_get_fid(ds, db), ret; 507 + struct mxl862xx_priv *priv = ds->priv; 508 + 509 + if (fid < 0) 510 + return fid; 511 + 512 + qparam.fid = cpu_to_le16(fid); 513 + qparam.tci = cpu_to_le16(FIELD_PREP(MXL862XX_TCI_VLAN_ID, mdb->vid)); 514 + ether_addr_copy(qparam.mac, mdb->addr); 515 + 516 + ret = MXL862XX_API_READ(priv, MXL862XX_MAC_TABLEENTRYQUERY, qparam); 517 + if (ret) 518 + return ret; 519 + 520 + if (!qparam.found) 521 + return 0; 522 + 523 + mxl862xx_fw_portmap_clear_bit(qparam.port_map, port); 524 + 525 + if (mxl862xx_fw_portmap_is_empty(qparam.port_map)) { 526 + rparam.fid = cpu_to_le16(fid); 527 + rparam.tci = cpu_to_le16(FIELD_PREP(MXL862XX_TCI_VLAN_ID, mdb->vid)); 528 + ether_addr_copy(rparam.mac, mdb->addr); 529 + ret = MXL862XX_API_WRITE(priv, MXL862XX_MAC_TABLEENTRYREMOVE, rparam); 530 + } else { 531 + /* Write back with reduced portmap */ 532 + aparam.fid = cpu_to_le16(fid); 533 + aparam.tci = cpu_to_le16(FIELD_PREP(MXL862XX_TCI_VLAN_ID, mdb->vid)); 534 + ether_addr_copy(aparam.mac, mdb->addr); 535 + aparam.static_entry = true; 536 + aparam.port_id = cpu_to_le32(MXL862XX_PORTMAP_FLAG); 537 + memcpy(aparam.port_map, qparam.port_map, sizeof(aparam.port_map)); 538 + ret = MXL862XX_API_WRITE(priv, MXL862XX_MAC_TABLEENTRYADD, aparam); 539 + } 540 + 541 + return ret; 542 + } 543 + 544 + static int mxl862xx_set_ageing_time(struct dsa_switch *ds, unsigned int msecs) 545 + { 546 + struct mxl862xx_cfg param = {}; 547 + int ret; 548 + 549 + ret = MXL862XX_API_READ(ds->priv, MXL862XX_COMMON_CFGGET, param); 550 + if (ret) { 551 + dev_err(ds->dev, "failed to read switch config\n"); 552 + return ret; 553 + } 554 + 555 + param.mac_table_age_timer = cpu_to_le32(MXL862XX_AGETIMER_CUSTOM); 556 + param.age_timer = cpu_to_le32(msecs / 1000); 557 + ret = MXL862XX_API_WRITE(ds->priv, MXL862XX_COMMON_CFGSET, param); 558 + if (ret) 559 + dev_err(ds->dev, "failed to set ageing\n"); 560 + 561 + return ret; 562 + } 563 + 564 + static void mxl862xx_port_stp_state_set(struct dsa_switch *ds, int port, 565 + u8 state) 566 + { 567 + struct mxl862xx_stp_port_cfg param = { 568 + .port_id = cpu_to_le16(port), 569 + }; 570 + struct mxl862xx_priv *priv = ds->priv; 571 + int ret; 572 + 573 + switch (state) { 574 + case BR_STATE_DISABLED: 575 + param.port_state = cpu_to_le32(MXL862XX_STP_PORT_STATE_DISABLE); 576 + break; 577 + case BR_STATE_BLOCKING: 578 + case BR_STATE_LISTENING: 579 + param.port_state = cpu_to_le32(MXL862XX_STP_PORT_STATE_BLOCKING); 580 + break; 581 + case BR_STATE_LEARNING: 582 + param.port_state = cpu_to_le32(MXL862XX_STP_PORT_STATE_LEARNING); 583 + break; 584 + case BR_STATE_FORWARDING: 585 + param.port_state = cpu_to_le32(MXL862XX_STP_PORT_STATE_FORWARD); 586 + break; 587 + default: 588 + dev_err(ds->dev, "invalid STP state: %d\n", state); 589 + return; 590 + } 591 + 592 + ret = MXL862XX_API_WRITE(priv, MXL862XX_STP_PORTCFGSET, param); 593 + if (ret) { 594 + dev_err(ds->dev, "failed to set STP state on port %d\n", port); 595 + return; 596 + } 597 + 598 + /* The firmware may re-enable MAC learning as a side-effect of entering 599 + * LEARNING or FORWARDING state (per 802.1D defaults). 600 + * Re-apply the driver's intended learning and metering config so that 601 + * standalone ports keep learning disabled. 602 + */ 603 + ret = mxl862xx_set_bridge_port(ds, port); 604 + if (ret) 605 + dev_err(ds->dev, "failed to reapply brport flags on port %d\n", 606 + port); 607 + 608 + mxl862xx_port_fast_age(ds, port); 609 + } 610 + 611 + /* Deferred work handler for host flood configuration. 612 + * 613 + * port_set_host_flood is called from atomic context (under 614 + * netif_addr_lock), so firmware calls must be deferred. The worker 615 + * acquires rtnl_lock() to serialize with DSA callbacks that access the 616 + * same driver state. 617 + */ 618 + static void mxl862xx_host_flood_work_fn(struct work_struct *work) 619 + { 620 + struct mxl862xx_port *p = container_of(work, struct mxl862xx_port, 621 + host_flood_work); 622 + struct mxl862xx_priv *priv = p->priv; 623 + struct dsa_switch *ds = priv->ds; 624 + 625 + rtnl_lock(); 626 + 627 + /* Port may have been torn down between scheduling and now. */ 628 + if (!p->setup_done) { 629 + rtnl_unlock(); 630 + return; 631 + } 632 + 633 + /* Always write to the standalone FID. When standalone it takes effect 634 + * immediately; when bridged the port uses the shared bridge FID so the 635 + * write is a no-op for current forwarding, but the state is preserved 636 + * in hardware and is ready once the port returns to standalone. 637 + */ 638 + mxl862xx_bridge_config_fwd(ds, p->fid, p->host_flood_uc, 639 + p->host_flood_mc, true); 640 + 641 + rtnl_unlock(); 642 + } 643 + 644 + static void mxl862xx_port_set_host_flood(struct dsa_switch *ds, int port, 645 + bool uc, bool mc) 646 + { 647 + struct mxl862xx_priv *priv = ds->priv; 648 + struct mxl862xx_port *p = &priv->ports[port]; 649 + 650 + p->host_flood_uc = uc; 651 + p->host_flood_mc = mc; 652 + schedule_work(&p->host_flood_work); 653 + } 654 + 655 + static int mxl862xx_port_pre_bridge_flags(struct dsa_switch *ds, int port, 656 + const struct switchdev_brport_flags flags, 657 + struct netlink_ext_ack *extack) 658 + { 659 + if (flags.mask & ~(BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD | 660 + BR_LEARNING)) 661 + return -EINVAL; 662 + 663 + return 0; 664 + } 665 + 666 + static int mxl862xx_port_bridge_flags(struct dsa_switch *ds, int port, 667 + const struct switchdev_brport_flags flags, 668 + struct netlink_ext_ack *extack) 669 + { 670 + struct mxl862xx_priv *priv = ds->priv; 671 + unsigned long old_block = priv->ports[port].flood_block; 672 + unsigned long block = old_block; 673 + int ret; 674 + 675 + if (flags.mask & BR_FLOOD) { 676 + if (flags.val & BR_FLOOD) 677 + block &= ~BIT(MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_UC); 678 + else 679 + block |= BIT(MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_UC); 680 + } 681 + 682 + if (flags.mask & BR_MCAST_FLOOD) { 683 + if (flags.val & BR_MCAST_FLOOD) { 684 + block &= ~BIT(MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_MC_IP); 685 + block &= ~BIT(MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_MC_NON_IP); 686 + } else { 687 + block |= BIT(MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_MC_IP); 688 + block |= BIT(MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_MC_NON_IP); 689 + } 690 + } 691 + 692 + if (flags.mask & BR_BCAST_FLOOD) { 693 + if (flags.val & BR_BCAST_FLOOD) 694 + block &= ~BIT(MXL862XX_BRIDGE_PORT_EGRESS_METER_BROADCAST); 695 + else 696 + block |= BIT(MXL862XX_BRIDGE_PORT_EGRESS_METER_BROADCAST); 697 + } 698 + 699 + if (flags.mask & BR_LEARNING) 700 + priv->ports[port].learning = !!(flags.val & BR_LEARNING); 701 + 702 + if (block != old_block || (flags.mask & BR_LEARNING)) { 703 + priv->ports[port].flood_block = block; 704 + ret = mxl862xx_set_bridge_port(ds, port); 705 + if (ret) 706 + return ret; 707 + } 708 + 709 + return 0; 710 + } 711 + 617 712 static const struct dsa_switch_ops mxl862xx_switch_ops = { 618 713 .get_tag_protocol = mxl862xx_get_tag_protocol, 619 714 .setup = mxl862xx_setup, 620 715 .port_setup = mxl862xx_port_setup, 716 + .port_teardown = mxl862xx_port_teardown, 621 717 .phylink_get_caps = mxl862xx_phylink_get_caps, 622 718 .port_enable = mxl862xx_port_enable, 623 719 .port_disable = mxl862xx_port_disable, 624 720 .port_fast_age = mxl862xx_port_fast_age, 721 + .set_ageing_time = mxl862xx_set_ageing_time, 722 + .port_bridge_join = mxl862xx_port_bridge_join, 723 + .port_bridge_leave = mxl862xx_port_bridge_leave, 724 + .port_pre_bridge_flags = mxl862xx_port_pre_bridge_flags, 725 + .port_bridge_flags = mxl862xx_port_bridge_flags, 726 + .port_stp_state_set = mxl862xx_port_stp_state_set, 727 + .port_set_host_flood = mxl862xx_port_set_host_flood, 728 + .port_fdb_add = mxl862xx_port_fdb_add, 729 + .port_fdb_del = mxl862xx_port_fdb_del, 730 + .port_fdb_dump = mxl862xx_port_fdb_dump, 731 + .port_mdb_add = mxl862xx_port_mdb_add, 732 + .port_mdb_del = mxl862xx_port_mdb_del, 625 733 }; 626 734 627 735 static void mxl862xx_phylink_mac_config(struct phylink_config *config, ··· 1013 407 struct device *dev = &mdiodev->dev; 1014 408 struct mxl862xx_priv *priv; 1015 409 struct dsa_switch *ds; 1016 - int err; 410 + int err, i; 1017 411 1018 412 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 1019 413 if (!priv) ··· 1031 425 ds->ops = &mxl862xx_switch_ops; 1032 426 ds->phylink_mac_ops = &mxl862xx_phylink_mac_ops; 1033 427 ds->num_ports = MXL862XX_MAX_PORTS; 428 + ds->fdb_isolation = true; 429 + ds->max_num_bridges = MXL862XX_MAX_BRIDGES; 430 + 1034 431 mxl862xx_host_init(priv); 432 + 433 + for (i = 0; i < MXL862XX_MAX_PORTS; i++) { 434 + priv->ports[i].priv = priv; 435 + INIT_WORK(&priv->ports[i].host_flood_work, 436 + mxl862xx_host_flood_work_fn); 437 + } 1035 438 1036 439 dev_set_drvdata(dev, ds); 1037 440 1038 441 err = dsa_register_switch(ds); 1039 - if (err) 442 + if (err) { 1040 443 mxl862xx_host_shutdown(priv); 1041 - 444 + for (i = 0; i < MXL862XX_MAX_PORTS; i++) 445 + cancel_work_sync(&priv->ports[i].host_flood_work); 446 + } 1042 447 return err; 1043 448 } 1044 449 ··· 1057 440 { 1058 441 struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev); 1059 442 struct mxl862xx_priv *priv; 443 + int i; 1060 444 1061 445 if (!ds) 1062 446 return; ··· 1067 449 dsa_unregister_switch(ds); 1068 450 1069 451 mxl862xx_host_shutdown(priv); 452 + 453 + /* Cancel any pending host flood work. dsa_unregister_switch() 454 + * has already called port_teardown (which sets setup_done=false), 455 + * but a worker could still be blocked on rtnl_lock(). Since we 456 + * are now outside RTNL, cancel_work_sync() will not deadlock. 457 + */ 458 + for (i = 0; i < MXL862XX_MAX_PORTS; i++) 459 + cancel_work_sync(&priv->ports[i].host_flood_work); 1070 460 } 1071 461 1072 462 static void mxl862xx_shutdown(struct mdio_device *mdiodev) 1073 463 { 1074 464 struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev); 1075 465 struct mxl862xx_priv *priv; 466 + int i; 1076 467 1077 468 if (!ds) 1078 469 return; ··· 1091 464 dsa_switch_shutdown(ds); 1092 465 1093 466 mxl862xx_host_shutdown(priv); 467 + 468 + for (i = 0; i < MXL862XX_MAX_PORTS; i++) 469 + cancel_work_sync(&priv->ports[i].host_flood_work); 1094 470 1095 471 dev_set_drvdata(&mdiodev->dev, NULL); 1096 472 }
+100 -1
drivers/net/dsa/mxl862xx/mxl862xx.h
··· 4 4 #define __MXL862XX_H 5 5 6 6 #include <linux/mdio.h> 7 + #include <linux/workqueue.h> 7 8 #include <net/dsa.h> 8 9 9 - #define MXL862XX_MAX_PORTS 17 10 + struct mxl862xx_priv; 10 11 12 + #define MXL862XX_MAX_PORTS 17 13 + #define MXL862XX_DEFAULT_BRIDGE 0 14 + #define MXL862XX_MAX_BRIDGES 48 15 + #define MXL862XX_MAX_BRIDGE_PORTS 128 16 + 17 + /* Number of __le16 words in a firmware portmap (128-bit bitmap). */ 18 + #define MXL862XX_FW_PORTMAP_WORDS (MXL862XX_MAX_BRIDGE_PORTS / 16) 19 + 20 + /** 21 + * mxl862xx_fw_portmap_set_bit - set a single port bit in a firmware portmap 22 + * @map: firmware portmap array (MXL862XX_FW_PORTMAP_WORDS entries) 23 + * @port: port index (0..MXL862XX_MAX_BRIDGE_PORTS-1) 24 + */ 25 + static inline void mxl862xx_fw_portmap_set_bit(__le16 *map, int port) 26 + { 27 + map[port / 16] |= cpu_to_le16(BIT(port % 16)); 28 + } 29 + 30 + /** 31 + * mxl862xx_fw_portmap_clear_bit - clear a single port bit in a firmware portmap 32 + * @map: firmware portmap array (MXL862XX_FW_PORTMAP_WORDS entries) 33 + * @port: port index (0..MXL862XX_MAX_BRIDGE_PORTS-1) 34 + */ 35 + static inline void mxl862xx_fw_portmap_clear_bit(__le16 *map, int port) 36 + { 37 + map[port / 16] &= ~cpu_to_le16(BIT(port % 16)); 38 + } 39 + 40 + /** 41 + * mxl862xx_fw_portmap_is_empty - check whether a firmware portmap has no 42 + * bits set 43 + * @map: firmware portmap array (MXL862XX_FW_PORTMAP_WORDS entries) 44 + * 45 + * Return: true if every word in @map is zero. 46 + */ 47 + static inline bool mxl862xx_fw_portmap_is_empty(const __le16 *map) 48 + { 49 + int i; 50 + 51 + for (i = 0; i < MXL862XX_FW_PORTMAP_WORDS; i++) 52 + if (map[i]) 53 + return false; 54 + return true; 55 + } 56 + 57 + /** 58 + * struct mxl862xx_port - per-port state tracked by the driver 59 + * @priv: back-pointer to switch private data; needed by 60 + * deferred work handlers to access ds and priv 61 + * @fid: firmware FID for the permanent single-port bridge; 62 + * kept alive for the lifetime of the port so traffic is 63 + * never forwarded while the port is unbridged 64 + * @flood_block: bitmask of firmware meter indices that are currently 65 + * rate-limiting flood traffic on this port (zero-rate 66 + * meters used to block flooding) 67 + * @learning: true when address learning is enabled on this port 68 + * @setup_done: set at end of port_setup, cleared at start of 69 + * port_teardown; guards deferred work against 70 + * acting on torn-down state 71 + * @host_flood_uc: desired host unicast flood state (true = flood); 72 + * updated atomically by port_set_host_flood, consumed 73 + * by the deferred host_flood_work 74 + * @host_flood_mc: desired host multicast flood state (true = flood) 75 + * @host_flood_work: deferred work for applying host flood changes; 76 + * port_set_host_flood runs in atomic context (under 77 + * netif_addr_lock) so firmware calls must be deferred. 78 + * The worker acquires rtnl_lock() to serialize with 79 + * DSA callbacks and checks @setup_done to avoid 80 + * acting on torn-down ports. 81 + */ 82 + struct mxl862xx_port { 83 + struct mxl862xx_priv *priv; 84 + u16 fid; 85 + unsigned long flood_block; 86 + bool learning; 87 + bool setup_done; 88 + bool host_flood_uc; 89 + bool host_flood_mc; 90 + struct work_struct host_flood_work; 91 + }; 92 + 93 + /** 94 + * struct mxl862xx_priv - driver private data for an MxL862xx switch 95 + * @ds: pointer to the DSA switch instance 96 + * @mdiodev: MDIO device used to communicate with the switch firmware 97 + * @crc_err_work: deferred work for shutting down all ports on MDIO CRC errors 98 + * @crc_err: set atomically before CRC-triggered shutdown, cleared after 99 + * @drop_meter: index of the single shared zero-rate firmware meter used 100 + * to unconditionally drop traffic (used to block flooding) 101 + * @ports: per-port state, indexed by switch port number 102 + * @bridges: maps DSA bridge number to firmware bridge ID; 103 + * zero means no firmware bridge allocated for that 104 + * DSA bridge number. Indexed by dsa_bridge.num 105 + * (0 .. ds->max_num_bridges). 106 + */ 11 107 struct mxl862xx_priv { 12 108 struct dsa_switch *ds; 13 109 struct mdio_device *mdiodev; 14 110 struct work_struct crc_err_work; 15 111 unsigned long crc_err; 112 + u16 drop_meter; 113 + struct mxl862xx_port ports[MXL862XX_MAX_PORTS]; 114 + u16 bridges[MXL862XX_MAX_BRIDGES + 1]; 16 115 }; 17 116 18 117 #endif /* __MXL862XX_H */
-13
drivers/net/dsa/yt921x.c
··· 2154 2154 return 0; 2155 2155 } 2156 2156 2157 - static u32 2158 - dsa_bridge_ports(struct dsa_switch *ds, const struct net_device *bdev) 2159 - { 2160 - struct dsa_port *dp; 2161 - u32 mask = 0; 2162 - 2163 - dsa_switch_for_each_user_port(dp, ds) 2164 - if (dsa_port_offloads_bridge_dev(dp, bdev)) 2165 - mask |= BIT(dp->index); 2166 - 2167 - return mask; 2168 - } 2169 - 2170 2157 static int 2171 2158 yt921x_bridge_flags(struct yt921x_priv *priv, int port, 2172 2159 struct switchdev_brport_flags flags)
+16
include/net/dsa.h
··· 831 831 return false; 832 832 } 833 833 834 + #define dsa_switch_for_each_bridge_member(_dp, _ds, _bdev) \ 835 + dsa_switch_for_each_user_port(_dp, _ds) \ 836 + if (dsa_port_offloads_bridge_dev(_dp, _bdev)) 837 + 838 + static inline u32 839 + dsa_bridge_ports(struct dsa_switch *ds, const struct net_device *bdev) 840 + { 841 + struct dsa_port *dp; 842 + u32 mask = 0; 843 + 844 + dsa_switch_for_each_bridge_member(dp, ds, bdev) 845 + mask |= BIT(dp->index); 846 + 847 + return mask; 848 + } 849 + 834 850 static inline bool dsa_port_tree_same(const struct dsa_port *a, 835 851 const struct dsa_port *b) 836 852 {
+3
net/dsa/tag_mxl862xx.c
··· 86 86 return NULL; 87 87 } 88 88 89 + if (likely(!is_link_local_ether_addr(eth_hdr(skb)->h_dest))) 90 + dsa_default_offload_fwd_mark(skb); 91 + 89 92 /* remove the MxL862xx special tag between the MAC addresses and the 90 93 * current ethertype field. 91 94 */