1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | |
3 | #include <linux/if_bridge.h> |
4 | |
5 | #include "lan966x_main.h" |
6 | |
7 | static void lan966x_lag_set_aggr_pgids(struct lan966x *lan966x) |
8 | { |
9 | u32 visited = GENMASK(lan966x->num_phys_ports - 1, 0); |
10 | int p, lag, i; |
11 | |
12 | /* Reset destination and aggregation PGIDS */ |
13 | for (p = 0; p < lan966x->num_phys_ports; ++p) |
14 | lan_wr(ANA_PGID_PGID_SET(BIT(p)), |
15 | lan966x, ANA_PGID(p)); |
16 | |
17 | for (p = PGID_AGGR; p < PGID_SRC; ++p) |
18 | lan_wr(ANA_PGID_PGID_SET(visited), |
19 | lan966x, ANA_PGID(p)); |
20 | |
21 | /* The visited ports bitmask holds the list of ports offloading any |
22 | * bonding interface. Initially we mark all these ports as unvisited, |
23 | * then every time we visit a port in this bitmask, we know that it is |
24 | * the lowest numbered port, i.e. the one whose logical ID == physical |
25 | * port ID == LAG ID. So we mark as visited all further ports in the |
26 | * bitmask that are offloading the same bonding interface. This way, |
27 | * we set up the aggregation PGIDs only once per bonding interface. |
28 | */ |
29 | for (p = 0; p < lan966x->num_phys_ports; ++p) { |
30 | struct lan966x_port *port = lan966x->ports[p]; |
31 | |
32 | if (!port || !port->bond) |
33 | continue; |
34 | |
35 | visited &= ~BIT(p); |
36 | } |
37 | |
38 | /* Now, set PGIDs for each active LAG */ |
39 | for (lag = 0; lag < lan966x->num_phys_ports; ++lag) { |
40 | struct lan966x_port *port = lan966x->ports[lag]; |
41 | int num_active_ports = 0; |
42 | struct net_device *bond; |
43 | unsigned long bond_mask; |
44 | u8 aggr_idx[16]; |
45 | |
46 | if (!port || !port->bond || (visited & BIT(lag))) |
47 | continue; |
48 | |
49 | bond = port->bond; |
50 | bond_mask = lan966x_lag_get_mask(lan966x, bond); |
51 | |
52 | for_each_set_bit(p, &bond_mask, lan966x->num_phys_ports) { |
53 | struct lan966x_port *port = lan966x->ports[p]; |
54 | |
55 | if (!port) |
56 | continue; |
57 | |
58 | lan_wr(ANA_PGID_PGID_SET(bond_mask), |
59 | lan966x, ANA_PGID(p)); |
60 | if (port->lag_tx_active) |
61 | aggr_idx[num_active_ports++] = p; |
62 | } |
63 | |
64 | for (i = PGID_AGGR; i < PGID_SRC; ++i) { |
65 | u32 ac; |
66 | |
67 | ac = lan_rd(lan966x, ANA_PGID(i)); |
68 | ac &= ~bond_mask; |
69 | /* Don't do division by zero if there was no active |
70 | * port. Just make all aggregation codes zero. |
71 | */ |
72 | if (num_active_ports) |
73 | ac |= BIT(aggr_idx[i % num_active_ports]); |
74 | lan_wr(ANA_PGID_PGID_SET(ac), |
75 | lan966x, ANA_PGID(i)); |
76 | } |
77 | |
78 | /* Mark all ports in the same LAG as visited to avoid applying |
79 | * the same config again. |
80 | */ |
81 | for (p = lag; p < lan966x->num_phys_ports; p++) { |
82 | struct lan966x_port *port = lan966x->ports[p]; |
83 | |
84 | if (!port) |
85 | continue; |
86 | |
87 | if (port->bond == bond) |
88 | visited |= BIT(p); |
89 | } |
90 | } |
91 | } |
92 | |
93 | static void lan966x_lag_set_port_ids(struct lan966x *lan966x) |
94 | { |
95 | struct lan966x_port *port; |
96 | u32 bond_mask; |
97 | u32 lag_id; |
98 | int p; |
99 | |
100 | for (p = 0; p < lan966x->num_phys_ports; ++p) { |
101 | port = lan966x->ports[p]; |
102 | if (!port) |
103 | continue; |
104 | |
105 | lag_id = port->chip_port; |
106 | |
107 | bond_mask = lan966x_lag_get_mask(lan966x, bond: port->bond); |
108 | if (bond_mask) |
109 | lag_id = __ffs(bond_mask); |
110 | |
111 | lan_rmw(ANA_PORT_CFG_PORTID_VAL_SET(lag_id), |
112 | ANA_PORT_CFG_PORTID_VAL, |
113 | lan966x, ANA_PORT_CFG(port->chip_port)); |
114 | } |
115 | } |
116 | |
117 | static void lan966x_lag_update_ids(struct lan966x *lan966x) |
118 | { |
119 | lan966x_lag_set_port_ids(lan966x); |
120 | lan966x_update_fwd_mask(lan966x); |
121 | lan966x_lag_set_aggr_pgids(lan966x); |
122 | } |
123 | |
124 | int lan966x_lag_port_join(struct lan966x_port *port, |
125 | struct net_device *brport_dev, |
126 | struct net_device *bond, |
127 | struct netlink_ext_ack *extack) |
128 | { |
129 | struct lan966x *lan966x = port->lan966x; |
130 | struct net_device *dev = port->dev; |
131 | u32 lag_id = -1; |
132 | u32 bond_mask; |
133 | int err; |
134 | |
135 | bond_mask = lan966x_lag_get_mask(lan966x, bond); |
136 | if (bond_mask) |
137 | lag_id = __ffs(bond_mask); |
138 | |
139 | port->bond = bond; |
140 | lan966x_lag_update_ids(lan966x); |
141 | |
142 | err = switchdev_bridge_port_offload(brport_dev, dev, ctx: port, |
143 | atomic_nb: &lan966x_switchdev_nb, |
144 | blocking_nb: &lan966x_switchdev_blocking_nb, |
145 | tx_fwd_offload: false, extack); |
146 | if (err) |
147 | goto out; |
148 | |
149 | lan966x_port_stp_state_set(port, state: br_port_get_stp_state(dev: brport_dev)); |
150 | |
151 | if (lan966x_lag_first_port(lag: port->bond, dev: port->dev) && |
152 | lag_id != -1) |
153 | lan966x_mac_lag_replace_port_entry(lan966x, |
154 | src: lan966x->ports[lag_id], |
155 | dst: port); |
156 | |
157 | return 0; |
158 | |
159 | out: |
160 | port->bond = NULL; |
161 | lan966x_lag_update_ids(lan966x); |
162 | |
163 | return err; |
164 | } |
165 | |
166 | void lan966x_lag_port_leave(struct lan966x_port *port, struct net_device *bond) |
167 | { |
168 | struct lan966x *lan966x = port->lan966x; |
169 | u32 bond_mask; |
170 | u32 lag_id; |
171 | |
172 | if (lan966x_lag_first_port(lag: port->bond, dev: port->dev)) { |
173 | bond_mask = lan966x_lag_get_mask(lan966x, bond: port->bond); |
174 | bond_mask &= ~BIT(port->chip_port); |
175 | if (bond_mask) { |
176 | lag_id = __ffs(bond_mask); |
177 | lan966x_mac_lag_replace_port_entry(lan966x, src: port, |
178 | dst: lan966x->ports[lag_id]); |
179 | } else { |
180 | lan966x_mac_lag_remove_port_entry(lan966x, src: port); |
181 | } |
182 | } |
183 | |
184 | port->bond = NULL; |
185 | lan966x_lag_update_ids(lan966x); |
186 | lan966x_port_stp_state_set(port, BR_STATE_FORWARDING); |
187 | } |
188 | |
189 | static bool lan966x_lag_port_check_hash_types(struct lan966x *lan966x, |
190 | enum netdev_lag_hash hash_type) |
191 | { |
192 | int p; |
193 | |
194 | for (p = 0; p < lan966x->num_phys_ports; ++p) { |
195 | struct lan966x_port *port = lan966x->ports[p]; |
196 | |
197 | if (!port || !port->bond) |
198 | continue; |
199 | |
200 | if (port->hash_type != hash_type) |
201 | return false; |
202 | } |
203 | |
204 | return true; |
205 | } |
206 | |
207 | int lan966x_lag_port_prechangeupper(struct net_device *dev, |
208 | struct netdev_notifier_changeupper_info *info) |
209 | { |
210 | struct lan966x_port *port = netdev_priv(dev); |
211 | struct lan966x *lan966x = port->lan966x; |
212 | struct netdev_lag_upper_info *lui; |
213 | struct netlink_ext_ack *extack; |
214 | |
215 | extack = netdev_notifier_info_to_extack(info: &info->info); |
216 | lui = info->upper_info; |
217 | if (!lui) { |
218 | port->hash_type = NETDEV_LAG_HASH_NONE; |
219 | return NOTIFY_DONE; |
220 | } |
221 | |
222 | if (lui->tx_type != NETDEV_LAG_TX_TYPE_HASH) { |
223 | NL_SET_ERR_MSG_MOD(extack, |
224 | "LAG device using unsupported Tx type" ); |
225 | return -EINVAL; |
226 | } |
227 | |
228 | if (!lan966x_lag_port_check_hash_types(lan966x, hash_type: lui->hash_type)) { |
229 | NL_SET_ERR_MSG_MOD(extack, |
230 | "LAG devices can have only the same hash_type" ); |
231 | return -EINVAL; |
232 | } |
233 | |
234 | switch (lui->hash_type) { |
235 | case NETDEV_LAG_HASH_L2: |
236 | lan_wr(ANA_AGGR_CFG_AC_DMAC_ENA_SET(1) | |
237 | ANA_AGGR_CFG_AC_SMAC_ENA_SET(1), |
238 | lan966x, ANA_AGGR_CFG); |
239 | break; |
240 | case NETDEV_LAG_HASH_L34: |
241 | lan_wr(ANA_AGGR_CFG_AC_IP6_TCPUDP_ENA_SET(1) | |
242 | ANA_AGGR_CFG_AC_IP4_TCPUDP_ENA_SET(1) | |
243 | ANA_AGGR_CFG_AC_IP4_SIPDIP_ENA_SET(1), |
244 | lan966x, ANA_AGGR_CFG); |
245 | break; |
246 | case NETDEV_LAG_HASH_L23: |
247 | lan_wr(ANA_AGGR_CFG_AC_DMAC_ENA_SET(1) | |
248 | ANA_AGGR_CFG_AC_SMAC_ENA_SET(1) | |
249 | ANA_AGGR_CFG_AC_IP6_TCPUDP_ENA_SET(1) | |
250 | ANA_AGGR_CFG_AC_IP4_TCPUDP_ENA_SET(1), |
251 | lan966x, ANA_AGGR_CFG); |
252 | break; |
253 | default: |
254 | NL_SET_ERR_MSG_MOD(extack, |
255 | "LAG device using unsupported hash type" ); |
256 | return -EINVAL; |
257 | } |
258 | |
259 | port->hash_type = lui->hash_type; |
260 | |
261 | return NOTIFY_OK; |
262 | } |
263 | |
264 | int lan966x_lag_port_changelowerstate(struct net_device *dev, |
265 | struct netdev_notifier_changelowerstate_info *info) |
266 | { |
267 | struct netdev_lag_lower_state_info *lag = info->lower_state_info; |
268 | struct lan966x_port *port = netdev_priv(dev); |
269 | struct lan966x *lan966x = port->lan966x; |
270 | bool is_active; |
271 | |
272 | if (!port->bond) |
273 | return NOTIFY_DONE; |
274 | |
275 | is_active = lag->link_up && lag->tx_enabled; |
276 | if (port->lag_tx_active == is_active) |
277 | return NOTIFY_DONE; |
278 | |
279 | port->lag_tx_active = is_active; |
280 | lan966x_lag_set_aggr_pgids(lan966x); |
281 | |
282 | return NOTIFY_OK; |
283 | } |
284 | |
285 | int lan966x_lag_netdev_prechangeupper(struct net_device *dev, |
286 | struct netdev_notifier_changeupper_info *info) |
287 | { |
288 | struct lan966x_port *port; |
289 | struct net_device *lower; |
290 | struct list_head *iter; |
291 | int err; |
292 | |
293 | netdev_for_each_lower_dev(dev, lower, iter) { |
294 | if (!lan966x_netdevice_check(dev: lower)) |
295 | continue; |
296 | |
297 | port = netdev_priv(dev: lower); |
298 | if (port->bond != dev) |
299 | continue; |
300 | |
301 | err = lan966x_port_prechangeupper(dev: lower, brport_dev: dev, info); |
302 | if (err) |
303 | return err; |
304 | } |
305 | |
306 | return NOTIFY_DONE; |
307 | } |
308 | |
309 | int lan966x_lag_netdev_changeupper(struct net_device *dev, |
310 | struct netdev_notifier_changeupper_info *info) |
311 | { |
312 | struct lan966x_port *port; |
313 | struct net_device *lower; |
314 | struct list_head *iter; |
315 | int err; |
316 | |
317 | netdev_for_each_lower_dev(dev, lower, iter) { |
318 | if (!lan966x_netdevice_check(dev: lower)) |
319 | continue; |
320 | |
321 | port = netdev_priv(dev: lower); |
322 | if (port->bond != dev) |
323 | continue; |
324 | |
325 | err = lan966x_port_changeupper(dev: lower, brport_dev: dev, info); |
326 | if (err) |
327 | return err; |
328 | } |
329 | |
330 | return NOTIFY_DONE; |
331 | } |
332 | |
333 | bool lan966x_lag_first_port(struct net_device *lag, struct net_device *dev) |
334 | { |
335 | struct lan966x_port *port = netdev_priv(dev); |
336 | struct lan966x *lan966x = port->lan966x; |
337 | unsigned long bond_mask; |
338 | |
339 | if (port->bond != lag) |
340 | return false; |
341 | |
342 | bond_mask = lan966x_lag_get_mask(lan966x, bond: lag); |
343 | if (bond_mask && port->chip_port == __ffs(bond_mask)) |
344 | return true; |
345 | |
346 | return false; |
347 | } |
348 | |
349 | u32 lan966x_lag_get_mask(struct lan966x *lan966x, struct net_device *bond) |
350 | { |
351 | struct lan966x_port *port; |
352 | u32 mask = 0; |
353 | int p; |
354 | |
355 | if (!bond) |
356 | return mask; |
357 | |
358 | for (p = 0; p < lan966x->num_phys_ports; p++) { |
359 | port = lan966x->ports[p]; |
360 | if (!port) |
361 | continue; |
362 | |
363 | if (port->bond == bond) |
364 | mask |= BIT(p); |
365 | } |
366 | |
367 | return mask; |
368 | } |
369 | |