1 // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2 /* Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. */
3
4 #include "lib/devcom.h"
5 #include "bridge.h"
6 #include "eswitch.h"
7 #include "bridge_priv.h"
8 #include "diag/bridge_tracepoint.h"
9
10 static const struct rhashtable_params mdb_ht_params = {
11 .key_offset = offsetof(struct mlx5_esw_bridge_mdb_entry, key),
12 .key_len = sizeof(struct mlx5_esw_bridge_mdb_key),
13 .head_offset = offsetof(struct mlx5_esw_bridge_mdb_entry, ht_node),
14 .automatic_shrinking = true,
15 };
16
mlx5_esw_bridge_mdb_init(struct mlx5_esw_bridge * bridge)17 int mlx5_esw_bridge_mdb_init(struct mlx5_esw_bridge *bridge)
18 {
19 INIT_LIST_HEAD(&bridge->mdb_list);
20 return rhashtable_init(&bridge->mdb_ht, &mdb_ht_params);
21 }
22
mlx5_esw_bridge_mdb_cleanup(struct mlx5_esw_bridge * bridge)23 void mlx5_esw_bridge_mdb_cleanup(struct mlx5_esw_bridge *bridge)
24 {
25 rhashtable_destroy(&bridge->mdb_ht);
26 }
27
28 static struct mlx5_esw_bridge_port *
mlx5_esw_bridge_mdb_port_lookup(struct mlx5_esw_bridge_port * port,struct mlx5_esw_bridge_mdb_entry * entry)29 mlx5_esw_bridge_mdb_port_lookup(struct mlx5_esw_bridge_port *port,
30 struct mlx5_esw_bridge_mdb_entry *entry)
31 {
32 return xa_load(&entry->ports, mlx5_esw_bridge_port_key(port));
33 }
34
mlx5_esw_bridge_mdb_port_insert(struct mlx5_esw_bridge_port * port,struct mlx5_esw_bridge_mdb_entry * entry)35 static int mlx5_esw_bridge_mdb_port_insert(struct mlx5_esw_bridge_port *port,
36 struct mlx5_esw_bridge_mdb_entry *entry)
37 {
38 int err = xa_insert(&entry->ports, mlx5_esw_bridge_port_key(port), port, GFP_KERNEL);
39
40 if (!err)
41 entry->num_ports++;
42 return err;
43 }
44
mlx5_esw_bridge_mdb_port_remove(struct mlx5_esw_bridge_port * port,struct mlx5_esw_bridge_mdb_entry * entry)45 static void mlx5_esw_bridge_mdb_port_remove(struct mlx5_esw_bridge_port *port,
46 struct mlx5_esw_bridge_mdb_entry *entry)
47 {
48 xa_erase(&entry->ports, mlx5_esw_bridge_port_key(port));
49 entry->num_ports--;
50 }
51
52 static struct mlx5_flow_handle *
mlx5_esw_bridge_mdb_flow_create(u16 esw_owner_vhca_id,struct mlx5_esw_bridge_mdb_entry * entry,struct mlx5_esw_bridge * bridge)53 mlx5_esw_bridge_mdb_flow_create(u16 esw_owner_vhca_id, struct mlx5_esw_bridge_mdb_entry *entry,
54 struct mlx5_esw_bridge *bridge)
55 {
56 struct mlx5_flow_act flow_act = {
57 .action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST,
58 .flags = FLOW_ACT_NO_APPEND | FLOW_ACT_IGNORE_FLOW_LEVEL,
59 };
60 int num_dests = entry->num_ports, i = 0;
61 struct mlx5_flow_destination *dests;
62 struct mlx5_esw_bridge_port *port;
63 struct mlx5_flow_spec *rule_spec;
64 struct mlx5_flow_handle *handle;
65 u8 *dmac_v, *dmac_c;
66 unsigned long idx;
67
68 rule_spec = kvzalloc(sizeof(*rule_spec), GFP_KERNEL);
69 if (!rule_spec)
70 return ERR_PTR(-ENOMEM);
71
72 dests = kvcalloc(num_dests, sizeof(*dests), GFP_KERNEL);
73 if (!dests) {
74 kvfree(rule_spec);
75 return ERR_PTR(-ENOMEM);
76 }
77
78 xa_for_each(&entry->ports, idx, port) {
79 dests[i].type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE;
80 dests[i].ft = port->mcast.ft;
81 i++;
82 }
83
84 rule_spec->match_criteria_enable = MLX5_MATCH_OUTER_HEADERS;
85 dmac_v = MLX5_ADDR_OF(fte_match_param, rule_spec->match_value, outer_headers.dmac_47_16);
86 ether_addr_copy(dmac_v, entry->key.addr);
87 dmac_c = MLX5_ADDR_OF(fte_match_param, rule_spec->match_criteria, outer_headers.dmac_47_16);
88 eth_broadcast_addr(dmac_c);
89
90 if (entry->key.vid) {
91 if (bridge->vlan_proto == ETH_P_8021Q) {
92 MLX5_SET_TO_ONES(fte_match_param, rule_spec->match_criteria,
93 outer_headers.cvlan_tag);
94 MLX5_SET_TO_ONES(fte_match_param, rule_spec->match_value,
95 outer_headers.cvlan_tag);
96 } else if (bridge->vlan_proto == ETH_P_8021AD) {
97 MLX5_SET_TO_ONES(fte_match_param, rule_spec->match_criteria,
98 outer_headers.svlan_tag);
99 MLX5_SET_TO_ONES(fte_match_param, rule_spec->match_value,
100 outer_headers.svlan_tag);
101 }
102 MLX5_SET_TO_ONES(fte_match_param, rule_spec->match_criteria,
103 outer_headers.first_vid);
104 MLX5_SET(fte_match_param, rule_spec->match_value, outer_headers.first_vid,
105 entry->key.vid);
106 }
107
108 handle = mlx5_add_flow_rules(bridge->egress_ft, rule_spec, &flow_act, dests, num_dests);
109
110 kvfree(dests);
111 kvfree(rule_spec);
112 return handle;
113 }
114
115 static int
mlx5_esw_bridge_port_mdb_offload(struct mlx5_esw_bridge_port * port,struct mlx5_esw_bridge_mdb_entry * entry)116 mlx5_esw_bridge_port_mdb_offload(struct mlx5_esw_bridge_port *port,
117 struct mlx5_esw_bridge_mdb_entry *entry)
118 {
119 struct mlx5_flow_handle *handle;
120
121 handle = mlx5_esw_bridge_mdb_flow_create(port->esw_owner_vhca_id, entry, port->bridge);
122 if (entry->egress_handle) {
123 mlx5_del_flow_rules(entry->egress_handle);
124 entry->egress_handle = NULL;
125 }
126 if (IS_ERR(handle))
127 return PTR_ERR(handle);
128
129 entry->egress_handle = handle;
130 return 0;
131 }
132
133 static struct mlx5_esw_bridge_mdb_entry *
mlx5_esw_bridge_mdb_lookup(struct mlx5_esw_bridge * bridge,const unsigned char * addr,u16 vid)134 mlx5_esw_bridge_mdb_lookup(struct mlx5_esw_bridge *bridge,
135 const unsigned char *addr, u16 vid)
136 {
137 struct mlx5_esw_bridge_mdb_key key = {};
138
139 ether_addr_copy(key.addr, addr);
140 key.vid = vid;
141 return rhashtable_lookup_fast(&bridge->mdb_ht, &key, mdb_ht_params);
142 }
143
144 static struct mlx5_esw_bridge_mdb_entry *
mlx5_esw_bridge_port_mdb_entry_init(struct mlx5_esw_bridge_port * port,const unsigned char * addr,u16 vid)145 mlx5_esw_bridge_port_mdb_entry_init(struct mlx5_esw_bridge_port *port,
146 const unsigned char *addr, u16 vid)
147 {
148 struct mlx5_esw_bridge *bridge = port->bridge;
149 struct mlx5_esw_bridge_mdb_entry *entry;
150 int err;
151
152 entry = kvzalloc(sizeof(*entry), GFP_KERNEL);
153 if (!entry)
154 return ERR_PTR(-ENOMEM);
155
156 ether_addr_copy(entry->key.addr, addr);
157 entry->key.vid = vid;
158 xa_init(&entry->ports);
159 err = rhashtable_insert_fast(&bridge->mdb_ht, &entry->ht_node, mdb_ht_params);
160 if (err)
161 goto err_ht_insert;
162
163 list_add(&entry->list, &bridge->mdb_list);
164
165 return entry;
166
167 err_ht_insert:
168 xa_destroy(&entry->ports);
169 kvfree(entry);
170 return ERR_PTR(err);
171 }
172
mlx5_esw_bridge_port_mdb_entry_cleanup(struct mlx5_esw_bridge * bridge,struct mlx5_esw_bridge_mdb_entry * entry)173 static void mlx5_esw_bridge_port_mdb_entry_cleanup(struct mlx5_esw_bridge *bridge,
174 struct mlx5_esw_bridge_mdb_entry *entry)
175 {
176 if (entry->egress_handle)
177 mlx5_del_flow_rules(entry->egress_handle);
178 list_del(&entry->list);
179 rhashtable_remove_fast(&bridge->mdb_ht, &entry->ht_node, mdb_ht_params);
180 xa_destroy(&entry->ports);
181 kvfree(entry);
182 }
183
mlx5_esw_bridge_port_mdb_attach(struct net_device * dev,struct mlx5_esw_bridge_port * port,const unsigned char * addr,u16 vid)184 int mlx5_esw_bridge_port_mdb_attach(struct net_device *dev, struct mlx5_esw_bridge_port *port,
185 const unsigned char *addr, u16 vid)
186 {
187 struct mlx5_esw_bridge *bridge = port->bridge;
188 struct mlx5_esw_bridge_mdb_entry *entry;
189 int err;
190
191 if (!(bridge->flags & MLX5_ESW_BRIDGE_MCAST_FLAG))
192 return -EOPNOTSUPP;
193
194 entry = mlx5_esw_bridge_mdb_lookup(bridge, addr, vid);
195 if (entry) {
196 if (mlx5_esw_bridge_mdb_port_lookup(port, entry)) {
197 esw_warn(bridge->br_offloads->esw->dev, "MDB attach entry is already attached to port (MAC=%pM,vid=%u,vport=%u)\n",
198 addr, vid, port->vport_num);
199 return 0;
200 }
201 } else {
202 entry = mlx5_esw_bridge_port_mdb_entry_init(port, addr, vid);
203 if (IS_ERR(entry)) {
204 err = PTR_ERR(entry);
205 esw_warn(bridge->br_offloads->esw->dev, "MDB attach failed to init entry (MAC=%pM,vid=%u,vport=%u,err=%d)\n",
206 addr, vid, port->vport_num, err);
207 return err;
208 }
209 }
210
211 err = mlx5_esw_bridge_mdb_port_insert(port, entry);
212 if (err) {
213 if (!entry->num_ports)
214 mlx5_esw_bridge_port_mdb_entry_cleanup(bridge, entry); /* new mdb entry */
215 esw_warn(bridge->br_offloads->esw->dev,
216 "MDB attach failed to insert port (MAC=%pM,vid=%u,vport=%u,err=%d)\n",
217 addr, vid, port->vport_num, err);
218 return err;
219 }
220
221 err = mlx5_esw_bridge_port_mdb_offload(port, entry);
222 if (err)
223 /* Single mdb can be used by multiple ports, so just log the
224 * error and continue.
225 */
226 esw_warn(bridge->br_offloads->esw->dev, "MDB attach failed to offload (MAC=%pM,vid=%u,vport=%u,err=%d)\n",
227 addr, vid, port->vport_num, err);
228
229 trace_mlx5_esw_bridge_port_mdb_attach(dev, entry);
230 return 0;
231 }
232
mlx5_esw_bridge_port_mdb_entry_detach(struct mlx5_esw_bridge_port * port,struct mlx5_esw_bridge_mdb_entry * entry)233 static void mlx5_esw_bridge_port_mdb_entry_detach(struct mlx5_esw_bridge_port *port,
234 struct mlx5_esw_bridge_mdb_entry *entry)
235 {
236 struct mlx5_esw_bridge *bridge = port->bridge;
237 int err;
238
239 mlx5_esw_bridge_mdb_port_remove(port, entry);
240 if (!entry->num_ports) {
241 mlx5_esw_bridge_port_mdb_entry_cleanup(bridge, entry);
242 return;
243 }
244
245 err = mlx5_esw_bridge_port_mdb_offload(port, entry);
246 if (err)
247 /* Single mdb can be used by multiple ports, so just log the
248 * error and continue.
249 */
250 esw_warn(bridge->br_offloads->esw->dev, "MDB detach failed to offload (MAC=%pM,vid=%u,vport=%u)\n",
251 entry->key.addr, entry->key.vid, port->vport_num);
252 }
253
mlx5_esw_bridge_port_mdb_detach(struct net_device * dev,struct mlx5_esw_bridge_port * port,const unsigned char * addr,u16 vid)254 void mlx5_esw_bridge_port_mdb_detach(struct net_device *dev, struct mlx5_esw_bridge_port *port,
255 const unsigned char *addr, u16 vid)
256 {
257 struct mlx5_esw_bridge *bridge = port->bridge;
258 struct mlx5_esw_bridge_mdb_entry *entry;
259
260 entry = mlx5_esw_bridge_mdb_lookup(bridge, addr, vid);
261 if (!entry) {
262 esw_debug(bridge->br_offloads->esw->dev,
263 "MDB detach entry not found (MAC=%pM,vid=%u,vport=%u)\n",
264 addr, vid, port->vport_num);
265 return;
266 }
267
268 if (!mlx5_esw_bridge_mdb_port_lookup(port, entry)) {
269 esw_debug(bridge->br_offloads->esw->dev,
270 "MDB detach entry not attached to the port (MAC=%pM,vid=%u,vport=%u)\n",
271 addr, vid, port->vport_num);
272 return;
273 }
274
275 trace_mlx5_esw_bridge_port_mdb_detach(dev, entry);
276 mlx5_esw_bridge_port_mdb_entry_detach(port, entry);
277 }
278
mlx5_esw_bridge_port_mdb_vlan_flush(struct mlx5_esw_bridge_port * port,struct mlx5_esw_bridge_vlan * vlan)279 void mlx5_esw_bridge_port_mdb_vlan_flush(struct mlx5_esw_bridge_port *port,
280 struct mlx5_esw_bridge_vlan *vlan)
281 {
282 struct mlx5_esw_bridge *bridge = port->bridge;
283 struct mlx5_esw_bridge_mdb_entry *entry, *tmp;
284
285 list_for_each_entry_safe(entry, tmp, &bridge->mdb_list, list)
286 if (entry->key.vid == vlan->vid && mlx5_esw_bridge_mdb_port_lookup(port, entry))
287 mlx5_esw_bridge_port_mdb_entry_detach(port, entry);
288 }
289
mlx5_esw_bridge_port_mdb_flush(struct mlx5_esw_bridge_port * port)290 static void mlx5_esw_bridge_port_mdb_flush(struct mlx5_esw_bridge_port *port)
291 {
292 struct mlx5_esw_bridge *bridge = port->bridge;
293 struct mlx5_esw_bridge_mdb_entry *entry, *tmp;
294
295 list_for_each_entry_safe(entry, tmp, &bridge->mdb_list, list)
296 if (mlx5_esw_bridge_mdb_port_lookup(port, entry))
297 mlx5_esw_bridge_port_mdb_entry_detach(port, entry);
298 }
299
mlx5_esw_bridge_mdb_flush(struct mlx5_esw_bridge * bridge)300 void mlx5_esw_bridge_mdb_flush(struct mlx5_esw_bridge *bridge)
301 {
302 struct mlx5_esw_bridge_mdb_entry *entry, *tmp;
303
304 list_for_each_entry_safe(entry, tmp, &bridge->mdb_list, list)
305 mlx5_esw_bridge_port_mdb_entry_cleanup(bridge, entry);
306 }
mlx5_esw_bridge_port_mcast_fts_init(struct mlx5_esw_bridge_port * port,struct mlx5_esw_bridge * bridge)307 static int mlx5_esw_bridge_port_mcast_fts_init(struct mlx5_esw_bridge_port *port,
308 struct mlx5_esw_bridge *bridge)
309 {
310 struct mlx5_eswitch *esw = bridge->br_offloads->esw;
311 struct mlx5_flow_table *mcast_ft;
312
313 mcast_ft = mlx5_esw_bridge_table_create(MLX5_ESW_BRIDGE_MCAST_TABLE_SIZE,
314 MLX5_ESW_BRIDGE_LEVEL_MCAST_TABLE,
315 esw);
316 if (IS_ERR(mcast_ft))
317 return PTR_ERR(mcast_ft);
318
319 port->mcast.ft = mcast_ft;
320 return 0;
321 }
322
mlx5_esw_bridge_port_mcast_fts_cleanup(struct mlx5_esw_bridge_port * port)323 static void mlx5_esw_bridge_port_mcast_fts_cleanup(struct mlx5_esw_bridge_port *port)
324 {
325 if (port->mcast.ft)
326 mlx5_destroy_flow_table(port->mcast.ft);
327 port->mcast.ft = NULL;
328 }
329
330 static struct mlx5_flow_group *
mlx5_esw_bridge_mcast_filter_fg_create(struct mlx5_eswitch * esw,struct mlx5_flow_table * mcast_ft)331 mlx5_esw_bridge_mcast_filter_fg_create(struct mlx5_eswitch *esw,
332 struct mlx5_flow_table *mcast_ft)
333 {
334 int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
335 struct mlx5_flow_group *fg;
336 u32 *in, *match;
337
338 in = kvzalloc(inlen, GFP_KERNEL);
339 if (!in)
340 return ERR_PTR(-ENOMEM);
341
342 MLX5_SET(create_flow_group_in, in, match_criteria_enable, MLX5_MATCH_MISC_PARAMETERS_2);
343 match = MLX5_ADDR_OF(create_flow_group_in, in, match_criteria);
344
345 MLX5_SET(fte_match_param, match, misc_parameters_2.metadata_reg_c_0,
346 mlx5_eswitch_get_vport_metadata_mask());
347
348 MLX5_SET(create_flow_group_in, in, start_flow_index,
349 MLX5_ESW_BRIDGE_MCAST_TABLE_FILTER_GRP_IDX_FROM);
350 MLX5_SET(create_flow_group_in, in, end_flow_index,
351 MLX5_ESW_BRIDGE_MCAST_TABLE_FILTER_GRP_IDX_TO);
352
353 fg = mlx5_create_flow_group(mcast_ft, in);
354 kvfree(in);
355 if (IS_ERR(fg))
356 esw_warn(esw->dev,
357 "Failed to create filter flow group for bridge mcast table (err=%pe)\n",
358 fg);
359
360 return fg;
361 }
362
363 static struct mlx5_flow_group *
mlx5_esw_bridge_mcast_vlan_proto_fg_create(unsigned int from,unsigned int to,u16 vlan_proto,struct mlx5_eswitch * esw,struct mlx5_flow_table * mcast_ft)364 mlx5_esw_bridge_mcast_vlan_proto_fg_create(unsigned int from, unsigned int to, u16 vlan_proto,
365 struct mlx5_eswitch *esw,
366 struct mlx5_flow_table *mcast_ft)
367 {
368 int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
369 struct mlx5_flow_group *fg;
370 u32 *in, *match;
371
372 in = kvzalloc(inlen, GFP_KERNEL);
373 if (!in)
374 return ERR_PTR(-ENOMEM);
375
376 MLX5_SET(create_flow_group_in, in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS);
377 match = MLX5_ADDR_OF(create_flow_group_in, in, match_criteria);
378
379 if (vlan_proto == ETH_P_8021Q)
380 MLX5_SET_TO_ONES(fte_match_param, match, outer_headers.cvlan_tag);
381 else if (vlan_proto == ETH_P_8021AD)
382 MLX5_SET_TO_ONES(fte_match_param, match, outer_headers.svlan_tag);
383 MLX5_SET_TO_ONES(fte_match_param, match, outer_headers.first_vid);
384
385 MLX5_SET(create_flow_group_in, in, start_flow_index, from);
386 MLX5_SET(create_flow_group_in, in, end_flow_index, to);
387
388 fg = mlx5_create_flow_group(mcast_ft, in);
389 kvfree(in);
390 if (IS_ERR(fg))
391 esw_warn(esw->dev,
392 "Failed to create VLAN(proto=%x) flow group for bridge mcast table (err=%pe)\n",
393 vlan_proto, fg);
394
395 return fg;
396 }
397
398 static struct mlx5_flow_group *
mlx5_esw_bridge_mcast_vlan_fg_create(struct mlx5_eswitch * esw,struct mlx5_flow_table * mcast_ft)399 mlx5_esw_bridge_mcast_vlan_fg_create(struct mlx5_eswitch *esw, struct mlx5_flow_table *mcast_ft)
400 {
401 unsigned int from = MLX5_ESW_BRIDGE_MCAST_TABLE_VLAN_GRP_IDX_FROM;
402 unsigned int to = MLX5_ESW_BRIDGE_MCAST_TABLE_VLAN_GRP_IDX_TO;
403
404 return mlx5_esw_bridge_mcast_vlan_proto_fg_create(from, to, ETH_P_8021Q, esw, mcast_ft);
405 }
406
407 static struct mlx5_flow_group *
mlx5_esw_bridge_mcast_qinq_fg_create(struct mlx5_eswitch * esw,struct mlx5_flow_table * mcast_ft)408 mlx5_esw_bridge_mcast_qinq_fg_create(struct mlx5_eswitch *esw,
409 struct mlx5_flow_table *mcast_ft)
410 {
411 unsigned int from = MLX5_ESW_BRIDGE_MCAST_TABLE_QINQ_GRP_IDX_FROM;
412 unsigned int to = MLX5_ESW_BRIDGE_MCAST_TABLE_QINQ_GRP_IDX_TO;
413
414 return mlx5_esw_bridge_mcast_vlan_proto_fg_create(from, to, ETH_P_8021AD, esw, mcast_ft);
415 }
416
417 static struct mlx5_flow_group *
mlx5_esw_bridge_mcast_fwd_fg_create(struct mlx5_eswitch * esw,struct mlx5_flow_table * mcast_ft)418 mlx5_esw_bridge_mcast_fwd_fg_create(struct mlx5_eswitch *esw,
419 struct mlx5_flow_table *mcast_ft)
420 {
421 int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
422 struct mlx5_flow_group *fg;
423 u32 *in;
424
425 in = kvzalloc(inlen, GFP_KERNEL);
426 if (!in)
427 return ERR_PTR(-ENOMEM);
428
429 MLX5_SET(create_flow_group_in, in, start_flow_index,
430 MLX5_ESW_BRIDGE_MCAST_TABLE_FWD_GRP_IDX_FROM);
431 MLX5_SET(create_flow_group_in, in, end_flow_index,
432 MLX5_ESW_BRIDGE_MCAST_TABLE_FWD_GRP_IDX_TO);
433
434 fg = mlx5_create_flow_group(mcast_ft, in);
435 kvfree(in);
436 if (IS_ERR(fg))
437 esw_warn(esw->dev,
438 "Failed to create forward flow group for bridge mcast table (err=%pe)\n",
439 fg);
440
441 return fg;
442 }
443
mlx5_esw_bridge_port_mcast_fgs_init(struct mlx5_esw_bridge_port * port)444 static int mlx5_esw_bridge_port_mcast_fgs_init(struct mlx5_esw_bridge_port *port)
445 {
446 struct mlx5_flow_group *fwd_fg, *qinq_fg, *vlan_fg, *filter_fg;
447 struct mlx5_eswitch *esw = port->bridge->br_offloads->esw;
448 struct mlx5_flow_table *mcast_ft = port->mcast.ft;
449 int err;
450
451 filter_fg = mlx5_esw_bridge_mcast_filter_fg_create(esw, mcast_ft);
452 if (IS_ERR(filter_fg))
453 return PTR_ERR(filter_fg);
454
455 vlan_fg = mlx5_esw_bridge_mcast_vlan_fg_create(esw, mcast_ft);
456 if (IS_ERR(vlan_fg)) {
457 err = PTR_ERR(vlan_fg);
458 goto err_vlan_fg;
459 }
460
461 qinq_fg = mlx5_esw_bridge_mcast_qinq_fg_create(esw, mcast_ft);
462 if (IS_ERR(qinq_fg)) {
463 err = PTR_ERR(qinq_fg);
464 goto err_qinq_fg;
465 }
466
467 fwd_fg = mlx5_esw_bridge_mcast_fwd_fg_create(esw, mcast_ft);
468 if (IS_ERR(fwd_fg)) {
469 err = PTR_ERR(fwd_fg);
470 goto err_fwd_fg;
471 }
472
473 port->mcast.filter_fg = filter_fg;
474 port->mcast.vlan_fg = vlan_fg;
475 port->mcast.qinq_fg = qinq_fg;
476 port->mcast.fwd_fg = fwd_fg;
477
478 return 0;
479
480 err_fwd_fg:
481 mlx5_destroy_flow_group(qinq_fg);
482 err_qinq_fg:
483 mlx5_destroy_flow_group(vlan_fg);
484 err_vlan_fg:
485 mlx5_destroy_flow_group(filter_fg);
486 return err;
487 }
488
mlx5_esw_bridge_port_mcast_fgs_cleanup(struct mlx5_esw_bridge_port * port)489 static void mlx5_esw_bridge_port_mcast_fgs_cleanup(struct mlx5_esw_bridge_port *port)
490 {
491 if (port->mcast.fwd_fg)
492 mlx5_destroy_flow_group(port->mcast.fwd_fg);
493 port->mcast.fwd_fg = NULL;
494 if (port->mcast.qinq_fg)
495 mlx5_destroy_flow_group(port->mcast.qinq_fg);
496 port->mcast.qinq_fg = NULL;
497 if (port->mcast.vlan_fg)
498 mlx5_destroy_flow_group(port->mcast.vlan_fg);
499 port->mcast.vlan_fg = NULL;
500 if (port->mcast.filter_fg)
501 mlx5_destroy_flow_group(port->mcast.filter_fg);
502 port->mcast.filter_fg = NULL;
503 }
504
505 static struct mlx5_flow_handle *
mlx5_esw_bridge_mcast_flow_with_esw_create(struct mlx5_esw_bridge_port * port,struct mlx5_eswitch * esw)506 mlx5_esw_bridge_mcast_flow_with_esw_create(struct mlx5_esw_bridge_port *port,
507 struct mlx5_eswitch *esw)
508 {
509 struct mlx5_flow_act flow_act = {
510 .action = MLX5_FLOW_CONTEXT_ACTION_DROP,
511 .flags = FLOW_ACT_NO_APPEND,
512 };
513 struct mlx5_flow_spec *rule_spec;
514 struct mlx5_flow_handle *handle;
515
516 rule_spec = kvzalloc(sizeof(*rule_spec), GFP_KERNEL);
517 if (!rule_spec)
518 return ERR_PTR(-ENOMEM);
519
520 rule_spec->match_criteria_enable = MLX5_MATCH_MISC_PARAMETERS_2;
521
522 MLX5_SET(fte_match_param, rule_spec->match_criteria,
523 misc_parameters_2.metadata_reg_c_0, mlx5_eswitch_get_vport_metadata_mask());
524 MLX5_SET(fte_match_param, rule_spec->match_value, misc_parameters_2.metadata_reg_c_0,
525 mlx5_eswitch_get_vport_metadata_for_match(esw, port->vport_num));
526
527 handle = mlx5_add_flow_rules(port->mcast.ft, rule_spec, &flow_act, NULL, 0);
528
529 kvfree(rule_spec);
530 return handle;
531 }
532
533 static struct mlx5_flow_handle *
mlx5_esw_bridge_mcast_filter_flow_create(struct mlx5_esw_bridge_port * port)534 mlx5_esw_bridge_mcast_filter_flow_create(struct mlx5_esw_bridge_port *port)
535 {
536 return mlx5_esw_bridge_mcast_flow_with_esw_create(port, port->bridge->br_offloads->esw);
537 }
538
539 static struct mlx5_flow_handle *
mlx5_esw_bridge_mcast_filter_flow_peer_create(struct mlx5_esw_bridge_port * port)540 mlx5_esw_bridge_mcast_filter_flow_peer_create(struct mlx5_esw_bridge_port *port)
541 {
542 struct mlx5_devcom_comp_dev *devcom = port->bridge->br_offloads->esw->devcom, *pos;
543 struct mlx5_eswitch *tmp, *peer_esw = NULL;
544 static struct mlx5_flow_handle *handle;
545
546 if (!mlx5_devcom_for_each_peer_begin(devcom))
547 return ERR_PTR(-ENODEV);
548
549 mlx5_devcom_for_each_peer_entry(devcom, tmp, pos) {
550 if (mlx5_esw_is_owner(tmp, port->vport_num, port->esw_owner_vhca_id)) {
551 peer_esw = tmp;
552 break;
553 }
554 }
555
556 if (!peer_esw) {
557 handle = ERR_PTR(-ENODEV);
558 goto out;
559 }
560
561 handle = mlx5_esw_bridge_mcast_flow_with_esw_create(port, peer_esw);
562
563 out:
564 mlx5_devcom_for_each_peer_end(devcom);
565 return handle;
566 }
567
568 static struct mlx5_flow_handle *
mlx5_esw_bridge_mcast_vlan_flow_create(u16 vlan_proto,struct mlx5_esw_bridge_port * port,struct mlx5_esw_bridge_vlan * vlan)569 mlx5_esw_bridge_mcast_vlan_flow_create(u16 vlan_proto, struct mlx5_esw_bridge_port *port,
570 struct mlx5_esw_bridge_vlan *vlan)
571 {
572 struct mlx5_flow_act flow_act = {
573 .action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST,
574 .flags = FLOW_ACT_NO_APPEND,
575 };
576 struct mlx5_flow_destination dest = {
577 .type = MLX5_FLOW_DESTINATION_TYPE_VPORT,
578 .vport.num = port->vport_num,
579 };
580 struct mlx5_esw_bridge *bridge = port->bridge;
581 struct mlx5_flow_spec *rule_spec;
582 struct mlx5_flow_handle *handle;
583
584 rule_spec = kvzalloc(sizeof(*rule_spec), GFP_KERNEL);
585 if (!rule_spec)
586 return ERR_PTR(-ENOMEM);
587
588 if (MLX5_CAP_ESW_FLOWTABLE(bridge->br_offloads->esw->dev, flow_source) &&
589 port->vport_num == MLX5_VPORT_UPLINK)
590 rule_spec->flow_context.flow_source =
591 MLX5_FLOW_CONTEXT_FLOW_SOURCE_LOCAL_VPORT;
592 rule_spec->match_criteria_enable = MLX5_MATCH_OUTER_HEADERS;
593
594 flow_act.action |= MLX5_FLOW_CONTEXT_ACTION_PACKET_REFORMAT;
595 flow_act.pkt_reformat = vlan->pkt_reformat_pop;
596
597 if (vlan_proto == ETH_P_8021Q) {
598 MLX5_SET_TO_ONES(fte_match_param, rule_spec->match_criteria,
599 outer_headers.cvlan_tag);
600 MLX5_SET_TO_ONES(fte_match_param, rule_spec->match_value,
601 outer_headers.cvlan_tag);
602 } else if (vlan_proto == ETH_P_8021AD) {
603 MLX5_SET_TO_ONES(fte_match_param, rule_spec->match_criteria,
604 outer_headers.svlan_tag);
605 MLX5_SET_TO_ONES(fte_match_param, rule_spec->match_value,
606 outer_headers.svlan_tag);
607 }
608 MLX5_SET_TO_ONES(fte_match_param, rule_spec->match_criteria, outer_headers.first_vid);
609 MLX5_SET(fte_match_param, rule_spec->match_value, outer_headers.first_vid, vlan->vid);
610
611 if (MLX5_CAP_ESW(bridge->br_offloads->esw->dev, merged_eswitch)) {
612 dest.vport.flags = MLX5_FLOW_DEST_VPORT_VHCA_ID;
613 dest.vport.vhca_id = port->esw_owner_vhca_id;
614 }
615 handle = mlx5_add_flow_rules(port->mcast.ft, rule_spec, &flow_act, &dest, 1);
616
617 kvfree(rule_spec);
618 return handle;
619 }
620
mlx5_esw_bridge_vlan_mcast_init(u16 vlan_proto,struct mlx5_esw_bridge_port * port,struct mlx5_esw_bridge_vlan * vlan)621 int mlx5_esw_bridge_vlan_mcast_init(u16 vlan_proto, struct mlx5_esw_bridge_port *port,
622 struct mlx5_esw_bridge_vlan *vlan)
623 {
624 struct mlx5_flow_handle *handle;
625
626 if (!(port->bridge->flags & MLX5_ESW_BRIDGE_MCAST_FLAG))
627 return 0;
628
629 handle = mlx5_esw_bridge_mcast_vlan_flow_create(vlan_proto, port, vlan);
630 if (IS_ERR(handle))
631 return PTR_ERR(handle);
632
633 vlan->mcast_handle = handle;
634 return 0;
635 }
636
mlx5_esw_bridge_vlan_mcast_cleanup(struct mlx5_esw_bridge_vlan * vlan)637 void mlx5_esw_bridge_vlan_mcast_cleanup(struct mlx5_esw_bridge_vlan *vlan)
638 {
639 if (vlan->mcast_handle)
640 mlx5_del_flow_rules(vlan->mcast_handle);
641 vlan->mcast_handle = NULL;
642 }
643
644 static struct mlx5_flow_handle *
mlx5_esw_bridge_mcast_fwd_flow_create(struct mlx5_esw_bridge_port * port)645 mlx5_esw_bridge_mcast_fwd_flow_create(struct mlx5_esw_bridge_port *port)
646 {
647 struct mlx5_flow_act flow_act = {
648 .action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST,
649 .flags = FLOW_ACT_NO_APPEND,
650 };
651 struct mlx5_flow_destination dest = {
652 .type = MLX5_FLOW_DESTINATION_TYPE_VPORT,
653 .vport.num = port->vport_num,
654 };
655 struct mlx5_esw_bridge *bridge = port->bridge;
656 struct mlx5_flow_spec *rule_spec;
657 struct mlx5_flow_handle *handle;
658
659 rule_spec = kvzalloc(sizeof(*rule_spec), GFP_KERNEL);
660 if (!rule_spec)
661 return ERR_PTR(-ENOMEM);
662
663 if (MLX5_CAP_ESW_FLOWTABLE(bridge->br_offloads->esw->dev, flow_source) &&
664 port->vport_num == MLX5_VPORT_UPLINK)
665 rule_spec->flow_context.flow_source =
666 MLX5_FLOW_CONTEXT_FLOW_SOURCE_LOCAL_VPORT;
667
668 if (MLX5_CAP_ESW(bridge->br_offloads->esw->dev, merged_eswitch)) {
669 dest.vport.flags = MLX5_FLOW_DEST_VPORT_VHCA_ID;
670 dest.vport.vhca_id = port->esw_owner_vhca_id;
671 }
672 handle = mlx5_add_flow_rules(port->mcast.ft, rule_spec, &flow_act, &dest, 1);
673
674 kvfree(rule_spec);
675 return handle;
676 }
677
mlx5_esw_bridge_port_mcast_fhs_init(struct mlx5_esw_bridge_port * port)678 static int mlx5_esw_bridge_port_mcast_fhs_init(struct mlx5_esw_bridge_port *port)
679 {
680 struct mlx5_flow_handle *filter_handle, *fwd_handle;
681 struct mlx5_esw_bridge_vlan *vlan, *failed;
682 unsigned long index;
683 int err;
684
685
686 filter_handle = (port->flags & MLX5_ESW_BRIDGE_PORT_FLAG_PEER) ?
687 mlx5_esw_bridge_mcast_filter_flow_peer_create(port) :
688 mlx5_esw_bridge_mcast_filter_flow_create(port);
689 if (IS_ERR(filter_handle))
690 return PTR_ERR(filter_handle);
691
692 fwd_handle = mlx5_esw_bridge_mcast_fwd_flow_create(port);
693 if (IS_ERR(fwd_handle)) {
694 err = PTR_ERR(fwd_handle);
695 goto err_fwd;
696 }
697
698 xa_for_each(&port->vlans, index, vlan) {
699 err = mlx5_esw_bridge_vlan_mcast_init(port->bridge->vlan_proto, port, vlan);
700 if (err) {
701 failed = vlan;
702 goto err_vlan;
703 }
704 }
705
706 port->mcast.filter_handle = filter_handle;
707 port->mcast.fwd_handle = fwd_handle;
708
709 return 0;
710
711 err_vlan:
712 xa_for_each(&port->vlans, index, vlan) {
713 if (vlan == failed)
714 break;
715
716 mlx5_esw_bridge_vlan_mcast_cleanup(vlan);
717 }
718 mlx5_del_flow_rules(fwd_handle);
719 err_fwd:
720 mlx5_del_flow_rules(filter_handle);
721 return err;
722 }
723
mlx5_esw_bridge_port_mcast_fhs_cleanup(struct mlx5_esw_bridge_port * port)724 static void mlx5_esw_bridge_port_mcast_fhs_cleanup(struct mlx5_esw_bridge_port *port)
725 {
726 struct mlx5_esw_bridge_vlan *vlan;
727 unsigned long index;
728
729 xa_for_each(&port->vlans, index, vlan)
730 mlx5_esw_bridge_vlan_mcast_cleanup(vlan);
731
732 if (port->mcast.fwd_handle)
733 mlx5_del_flow_rules(port->mcast.fwd_handle);
734 port->mcast.fwd_handle = NULL;
735 if (port->mcast.filter_handle)
736 mlx5_del_flow_rules(port->mcast.filter_handle);
737 port->mcast.filter_handle = NULL;
738 }
739
mlx5_esw_bridge_port_mcast_init(struct mlx5_esw_bridge_port * port)740 int mlx5_esw_bridge_port_mcast_init(struct mlx5_esw_bridge_port *port)
741 {
742 struct mlx5_esw_bridge *bridge = port->bridge;
743 int err;
744
745 if (!(bridge->flags & MLX5_ESW_BRIDGE_MCAST_FLAG))
746 return 0;
747
748 err = mlx5_esw_bridge_port_mcast_fts_init(port, bridge);
749 if (err)
750 return err;
751
752 err = mlx5_esw_bridge_port_mcast_fgs_init(port);
753 if (err)
754 goto err_fgs;
755
756 err = mlx5_esw_bridge_port_mcast_fhs_init(port);
757 if (err)
758 goto err_fhs;
759 return err;
760
761 err_fhs:
762 mlx5_esw_bridge_port_mcast_fgs_cleanup(port);
763 err_fgs:
764 mlx5_esw_bridge_port_mcast_fts_cleanup(port);
765 return err;
766 }
767
mlx5_esw_bridge_port_mcast_cleanup(struct mlx5_esw_bridge_port * port)768 void mlx5_esw_bridge_port_mcast_cleanup(struct mlx5_esw_bridge_port *port)
769 {
770 mlx5_esw_bridge_port_mdb_flush(port);
771 mlx5_esw_bridge_port_mcast_fhs_cleanup(port);
772 mlx5_esw_bridge_port_mcast_fgs_cleanup(port);
773 mlx5_esw_bridge_port_mcast_fts_cleanup(port);
774 }
775
776 static struct mlx5_flow_group *
mlx5_esw_bridge_ingress_igmp_fg_create(struct mlx5_eswitch * esw,struct mlx5_flow_table * ingress_ft)777 mlx5_esw_bridge_ingress_igmp_fg_create(struct mlx5_eswitch *esw,
778 struct mlx5_flow_table *ingress_ft)
779 {
780 int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
781 struct mlx5_flow_group *fg;
782 u32 *in, *match;
783
784 in = kvzalloc(inlen, GFP_KERNEL);
785 if (!in)
786 return ERR_PTR(-ENOMEM);
787
788 MLX5_SET(create_flow_group_in, in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS);
789 match = MLX5_ADDR_OF(create_flow_group_in, in, match_criteria);
790
791 MLX5_SET_TO_ONES(fte_match_param, match, outer_headers.ip_version);
792 MLX5_SET_TO_ONES(fte_match_param, match, outer_headers.ip_protocol);
793
794 MLX5_SET(create_flow_group_in, in, start_flow_index,
795 MLX5_ESW_BRIDGE_INGRESS_TABLE_IGMP_GRP_IDX_FROM);
796 MLX5_SET(create_flow_group_in, in, end_flow_index,
797 MLX5_ESW_BRIDGE_INGRESS_TABLE_IGMP_GRP_IDX_TO);
798
799 fg = mlx5_create_flow_group(ingress_ft, in);
800 kvfree(in);
801 if (IS_ERR(fg))
802 esw_warn(esw->dev,
803 "Failed to create IGMP flow group for bridge ingress table (err=%pe)\n",
804 fg);
805
806 return fg;
807 }
808
809 static struct mlx5_flow_group *
mlx5_esw_bridge_ingress_mld_fg_create(struct mlx5_eswitch * esw,struct mlx5_flow_table * ingress_ft)810 mlx5_esw_bridge_ingress_mld_fg_create(struct mlx5_eswitch *esw,
811 struct mlx5_flow_table *ingress_ft)
812 {
813 int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
814 struct mlx5_flow_group *fg;
815 u32 *in, *match;
816
817 if (!(MLX5_CAP_GEN(esw->dev, flex_parser_protocols) & MLX5_FLEX_PROTO_ICMPV6)) {
818 esw_warn(esw->dev,
819 "Can't create MLD flow group due to missing hardware ICMPv6 parsing support\n");
820 return NULL;
821 }
822
823 in = kvzalloc(inlen, GFP_KERNEL);
824 if (!in)
825 return ERR_PTR(-ENOMEM);
826
827 MLX5_SET(create_flow_group_in, in, match_criteria_enable,
828 MLX5_MATCH_OUTER_HEADERS | MLX5_MATCH_MISC_PARAMETERS_3);
829 match = MLX5_ADDR_OF(create_flow_group_in, in, match_criteria);
830
831 MLX5_SET_TO_ONES(fte_match_param, match, outer_headers.ip_version);
832 MLX5_SET_TO_ONES(fte_match_param, match, misc_parameters_3.icmpv6_type);
833
834 MLX5_SET(create_flow_group_in, in, start_flow_index,
835 MLX5_ESW_BRIDGE_INGRESS_TABLE_MLD_GRP_IDX_FROM);
836 MLX5_SET(create_flow_group_in, in, end_flow_index,
837 MLX5_ESW_BRIDGE_INGRESS_TABLE_MLD_GRP_IDX_TO);
838
839 fg = mlx5_create_flow_group(ingress_ft, in);
840 kvfree(in);
841 if (IS_ERR(fg))
842 esw_warn(esw->dev,
843 "Failed to create MLD flow group for bridge ingress table (err=%pe)\n",
844 fg);
845
846 return fg;
847 }
848
849 static int
mlx5_esw_bridge_ingress_mcast_fgs_init(struct mlx5_esw_bridge_offloads * br_offloads)850 mlx5_esw_bridge_ingress_mcast_fgs_init(struct mlx5_esw_bridge_offloads *br_offloads)
851 {
852 struct mlx5_flow_table *ingress_ft = br_offloads->ingress_ft;
853 struct mlx5_eswitch *esw = br_offloads->esw;
854 struct mlx5_flow_group *igmp_fg, *mld_fg;
855
856 igmp_fg = mlx5_esw_bridge_ingress_igmp_fg_create(esw, ingress_ft);
857 if (IS_ERR(igmp_fg))
858 return PTR_ERR(igmp_fg);
859
860 mld_fg = mlx5_esw_bridge_ingress_mld_fg_create(esw, ingress_ft);
861 if (IS_ERR(mld_fg)) {
862 mlx5_destroy_flow_group(igmp_fg);
863 return PTR_ERR(mld_fg);
864 }
865
866 br_offloads->ingress_igmp_fg = igmp_fg;
867 br_offloads->ingress_mld_fg = mld_fg;
868 return 0;
869 }
870
871 static void
mlx5_esw_bridge_ingress_mcast_fgs_cleanup(struct mlx5_esw_bridge_offloads * br_offloads)872 mlx5_esw_bridge_ingress_mcast_fgs_cleanup(struct mlx5_esw_bridge_offloads *br_offloads)
873 {
874 if (br_offloads->ingress_mld_fg)
875 mlx5_destroy_flow_group(br_offloads->ingress_mld_fg);
876 br_offloads->ingress_mld_fg = NULL;
877 if (br_offloads->ingress_igmp_fg)
878 mlx5_destroy_flow_group(br_offloads->ingress_igmp_fg);
879 br_offloads->ingress_igmp_fg = NULL;
880 }
881
882 static struct mlx5_flow_handle *
mlx5_esw_bridge_ingress_igmp_fh_create(struct mlx5_flow_table * ingress_ft,struct mlx5_flow_table * skip_ft)883 mlx5_esw_bridge_ingress_igmp_fh_create(struct mlx5_flow_table *ingress_ft,
884 struct mlx5_flow_table *skip_ft)
885 {
886 struct mlx5_flow_destination dest = {
887 .type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE,
888 .ft = skip_ft,
889 };
890 struct mlx5_flow_act flow_act = {
891 .action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST,
892 .flags = FLOW_ACT_NO_APPEND,
893 };
894 struct mlx5_flow_spec *rule_spec;
895 struct mlx5_flow_handle *handle;
896
897 rule_spec = kvzalloc(sizeof(*rule_spec), GFP_KERNEL);
898 if (!rule_spec)
899 return ERR_PTR(-ENOMEM);
900
901 rule_spec->match_criteria_enable = MLX5_MATCH_OUTER_HEADERS;
902
903 MLX5_SET_TO_ONES(fte_match_param, rule_spec->match_criteria, outer_headers.ip_version);
904 MLX5_SET(fte_match_param, rule_spec->match_value, outer_headers.ip_version, 4);
905 MLX5_SET_TO_ONES(fte_match_param, rule_spec->match_criteria, outer_headers.ip_protocol);
906 MLX5_SET(fte_match_param, rule_spec->match_value, outer_headers.ip_protocol, IPPROTO_IGMP);
907
908 handle = mlx5_add_flow_rules(ingress_ft, rule_spec, &flow_act, &dest, 1);
909
910 kvfree(rule_spec);
911 return handle;
912 }
913
914 static struct mlx5_flow_handle *
mlx5_esw_bridge_ingress_mld_fh_create(u8 type,struct mlx5_flow_table * ingress_ft,struct mlx5_flow_table * skip_ft)915 mlx5_esw_bridge_ingress_mld_fh_create(u8 type, struct mlx5_flow_table *ingress_ft,
916 struct mlx5_flow_table *skip_ft)
917 {
918 struct mlx5_flow_destination dest = {
919 .type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE,
920 .ft = skip_ft,
921 };
922 struct mlx5_flow_act flow_act = {
923 .action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST,
924 .flags = FLOW_ACT_NO_APPEND,
925 };
926 struct mlx5_flow_spec *rule_spec;
927 struct mlx5_flow_handle *handle;
928
929 rule_spec = kvzalloc(sizeof(*rule_spec), GFP_KERNEL);
930 if (!rule_spec)
931 return ERR_PTR(-ENOMEM);
932
933 rule_spec->match_criteria_enable = MLX5_MATCH_OUTER_HEADERS | MLX5_MATCH_MISC_PARAMETERS_3;
934
935 MLX5_SET_TO_ONES(fte_match_param, rule_spec->match_criteria, outer_headers.ip_version);
936 MLX5_SET(fte_match_param, rule_spec->match_value, outer_headers.ip_version, 6);
937 MLX5_SET_TO_ONES(fte_match_param, rule_spec->match_criteria, misc_parameters_3.icmpv6_type);
938 MLX5_SET(fte_match_param, rule_spec->match_value, misc_parameters_3.icmpv6_type, type);
939
940 handle = mlx5_add_flow_rules(ingress_ft, rule_spec, &flow_act, &dest, 1);
941
942 kvfree(rule_spec);
943 return handle;
944 }
945
946 static int
mlx5_esw_bridge_ingress_mcast_fhs_create(struct mlx5_esw_bridge_offloads * br_offloads)947 mlx5_esw_bridge_ingress_mcast_fhs_create(struct mlx5_esw_bridge_offloads *br_offloads)
948 {
949 struct mlx5_flow_handle *igmp_handle, *mld_query_handle, *mld_report_handle,
950 *mld_done_handle;
951 struct mlx5_flow_table *ingress_ft = br_offloads->ingress_ft,
952 *skip_ft = br_offloads->skip_ft;
953 int err;
954
955 igmp_handle = mlx5_esw_bridge_ingress_igmp_fh_create(ingress_ft, skip_ft);
956 if (IS_ERR(igmp_handle))
957 return PTR_ERR(igmp_handle);
958
959 if (br_offloads->ingress_mld_fg) {
960 mld_query_handle = mlx5_esw_bridge_ingress_mld_fh_create(ICMPV6_MGM_QUERY,
961 ingress_ft,
962 skip_ft);
963 if (IS_ERR(mld_query_handle)) {
964 err = PTR_ERR(mld_query_handle);
965 goto err_mld_query;
966 }
967
968 mld_report_handle = mlx5_esw_bridge_ingress_mld_fh_create(ICMPV6_MGM_REPORT,
969 ingress_ft,
970 skip_ft);
971 if (IS_ERR(mld_report_handle)) {
972 err = PTR_ERR(mld_report_handle);
973 goto err_mld_report;
974 }
975
976 mld_done_handle = mlx5_esw_bridge_ingress_mld_fh_create(ICMPV6_MGM_REDUCTION,
977 ingress_ft,
978 skip_ft);
979 if (IS_ERR(mld_done_handle)) {
980 err = PTR_ERR(mld_done_handle);
981 goto err_mld_done;
982 }
983 } else {
984 mld_query_handle = NULL;
985 mld_report_handle = NULL;
986 mld_done_handle = NULL;
987 }
988
989 br_offloads->igmp_handle = igmp_handle;
990 br_offloads->mld_query_handle = mld_query_handle;
991 br_offloads->mld_report_handle = mld_report_handle;
992 br_offloads->mld_done_handle = mld_done_handle;
993
994 return 0;
995
996 err_mld_done:
997 mlx5_del_flow_rules(mld_report_handle);
998 err_mld_report:
999 mlx5_del_flow_rules(mld_query_handle);
1000 err_mld_query:
1001 mlx5_del_flow_rules(igmp_handle);
1002 return err;
1003 }
1004
1005 static void
mlx5_esw_bridge_ingress_mcast_fhs_cleanup(struct mlx5_esw_bridge_offloads * br_offloads)1006 mlx5_esw_bridge_ingress_mcast_fhs_cleanup(struct mlx5_esw_bridge_offloads *br_offloads)
1007 {
1008 if (br_offloads->mld_done_handle)
1009 mlx5_del_flow_rules(br_offloads->mld_done_handle);
1010 br_offloads->mld_done_handle = NULL;
1011 if (br_offloads->mld_report_handle)
1012 mlx5_del_flow_rules(br_offloads->mld_report_handle);
1013 br_offloads->mld_report_handle = NULL;
1014 if (br_offloads->mld_query_handle)
1015 mlx5_del_flow_rules(br_offloads->mld_query_handle);
1016 br_offloads->mld_query_handle = NULL;
1017 if (br_offloads->igmp_handle)
1018 mlx5_del_flow_rules(br_offloads->igmp_handle);
1019 br_offloads->igmp_handle = NULL;
1020 }
1021
mlx5_esw_brige_mcast_init(struct mlx5_esw_bridge * bridge)1022 static int mlx5_esw_brige_mcast_init(struct mlx5_esw_bridge *bridge)
1023 {
1024 struct mlx5_esw_bridge_offloads *br_offloads = bridge->br_offloads;
1025 struct mlx5_esw_bridge_port *port, *failed;
1026 unsigned long i;
1027 int err;
1028
1029 xa_for_each(&br_offloads->ports, i, port) {
1030 if (port->bridge != bridge)
1031 continue;
1032
1033 err = mlx5_esw_bridge_port_mcast_init(port);
1034 if (err) {
1035 failed = port;
1036 goto err_port;
1037 }
1038 }
1039 return 0;
1040
1041 err_port:
1042 xa_for_each(&br_offloads->ports, i, port) {
1043 if (port == failed)
1044 break;
1045 if (port->bridge != bridge)
1046 continue;
1047
1048 mlx5_esw_bridge_port_mcast_cleanup(port);
1049 }
1050 return err;
1051 }
1052
mlx5_esw_brige_mcast_cleanup(struct mlx5_esw_bridge * bridge)1053 static void mlx5_esw_brige_mcast_cleanup(struct mlx5_esw_bridge *bridge)
1054 {
1055 struct mlx5_esw_bridge_offloads *br_offloads = bridge->br_offloads;
1056 struct mlx5_esw_bridge_port *port;
1057 unsigned long i;
1058
1059 xa_for_each(&br_offloads->ports, i, port) {
1060 if (port->bridge != bridge)
1061 continue;
1062
1063 mlx5_esw_bridge_port_mcast_cleanup(port);
1064 }
1065 }
1066
mlx5_esw_brige_mcast_global_enable(struct mlx5_esw_bridge_offloads * br_offloads)1067 static int mlx5_esw_brige_mcast_global_enable(struct mlx5_esw_bridge_offloads *br_offloads)
1068 {
1069 int err;
1070
1071 if (br_offloads->ingress_igmp_fg)
1072 return 0; /* already enabled by another bridge */
1073
1074 err = mlx5_esw_bridge_ingress_mcast_fgs_init(br_offloads);
1075 if (err) {
1076 esw_warn(br_offloads->esw->dev,
1077 "Failed to create global multicast flow groups (err=%d)\n",
1078 err);
1079 return err;
1080 }
1081
1082 err = mlx5_esw_bridge_ingress_mcast_fhs_create(br_offloads);
1083 if (err) {
1084 esw_warn(br_offloads->esw->dev,
1085 "Failed to create global multicast flows (err=%d)\n",
1086 err);
1087 goto err_fhs;
1088 }
1089
1090 return 0;
1091
1092 err_fhs:
1093 mlx5_esw_bridge_ingress_mcast_fgs_cleanup(br_offloads);
1094 return err;
1095 }
1096
mlx5_esw_brige_mcast_global_disable(struct mlx5_esw_bridge_offloads * br_offloads)1097 static void mlx5_esw_brige_mcast_global_disable(struct mlx5_esw_bridge_offloads *br_offloads)
1098 {
1099 struct mlx5_esw_bridge *br;
1100
1101 list_for_each_entry(br, &br_offloads->bridges, list) {
1102 /* Ingress table is global, so only disable snooping when all
1103 * bridges on esw have multicast disabled.
1104 */
1105 if (br->flags & MLX5_ESW_BRIDGE_MCAST_FLAG)
1106 return;
1107 }
1108
1109 mlx5_esw_bridge_ingress_mcast_fhs_cleanup(br_offloads);
1110 mlx5_esw_bridge_ingress_mcast_fgs_cleanup(br_offloads);
1111 }
1112
mlx5_esw_bridge_mcast_enable(struct mlx5_esw_bridge * bridge)1113 int mlx5_esw_bridge_mcast_enable(struct mlx5_esw_bridge *bridge)
1114 {
1115 int err;
1116
1117 err = mlx5_esw_brige_mcast_global_enable(bridge->br_offloads);
1118 if (err)
1119 return err;
1120
1121 bridge->flags |= MLX5_ESW_BRIDGE_MCAST_FLAG;
1122
1123 err = mlx5_esw_brige_mcast_init(bridge);
1124 if (err) {
1125 esw_warn(bridge->br_offloads->esw->dev, "Failed to enable multicast (err=%d)\n",
1126 err);
1127 bridge->flags &= ~MLX5_ESW_BRIDGE_MCAST_FLAG;
1128 mlx5_esw_brige_mcast_global_disable(bridge->br_offloads);
1129 }
1130 return err;
1131 }
1132
mlx5_esw_bridge_mcast_disable(struct mlx5_esw_bridge * bridge)1133 void mlx5_esw_bridge_mcast_disable(struct mlx5_esw_bridge *bridge)
1134 {
1135 mlx5_esw_brige_mcast_cleanup(bridge);
1136 bridge->flags &= ~MLX5_ESW_BRIDGE_MCAST_FLAG;
1137 mlx5_esw_brige_mcast_global_disable(bridge->br_offloads);
1138 }
1139