File Index
Files and directories
Sign in
FILE

bridge_mcast.c

File preview with syntax highlighting.

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