Commit e473ea81 authored by Jakub Kicinski's avatar Jakub Kicinski

Merge tag 'mlx5-updates-2023-04-11' of git://git.kernel.org/pub/scm/linux/kernel/git/saeed/linux

Saeed Mahameed says:

====================
mlx5-updates-2023-04-11

1) Vlad adds the support for linux bridge multicast offload support
   Patches #1 through #9
   Synopsis

Vlad Says:
==============
Implement support of bridge multicast offload in mlx5. Handle port object
attribute SWITCHDEV_ATTR_ID_BRIDGE_MC_DISABLED notification to toggle multicast
offload and bridge snooping support on bridge. Handle port object
SWITCHDEV_OBJ_ID_PORT_MDB notification to attach a bridge port to MDB.

Steering architecture

Existing offload infrastructure relies on two levels of flow tables - bridge
ingress and egress. For multicast offload the architecture is extended with
additional layer of per-port multicast replication tables. Such tables filter
loopback traffic (so packets are not replicated to their source port) and pop
VLAN headers for "untagged" VLANs. The tables are referenced by the MDB rules in
egress table. MDB egress rule can point to multiple per-port multicast tables,
which causes matching multicast traffic to be replicated to all of them, and,
consecutively, to several bridge ports:

                                                                                                                            +--------+--+
                                                                                    +---------------------------------------> Port 1 |  |
                                                                                    |                                       +-^------+--+
                                                                                    |                                         |
                                                                                    |                                         |
                                       +-----------------------------------------+  |     +---------------------------+       |
                                       | EGRESS table                            |  |  +--> PORT 1 multicast table    |       |
+----------------------------------+   +-----------------------------------------+  |  |  +---------------------------+       |
| INGRESS table                    |   |                                         |  |  |  |                           |       |
+----------------------------------+   | dst_mac=P1,vlan=X -> pop vlan, goto P1  +--+  |  | FG0:                      |       |
|                                  |   | dst_mac=P1,vlan=Y -> pop vlan, goto P1  |     |  | src_port=dst_port -> drop |       |
| src_mac=M1,vlan=X -> goto egress +---> dst_mac=P2,vlan=X -> pop vlan, goto P2  +--+  |  | FG1:                      |       |
| ...                              |   | dst_mac=P2,vlan=Y -> goto P2            |  |  |  | VLAN X -> pop, goto port  |       |
|                                  |   | dst_mac=MDB1,vlan=Y -> goto mcast P1,P2 +-----+  | ...                       |       |
+----------------------------------+   |                                         |  |  |  | VLAN Y -> pop, goto port  +-------+
                                       +-----------------------------------------+  |  |  | FG3:                      |
                                                                                    |  |  | matchall -> goto port     |
                                                                                    |  |  |                           |
                                                                                    |  |  +---------------------------+
                                                                                    |  |
                                                                                    |  |
                                                                                    |  |                                    +--------+--+
                                                                                    +---------------------------------------> Port 2 |  |
                                                                                       |                                    +-^------+--+
                                                                                       |                                      |
                                                                                       |                                      |
                                                                                       |  +---------------------------+       |
                                                                                       +--> PORT 2 multicast table    |       |
                                                                                          +---------------------------+       |
                                                                                          |                           |       |
                                                                                          | FG0:                      |       |
                                                                                          | src_port=dst_port -> drop |       |
                                                                                          | FG1:                      |       |
                                                                                          | VLAN X -> pop, goto port  |       |
                                                                                          | ...                       |       |
                                                                                          |                           |       |
                                                                                          | FG3:                      |       |
                                                                                          | matchall -> goto port     +-------+
                                                                                          |                           |
                                                                                          +---------------------------+

Patches overview:

- Patch 1 adds hardware definition bits for capabilities required to replicate
  multicast packets to multiple per-port tables. These bits are used by
  following patches to only attempt multicast offload if firmware and hardware
  provide necessary support.

- Pathces 2-4 patches are preparations and refactoring.

- Patch 5 implements necessary infrastructure to toggle multicast offload
  via SWITCHDEV_ATTR_ID_BRIDGE_MC_DISABLED port object attribute notification.
  This also enabled IGMP and MLD snooping.

- Patch 6 implements per-port multicast replication tables. It only supports
  filtering of loopback packets.

- Patch 7 extends per-port multicast tables with VLAN pop support for 'untagged'
  VLANs.

- Patch 8 handles SWITCHDEV_OBJ_ID_PORT_MDB port object notifications. It
  creates MDB replication rules in egress table that can replicate packets to
  multiple per-port multicast tables.

- Patch 9 adds tracepoints for MDB events.

==============

2) Parav Create a new allocation profile for SFs, to save on memory

3) Yevgeny provides some initial patches for upcoming software steering
   support new pattern/arguments type of modify_header actions.

Starting with ConnectX-6 DX, we use a new design of modify_header FW object.
The current modify_header object allows for having only limited number of
these FW objects, which means that we are limited in the number of offloaded
flows that require modify_header action.

As a preparation Yevgeny provides the following 4 patches:
 - Patch 1: Add required mlx5_ifc HW bits
 - Patch 2, 3: Add new WQE type and opcode that is required for pattern/arg
   support and adds appropriate support in dr_send.c
 - Patch 4: Add ICM pool for modify-header-pattern objects and implement
   patterns cache, allowing patterns reuse for different flows

* tag 'mlx5-updates-2023-04-11' of git://git.kernel.org/pub/scm/linux/kernel/git/saeed/linux:
  net/mlx5: DR, Add modify-header-pattern ICM pool
  net/mlx5: DR, Prepare sending new WQE type
  net/mlx5: Add new WQE for updating flow table
  net/mlx5: Add mlx5_ifc bits for modify header argument
  net/mlx5: DR, Set counter ID on the last STE for STEv1 TX
  net/mlx5: Create a new profile for SFs
  net/mlx5: Bridge, add tracepoints for multicast
  net/mlx5: Bridge, implement mdb offload
  net/mlx5: Bridge, support multicast VLAN pop
  net/mlx5: Bridge, add per-port multicast replication tables
  net/mlx5: Bridge, snoop igmp/mld packets
  net/mlx5: Bridge, extract code to lookup parent bridge of port
  net/mlx5: Bridge, move additional data structures to priv header
  net/mlx5: Bridge, increase bridge tables sizes
  net/mlx5: Add mlx5_ifc definitions for bridge multicast support
====================

Link: https://lore.kernel.org/r/20230412040752.14220-1-saeed@kernel.orgSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents f7d29571 108ff821
......@@ -75,7 +75,7 @@ mlx5_core-$(CONFIG_MLX5_ESWITCH) += esw/acl/helper.o \
esw/acl/egress_lgcy.o esw/acl/egress_ofld.o \
esw/acl/ingress_lgcy.o esw/acl/ingress_ofld.o
mlx5_core-$(CONFIG_MLX5_BRIDGE) += esw/bridge.o en/rep/bridge.o
mlx5_core-$(CONFIG_MLX5_BRIDGE) += esw/bridge.o esw/bridge_mcast.o en/rep/bridge.o
mlx5_core-$(CONFIG_THERMAL) += thermal.o
mlx5_core-$(CONFIG_MLX5_MPFS) += lib/mpfs.o
......@@ -112,7 +112,7 @@ mlx5_core-$(CONFIG_MLX5_SW_STEERING) += steering/dr_domain.o steering/dr_table.o
steering/dr_ste_v2.o \
steering/dr_cmd.o steering/dr_fw.o \
steering/dr_action.o steering/fs_dr.o \
steering/dr_definer.o \
steering/dr_definer.o steering/dr_ptrn.o \
steering/dr_dbg.o lib/smfs.o
#
# SF device
......
......@@ -1802,7 +1802,7 @@ static struct mlx5_cmd_msg *alloc_msg(struct mlx5_core_dev *dev, int in_size,
if (in_size <= 16)
goto cache_miss;
for (i = 0; i < MLX5_NUM_COMMAND_CACHES; i++) {
for (i = 0; i < dev->profile.num_cmd_caches; i++) {
ch = &cmd->cache[i];
if (in_size > ch->max_inbox_size)
continue;
......@@ -2097,7 +2097,7 @@ static void destroy_msg_cache(struct mlx5_core_dev *dev)
struct mlx5_cmd_msg *n;
int i;
for (i = 0; i < MLX5_NUM_COMMAND_CACHES; i++) {
for (i = 0; i < dev->profile.num_cmd_caches; i++) {
ch = &dev->cmd.cache[i];
list_for_each_entry_safe(msg, n, &ch->head, list) {
list_del(&msg->list);
......@@ -2127,7 +2127,7 @@ static void create_msg_cache(struct mlx5_core_dev *dev)
int k;
/* Initialize and fill the caches with initial entries */
for (k = 0; k < MLX5_NUM_COMMAND_CACHES; k++) {
for (k = 0; k < dev->profile.num_cmd_caches; k++) {
ch = &cmd->cache[k];
spin_lock_init(&ch->lock);
INIT_LIST_HEAD(&ch->head);
......
......@@ -220,6 +220,7 @@ mlx5_esw_bridge_port_obj_add(struct net_device *dev,
struct netlink_ext_ack *extack = switchdev_notifier_info_to_extack(&port_obj_info->info);
const struct switchdev_obj *obj = port_obj_info->obj;
const struct switchdev_obj_port_vlan *vlan;
const struct switchdev_obj_port_mdb *mdb;
u16 vport_num, esw_owner_vhca_id;
int err;
......@@ -235,6 +236,11 @@ mlx5_esw_bridge_port_obj_add(struct net_device *dev,
err = mlx5_esw_bridge_port_vlan_add(vport_num, esw_owner_vhca_id, vlan->vid,
vlan->flags, br_offloads, extack);
break;
case SWITCHDEV_OBJ_ID_PORT_MDB:
mdb = SWITCHDEV_OBJ_PORT_MDB(obj);
err = mlx5_esw_bridge_port_mdb_add(dev, vport_num, esw_owner_vhca_id, mdb->addr,
mdb->vid, br_offloads, extack);
break;
default:
return -EOPNOTSUPP;
}
......@@ -248,6 +254,7 @@ mlx5_esw_bridge_port_obj_del(struct net_device *dev,
{
const struct switchdev_obj *obj = port_obj_info->obj;
const struct switchdev_obj_port_vlan *vlan;
const struct switchdev_obj_port_mdb *mdb;
u16 vport_num, esw_owner_vhca_id;
if (!mlx5_esw_bridge_rep_vport_num_vhca_id_get(dev, br_offloads->esw, &vport_num,
......@@ -261,6 +268,11 @@ mlx5_esw_bridge_port_obj_del(struct net_device *dev,
vlan = SWITCHDEV_OBJ_PORT_VLAN(obj);
mlx5_esw_bridge_port_vlan_del(vport_num, esw_owner_vhca_id, vlan->vid, br_offloads);
break;
case SWITCHDEV_OBJ_ID_PORT_MDB:
mdb = SWITCHDEV_OBJ_PORT_MDB(obj);
mlx5_esw_bridge_port_mdb_del(dev, vport_num, esw_owner_vhca_id, mdb->addr, mdb->vid,
br_offloads);
break;
default:
return -EOPNOTSUPP;
}
......@@ -306,6 +318,10 @@ mlx5_esw_bridge_port_obj_attr_set(struct net_device *dev,
attr->u.vlan_protocol,
br_offloads);
break;
case SWITCHDEV_ATTR_ID_BRIDGE_MC_DISABLED:
err = mlx5_esw_bridge_mcast_set(vport_num, esw_owner_vhca_id,
!attr->u.mc_disabled, br_offloads);
break;
default:
err = -EOPNOTSUPP;
}
......
......@@ -13,66 +13,6 @@
#define CREATE_TRACE_POINTS
#include "diag/bridge_tracepoint.h"
#define MLX5_ESW_BRIDGE_INGRESS_TABLE_VLAN_GRP_SIZE 12000
#define MLX5_ESW_BRIDGE_INGRESS_TABLE_UNTAGGED_GRP_SIZE 16000
#define MLX5_ESW_BRIDGE_INGRESS_TABLE_VLAN_GRP_IDX_FROM 0
#define MLX5_ESW_BRIDGE_INGRESS_TABLE_VLAN_GRP_IDX_TO \
(MLX5_ESW_BRIDGE_INGRESS_TABLE_VLAN_GRP_SIZE - 1)
#define MLX5_ESW_BRIDGE_INGRESS_TABLE_VLAN_FILTER_GRP_IDX_FROM \
(MLX5_ESW_BRIDGE_INGRESS_TABLE_VLAN_GRP_IDX_TO + 1)
#define MLX5_ESW_BRIDGE_INGRESS_TABLE_VLAN_FILTER_GRP_IDX_TO \
(MLX5_ESW_BRIDGE_INGRESS_TABLE_VLAN_FILTER_GRP_IDX_FROM + \
MLX5_ESW_BRIDGE_INGRESS_TABLE_VLAN_GRP_SIZE - 1)
#define MLX5_ESW_BRIDGE_INGRESS_TABLE_QINQ_GRP_IDX_FROM \
(MLX5_ESW_BRIDGE_INGRESS_TABLE_VLAN_FILTER_GRP_IDX_TO + 1)
#define MLX5_ESW_BRIDGE_INGRESS_TABLE_QINQ_GRP_IDX_TO \
(MLX5_ESW_BRIDGE_INGRESS_TABLE_QINQ_GRP_IDX_FROM + \
MLX5_ESW_BRIDGE_INGRESS_TABLE_VLAN_GRP_SIZE - 1)
#define MLX5_ESW_BRIDGE_INGRESS_TABLE_QINQ_FILTER_GRP_IDX_FROM \
(MLX5_ESW_BRIDGE_INGRESS_TABLE_QINQ_GRP_IDX_TO + 1)
#define MLX5_ESW_BRIDGE_INGRESS_TABLE_QINQ_FILTER_GRP_IDX_TO \
(MLX5_ESW_BRIDGE_INGRESS_TABLE_QINQ_FILTER_GRP_IDX_FROM + \
MLX5_ESW_BRIDGE_INGRESS_TABLE_VLAN_GRP_SIZE - 1)
#define MLX5_ESW_BRIDGE_INGRESS_TABLE_MAC_GRP_IDX_FROM \
(MLX5_ESW_BRIDGE_INGRESS_TABLE_QINQ_FILTER_GRP_IDX_TO + 1)
#define MLX5_ESW_BRIDGE_INGRESS_TABLE_MAC_GRP_IDX_TO \
(MLX5_ESW_BRIDGE_INGRESS_TABLE_MAC_GRP_IDX_FROM + \
MLX5_ESW_BRIDGE_INGRESS_TABLE_UNTAGGED_GRP_SIZE - 1)
#define MLX5_ESW_BRIDGE_INGRESS_TABLE_SIZE \
(MLX5_ESW_BRIDGE_INGRESS_TABLE_MAC_GRP_IDX_TO + 1)
static_assert(MLX5_ESW_BRIDGE_INGRESS_TABLE_SIZE == 64000);
#define MLX5_ESW_BRIDGE_EGRESS_TABLE_VLAN_GRP_SIZE 16000
#define MLX5_ESW_BRIDGE_EGRESS_TABLE_MAC_GRP_SIZE (32000 - 1)
#define MLX5_ESW_BRIDGE_EGRESS_TABLE_VLAN_GRP_IDX_FROM 0
#define MLX5_ESW_BRIDGE_EGRESS_TABLE_VLAN_GRP_IDX_TO \
(MLX5_ESW_BRIDGE_EGRESS_TABLE_VLAN_GRP_SIZE - 1)
#define MLX5_ESW_BRIDGE_EGRESS_TABLE_QINQ_GRP_IDX_FROM \
(MLX5_ESW_BRIDGE_EGRESS_TABLE_VLAN_GRP_IDX_TO + 1)
#define MLX5_ESW_BRIDGE_EGRESS_TABLE_QINQ_GRP_IDX_TO \
(MLX5_ESW_BRIDGE_EGRESS_TABLE_QINQ_GRP_IDX_FROM + \
MLX5_ESW_BRIDGE_EGRESS_TABLE_VLAN_GRP_SIZE - 1)
#define MLX5_ESW_BRIDGE_EGRESS_TABLE_MAC_GRP_IDX_FROM \
(MLX5_ESW_BRIDGE_EGRESS_TABLE_QINQ_GRP_IDX_TO + 1)
#define MLX5_ESW_BRIDGE_EGRESS_TABLE_MAC_GRP_IDX_TO \
(MLX5_ESW_BRIDGE_EGRESS_TABLE_MAC_GRP_IDX_FROM + \
MLX5_ESW_BRIDGE_EGRESS_TABLE_MAC_GRP_SIZE - 1)
#define MLX5_ESW_BRIDGE_EGRESS_TABLE_MISS_GRP_IDX_FROM \
(MLX5_ESW_BRIDGE_EGRESS_TABLE_MAC_GRP_IDX_TO + 1)
#define MLX5_ESW_BRIDGE_EGRESS_TABLE_MISS_GRP_IDX_TO \
MLX5_ESW_BRIDGE_EGRESS_TABLE_MISS_GRP_IDX_FROM
#define MLX5_ESW_BRIDGE_EGRESS_TABLE_SIZE \
(MLX5_ESW_BRIDGE_EGRESS_TABLE_MISS_GRP_IDX_TO + 1)
static_assert(MLX5_ESW_BRIDGE_EGRESS_TABLE_SIZE == 64000);
#define MLX5_ESW_BRIDGE_SKIP_TABLE_SIZE 0
enum {
MLX5_ESW_BRIDGE_LEVEL_INGRESS_TABLE,
MLX5_ESW_BRIDGE_LEVEL_EGRESS_TABLE,
MLX5_ESW_BRIDGE_LEVEL_SKIP_TABLE,
};
static const struct rhashtable_params fdb_ht_params = {
.key_offset = offsetof(struct mlx5_esw_bridge_fdb_entry, key),
.key_len = sizeof(struct mlx5_esw_bridge_fdb_key),
......@@ -80,31 +20,6 @@ static const struct rhashtable_params fdb_ht_params = {
.automatic_shrinking = true,
};
enum {
MLX5_ESW_BRIDGE_VLAN_FILTERING_FLAG = BIT(0),
};
struct mlx5_esw_bridge {
int ifindex;
int refcnt;
struct list_head list;
struct mlx5_esw_bridge_offloads *br_offloads;
struct list_head fdb_list;
struct rhashtable fdb_ht;
struct mlx5_flow_table *egress_ft;
struct mlx5_flow_group *egress_vlan_fg;
struct mlx5_flow_group *egress_qinq_fg;
struct mlx5_flow_group *egress_mac_fg;
struct mlx5_flow_group *egress_miss_fg;
struct mlx5_pkt_reformat *egress_miss_pkt_reformat;
struct mlx5_flow_handle *egress_miss_handle;
unsigned long ageing_time;
u32 flags;
u16 vlan_proto;
};
static void
mlx5_esw_bridge_fdb_offload_notify(struct net_device *dev, const unsigned char *addr, u16 vid,
unsigned long val)
......@@ -146,7 +61,7 @@ mlx5_esw_bridge_pkt_reformat_vlan_pop_create(struct mlx5_eswitch *esw)
return mlx5_packet_reformat_alloc(esw->dev, &reformat_params, MLX5_FLOW_NAMESPACE_FDB);
}
static struct mlx5_flow_table *
struct mlx5_flow_table *
mlx5_esw_bridge_table_create(int max_fte, u32 level, struct mlx5_eswitch *esw)
{
struct mlx5_flow_table_attr ft_attr = {};
......@@ -925,6 +840,10 @@ static struct mlx5_esw_bridge *mlx5_esw_bridge_create(int ifindex,
if (err)
goto err_fdb_ht;
err = mlx5_esw_bridge_mdb_init(bridge);
if (err)
goto err_mdb_ht;
INIT_LIST_HEAD(&bridge->fdb_list);
bridge->ifindex = ifindex;
bridge->refcnt = 1;
......@@ -934,6 +853,8 @@ static struct mlx5_esw_bridge *mlx5_esw_bridge_create(int ifindex,
return bridge;
err_mdb_ht:
rhashtable_destroy(&bridge->fdb_ht);
err_fdb_ht:
mlx5_esw_bridge_egress_table_cleanup(bridge);
err_egress_tbl:
......@@ -953,7 +874,9 @@ static void mlx5_esw_bridge_put(struct mlx5_esw_bridge_offloads *br_offloads,
return;
mlx5_esw_bridge_egress_table_cleanup(bridge);
mlx5_esw_bridge_mcast_disable(bridge);
list_del(&bridge->list);
mlx5_esw_bridge_mdb_cleanup(bridge);
rhashtable_destroy(&bridge->fdb_ht);
kvfree(bridge);
......@@ -993,7 +916,7 @@ static unsigned long mlx5_esw_bridge_port_key_from_data(u16 vport_num, u16 esw_o
return vport_num | (unsigned long)esw_owner_vhca_id << sizeof(vport_num) * BITS_PER_BYTE;
}
static unsigned long mlx5_esw_bridge_port_key(struct mlx5_esw_bridge_port *port)
unsigned long mlx5_esw_bridge_port_key(struct mlx5_esw_bridge_port *port)
{
return mlx5_esw_bridge_port_key_from_data(port->vport_num, port->esw_owner_vhca_id);
}
......@@ -1018,6 +941,19 @@ static void mlx5_esw_bridge_port_erase(struct mlx5_esw_bridge_port *port,
xa_erase(&br_offloads->ports, mlx5_esw_bridge_port_key(port));
}
static struct mlx5_esw_bridge *
mlx5_esw_bridge_from_port_lookup(u16 vport_num, u16 esw_owner_vhca_id,
struct mlx5_esw_bridge_offloads *br_offloads)
{
struct mlx5_esw_bridge_port *port;
port = mlx5_esw_bridge_port_lookup(vport_num, esw_owner_vhca_id, br_offloads);
if (!port)
return NULL;
return port->bridge;
}
static void mlx5_esw_bridge_fdb_entry_refresh(struct mlx5_esw_bridge_fdb_entry *entry)
{
trace_mlx5_esw_bridge_fdb_entry_refresh(entry);
......@@ -1166,8 +1102,21 @@ mlx5_esw_bridge_vlan_push_mark_cleanup(struct mlx5_esw_bridge_vlan *vlan, struct
}
static int
mlx5_esw_bridge_vlan_push_pop_create(u16 vlan_proto, u16 flags, struct mlx5_esw_bridge_vlan *vlan,
struct mlx5_eswitch *esw)
mlx5_esw_bridge_vlan_push_pop_fhs_create(u16 vlan_proto, struct mlx5_esw_bridge_port *port,
struct mlx5_esw_bridge_vlan *vlan)
{
return mlx5_esw_bridge_vlan_mcast_init(vlan_proto, port, vlan);
}
static void
mlx5_esw_bridge_vlan_push_pop_fhs_cleanup(struct mlx5_esw_bridge_vlan *vlan)
{
mlx5_esw_bridge_vlan_mcast_cleanup(vlan);
}
static int
mlx5_esw_bridge_vlan_push_pop_create(u16 vlan_proto, u16 flags, struct mlx5_esw_bridge_port *port,
struct mlx5_esw_bridge_vlan *vlan, struct mlx5_eswitch *esw)
{
int err;
......@@ -1185,10 +1134,16 @@ mlx5_esw_bridge_vlan_push_pop_create(u16 vlan_proto, u16 flags, struct mlx5_esw_
err = mlx5_esw_bridge_vlan_pop_create(vlan, esw);
if (err)
goto err_vlan_pop;
err = mlx5_esw_bridge_vlan_push_pop_fhs_create(vlan_proto, port, vlan);
if (err)
goto err_vlan_pop_fhs;
}
return 0;
err_vlan_pop_fhs:
mlx5_esw_bridge_vlan_pop_cleanup(vlan, esw);
err_vlan_pop:
if (vlan->pkt_mod_hdr_push_mark)
mlx5_esw_bridge_vlan_push_mark_cleanup(vlan, esw);
......@@ -1213,7 +1168,7 @@ mlx5_esw_bridge_vlan_create(u16 vlan_proto, u16 vid, u16 flags, struct mlx5_esw_
vlan->flags = flags;
INIT_LIST_HEAD(&vlan->fdb_list);
err = mlx5_esw_bridge_vlan_push_pop_create(vlan_proto, flags, vlan, esw);
err = mlx5_esw_bridge_vlan_push_pop_create(vlan_proto, flags, port, vlan, esw);
if (err)
goto err_vlan_push_pop;
......@@ -1225,6 +1180,8 @@ mlx5_esw_bridge_vlan_create(u16 vlan_proto, u16 vid, u16 flags, struct mlx5_esw_
return vlan;
err_xa_insert:
if (vlan->mcast_handle)
mlx5_esw_bridge_vlan_push_pop_fhs_cleanup(vlan);
if (vlan->pkt_reformat_pop)
mlx5_esw_bridge_vlan_pop_cleanup(vlan, esw);
if (vlan->pkt_mod_hdr_push_mark)
......@@ -1242,7 +1199,8 @@ static void mlx5_esw_bridge_vlan_erase(struct mlx5_esw_bridge_port *port,
xa_erase(&port->vlans, vlan->vid);
}
static void mlx5_esw_bridge_vlan_flush(struct mlx5_esw_bridge_vlan *vlan,
static void mlx5_esw_bridge_vlan_flush(struct mlx5_esw_bridge_port *port,
struct mlx5_esw_bridge_vlan *vlan,
struct mlx5_esw_bridge *bridge)
{
struct mlx5_eswitch *esw = bridge->br_offloads->esw;
......@@ -1250,7 +1208,10 @@ static void mlx5_esw_bridge_vlan_flush(struct mlx5_esw_bridge_vlan *vlan,
list_for_each_entry_safe(entry, tmp, &vlan->fdb_list, vlan_list)
mlx5_esw_bridge_fdb_entry_notify_and_cleanup(entry, bridge);
mlx5_esw_bridge_port_mdb_vlan_flush(port, vlan);
if (vlan->mcast_handle)
mlx5_esw_bridge_vlan_push_pop_fhs_cleanup(vlan);
if (vlan->pkt_reformat_pop)
mlx5_esw_bridge_vlan_pop_cleanup(vlan, esw);
if (vlan->pkt_mod_hdr_push_mark)
......@@ -1264,7 +1225,7 @@ static void mlx5_esw_bridge_vlan_cleanup(struct mlx5_esw_bridge_port *port,
struct mlx5_esw_bridge *bridge)
{
trace_mlx5_esw_bridge_vlan_cleanup(vlan);
mlx5_esw_bridge_vlan_flush(vlan, bridge);
mlx5_esw_bridge_vlan_flush(port, vlan, bridge);
mlx5_esw_bridge_vlan_erase(port, vlan);
kvfree(vlan);
}
......@@ -1288,9 +1249,9 @@ static int mlx5_esw_bridge_port_vlans_recreate(struct mlx5_esw_bridge_port *port
int err;
xa_for_each(&port->vlans, i, vlan) {
mlx5_esw_bridge_vlan_flush(vlan, bridge);
err = mlx5_esw_bridge_vlan_push_pop_create(bridge->vlan_proto, vlan->flags, vlan,
br_offloads->esw);
mlx5_esw_bridge_vlan_flush(port, vlan, bridge);
err = mlx5_esw_bridge_vlan_push_pop_create(bridge->vlan_proto, vlan->flags, port,
vlan, br_offloads->esw);
if (err) {
esw_warn(br_offloads->esw->dev,
"Failed to create VLAN=%u(proto=%x) push/pop actions (vport=%u,err=%d)\n",
......@@ -1473,33 +1434,32 @@ mlx5_esw_bridge_fdb_entry_init(struct net_device *dev, u16 vport_num, u16 esw_ow
int mlx5_esw_bridge_ageing_time_set(u16 vport_num, u16 esw_owner_vhca_id, unsigned long ageing_time,
struct mlx5_esw_bridge_offloads *br_offloads)
{
struct mlx5_esw_bridge_port *port;
struct mlx5_esw_bridge *bridge;
port = mlx5_esw_bridge_port_lookup(vport_num, esw_owner_vhca_id, br_offloads);
if (!port)
bridge = mlx5_esw_bridge_from_port_lookup(vport_num, esw_owner_vhca_id, br_offloads);
if (!bridge)
return -EINVAL;
port->bridge->ageing_time = clock_t_to_jiffies(ageing_time);
bridge->ageing_time = clock_t_to_jiffies(ageing_time);
return 0;
}
int mlx5_esw_bridge_vlan_filtering_set(u16 vport_num, u16 esw_owner_vhca_id, bool enable,
struct mlx5_esw_bridge_offloads *br_offloads)
{
struct mlx5_esw_bridge_port *port;
struct mlx5_esw_bridge *bridge;
bool filtering;
port = mlx5_esw_bridge_port_lookup(vport_num, esw_owner_vhca_id, br_offloads);
if (!port)
bridge = mlx5_esw_bridge_from_port_lookup(vport_num, esw_owner_vhca_id, br_offloads);
if (!bridge)
return -EINVAL;
bridge = port->bridge;
filtering = bridge->flags & MLX5_ESW_BRIDGE_VLAN_FILTERING_FLAG;
if (filtering == enable)
return 0;
mlx5_esw_bridge_fdb_flush(bridge);
mlx5_esw_bridge_mdb_flush(bridge);
if (enable)
bridge->flags |= MLX5_ESW_BRIDGE_VLAN_FILTERING_FLAG;
else
......@@ -1511,15 +1471,13 @@ int mlx5_esw_bridge_vlan_filtering_set(u16 vport_num, u16 esw_owner_vhca_id, boo
int mlx5_esw_bridge_vlan_proto_set(u16 vport_num, u16 esw_owner_vhca_id, u16 proto,
struct mlx5_esw_bridge_offloads *br_offloads)
{
struct mlx5_esw_bridge_port *port;
struct mlx5_esw_bridge *bridge;
port = mlx5_esw_bridge_port_lookup(vport_num, esw_owner_vhca_id,
br_offloads);
if (!port)
bridge = mlx5_esw_bridge_from_port_lookup(vport_num, esw_owner_vhca_id,
br_offloads);
if (!bridge)
return -EINVAL;
bridge = port->bridge;
if (bridge->vlan_proto == proto)
return 0;
if (proto != ETH_P_8021Q && proto != ETH_P_8021AD) {
......@@ -1528,12 +1486,43 @@ int mlx5_esw_bridge_vlan_proto_set(u16 vport_num, u16 esw_owner_vhca_id, u16 pro
}
mlx5_esw_bridge_fdb_flush(bridge);
mlx5_esw_bridge_mdb_flush(bridge);
bridge->vlan_proto = proto;
mlx5_esw_bridge_vlans_recreate(bridge);
return 0;
}
int mlx5_esw_bridge_mcast_set(u16 vport_num, u16 esw_owner_vhca_id, bool enable,
struct mlx5_esw_bridge_offloads *br_offloads)
{
struct mlx5_eswitch *esw = br_offloads->esw;
struct mlx5_esw_bridge *bridge;
int err = 0;
bool mcast;
if (!(MLX5_CAP_ESW_FLOWTABLE((esw)->dev, fdb_multi_path_any_table) ||
MLX5_CAP_ESW_FLOWTABLE((esw)->dev, fdb_multi_path_any_table_limit_regc)) ||
!MLX5_CAP_ESW_FLOWTABLE((esw)->dev, fdb_uplink_hairpin) ||
!MLX5_CAP_ESW_FLOWTABLE_FDB((esw)->dev, ignore_flow_level))
return -EOPNOTSUPP;
bridge = mlx5_esw_bridge_from_port_lookup(vport_num, esw_owner_vhca_id, br_offloads);
if (!bridge)
return -EINVAL;
mcast = bridge->flags & MLX5_ESW_BRIDGE_MCAST_FLAG;
if (mcast == enable)
return 0;
if (enable)
err = mlx5_esw_bridge_mcast_enable(bridge);
else
mlx5_esw_bridge_mcast_disable(bridge);
return err;
}
static int mlx5_esw_bridge_vport_init(u16 vport_num, u16 esw_owner_vhca_id, u16 flags,
struct mlx5_esw_bridge_offloads *br_offloads,
struct mlx5_esw_bridge *bridge)
......@@ -1551,6 +1540,15 @@ static int mlx5_esw_bridge_vport_init(u16 vport_num, u16 esw_owner_vhca_id, u16
port->bridge = bridge;
port->flags |= flags;
xa_init(&port->vlans);
err = mlx5_esw_bridge_port_mcast_init(port);
if (err) {
esw_warn(esw->dev,
"Failed to initialize port multicast (vport=%u,esw_owner_vhca_id=%u,err=%d)\n",
port->vport_num, port->esw_owner_vhca_id, err);
goto err_port_mcast;
}
err = mlx5_esw_bridge_port_insert(port, br_offloads);
if (err) {
esw_warn(esw->dev,
......@@ -1563,6 +1561,8 @@ static int mlx5_esw_bridge_vport_init(u16 vport_num, u16 esw_owner_vhca_id, u16
return 0;
err_port_insert:
mlx5_esw_bridge_port_mcast_cleanup(port);
err_port_mcast:
kvfree(port);
return err;
}
......@@ -1580,6 +1580,7 @@ static int mlx5_esw_bridge_vport_cleanup(struct mlx5_esw_bridge_offloads *br_off
trace_mlx5_esw_bridge_vport_cleanup(port);
mlx5_esw_bridge_port_vlans_flush(port, bridge);
mlx5_esw_bridge_port_mcast_cleanup(port);
mlx5_esw_bridge_port_erase(port, br_offloads);
kvfree(port);
mlx5_esw_bridge_put(br_offloads, bridge);
......@@ -1711,14 +1712,12 @@ void mlx5_esw_bridge_fdb_update_used(struct net_device *dev, u16 vport_num, u16
struct switchdev_notifier_fdb_info *fdb_info)
{
struct mlx5_esw_bridge_fdb_entry *entry;
struct mlx5_esw_bridge_port *port;
struct mlx5_esw_bridge *bridge;
port = mlx5_esw_bridge_port_lookup(vport_num, esw_owner_vhca_id, br_offloads);
if (!port)
bridge = mlx5_esw_bridge_from_port_lookup(vport_num, esw_owner_vhca_id, br_offloads);
if (!bridge)
return;
bridge = port->bridge;
entry = mlx5_esw_bridge_fdb_lookup(bridge, fdb_info->addr, fdb_info->vid);
if (!entry) {
esw_debug(br_offloads->esw->dev,
......@@ -1765,14 +1764,12 @@ void mlx5_esw_bridge_fdb_remove(struct net_device *dev, u16 vport_num, u16 esw_o
{
struct mlx5_eswitch *esw = br_offloads->esw;
struct mlx5_esw_bridge_fdb_entry *entry;
struct mlx5_esw_bridge_port *port;
struct mlx5_esw_bridge *bridge;
port = mlx5_esw_bridge_port_lookup(vport_num, esw_owner_vhca_id, br_offloads);
if (!port)
bridge = mlx5_esw_bridge_from_port_lookup(vport_num, esw_owner_vhca_id, br_offloads);
if (!bridge)
return;
bridge = port->bridge;
entry = mlx5_esw_bridge_fdb_lookup(bridge, fdb_info->addr, fdb_info->vid);
if (!entry) {
esw_debug(esw->dev,
......@@ -1806,6 +1803,64 @@ void mlx5_esw_bridge_update(struct mlx5_esw_bridge_offloads *br_offloads)
}
}
int mlx5_esw_bridge_port_mdb_add(struct net_device *dev, u16 vport_num, u16 esw_owner_vhca_id,
const unsigned char *addr, u16 vid,
struct mlx5_esw_bridge_offloads *br_offloads,
struct netlink_ext_ack *extack)
{
struct mlx5_esw_bridge_vlan *vlan;
struct mlx5_esw_bridge_port *port;
struct mlx5_esw_bridge *bridge;
int err;
port = mlx5_esw_bridge_port_lookup(vport_num, esw_owner_vhca_id, br_offloads);
if (!port) {
esw_warn(br_offloads->esw->dev,
"Failed to lookup bridge port to add MDB (MAC=%pM,vport=%u)\n",
addr, vport_num);
NL_SET_ERR_MSG_FMT_MOD(extack,
"Failed to lookup bridge port to add MDB (MAC=%pM,vport=%u)\n",
addr, vport_num);
return -EINVAL;
}
bridge = port->bridge;
if (bridge->flags & MLX5_ESW_BRIDGE_VLAN_FILTERING_FLAG && vid) {
vlan = mlx5_esw_bridge_vlan_lookup(vid, port);
if (!vlan) {
esw_warn(br_offloads->esw->dev,
"Failed to lookup bridge port vlan metadata to create MDB (MAC=%pM,vid=%u,vport=%u)\n",
addr, vid, vport_num);
NL_SET_ERR_MSG_FMT_MOD(extack,
"Failed to lookup bridge port vlan metadata to create MDB (MAC=%pM,vid=%u,vport=%u)\n",
addr, vid, vport_num);
return -EINVAL;
}
}
err = mlx5_esw_bridge_port_mdb_attach(dev, port, addr, vid);
if (err) {
NL_SET_ERR_MSG_FMT_MOD(extack, "Failed to add MDB (MAC=%pM,vid=%u,vport=%u)\n",
addr, vid, vport_num);
return err;
}
return 0;
}
void mlx5_esw_bridge_port_mdb_del(struct net_device *dev, u16 vport_num, u16 esw_owner_vhca_id,
const unsigned char *addr, u16 vid,
struct mlx5_esw_bridge_offloads *br_offloads)
{
struct mlx5_esw_bridge_port *port;
port = mlx5_esw_bridge_port_lookup(vport_num, esw_owner_vhca_id, br_offloads);
if (!port)
return;
mlx5_esw_bridge_port_mdb_detach(dev, port, addr, vid);
}
static void mlx5_esw_bridge_flush(struct mlx5_esw_bridge_offloads *br_offloads)
{
struct mlx5_esw_bridge_port *port;
......
......@@ -25,12 +25,19 @@ struct mlx5_esw_bridge_offloads {
struct delayed_work update_work;
struct mlx5_flow_table *ingress_ft;
struct mlx5_flow_group *ingress_igmp_fg;
struct mlx5_flow_group *ingress_mld_fg;
struct mlx5_flow_group *ingress_vlan_fg;
struct mlx5_flow_group *ingress_vlan_filter_fg;
struct mlx5_flow_group *ingress_qinq_fg;
struct mlx5_flow_group *ingress_qinq_filter_fg;
struct mlx5_flow_group *ingress_mac_fg;
struct mlx5_flow_handle *igmp_handle;
struct mlx5_flow_handle *mld_query_handle;
struct mlx5_flow_handle *mld_report_handle;
struct mlx5_flow_handle *mld_done_handle;
struct mlx5_flow_table *skip_ft;
};
......@@ -64,10 +71,20 @@ int mlx5_esw_bridge_vlan_filtering_set(u16 vport_num, u16 esw_owner_vhca_id, boo
struct mlx5_esw_bridge_offloads *br_offloads);
int mlx5_esw_bridge_vlan_proto_set(u16 vport_num, u16 esw_owner_vhca_id, u16 proto,
struct mlx5_esw_bridge_offloads *br_offloads);
int mlx5_esw_bridge_mcast_set(u16 vport_num, u16 esw_owner_vhca_id, bool enable,
struct mlx5_esw_bridge_offloads *br_offloads);
int mlx5_esw_bridge_port_vlan_add(u16 vport_num, u16 esw_owner_vhca_id, u16 vid, u16 flags,
struct mlx5_esw_bridge_offloads *br_offloads,
struct netlink_ext_ack *extack);
void mlx5_esw_bridge_port_vlan_del(u16 vport_num, u16 esw_owner_vhca_id, u16 vid,
struct mlx5_esw_bridge_offloads *br_offloads);
int mlx5_esw_bridge_port_mdb_add(struct net_device *dev, u16 vport_num, u16 esw_owner_vhca_id,
const unsigned char *addr, u16 vid,
struct mlx5_esw_bridge_offloads *br_offloads,
struct netlink_ext_ack *extack);
void mlx5_esw_bridge_port_mdb_del(struct net_device *dev, u16 vport_num, u16 esw_owner_vhca_id,
const unsigned char *addr, u16 vid,
struct mlx5_esw_bridge_offloads *br_offloads);
#endif /* __MLX5_ESW_BRIDGE_H__ */
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
/* Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. */
#include "lib/devcom.h"
#include "bridge.h"
#include "eswitch.h"
#include "bridge_priv.h"
#include "diag/bridge_tracepoint.h"
static const struct rhashtable_params mdb_ht_params = {
.key_offset = offsetof(struct mlx5_esw_bridge_mdb_entry, key),
.key_len = sizeof(struct mlx5_esw_bridge_mdb_key),
.head_offset = offsetof(struct mlx5_esw_bridge_mdb_entry, ht_node),
.automatic_shrinking = true,
};
int mlx5_esw_bridge_mdb_init(struct mlx5_esw_bridge *bridge)
{
INIT_LIST_HEAD(&bridge->mdb_list);
return rhashtable_init(&bridge->mdb_ht, &mdb_ht_params);
}
void mlx5_esw_bridge_mdb_cleanup(struct mlx5_esw_bridge *bridge)
{
rhashtable_destroy(&bridge->mdb_ht);
}
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)
{
return xa_load(&entry->ports, mlx5_esw_bridge_port_key(port));
}
static int mlx5_esw_bridge_mdb_port_insert(struct mlx5_esw_bridge_port *port,
struct mlx5_esw_bridge_mdb_entry *entry)
{
int err = xa_insert(&entry->ports, mlx5_esw_bridge_port_key(port), port, GFP_KERNEL);
if (!err)
entry->num_ports++;
return err;
}
static void mlx5_esw_bridge_mdb_port_remove(struct mlx5_esw_bridge_port *port,
struct mlx5_esw_bridge_mdb_entry *entry)
{
xa_erase(&entry->ports, mlx5_esw_bridge_port_key(port));
entry->num_ports--;
}
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)
{
struct mlx5_flow_act flow_act = {
.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST,
.flags = FLOW_ACT_NO_APPEND | FLOW_ACT_IGNORE_FLOW_LEVEL,
};
int num_dests = entry->num_ports, i = 0;
struct mlx5_flow_destination *dests;
struct mlx5_esw_bridge_port *port;
struct mlx5_flow_spec *rule_spec;
struct mlx5_flow_handle *handle;
u8 *dmac_v, *dmac_c;
unsigned long idx;
rule_spec = kvzalloc(sizeof(*rule_spec), GFP_KERNEL);
if (!rule_spec)
return ERR_PTR(-ENOMEM);
dests = kvcalloc(num_dests, sizeof(*dests), GFP_KERNEL);
if (!dests) {
kvfree(rule_spec);
return ERR_PTR(-ENOMEM);
}
xa_for_each(&entry->ports, idx, port) {
dests[i].type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE;
dests[i].ft = port->mcast.ft;
i++;
}
rule_spec->match_criteria_enable = MLX5_MATCH_OUTER_HEADERS;
dmac_v = MLX5_ADDR_OF(fte_match_param, rule_spec->match_value, outer_headers.dmac_47_16);
ether_addr_copy(dmac_v, entry->key.addr);
dmac_c = MLX5_ADDR_OF(fte_match_param, rule_spec->match_criteria, outer_headers.dmac_47_16);
eth_broadcast_addr(dmac_c);
if (entry->key.vid) {
if (bridge->vlan_proto == ETH_P_8021Q) {
MLX5_SET_TO_ONES(fte_match_param, rule_spec->match_criteria,
outer_headers.cvlan_tag);
MLX5_SET_TO_ONES(fte_match_param, rule_spec->match_value,
outer_headers.cvlan_tag);
} else if (bridge->vlan_proto == ETH_P_8021AD) {
MLX5_SET_TO_ONES(fte_match_param, rule_spec->match_criteria,
outer_headers.svlan_tag);
MLX5_SET_TO_ONES(fte_match_param, rule_spec->match_value,
outer_headers.svlan_tag);
}
MLX5_SET_TO_ONES(fte_match_param, rule_spec->match_criteria,
outer_headers.first_vid);
MLX5_SET(fte_match_param, rule_spec->match_value, outer_headers.first_vid,
entry->key.vid);
}
handle = mlx5_add_flow_rules(bridge->egress_ft, rule_spec, &flow_act, dests, num_dests);
kvfree(dests);
kvfree(rule_spec);
return handle;
}
static int
mlx5_esw_bridge_port_mdb_offload(struct mlx5_esw_bridge_port *port,
struct mlx5_esw_bridge_mdb_entry *entry)
{
struct mlx5_flow_handle *handle;
handle = mlx5_esw_bridge_mdb_flow_create(port->esw_owner_vhca_id, entry, port->bridge);
if (entry->egress_handle) {
mlx5_del_flow_rules(entry->egress_handle);
entry->egress_handle = NULL;
}
if (IS_ERR(handle))
return PTR_ERR(handle);
entry->egress_handle = handle;
return 0;
}
static struct mlx5_esw_bridge_mdb_entry *
mlx5_esw_bridge_mdb_lookup(struct mlx5_esw_bridge *bridge,
const unsigned char *addr, u16 vid)
{
struct mlx5_esw_bridge_mdb_key key = {};
ether_addr_copy(key.addr, addr);
key.vid = vid;
return rhashtable_lookup_fast(&bridge->mdb_ht, &key, mdb_ht_params);
}
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)
{
struct mlx5_esw_bridge *bridge = port->bridge;
struct mlx5_esw_bridge_mdb_entry *entry;
int err;
entry = kvzalloc(sizeof(*entry), GFP_KERNEL);
if (!entry)
return ERR_PTR(-ENOMEM);
ether_addr_copy(entry->key.addr, addr);
entry->key.vid = vid;
xa_init(&entry->ports);
err = rhashtable_insert_fast(&bridge->mdb_ht, &entry->ht_node, mdb_ht_params);
if (err)
goto err_ht_insert;
list_add(&entry->list, &bridge->mdb_list);
return entry;
err_ht_insert:
xa_destroy(&entry->ports);
kvfree(entry);
return ERR_PTR(err);
}
static void mlx5_esw_bridge_port_mdb_entry_cleanup(struct mlx5_esw_bridge *bridge,
struct mlx5_esw_bridge_mdb_entry *entry)
{
if (entry->egress_handle)
mlx5_del_flow_rules(entry->egress_handle);
list_del(&entry->list);
rhashtable_remove_fast(&bridge->mdb_ht, &entry->ht_node, mdb_ht_params);
xa_destroy(&entry->ports);
kvfree(entry);
}
int mlx5_esw_bridge_port_mdb_attach(struct net_device *dev, struct mlx5_esw_bridge_port *port,
const unsigned char *addr, u16 vid)
{
struct mlx5_esw_bridge *bridge = port->bridge;
struct mlx5_esw_bridge_mdb_entry *entry;
int err;
if (!(bridge->flags & MLX5_ESW_BRIDGE_MCAST_FLAG))
return -EOPNOTSUPP;
entry = mlx5_esw_bridge_mdb_lookup(bridge, addr, vid);
if (entry) {
if (mlx5_esw_bridge_mdb_port_lookup(port, entry)) {
esw_warn(bridge->br_offloads->esw->dev, "MDB attach entry is already attached to port (MAC=%pM,vid=%u,vport=%u)\n",
addr, vid, port->vport_num);
return 0;
}
} else {
entry = mlx5_esw_bridge_port_mdb_entry_init(port, addr, vid);
if (IS_ERR(entry)) {
err = PTR_ERR(entry);
esw_warn(bridge->br_offloads->esw->dev, "MDB attach failed to init entry (MAC=%pM,vid=%u,vport=%u,err=%d)\n",
addr, vid, port->vport_num, err);
return err;
}
}
err = mlx5_esw_bridge_mdb_port_insert(port, entry);
if (err) {
if (!entry->num_ports)
mlx5_esw_bridge_port_mdb_entry_cleanup(bridge, entry); /* new mdb entry */
esw_warn(bridge->br_offloads->esw->dev,
"MDB attach failed to insert port (MAC=%pM,vid=%u,vport=%u,err=%d)\n",
addr, vid, port->vport_num, err);
return err;
}
err = mlx5_esw_bridge_port_mdb_offload(port, entry);
if (err)
/* Single mdb can be used by multiple ports, so just log the
* error and continue.
*/
esw_warn(bridge->br_offloads->esw->dev, "MDB attach failed to offload (MAC=%pM,vid=%u,vport=%u,err=%d)\n",
addr, vid, port->vport_num, err);
trace_mlx5_esw_bridge_port_mdb_attach(dev, entry);
return 0;
}
static void mlx5_esw_bridge_port_mdb_entry_detach(struct mlx5_esw_bridge_port *port,
struct mlx5_esw_bridge_mdb_entry *entry)
{
struct mlx5_esw_bridge *bridge = port->bridge;
int err;
mlx5_esw_bridge_mdb_port_remove(port, entry);
if (!entry->num_ports) {
mlx5_esw_bridge_port_mdb_entry_cleanup(bridge, entry);
return;
}
err = mlx5_esw_bridge_port_mdb_offload(port, entry);
if (err)
/* Single mdb can be used by multiple ports, so just log the
* error and continue.
*/
esw_warn(bridge->br_offloads->esw->dev, "MDB detach failed to offload (MAC=%pM,vid=%u,vport=%u)\n",
entry->key.addr, entry->key.vid, port->vport_num);
}
void mlx5_esw_bridge_port_mdb_detach(struct net_device *dev, struct mlx5_esw_bridge_port *port,
const unsigned char *addr, u16 vid)
{
struct mlx5_esw_bridge *bridge = port->bridge;
struct mlx5_esw_bridge_mdb_entry *entry;
entry = mlx5_esw_bridge_mdb_lookup(bridge, addr, vid);
if (!entry) {
esw_debug(bridge->br_offloads->esw->dev,
"MDB detach entry not found (MAC=%pM,vid=%u,vport=%u)\n",
addr, vid, port->vport_num);
return;
}
if (!mlx5_esw_bridge_mdb_port_lookup(port, entry)) {
esw_debug(bridge->br_offloads->esw->dev,
"MDB detach entry not attached to the port (MAC=%pM,vid=%u,vport=%u)\n",
addr, vid, port->vport_num);
return;
}
trace_mlx5_esw_bridge_port_mdb_detach(dev, entry);
mlx5_esw_bridge_port_mdb_entry_detach(port, entry);
}
void mlx5_esw_bridge_port_mdb_vlan_flush(struct mlx5_esw_bridge_port *port,
struct mlx5_esw_bridge_vlan *vlan)
{
struct mlx5_esw_bridge *bridge = port->bridge;
struct mlx5_esw_bridge_mdb_entry *entry, *tmp;
list_for_each_entry_safe(entry, tmp, &bridge->mdb_list, list)
if (entry->key.vid == vlan->vid && mlx5_esw_bridge_mdb_port_lookup(port, entry))
mlx5_esw_bridge_port_mdb_entry_detach(port, entry);
}
static void mlx5_esw_bridge_port_mdb_flush(struct mlx5_esw_bridge_port *port)
{
struct mlx5_esw_bridge *bridge = port->bridge;
struct mlx5_esw_bridge_mdb_entry *entry, *tmp;
list_for_each_entry_safe(entry, tmp, &bridge->mdb_list, list)
if (mlx5_esw_bridge_mdb_port_lookup(port, entry))
mlx5_esw_bridge_port_mdb_entry_detach(port, entry);
}
void mlx5_esw_bridge_mdb_flush(struct mlx5_esw_bridge *bridge)
{
struct mlx5_esw_bridge_mdb_entry *entry, *tmp;
list_for_each_entry_safe(entry, tmp, &bridge->mdb_list, list)
mlx5_esw_bridge_port_mdb_entry_cleanup(bridge, entry);
}
static int mlx5_esw_bridge_port_mcast_fts_init(struct mlx5_esw_bridge_port *port,
struct mlx5_esw_bridge *bridge)
{
struct mlx5_eswitch *esw = bridge->br_offloads->esw;
struct mlx5_flow_table *mcast_ft;
mcast_ft = mlx5_esw_bridge_table_create(MLX5_ESW_BRIDGE_MCAST_TABLE_SIZE,
MLX5_ESW_BRIDGE_LEVEL_MCAST_TABLE,
esw);
if (IS_ERR(mcast_ft))
return PTR_ERR(mcast_ft);
port->mcast.ft = mcast_ft;
return 0;
}
static void mlx5_esw_bridge_port_mcast_fts_cleanup(struct mlx5_esw_bridge_port *port)
{
if (port->mcast.ft)
mlx5_destroy_flow_table(port->mcast.ft);
port->mcast.ft = NULL;
}
static struct mlx5_flow_group *
mlx5_esw_bridge_mcast_filter_fg_create(struct mlx5_eswitch *esw,
struct mlx5_flow_table *mcast_ft)
{
int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
struct mlx5_flow_group *fg;
u32 *in, *match;
in = kvzalloc(inlen, GFP_KERNEL);
if (!in)
return ERR_PTR(-ENOMEM);
MLX5_SET(create_flow_group_in, in, match_criteria_enable, MLX5_MATCH_MISC_PARAMETERS_2);
match = MLX5_ADDR_OF(create_flow_group_in, in, match_criteria);
MLX5_SET(fte_match_param, match, misc_parameters_2.metadata_reg_c_0,
mlx5_eswitch_get_vport_metadata_mask());
MLX5_SET(create_flow_group_in, in, start_flow_index,
MLX5_ESW_BRIDGE_MCAST_TABLE_FILTER_GRP_IDX_FROM);
MLX5_SET(create_flow_group_in, in, end_flow_index,
MLX5_ESW_BRIDGE_MCAST_TABLE_FILTER_GRP_IDX_TO);
fg = mlx5_create_flow_group(mcast_ft, in);
kvfree(in);
if (IS_ERR(fg))
esw_warn(esw->dev,
"Failed to create filter flow group for bridge mcast table (err=%pe)\n",
fg);
return fg;
}
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)
{
int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
struct mlx5_flow_group *fg;
u32 *in, *match;
in = kvzalloc(inlen, GFP_KERNEL);
if (!in)
return ERR_PTR(-ENOMEM);
MLX5_SET(create_flow_group_in, in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS);
match = MLX5_ADDR_OF(create_flow_group_in, in, match_criteria);
if (vlan_proto == ETH_P_8021Q)
MLX5_SET_TO_ONES(fte_match_param, match, outer_headers.cvlan_tag);
else if (vlan_proto == ETH_P_8021AD)
MLX5_SET_TO_ONES(fte_match_param, match, outer_headers.svlan_tag);
MLX5_SET_TO_ONES(fte_match_param, match, outer_headers.first_vid);
MLX5_SET(create_flow_group_in, in, start_flow_index, from);
MLX5_SET(create_flow_group_in, in, end_flow_index, to);
fg = mlx5_create_flow_group(mcast_ft, in);
kvfree(in);
if (IS_ERR(fg))
esw_warn(esw->dev,
"Failed to create VLAN(proto=%x) flow group for bridge mcast table (err=%pe)\n",
vlan_proto, fg);
return fg;
}
static struct mlx5_flow_group *
mlx5_esw_bridge_mcast_vlan_fg_create(struct mlx5_eswitch *esw, struct mlx5_flow_table *mcast_ft)
{
unsigned int from = MLX5_ESW_BRIDGE_MCAST_TABLE_VLAN_GRP_IDX_FROM;
unsigned int to = MLX5_ESW_BRIDGE_MCAST_TABLE_VLAN_GRP_IDX_TO;
return mlx5_esw_bridge_mcast_vlan_proto_fg_create(from, to, ETH_P_8021Q, esw, mcast_ft);
}
static struct mlx5_flow_group *
mlx5_esw_bridge_mcast_qinq_fg_create(struct mlx5_eswitch *esw,
struct mlx5_flow_table *mcast_ft)
{
unsigned int from = MLX5_ESW_BRIDGE_MCAST_TABLE_QINQ_GRP_IDX_FROM;
unsigned int to = MLX5_ESW_BRIDGE_MCAST_TABLE_QINQ_GRP_IDX_TO;
return mlx5_esw_bridge_mcast_vlan_proto_fg_create(from, to, ETH_P_8021AD, esw, mcast_ft);
}
static struct mlx5_flow_group *
mlx5_esw_bridge_mcast_fwd_fg_create(struct mlx5_eswitch *esw,
struct mlx5_flow_table *mcast_ft)
{
int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
struct mlx5_flow_group *fg;
u32 *in;
in = kvzalloc(inlen, GFP_KERNEL);
if (!in)
return ERR_PTR(-ENOMEM);
MLX5_SET(create_flow_group_in, in, start_flow_index,
MLX5_ESW_BRIDGE_MCAST_TABLE_FWD_GRP_IDX_FROM);
MLX5_SET(create_flow_group_in, in, end_flow_index,
MLX5_ESW_BRIDGE_MCAST_TABLE_FWD_GRP_IDX_TO);
fg = mlx5_create_flow_group(mcast_ft, in);
kvfree(in);
if (IS_ERR(fg))
esw_warn(esw->dev,
"Failed to create forward flow group for bridge mcast table (err=%pe)\n",
fg);
return fg;
}
static int mlx5_esw_bridge_port_mcast_fgs_init(struct mlx5_esw_bridge_port *port)
{
struct mlx5_flow_group *fwd_fg, *qinq_fg, *vlan_fg, *filter_fg;
struct mlx5_eswitch *esw = port->bridge->br_offloads->esw;
struct mlx5_flow_table *mcast_ft = port->mcast.ft;
int err;
filter_fg = mlx5_esw_bridge_mcast_filter_fg_create(esw, mcast_ft);
if (IS_ERR(filter_fg))
return PTR_ERR(filter_fg);
vlan_fg = mlx5_esw_bridge_mcast_vlan_fg_create(esw, mcast_ft);
if (IS_ERR(vlan_fg)) {
err = PTR_ERR(vlan_fg);
goto err_vlan_fg;
}
qinq_fg = mlx5_esw_bridge_mcast_qinq_fg_create(esw, mcast_ft);
if (IS_ERR(qinq_fg)) {
err = PTR_ERR(qinq_fg);
goto err_qinq_fg;
}
fwd_fg = mlx5_esw_bridge_mcast_fwd_fg_create(esw, mcast_ft);
if (IS_ERR(fwd_fg)) {
err = PTR_ERR(fwd_fg);
goto err_fwd_fg;
}
port->mcast.filter_fg = filter_fg;
port->mcast.vlan_fg = vlan_fg;
port->mcast.qinq_fg = qinq_fg;
port->mcast.fwd_fg = fwd_fg;
return 0;
err_fwd_fg:
mlx5_destroy_flow_group(qinq_fg);
err_qinq_fg:
mlx5_destroy_flow_group(vlan_fg);
err_vlan_fg:
mlx5_destroy_flow_group(filter_fg);
return err;
}
static void mlx5_esw_bridge_port_mcast_fgs_cleanup(struct mlx5_esw_bridge_port *port)
{
if (port->mcast.fwd_fg)
mlx5_destroy_flow_group(port->mcast.fwd_fg);
port->mcast.fwd_fg = NULL;
if (port->mcast.qinq_fg)
mlx5_destroy_flow_group(port->mcast.qinq_fg);
port->mcast.qinq_fg = NULL;
if (port->mcast.vlan_fg)
mlx5_destroy_flow_group(port->mcast.vlan_fg);
port->mcast.vlan_fg = NULL;
if (port->mcast.filter_fg)
mlx5_destroy_flow_group(port->mcast.filter_fg);
port->mcast.filter_fg = NULL;
}
static struct mlx5_flow_handle *
mlx5_esw_bridge_mcast_flow_with_esw_create(struct mlx5_esw_bridge_port *port,
struct mlx5_eswitch *esw)
{
struct mlx5_flow_act flow_act = {
.action = MLX5_FLOW_CONTEXT_ACTION_DROP,
.flags = FLOW_ACT_NO_APPEND,
};
struct mlx5_flow_spec *rule_spec;
struct mlx5_flow_handle *handle;
rule_spec = kvzalloc(sizeof(*rule_spec), GFP_KERNEL);
if (!rule_spec)
return ERR_PTR(-ENOMEM);
rule_spec->match_criteria_enable = MLX5_MATCH_MISC_PARAMETERS_2;
MLX5_SET(fte_match_param, rule_spec->match_criteria,
misc_parameters_2.metadata_reg_c_0, mlx5_eswitch_get_vport_metadata_mask());
MLX5_SET(fte_match_param, rule_spec->match_value, misc_parameters_2.metadata_reg_c_0,
mlx5_eswitch_get_vport_metadata_for_match(esw, port->vport_num));
handle = mlx5_add_flow_rules(port->mcast.ft, rule_spec, &flow_act, NULL, 0);
kvfree(rule_spec);
return handle;
}
static struct mlx5_flow_handle *
mlx5_esw_bridge_mcast_filter_flow_create(struct mlx5_esw_bridge_port *port)
{
return mlx5_esw_bridge_mcast_flow_with_esw_create(port, port->bridge->br_offloads->esw);
}
static struct mlx5_flow_handle *
mlx5_esw_bridge_mcast_filter_flow_peer_create(struct mlx5_esw_bridge_port *port)
{
struct mlx5_devcom *devcom = port->bridge->br_offloads->esw->dev->priv.devcom;
static struct mlx5_flow_handle *handle;
struct mlx5_eswitch *peer_esw;
peer_esw = mlx5_devcom_get_peer_data(devcom, MLX5_DEVCOM_ESW_OFFLOADS);
if (!peer_esw)
return ERR_PTR(-ENODEV);
handle = mlx5_esw_bridge_mcast_flow_with_esw_create(port, peer_esw);
mlx5_devcom_release_peer_data(devcom, MLX5_DEVCOM_ESW_OFFLOADS);
return handle;
}
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)
{
struct mlx5_flow_act flow_act = {
.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST,
.flags = FLOW_ACT_NO_APPEND,
};
struct mlx5_flow_destination dest = {
.type = MLX5_FLOW_DESTINATION_TYPE_VPORT,
.vport.num = port->vport_num,
};
struct mlx5_esw_bridge *bridge = port->bridge;
struct mlx5_flow_spec *rule_spec;
struct mlx5_flow_handle *handle;
rule_spec = kvzalloc(sizeof(*rule_spec), GFP_KERNEL);
if (!rule_spec)
return ERR_PTR(-ENOMEM);
if (MLX5_CAP_ESW_FLOWTABLE(bridge->br_offloads->esw->dev, flow_source) &&
port->vport_num == MLX5_VPORT_UPLINK)
rule_spec->flow_context.flow_source =
MLX5_FLOW_CONTEXT_FLOW_SOURCE_LOCAL_VPORT;
rule_spec->match_criteria_enable = MLX5_MATCH_OUTER_HEADERS;
flow_act.action |= MLX5_FLOW_CONTEXT_ACTION_PACKET_REFORMAT;
flow_act.pkt_reformat = vlan->pkt_reformat_pop;
if (vlan_proto == ETH_P_8021Q) {
MLX5_SET_TO_ONES(fte_match_param, rule_spec->match_criteria,
outer_headers.cvlan_tag);
MLX5_SET_TO_ONES(fte_match_param, rule_spec->match_value,
outer_headers.cvlan_tag);
} else if (vlan_proto == ETH_P_8021AD) {
MLX5_SET_TO_ONES(fte_match_param, rule_spec->match_criteria,
outer_headers.svlan_tag);
MLX5_SET_TO_ONES(fte_match_param, rule_spec->match_value,
outer_headers.svlan_tag);
}
MLX5_SET_TO_ONES(fte_match_param, rule_spec->match_criteria, outer_headers.first_vid);
MLX5_SET(fte_match_param, rule_spec->match_value, outer_headers.first_vid, vlan->vid);
if (MLX5_CAP_ESW(bridge->br_offloads->esw->dev, merged_eswitch)) {
dest.vport.flags = MLX5_FLOW_DEST_VPORT_VHCA_ID;
dest.vport.vhca_id = port->esw_owner_vhca_id;
}
handle = mlx5_add_flow_rules(port->mcast.ft, rule_spec, &flow_act, &dest, 1);
kvfree(rule_spec);
return handle;
}
int mlx5_esw_bridge_vlan_mcast_init(u16 vlan_proto, struct mlx5_esw_bridge_port *port,
struct mlx5_esw_bridge_vlan *vlan)
{
struct mlx5_flow_handle *handle;
if (!(port->bridge->flags & MLX5_ESW_BRIDGE_MCAST_FLAG))
return 0;
handle = mlx5_esw_bridge_mcast_vlan_flow_create(vlan_proto, port, vlan);
if (IS_ERR(handle))
return PTR_ERR(handle);
vlan->mcast_handle = handle;
return 0;
}
void mlx5_esw_bridge_vlan_mcast_cleanup(struct mlx5_esw_bridge_vlan *vlan)
{
if (vlan->mcast_handle)
mlx5_del_flow_rules(vlan->mcast_handle);
vlan->mcast_handle = NULL;
}
static struct mlx5_flow_handle *
mlx5_esw_bridge_mcast_fwd_flow_create(struct mlx5_esw_bridge_port *port)
{
struct mlx5_flow_act flow_act = {
.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST,
.flags = FLOW_ACT_NO_APPEND,
};
struct mlx5_flow_destination dest = {
.type = MLX5_FLOW_DESTINATION_TYPE_VPORT,
.vport.num = port->vport_num,
};
struct mlx5_esw_bridge *bridge = port->bridge;
struct mlx5_flow_spec *rule_spec;
struct mlx5_flow_handle *handle;
rule_spec = kvzalloc(sizeof(*rule_spec), GFP_KERNEL);
if (!rule_spec)
return ERR_PTR(-ENOMEM);
if (MLX5_CAP_ESW_FLOWTABLE(bridge->br_offloads->esw->dev, flow_source) &&
port->vport_num == MLX5_VPORT_UPLINK)
rule_spec->flow_context.flow_source =
MLX5_FLOW_CONTEXT_FLOW_SOURCE_LOCAL_VPORT;
if (MLX5_CAP_ESW(bridge->br_offloads->esw->dev, merged_eswitch)) {
dest.vport.flags = MLX5_FLOW_DEST_VPORT_VHCA_ID;
dest.vport.vhca_id = port->esw_owner_vhca_id;
}
handle = mlx5_add_flow_rules(port->mcast.ft, rule_spec, &flow_act, &dest, 1);
kvfree(rule_spec);
return handle;
}
static int mlx5_esw_bridge_port_mcast_fhs_init(struct mlx5_esw_bridge_port *port)
{
struct mlx5_flow_handle *filter_handle, *fwd_handle;
struct mlx5_esw_bridge_vlan *vlan, *failed;
unsigned long index;
int err;
filter_handle = (port->flags & MLX5_ESW_BRIDGE_PORT_FLAG_PEER) ?
mlx5_esw_bridge_mcast_filter_flow_peer_create(port) :
mlx5_esw_bridge_mcast_filter_flow_create(port);
if (IS_ERR(filter_handle))
return PTR_ERR(filter_handle);
fwd_handle = mlx5_esw_bridge_mcast_fwd_flow_create(port);
if (IS_ERR(fwd_handle)) {
err = PTR_ERR(fwd_handle);
goto err_fwd;
}
xa_for_each(&port->vlans, index, vlan) {
err = mlx5_esw_bridge_vlan_mcast_init(port->bridge->vlan_proto, port, vlan);
if (err) {
failed = vlan;
goto err_vlan;
}
}
port->mcast.filter_handle = filter_handle;
port->mcast.fwd_handle = fwd_handle;
return 0;
err_vlan:
xa_for_each(&port->vlans, index, vlan) {
if (vlan == failed)
break;
mlx5_esw_bridge_vlan_mcast_cleanup(vlan);
}
mlx5_del_flow_rules(fwd_handle);
err_fwd:
mlx5_del_flow_rules(filter_handle);
return err;
}
static void mlx5_esw_bridge_port_mcast_fhs_cleanup(struct mlx5_esw_bridge_port *port)
{
struct mlx5_esw_bridge_vlan *vlan;
unsigned long index;
xa_for_each(&port->vlans, index, vlan)
mlx5_esw_bridge_vlan_mcast_cleanup(vlan);
if (port->mcast.fwd_handle)
mlx5_del_flow_rules(port->mcast.fwd_handle);
port->mcast.fwd_handle = NULL;
if (port->mcast.filter_handle)
mlx5_del_flow_rules(port->mcast.filter_handle);
port->mcast.filter_handle = NULL;
}
int mlx5_esw_bridge_port_mcast_init(struct mlx5_esw_bridge_port *port)
{
struct mlx5_esw_bridge *bridge = port->bridge;
int err;
if (!(bridge->flags & MLX5_ESW_BRIDGE_MCAST_FLAG))
return 0;
err = mlx5_esw_bridge_port_mcast_fts_init(port, bridge);
if (err)
return err;
err = mlx5_esw_bridge_port_mcast_fgs_init(port);
if (err)
goto err_fgs;
err = mlx5_esw_bridge_port_mcast_fhs_init(port);
if (err)
goto err_fhs;
return err;
err_fhs:
mlx5_esw_bridge_port_mcast_fgs_cleanup(port);
err_fgs:
mlx5_esw_bridge_port_mcast_fts_cleanup(port);
return err;
}
void mlx5_esw_bridge_port_mcast_cleanup(struct mlx5_esw_bridge_port *port)
{
mlx5_esw_bridge_port_mdb_flush(port);
mlx5_esw_bridge_port_mcast_fhs_cleanup(port);
mlx5_esw_bridge_port_mcast_fgs_cleanup(port);
mlx5_esw_bridge_port_mcast_fts_cleanup(port);
}
static struct mlx5_flow_group *
mlx5_esw_bridge_ingress_igmp_fg_create(struct mlx5_eswitch *esw,
struct mlx5_flow_table *ingress_ft)
{
int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
struct mlx5_flow_group *fg;
u32 *in, *match;
in = kvzalloc(inlen, GFP_KERNEL);
if (!in)
return ERR_PTR(-ENOMEM);
MLX5_SET(create_flow_group_in, in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS);
match = MLX5_ADDR_OF(create_flow_group_in, in, match_criteria);
MLX5_SET_TO_ONES(fte_match_param, match, outer_headers.ip_version);
MLX5_SET_TO_ONES(fte_match_param, match, outer_headers.ip_protocol);
MLX5_SET(create_flow_group_in, in, start_flow_index,
MLX5_ESW_BRIDGE_INGRESS_TABLE_IGMP_GRP_IDX_FROM);
MLX5_SET(create_flow_group_in, in, end_flow_index,
MLX5_ESW_BRIDGE_INGRESS_TABLE_IGMP_GRP_IDX_TO);
fg = mlx5_create_flow_group(ingress_ft, in);
kvfree(in);
if (IS_ERR(fg))
esw_warn(esw->dev,
"Failed to create IGMP flow group for bridge ingress table (err=%pe)\n",
fg);
return fg;
}
static struct mlx5_flow_group *
mlx5_esw_bridge_ingress_mld_fg_create(struct mlx5_eswitch *esw,
struct mlx5_flow_table *ingress_ft)
{
int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
struct mlx5_flow_group *fg;
u32 *in, *match;
if (!(MLX5_CAP_GEN(esw->dev, flex_parser_protocols) & MLX5_FLEX_PROTO_ICMPV6)) {
esw_warn(esw->dev,
"Can't create MLD flow group due to missing hardware ICMPv6 parsing support\n");
return NULL;
}
in = kvzalloc(inlen, GFP_KERNEL);
if (!in)
return ERR_PTR(-ENOMEM);
MLX5_SET(create_flow_group_in, in, match_criteria_enable,
MLX5_MATCH_OUTER_HEADERS | MLX5_MATCH_MISC_PARAMETERS_3);
match = MLX5_ADDR_OF(create_flow_group_in, in, match_criteria);
MLX5_SET_TO_ONES(fte_match_param, match, outer_headers.ip_version);
MLX5_SET_TO_ONES(fte_match_param, match, misc_parameters_3.icmpv6_type);
MLX5_SET(create_flow_group_in, in, start_flow_index,
MLX5_ESW_BRIDGE_INGRESS_TABLE_MLD_GRP_IDX_FROM);
MLX5_SET(create_flow_group_in, in, end_flow_index,
MLX5_ESW_BRIDGE_INGRESS_TABLE_MLD_GRP_IDX_TO);
fg = mlx5_create_flow_group(ingress_ft, in);
kvfree(in);
if (IS_ERR(fg))
esw_warn(esw->dev,
"Failed to create MLD flow group for bridge ingress table (err=%pe)\n",
fg);
return fg;
}
static int
mlx5_esw_bridge_ingress_mcast_fgs_init(struct mlx5_esw_bridge_offloads *br_offloads)
{
struct mlx5_flow_table *ingress_ft = br_offloads->ingress_ft;
struct mlx5_eswitch *esw = br_offloads->esw;
struct mlx5_flow_group *igmp_fg, *mld_fg;
igmp_fg = mlx5_esw_bridge_ingress_igmp_fg_create(esw, ingress_ft);
if (IS_ERR(igmp_fg))
return PTR_ERR(igmp_fg);
mld_fg = mlx5_esw_bridge_ingress_mld_fg_create(esw, ingress_ft);
if (IS_ERR(mld_fg)) {
mlx5_destroy_flow_group(igmp_fg);
return PTR_ERR(mld_fg);
}
br_offloads->ingress_igmp_fg = igmp_fg;
br_offloads->ingress_mld_fg = mld_fg;
return 0;
}
static void
mlx5_esw_bridge_ingress_mcast_fgs_cleanup(struct mlx5_esw_bridge_offloads *br_offloads)
{
if (br_offloads->ingress_mld_fg)
mlx5_destroy_flow_group(br_offloads->ingress_mld_fg);
br_offloads->ingress_mld_fg = NULL;
if (br_offloads->ingress_igmp_fg)
mlx5_destroy_flow_group(br_offloads->ingress_igmp_fg);
br_offloads->ingress_igmp_fg = NULL;
}
static struct mlx5_flow_handle *
mlx5_esw_bridge_ingress_igmp_fh_create(struct mlx5_flow_table *ingress_ft,
struct mlx5_flow_table *skip_ft)
{
struct mlx5_flow_destination dest = {
.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE,
.ft = skip_ft,
};
struct mlx5_flow_act flow_act = {
.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST,
.flags = FLOW_ACT_NO_APPEND,
};
struct mlx5_flow_spec *rule_spec;
struct mlx5_flow_handle *handle;
rule_spec = kvzalloc(sizeof(*rule_spec), GFP_KERNEL);
if (!rule_spec)
return ERR_PTR(-ENOMEM);
rule_spec->match_criteria_enable = MLX5_MATCH_OUTER_HEADERS;
MLX5_SET_TO_ONES(fte_match_param, rule_spec->match_criteria, outer_headers.ip_version);
MLX5_SET(fte_match_param, rule_spec->match_value, outer_headers.ip_version, 4);
MLX5_SET_TO_ONES(fte_match_param, rule_spec->match_criteria, outer_headers.ip_protocol);
MLX5_SET(fte_match_param, rule_spec->match_value, outer_headers.ip_protocol, IPPROTO_IGMP);
handle = mlx5_add_flow_rules(ingress_ft, rule_spec, &flow_act, &dest, 1);
kvfree(rule_spec);
return handle;
}
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)
{
struct mlx5_flow_destination dest = {
.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE,
.ft = skip_ft,
};
struct mlx5_flow_act flow_act = {
.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST,
.flags = FLOW_ACT_NO_APPEND,
};
struct mlx5_flow_spec *rule_spec;
struct mlx5_flow_handle *handle;
rule_spec = kvzalloc(sizeof(*rule_spec), GFP_KERNEL);
if (!rule_spec)
return ERR_PTR(-ENOMEM);
rule_spec->match_criteria_enable = MLX5_MATCH_OUTER_HEADERS | MLX5_MATCH_MISC_PARAMETERS_3;
MLX5_SET_TO_ONES(fte_match_param, rule_spec->match_criteria, outer_headers.ip_version);
MLX5_SET(fte_match_param, rule_spec->match_value, outer_headers.ip_version, 6);
MLX5_SET_TO_ONES(fte_match_param, rule_spec->match_criteria, misc_parameters_3.icmpv6_type);
MLX5_SET(fte_match_param, rule_spec->match_value, misc_parameters_3.icmpv6_type, type);
handle = mlx5_add_flow_rules(ingress_ft, rule_spec, &flow_act, &dest, 1);
kvfree(rule_spec);
return handle;
}
static int
mlx5_esw_bridge_ingress_mcast_fhs_create(struct mlx5_esw_bridge_offloads *br_offloads)
{
struct mlx5_flow_handle *igmp_handle, *mld_query_handle, *mld_report_handle,
*mld_done_handle;
struct mlx5_flow_table *ingress_ft = br_offloads->ingress_ft,
*skip_ft = br_offloads->skip_ft;
int err;
igmp_handle = mlx5_esw_bridge_ingress_igmp_fh_create(ingress_ft, skip_ft);
if (IS_ERR(igmp_handle))
return PTR_ERR(igmp_handle);
if (br_offloads->ingress_mld_fg) {
mld_query_handle = mlx5_esw_bridge_ingress_mld_fh_create(ICMPV6_MGM_QUERY,
ingress_ft,
skip_ft);
if (IS_ERR(mld_query_handle)) {
err = PTR_ERR(mld_query_handle);
goto err_mld_query;
}
mld_report_handle = mlx5_esw_bridge_ingress_mld_fh_create(ICMPV6_MGM_REPORT,
ingress_ft,
skip_ft);
if (IS_ERR(mld_report_handle)) {
err = PTR_ERR(mld_report_handle);
goto err_mld_report;
}
mld_done_handle = mlx5_esw_bridge_ingress_mld_fh_create(ICMPV6_MGM_REDUCTION,
ingress_ft,
skip_ft);
if (IS_ERR(mld_done_handle)) {
err = PTR_ERR(mld_done_handle);
goto err_mld_done;
}
} else {
mld_query_handle = NULL;
mld_report_handle = NULL;
mld_done_handle = NULL;
}
br_offloads->igmp_handle = igmp_handle;
br_offloads->mld_query_handle = mld_query_handle;
br_offloads->mld_report_handle = mld_report_handle;
br_offloads->mld_done_handle = mld_done_handle;
return 0;
err_mld_done:
mlx5_del_flow_rules(mld_report_handle);
err_mld_report:
mlx5_del_flow_rules(mld_query_handle);
err_mld_query:
mlx5_del_flow_rules(igmp_handle);
return err;
}
static void
mlx5_esw_bridge_ingress_mcast_fhs_cleanup(struct mlx5_esw_bridge_offloads *br_offloads)
{
if (br_offloads->mld_done_handle)
mlx5_del_flow_rules(br_offloads->mld_done_handle);
br_offloads->mld_done_handle = NULL;
if (br_offloads->mld_report_handle)
mlx5_del_flow_rules(br_offloads->mld_report_handle);
br_offloads->mld_report_handle = NULL;
if (br_offloads->mld_query_handle)
mlx5_del_flow_rules(br_offloads->mld_query_handle);
br_offloads->mld_query_handle = NULL;
if (br_offloads->igmp_handle)
mlx5_del_flow_rules(br_offloads->igmp_handle);
br_offloads->igmp_handle = NULL;
}
static int mlx5_esw_brige_mcast_init(struct mlx5_esw_bridge *bridge)
{
struct mlx5_esw_bridge_offloads *br_offloads = bridge->br_offloads;
struct mlx5_esw_bridge_port *port, *failed;
unsigned long i;
int err;
xa_for_each(&br_offloads->ports, i, port) {
if (port->bridge != bridge)
continue;
err = mlx5_esw_bridge_port_mcast_init(port);
if (err) {
failed = port;
goto err_port;
}
}
return 0;
err_port:
xa_for_each(&br_offloads->ports, i, port) {
if (port == failed)
break;
if (port->bridge != bridge)
continue;
mlx5_esw_bridge_port_mcast_cleanup(port);
}
return err;
}
static void mlx5_esw_brige_mcast_cleanup(struct mlx5_esw_bridge *bridge)
{
struct mlx5_esw_bridge_offloads *br_offloads = bridge->br_offloads;
struct mlx5_esw_bridge_port *port;
unsigned long i;
xa_for_each(&br_offloads->ports, i, port) {
if (port->bridge != bridge)
continue;
mlx5_esw_bridge_port_mcast_cleanup(port);
}
}
static int mlx5_esw_brige_mcast_global_enable(struct mlx5_esw_bridge_offloads *br_offloads)
{
int err;
if (br_offloads->ingress_igmp_fg)
return 0; /* already enabled by another bridge */
err = mlx5_esw_bridge_ingress_mcast_fgs_init(br_offloads);
if (err) {
esw_warn(br_offloads->esw->dev,
"Failed to create global multicast flow groups (err=%d)\n",
err);
return err;
}
err = mlx5_esw_bridge_ingress_mcast_fhs_create(br_offloads);
if (err) {
esw_warn(br_offloads->esw->dev,
"Failed to create global multicast flows (err=%d)\n",
err);
goto err_fhs;
}
return 0;
err_fhs:
mlx5_esw_bridge_ingress_mcast_fgs_cleanup(br_offloads);
return err;
}
static void mlx5_esw_brige_mcast_global_disable(struct mlx5_esw_bridge_offloads *br_offloads)
{
struct mlx5_esw_bridge *br;
list_for_each_entry(br, &br_offloads->bridges, list) {
/* Ingress table is global, so only disable snooping when all
* bridges on esw have multicast disabled.
*/
if (br->flags & MLX5_ESW_BRIDGE_MCAST_FLAG)
return;
}
mlx5_esw_bridge_ingress_mcast_fhs_cleanup(br_offloads);
mlx5_esw_bridge_ingress_mcast_fgs_cleanup(br_offloads);
}
int mlx5_esw_bridge_mcast_enable(struct mlx5_esw_bridge *bridge)
{
int err;
err = mlx5_esw_brige_mcast_global_enable(bridge->br_offloads);
if (err)
return err;
bridge->flags |= MLX5_ESW_BRIDGE_MCAST_FLAG;
err = mlx5_esw_brige_mcast_init(bridge);
if (err) {
esw_warn(bridge->br_offloads->esw->dev, "Failed to enable multicast (err=%d)\n",
err);
bridge->flags &= ~MLX5_ESW_BRIDGE_MCAST_FLAG;
mlx5_esw_brige_mcast_global_disable(bridge->br_offloads);
}
return err;
}
void mlx5_esw_bridge_mcast_disable(struct mlx5_esw_bridge *bridge)
{
mlx5_esw_brige_mcast_cleanup(bridge);
bridge->flags &= ~MLX5_ESW_BRIDGE_MCAST_FLAG;
mlx5_esw_brige_mcast_global_disable(bridge->br_offloads);
}
......@@ -12,11 +12,124 @@
#include <linux/xarray.h>
#include "fs_core.h"
#define MLX5_ESW_BRIDGE_INGRESS_TABLE_IGMP_GRP_SIZE 1
#define MLX5_ESW_BRIDGE_INGRESS_TABLE_MLD_GRP_SIZE 3
#define MLX5_ESW_BRIDGE_INGRESS_TABLE_VLAN_GRP_SIZE 131072
#define MLX5_ESW_BRIDGE_INGRESS_TABLE_UNTAGGED_GRP_SIZE \
(524288 - MLX5_ESW_BRIDGE_INGRESS_TABLE_IGMP_GRP_SIZE - \
MLX5_ESW_BRIDGE_INGRESS_TABLE_MLD_GRP_SIZE)
#define MLX5_ESW_BRIDGE_INGRESS_TABLE_IGMP_GRP_IDX_FROM 0
#define MLX5_ESW_BRIDGE_INGRESS_TABLE_IGMP_GRP_IDX_TO \
(MLX5_ESW_BRIDGE_INGRESS_TABLE_IGMP_GRP_SIZE - 1)
#define MLX5_ESW_BRIDGE_INGRESS_TABLE_MLD_GRP_IDX_FROM \
(MLX5_ESW_BRIDGE_INGRESS_TABLE_IGMP_GRP_IDX_TO + 1)
#define MLX5_ESW_BRIDGE_INGRESS_TABLE_MLD_GRP_IDX_TO \
(MLX5_ESW_BRIDGE_INGRESS_TABLE_MLD_GRP_IDX_FROM + \
MLX5_ESW_BRIDGE_INGRESS_TABLE_MLD_GRP_SIZE - 1)
#define MLX5_ESW_BRIDGE_INGRESS_TABLE_VLAN_GRP_IDX_FROM \
(MLX5_ESW_BRIDGE_INGRESS_TABLE_MLD_GRP_IDX_TO + 1)
#define MLX5_ESW_BRIDGE_INGRESS_TABLE_VLAN_GRP_IDX_TO \
(MLX5_ESW_BRIDGE_INGRESS_TABLE_VLAN_GRP_IDX_FROM + \
MLX5_ESW_BRIDGE_INGRESS_TABLE_VLAN_GRP_SIZE - 1)
#define MLX5_ESW_BRIDGE_INGRESS_TABLE_VLAN_FILTER_GRP_IDX_FROM \
(MLX5_ESW_BRIDGE_INGRESS_TABLE_VLAN_GRP_IDX_TO + 1)
#define MLX5_ESW_BRIDGE_INGRESS_TABLE_VLAN_FILTER_GRP_IDX_TO \
(MLX5_ESW_BRIDGE_INGRESS_TABLE_VLAN_FILTER_GRP_IDX_FROM + \
MLX5_ESW_BRIDGE_INGRESS_TABLE_VLAN_GRP_SIZE - 1)
#define MLX5_ESW_BRIDGE_INGRESS_TABLE_QINQ_GRP_IDX_FROM \
(MLX5_ESW_BRIDGE_INGRESS_TABLE_VLAN_FILTER_GRP_IDX_TO + 1)
#define MLX5_ESW_BRIDGE_INGRESS_TABLE_QINQ_GRP_IDX_TO \
(MLX5_ESW_BRIDGE_INGRESS_TABLE_QINQ_GRP_IDX_FROM + \
MLX5_ESW_BRIDGE_INGRESS_TABLE_VLAN_GRP_SIZE - 1)
#define MLX5_ESW_BRIDGE_INGRESS_TABLE_QINQ_FILTER_GRP_IDX_FROM \
(MLX5_ESW_BRIDGE_INGRESS_TABLE_QINQ_GRP_IDX_TO + 1)
#define MLX5_ESW_BRIDGE_INGRESS_TABLE_QINQ_FILTER_GRP_IDX_TO \
(MLX5_ESW_BRIDGE_INGRESS_TABLE_QINQ_FILTER_GRP_IDX_FROM + \
MLX5_ESW_BRIDGE_INGRESS_TABLE_VLAN_GRP_SIZE - 1)
#define MLX5_ESW_BRIDGE_INGRESS_TABLE_MAC_GRP_IDX_FROM \
(MLX5_ESW_BRIDGE_INGRESS_TABLE_QINQ_FILTER_GRP_IDX_TO + 1)
#define MLX5_ESW_BRIDGE_INGRESS_TABLE_MAC_GRP_IDX_TO \
(MLX5_ESW_BRIDGE_INGRESS_TABLE_MAC_GRP_IDX_FROM + \
MLX5_ESW_BRIDGE_INGRESS_TABLE_UNTAGGED_GRP_SIZE - 1)
#define MLX5_ESW_BRIDGE_INGRESS_TABLE_SIZE \
(MLX5_ESW_BRIDGE_INGRESS_TABLE_MAC_GRP_IDX_TO + 1)
static_assert(MLX5_ESW_BRIDGE_INGRESS_TABLE_SIZE == 1048576);
#define MLX5_ESW_BRIDGE_EGRESS_TABLE_VLAN_GRP_SIZE 131072
#define MLX5_ESW_BRIDGE_EGRESS_TABLE_MAC_GRP_SIZE (262144 - 1)
#define MLX5_ESW_BRIDGE_EGRESS_TABLE_VLAN_GRP_IDX_FROM 0
#define MLX5_ESW_BRIDGE_EGRESS_TABLE_VLAN_GRP_IDX_TO \
(MLX5_ESW_BRIDGE_EGRESS_TABLE_VLAN_GRP_SIZE - 1)
#define MLX5_ESW_BRIDGE_EGRESS_TABLE_QINQ_GRP_IDX_FROM \
(MLX5_ESW_BRIDGE_EGRESS_TABLE_VLAN_GRP_IDX_TO + 1)
#define MLX5_ESW_BRIDGE_EGRESS_TABLE_QINQ_GRP_IDX_TO \
(MLX5_ESW_BRIDGE_EGRESS_TABLE_QINQ_GRP_IDX_FROM + \
MLX5_ESW_BRIDGE_EGRESS_TABLE_VLAN_GRP_SIZE - 1)
#define MLX5_ESW_BRIDGE_EGRESS_TABLE_MAC_GRP_IDX_FROM \
(MLX5_ESW_BRIDGE_EGRESS_TABLE_QINQ_GRP_IDX_TO + 1)
#define MLX5_ESW_BRIDGE_EGRESS_TABLE_MAC_GRP_IDX_TO \
(MLX5_ESW_BRIDGE_EGRESS_TABLE_MAC_GRP_IDX_FROM + \
MLX5_ESW_BRIDGE_EGRESS_TABLE_MAC_GRP_SIZE - 1)
#define MLX5_ESW_BRIDGE_EGRESS_TABLE_MISS_GRP_IDX_FROM \
(MLX5_ESW_BRIDGE_EGRESS_TABLE_MAC_GRP_IDX_TO + 1)
#define MLX5_ESW_BRIDGE_EGRESS_TABLE_MISS_GRP_IDX_TO \
MLX5_ESW_BRIDGE_EGRESS_TABLE_MISS_GRP_IDX_FROM
#define MLX5_ESW_BRIDGE_EGRESS_TABLE_SIZE \
(MLX5_ESW_BRIDGE_EGRESS_TABLE_MISS_GRP_IDX_TO + 1)
static_assert(MLX5_ESW_BRIDGE_EGRESS_TABLE_SIZE == 524288);
#define MLX5_ESW_BRIDGE_SKIP_TABLE_SIZE 0
#define MLX5_ESW_BRIDGE_MCAST_TABLE_FILTER_GRP_SIZE 1
#define MLX5_ESW_BRIDGE_MCAST_TABLE_FWD_GRP_SIZE 1
#define MLX5_ESW_BRIDGE_MCAST_TABLE_VLAN_GRP_SIZE 4095
#define MLX5_ESW_BRIDGE_MCAST_TABLE_QINQ_GRP_SIZE MLX5_ESW_BRIDGE_MCAST_TABLE_VLAN_GRP_SIZE
#define MLX5_ESW_BRIDGE_MCAST_TABLE_FILTER_GRP_IDX_FROM 0
#define MLX5_ESW_BRIDGE_MCAST_TABLE_FILTER_GRP_IDX_TO \
(MLX5_ESW_BRIDGE_MCAST_TABLE_FILTER_GRP_SIZE - 1)
#define MLX5_ESW_BRIDGE_MCAST_TABLE_VLAN_GRP_IDX_FROM \
(MLX5_ESW_BRIDGE_MCAST_TABLE_FILTER_GRP_IDX_TO + 1)
#define MLX5_ESW_BRIDGE_MCAST_TABLE_VLAN_GRP_IDX_TO \
(MLX5_ESW_BRIDGE_MCAST_TABLE_VLAN_GRP_IDX_FROM + \
MLX5_ESW_BRIDGE_MCAST_TABLE_VLAN_GRP_SIZE - 1)
#define MLX5_ESW_BRIDGE_MCAST_TABLE_QINQ_GRP_IDX_FROM \
(MLX5_ESW_BRIDGE_MCAST_TABLE_VLAN_GRP_IDX_TO + 1)
#define MLX5_ESW_BRIDGE_MCAST_TABLE_QINQ_GRP_IDX_TO \
(MLX5_ESW_BRIDGE_MCAST_TABLE_QINQ_GRP_IDX_FROM + \
MLX5_ESW_BRIDGE_MCAST_TABLE_QINQ_GRP_SIZE - 1)
#define MLX5_ESW_BRIDGE_MCAST_TABLE_FWD_GRP_IDX_FROM \
(MLX5_ESW_BRIDGE_MCAST_TABLE_QINQ_GRP_IDX_TO + 1)
#define MLX5_ESW_BRIDGE_MCAST_TABLE_FWD_GRP_IDX_TO \
(MLX5_ESW_BRIDGE_MCAST_TABLE_FWD_GRP_IDX_FROM + \
MLX5_ESW_BRIDGE_MCAST_TABLE_FWD_GRP_SIZE - 1)
#define MLX5_ESW_BRIDGE_MCAST_TABLE_SIZE \
(MLX5_ESW_BRIDGE_MCAST_TABLE_FWD_GRP_IDX_TO + 1)
static_assert(MLX5_ESW_BRIDGE_MCAST_TABLE_SIZE == 8192);
enum {
MLX5_ESW_BRIDGE_LEVEL_INGRESS_TABLE,
MLX5_ESW_BRIDGE_LEVEL_EGRESS_TABLE,
MLX5_ESW_BRIDGE_LEVEL_MCAST_TABLE,
MLX5_ESW_BRIDGE_LEVEL_SKIP_TABLE,
};
enum {
MLX5_ESW_BRIDGE_VLAN_FILTERING_FLAG = BIT(0),
MLX5_ESW_BRIDGE_MCAST_FLAG = BIT(1),
};
struct mlx5_esw_bridge_fdb_key {
unsigned char addr[ETH_ALEN];
u16 vid;
};
struct mlx5_esw_bridge_mdb_key {
unsigned char addr[ETH_ALEN];
u16 vid;
};
enum {
MLX5_ESW_BRIDGE_FLAG_ADDED_BY_USER = BIT(0),
MLX5_ESW_BRIDGE_FLAG_PEER = BIT(1),
......@@ -43,6 +156,16 @@ struct mlx5_esw_bridge_fdb_entry {
struct mlx5_flow_handle *filter_handle;
};
struct mlx5_esw_bridge_mdb_entry {
struct mlx5_esw_bridge_mdb_key key;
struct rhash_head ht_node;
struct list_head list;
struct xarray ports;
int num_ports;
struct mlx5_flow_handle *egress_handle;
};
struct mlx5_esw_bridge_vlan {
u16 vid;
u16 flags;
......@@ -50,6 +173,7 @@ struct mlx5_esw_bridge_vlan {
struct mlx5_pkt_reformat *pkt_reformat_push;
struct mlx5_pkt_reformat *pkt_reformat_pop;
struct mlx5_modify_hdr *pkt_mod_hdr_push_mark;
struct mlx5_flow_handle *mcast_handle;
};
struct mlx5_esw_bridge_port {
......@@ -58,6 +182,63 @@ struct mlx5_esw_bridge_port {
u16 flags;
struct mlx5_esw_bridge *bridge;
struct xarray vlans;
struct {
struct mlx5_flow_table *ft;
struct mlx5_flow_group *filter_fg;
struct mlx5_flow_group *vlan_fg;
struct mlx5_flow_group *qinq_fg;
struct mlx5_flow_group *fwd_fg;
struct mlx5_flow_handle *filter_handle;
struct mlx5_flow_handle *fwd_handle;
} mcast;
};
struct mlx5_esw_bridge {
int ifindex;
int refcnt;
struct list_head list;
struct mlx5_esw_bridge_offloads *br_offloads;
struct list_head fdb_list;
struct rhashtable fdb_ht;
struct list_head mdb_list;
struct rhashtable mdb_ht;
struct mlx5_flow_table *egress_ft;
struct mlx5_flow_group *egress_vlan_fg;
struct mlx5_flow_group *egress_qinq_fg;
struct mlx5_flow_group *egress_mac_fg;
struct mlx5_flow_group *egress_miss_fg;
struct mlx5_pkt_reformat *egress_miss_pkt_reformat;
struct mlx5_flow_handle *egress_miss_handle;
unsigned long ageing_time;
u32 flags;
u16 vlan_proto;
};
struct mlx5_flow_table *mlx5_esw_bridge_table_create(int max_fte, u32 level,
struct mlx5_eswitch *esw);
unsigned long mlx5_esw_bridge_port_key(struct mlx5_esw_bridge_port *port);
int mlx5_esw_bridge_port_mcast_init(struct mlx5_esw_bridge_port *port);
void mlx5_esw_bridge_port_mcast_cleanup(struct mlx5_esw_bridge_port *port);
int mlx5_esw_bridge_vlan_mcast_init(u16 vlan_proto, struct mlx5_esw_bridge_port *port,
struct mlx5_esw_bridge_vlan *vlan);
void mlx5_esw_bridge_vlan_mcast_cleanup(struct mlx5_esw_bridge_vlan *vlan);
int mlx5_esw_bridge_mcast_enable(struct mlx5_esw_bridge *bridge);
void mlx5_esw_bridge_mcast_disable(struct mlx5_esw_bridge *bridge);
int mlx5_esw_bridge_mdb_init(struct mlx5_esw_bridge *bridge);
void mlx5_esw_bridge_mdb_cleanup(struct mlx5_esw_bridge *bridge);
int mlx5_esw_bridge_port_mdb_attach(struct net_device *dev, struct mlx5_esw_bridge_port *port,
const unsigned char *addr, u16 vid);
void mlx5_esw_bridge_port_mdb_detach(struct net_device *dev, struct mlx5_esw_bridge_port *port,
const unsigned char *addr, u16 vid);
void mlx5_esw_bridge_port_mdb_vlan_flush(struct mlx5_esw_bridge_port *port,
struct mlx5_esw_bridge_vlan *vlan);
void mlx5_esw_bridge_mdb_flush(struct mlx5_esw_bridge *bridge);
#endif /* _MLX5_ESW_BRIDGE_PRIVATE_ */
......@@ -110,6 +110,41 @@ DEFINE_EVENT(mlx5_esw_bridge_port_template,
TP_ARGS(port)
);
DECLARE_EVENT_CLASS(mlx5_esw_bridge_mdb_port_change_template,
TP_PROTO(const struct net_device *dev,
const struct mlx5_esw_bridge_mdb_entry *mdb),
TP_ARGS(dev, mdb),
TP_STRUCT__entry(
__array(char, dev_name, IFNAMSIZ)
__array(unsigned char, addr, ETH_ALEN)
__field(u16, vid)
__field(int, num_ports)
__field(bool, offloaded)),
TP_fast_assign(
strscpy(__entry->dev_name, netdev_name(dev), IFNAMSIZ);
memcpy(__entry->addr, mdb->key.addr, ETH_ALEN);
__entry->vid = mdb->key.vid;
__entry->num_ports = mdb->num_ports;
__entry->offloaded = mdb->egress_handle;),
TP_printk("net_device=%s addr=%pM vid=%u num_ports=%d offloaded=%d",
__entry->dev_name,
__entry->addr,
__entry->vid,
__entry->num_ports,
__entry->offloaded));
DEFINE_EVENT(mlx5_esw_bridge_mdb_port_change_template,
mlx5_esw_bridge_port_mdb_attach,
TP_PROTO(const struct net_device *dev,
const struct mlx5_esw_bridge_mdb_entry *mdb),
TP_ARGS(dev, mdb));
DEFINE_EVENT(mlx5_esw_bridge_mdb_port_change_template,
mlx5_esw_bridge_port_mdb_detach,
TP_PROTO(const struct net_device *dev,
const struct mlx5_esw_bridge_mdb_entry *mdb),
TP_ARGS(dev, mdb));
#endif
/* This part must be outside protection */
......
......@@ -2997,7 +2997,7 @@ static int init_fdb_root_ns(struct mlx5_flow_steering *steering)
goto out_err;
}
maj_prio = fs_create_prio(&steering->fdb_root_ns->ns, FDB_BR_OFFLOAD, 3);
maj_prio = fs_create_prio(&steering->fdb_root_ns->ns, FDB_BR_OFFLOAD, 4);
if (IS_ERR(maj_prio)) {
err = PTR_ERR(maj_prio);
goto out_err;
......
......@@ -100,15 +100,19 @@ enum {
static struct mlx5_profile profile[] = {
[0] = {
.mask = 0,
.num_cmd_caches = MLX5_NUM_COMMAND_CACHES,
},
[1] = {
.mask = MLX5_PROF_MASK_QP_SIZE,
.log_max_qp = 12,
.num_cmd_caches = MLX5_NUM_COMMAND_CACHES,
},
[2] = {
.mask = MLX5_PROF_MASK_QP_SIZE |
MLX5_PROF_MASK_MR_CACHE,
.log_max_qp = LOG_MAX_SUPPORTED_QPS,
.num_cmd_caches = MLX5_NUM_COMMAND_CACHES,
.mr_cache[0] = {
.size = 500,
.limit = 250
......@@ -174,6 +178,11 @@ static struct mlx5_profile profile[] = {
.limit = 4
},
},
[3] = {
.mask = MLX5_PROF_MASK_QP_SIZE,
.log_max_qp = LOG_MAX_SUPPORTED_QPS,
.num_cmd_caches = 0,
},
};
static int wait_fw_init(struct mlx5_core_dev *dev, u32 max_wait_mili,
......
......@@ -142,6 +142,7 @@ enum mlx5_semaphore_space_address {
};
#define MLX5_DEFAULT_PROF 2
#define MLX5_SF_PROF 3
static inline int mlx5_flexible_inlen(struct mlx5_core_dev *dev, size_t fixed,
size_t item_size, size_t num_items,
......
......@@ -28,7 +28,7 @@ static int mlx5_sf_dev_probe(struct auxiliary_device *adev, const struct auxilia
mdev->priv.adev_idx = adev->id;
sf_dev->mdev = mdev;
err = mlx5_mdev_init(mdev, MLX5_DEFAULT_PROF);
err = mlx5_mdev_init(mdev, MLX5_SF_PROF);
if (err) {
mlx5_core_warn(mdev, "mlx5_mdev_init on err=%d\n", err);
goto mdev_err;
......
......@@ -200,6 +200,12 @@ int mlx5dr_cmd_query_device(struct mlx5_core_dev *mdev,
caps->hdr_modify_icm_addr =
MLX5_CAP64_DEV_MEM(mdev, header_modify_sw_icm_start_address);
caps->log_modify_pattern_icm_size =
MLX5_CAP_DEV_MEM(mdev, log_header_modify_pattern_sw_icm_size);
caps->hdr_modify_pattern_icm_addr =
MLX5_CAP64_DEV_MEM(mdev, header_modify_pattern_sw_icm_start_address);
caps->roce_min_src_udp = MLX5_CAP_ROCE(mdev, r_roce_min_src_udp_port);
caps->is_ecpf = mlx5_core_is_ecpf_esw_manager(mdev);
......
......@@ -10,6 +10,33 @@
((dmn)->info.caps.dmn_type##_sw_owner_v2 && \
(dmn)->info.caps.sw_format_ver <= MLX5_STEERING_FORMAT_CONNECTX_7))
bool mlx5dr_domain_is_support_ptrn_arg(struct mlx5dr_domain *dmn)
{
return false;
}
static int dr_domain_init_modify_header_resources(struct mlx5dr_domain *dmn)
{
if (!mlx5dr_domain_is_support_ptrn_arg(dmn))
return 0;
dmn->ptrn_mgr = mlx5dr_ptrn_mgr_create(dmn);
if (!dmn->ptrn_mgr) {
mlx5dr_err(dmn, "Couldn't create ptrn_mgr\n");
return -ENOMEM;
}
return 0;
}
static void dr_domain_destroy_modify_header_resources(struct mlx5dr_domain *dmn)
{
if (!mlx5dr_domain_is_support_ptrn_arg(dmn))
return;
mlx5dr_ptrn_mgr_destroy(dmn->ptrn_mgr);
}
static void dr_domain_init_csum_recalc_fts(struct mlx5dr_domain *dmn)
{
/* Per vport cached FW FT for checksum recalculation, this
......@@ -149,14 +176,22 @@ static int dr_domain_init_resources(struct mlx5dr_domain *dmn)
goto clean_uar;
}
ret = dr_domain_init_modify_header_resources(dmn);
if (ret) {
mlx5dr_err(dmn, "Couldn't create modify-header-resources\n");
goto clean_mem_resources;
}
ret = mlx5dr_send_ring_alloc(dmn);
if (ret) {
mlx5dr_err(dmn, "Couldn't create send-ring\n");
goto clean_mem_resources;
goto clean_modify_hdr;
}
return 0;
clean_modify_hdr:
dr_domain_destroy_modify_header_resources(dmn);
clean_mem_resources:
dr_domain_uninit_mem_resources(dmn);
clean_uar:
......@@ -170,6 +205,7 @@ static int dr_domain_init_resources(struct mlx5dr_domain *dmn)
static void dr_domain_uninit_resources(struct mlx5dr_domain *dmn)
{
mlx5dr_send_ring_free(dmn, dmn->send_ring);
dr_domain_destroy_modify_header_resources(dmn);
dr_domain_uninit_mem_resources(dmn);
mlx5_put_uars_page(dmn->mdev, dmn->uar);
mlx5_core_dealloc_pd(dmn->mdev, dmn->pdn);
......@@ -215,7 +251,7 @@ static int dr_domain_query_vport(struct mlx5dr_domain *dmn,
return 0;
}
static int dr_domain_query_esw_mngr(struct mlx5dr_domain *dmn)
static int dr_domain_query_esw_mgr(struct mlx5dr_domain *dmn)
{
return dr_domain_query_vport(dmn, 0, false,
&dmn->info.caps.vports.esw_manager_caps);
......@@ -321,7 +357,7 @@ static int dr_domain_query_fdb_caps(struct mlx5_core_dev *mdev,
* vports (vport 0, VFs and SFs) will be queried dynamically.
*/
ret = dr_domain_query_esw_mngr(dmn);
ret = dr_domain_query_esw_mgr(dmn);
if (ret) {
mlx5dr_err(dmn, "Failed to query eswitch manager vport caps (err: %d)", ret);
goto free_vports_caps_xa;
......@@ -435,6 +471,9 @@ mlx5dr_domain_create(struct mlx5_core_dev *mdev, enum mlx5dr_domain_type type)
dmn->info.max_log_action_icm_sz = DR_CHUNK_SIZE_4K;
dmn->info.max_log_sw_icm_sz = min_t(u32, DR_CHUNK_SIZE_1024K,
dmn->info.caps.log_icm_size);
dmn->info.max_log_modify_hdr_pattern_icm_sz =
min_t(u32, DR_CHUNK_SIZE_4K,
dmn->info.caps.log_modify_pattern_icm_size);
if (!dmn->info.supp_sw_steering) {
mlx5dr_err(dmn, "SW steering is not supported\n");
......
......@@ -107,9 +107,9 @@ static struct mlx5dr_icm_mr *
dr_icm_pool_mr_create(struct mlx5dr_icm_pool *pool)
{
struct mlx5_core_dev *mdev = pool->dmn->mdev;
enum mlx5_sw_icm_type dm_type;
enum mlx5_sw_icm_type dm_type = 0;
struct mlx5dr_icm_mr *icm_mr;
size_t log_align_base;
size_t log_align_base = 0;
int err;
icm_mr = kvzalloc(sizeof(*icm_mr), GFP_KERNEL);
......@@ -121,14 +121,25 @@ dr_icm_pool_mr_create(struct mlx5dr_icm_pool *pool)
icm_mr->dm.length = mlx5dr_icm_pool_chunk_size_to_byte(pool->max_log_chunk_sz,
pool->icm_type);
if (pool->icm_type == DR_ICM_TYPE_STE) {
switch (pool->icm_type) {
case DR_ICM_TYPE_STE:
dm_type = MLX5_SW_ICM_TYPE_STEERING;
log_align_base = ilog2(icm_mr->dm.length);
} else {
break;
case DR_ICM_TYPE_MODIFY_ACTION:
dm_type = MLX5_SW_ICM_TYPE_HEADER_MODIFY;
/* Align base is 64B */
log_align_base = ilog2(DR_ICM_MODIFY_HDR_ALIGN_BASE);
break;
case DR_ICM_TYPE_MODIFY_HDR_PTRN:
dm_type = MLX5_SW_ICM_TYPE_HEADER_MODIFY_PATTERN;
/* Align base is 64B */
log_align_base = ilog2(DR_ICM_MODIFY_HDR_ALIGN_BASE);
break;
default:
WARN_ON(pool->icm_type);
}
icm_mr->dm.type = dm_type;
err = mlx5_dm_sw_icm_alloc(mdev, icm_mr->dm.type, icm_mr->dm.length,
......@@ -493,27 +504,33 @@ struct mlx5dr_icm_pool *mlx5dr_icm_pool_create(struct mlx5dr_domain *dmn,
enum mlx5dr_icm_type icm_type)
{
u32 num_of_chunks, entry_size, max_hot_size;
enum mlx5dr_icm_chunk_size max_log_chunk_sz;
struct mlx5dr_icm_pool *pool;
if (icm_type == DR_ICM_TYPE_STE)
max_log_chunk_sz = dmn->info.max_log_sw_icm_sz;
else
max_log_chunk_sz = dmn->info.max_log_action_icm_sz;
pool = kvzalloc(sizeof(*pool), GFP_KERNEL);
if (!pool)
return NULL;
pool->dmn = dmn;
pool->icm_type = icm_type;
pool->max_log_chunk_sz = max_log_chunk_sz;
pool->chunks_kmem_cache = dmn->chunks_kmem_cache;
INIT_LIST_HEAD(&pool->buddy_mem_list);
mutex_init(&pool->mutex);
switch (icm_type) {
case DR_ICM_TYPE_STE:
pool->max_log_chunk_sz = dmn->info.max_log_sw_icm_sz;
break;
case DR_ICM_TYPE_MODIFY_ACTION:
pool->max_log_chunk_sz = dmn->info.max_log_action_icm_sz;
break;
case DR_ICM_TYPE_MODIFY_HDR_PTRN:
pool->max_log_chunk_sz = dmn->info.max_log_modify_hdr_pattern_icm_sz;
break;
default:
WARN_ON(icm_type);
}
entry_size = mlx5dr_icm_pool_dm_type_to_entry_size(pool->icm_type);
max_hot_size = mlx5dr_icm_pool_chunk_size_to_byte(pool->max_log_chunk_sz,
......
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
// Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
#include "dr_types.h"
struct mlx5dr_ptrn_mgr {
struct mlx5dr_domain *dmn;
struct mlx5dr_icm_pool *ptrn_icm_pool;
};
struct mlx5dr_ptrn_mgr *mlx5dr_ptrn_mgr_create(struct mlx5dr_domain *dmn)
{
struct mlx5dr_ptrn_mgr *mgr;
if (!mlx5dr_domain_is_support_ptrn_arg(dmn))
return NULL;
mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
if (!mgr)
return NULL;
mgr->dmn = dmn;
mgr->ptrn_icm_pool = mlx5dr_icm_pool_create(dmn, DR_ICM_TYPE_MODIFY_HDR_PTRN);
if (!mgr->ptrn_icm_pool) {
mlx5dr_err(dmn, "Couldn't get modify-header-pattern memory\n");
goto free_mgr;
}
return mgr;
free_mgr:
kfree(mgr);
return NULL;
}
void mlx5dr_ptrn_mgr_destroy(struct mlx5dr_ptrn_mgr *mgr)
{
if (!mgr)
return;
mlx5dr_icm_pool_destroy(mgr->ptrn_icm_pool);
kfree(mgr);
}
......@@ -18,7 +18,12 @@ struct dr_data_seg {
unsigned int send_flags;
};
enum send_info_type {
WRITE_ICM = 0,
};
struct postsend_info {
enum send_info_type type;
struct dr_data_seg write;
struct dr_data_seg read;
u64 remote_addr;
......@@ -402,10 +407,12 @@ static void dr_rdma_segments(struct mlx5dr_qp *dr_qp, u64 remote_addr,
static void dr_post_send(struct mlx5dr_qp *dr_qp, struct postsend_info *send_info)
{
dr_rdma_segments(dr_qp, send_info->remote_addr, send_info->rkey,
&send_info->write, MLX5_OPCODE_RDMA_WRITE, false);
dr_rdma_segments(dr_qp, send_info->remote_addr, send_info->rkey,
&send_info->read, MLX5_OPCODE_RDMA_READ, true);
if (send_info->type == WRITE_ICM) {
dr_rdma_segments(dr_qp, send_info->remote_addr, send_info->rkey,
&send_info->write, MLX5_OPCODE_RDMA_WRITE, false);
dr_rdma_segments(dr_qp, send_info->remote_addr, send_info->rkey,
&send_info->read, MLX5_OPCODE_RDMA_READ, true);
}
}
/**
......@@ -476,9 +483,26 @@ static int dr_handle_pending_wc(struct mlx5dr_domain *dmn,
return 0;
}
static void dr_fill_data_segs(struct mlx5dr_send_ring *send_ring,
struct postsend_info *send_info)
static void dr_fill_write_icm_segs(struct mlx5dr_domain *dmn,
struct mlx5dr_send_ring *send_ring,
struct postsend_info *send_info)
{
u32 buff_offset;
if (send_info->write.length > dmn->info.max_inline_size) {
buff_offset = (send_ring->tx_head &
(dmn->send_ring->signal_th - 1)) *
send_ring->max_post_send_size;
/* Copy to ring mr */
memcpy(send_ring->buf + buff_offset,
(void *)(uintptr_t)send_info->write.addr,
send_info->write.length);
send_info->write.addr = (uintptr_t)send_ring->mr->dma_addr + buff_offset;
send_info->write.lkey = send_ring->mr->mkey;
send_ring->tx_head++;
}
send_ring->pending_wqe++;
if (send_ring->pending_wqe % send_ring->signal_th == 0)
......@@ -496,11 +520,18 @@ static void dr_fill_data_segs(struct mlx5dr_send_ring *send_ring,
send_info->read.send_flags = 0;
}
static void dr_fill_data_segs(struct mlx5dr_domain *dmn,
struct mlx5dr_send_ring *send_ring,
struct postsend_info *send_info)
{
if (send_info->type == WRITE_ICM)
dr_fill_write_icm_segs(dmn, send_ring, send_info);
}
static int dr_postsend_icm_data(struct mlx5dr_domain *dmn,
struct postsend_info *send_info)
{
struct mlx5dr_send_ring *send_ring = dmn->send_ring;
u32 buff_offset;
int ret;
if (unlikely(dmn->mdev->state == MLX5_DEVICE_STATE_INTERNAL_ERROR ||
......@@ -517,20 +548,7 @@ static int dr_postsend_icm_data(struct mlx5dr_domain *dmn,
if (ret)
goto out_unlock;
if (send_info->write.length > dmn->info.max_inline_size) {
buff_offset = (send_ring->tx_head &
(dmn->send_ring->signal_th - 1)) *
send_ring->max_post_send_size;
/* Copy to ring mr */
memcpy(send_ring->buf + buff_offset,
(void *)(uintptr_t)send_info->write.addr,
send_info->write.length);
send_info->write.addr = (uintptr_t)send_ring->mr->dma_addr + buff_offset;
send_info->write.lkey = send_ring->mr->mkey;
}
send_ring->tx_head++;
dr_fill_data_segs(send_ring, send_info);
dr_fill_data_segs(dmn, send_ring, send_info);
dr_post_send(send_ring->qp, send_info);
out_unlock:
......
......@@ -604,9 +604,6 @@ void dr_ste_v1_set_actions_tx(struct mlx5dr_domain *dmn,
allow_modify_hdr = false;
}
if (action_type_set[DR_ACTION_TYP_CTR])
dr_ste_v1_set_counter_id(last_ste, attr->ctr_id);
if (action_type_set[DR_ACTION_TYP_MODIFY_HDR]) {
if (!allow_modify_hdr || action_sz < DR_STE_ACTION_DOUBLE_SZ) {
dr_ste_v1_arr_init_next_match(&last_ste, added_stes,
......@@ -724,6 +721,10 @@ void dr_ste_v1_set_actions_tx(struct mlx5dr_domain *dmn,
attr->range.max);
}
/* set counter ID on the last STE to adhere to DMFS behavior */
if (action_type_set[DR_ACTION_TYP_CTR])
dr_ste_v1_set_counter_id(last_ste, attr->ctr_id);
dr_ste_v1_set_hit_gvmi(last_ste, attr->hit_gvmi);
dr_ste_v1_set_hit_addr(last_ste, attr->final_icm_addr, 1);
}
......
......@@ -26,6 +26,8 @@
#define mlx5dr_info(dmn, arg...) mlx5_core_info((dmn)->mdev, ##arg)
#define mlx5dr_dbg(dmn, arg...) mlx5_core_dbg((dmn)->mdev, ##arg)
struct mlx5dr_ptrn_mgr;
static inline bool dr_is_flex_parser_0_id(u8 parser_id)
{
return parser_id <= DR_STE_MAX_FLEX_0_ID;
......@@ -66,6 +68,7 @@ enum mlx5dr_icm_chunk_size {
enum mlx5dr_icm_type {
DR_ICM_TYPE_STE,
DR_ICM_TYPE_MODIFY_ACTION,
DR_ICM_TYPE_MODIFY_HDR_PTRN,
};
static inline enum mlx5dr_icm_chunk_size
......@@ -861,6 +864,8 @@ struct mlx5dr_cmd_caps {
u64 esw_tx_drop_address;
u32 log_icm_size;
u64 hdr_modify_icm_addr;
u32 log_modify_pattern_icm_size;
u64 hdr_modify_pattern_icm_addr;
u32 flex_protocols;
u8 flex_parser_id_icmp_dw0;
u8 flex_parser_id_icmp_dw1;
......@@ -910,6 +915,7 @@ struct mlx5dr_domain_info {
u32 max_send_wr;
u32 max_log_sw_icm_sz;
u32 max_log_action_icm_sz;
u32 max_log_modify_hdr_pattern_icm_sz;
struct mlx5dr_domain_rx_tx rx;
struct mlx5dr_domain_rx_tx tx;
struct mlx5dr_cmd_caps caps;
......@@ -928,6 +934,7 @@ struct mlx5dr_domain {
struct mlx5dr_send_info_pool *send_info_pool_tx;
struct kmem_cache *chunks_kmem_cache;
struct kmem_cache *htbls_kmem_cache;
struct mlx5dr_ptrn_mgr *ptrn_mgr;
struct mlx5dr_send_ring *send_ring;
struct mlx5dr_domain_info info;
struct xarray csum_fts_xa;
......@@ -1526,4 +1533,8 @@ static inline bool mlx5dr_supp_match_ranges(struct mlx5_core_dev *dev)
(1ULL << MLX5_IFC_DEFINER_FORMAT_ID_SELECT));
}
bool mlx5dr_domain_is_support_ptrn_arg(struct mlx5dr_domain *dmn);
struct mlx5dr_ptrn_mgr *mlx5dr_ptrn_mgr_create(struct mlx5dr_domain *dmn);
void mlx5dr_ptrn_mgr_destroy(struct mlx5dr_ptrn_mgr *mgr);
#endif /* _DR_TYPES_H_ */
......@@ -443,6 +443,8 @@ enum {
MLX5_OPCODE_UMR = 0x25,
MLX5_OPCODE_FLOW_TBL_ACCESS = 0x2c,
MLX5_OPCODE_ACCESS_ASO = 0x2d,
};
......
......@@ -751,6 +751,7 @@ enum {
struct mlx5_profile {
u64 mask;
u8 log_max_qp;
u8 num_cmd_caches;
struct {
int size;
int limit;
......
......@@ -78,12 +78,15 @@ enum {
enum {
MLX5_OBJ_TYPE_SW_ICM = 0x0008,
MLX5_OBJ_TYPE_HEADER_MODIFY_ARGUMENT = 0x23,
};
enum {
MLX5_GENERAL_OBJ_TYPES_CAP_SW_ICM = (1ULL << MLX5_OBJ_TYPE_SW_ICM),
MLX5_GENERAL_OBJ_TYPES_CAP_GENEVE_TLV_OPT = (1ULL << 11),
MLX5_GENERAL_OBJ_TYPES_CAP_VIRTIO_NET_Q = (1ULL << 13),
MLX5_GENERAL_OBJ_TYPES_CAP_HEADER_MODIFY_ARGUMENT =
(1ULL << MLX5_OBJ_TYPE_HEADER_MODIFY_ARGUMENT),
MLX5_GENERAL_OBJ_TYPES_CAP_MACSEC_OFFLOAD = (1ULL << 39),
};
......@@ -321,6 +324,10 @@ enum {
MLX5_FT_NIC_TX_RDMA_2_NIC_TX = BIT(1),
};
enum {
MLX5_CMD_OP_MOD_UPDATE_HEADER_MODIFY_ARGUMENT = 0x1,
};
struct mlx5_ifc_flow_table_fields_supported_bits {
u8 outer_dmac[0x1];
u8 outer_smac[0x1];
......@@ -880,7 +887,12 @@ enum {
struct mlx5_ifc_flow_table_eswitch_cap_bits {
u8 fdb_to_vport_reg_c_id[0x8];
u8 reserved_at_8[0xd];
u8 reserved_at_8[0x5];
u8 fdb_uplink_hairpin[0x1];
u8 fdb_multi_path_any_table_limit_regc[0x1];
u8 reserved_at_f[0x3];
u8 fdb_multi_path_any_table[0x1];
u8 reserved_at_13[0x2];
u8 fdb_modify_header_fwd_to_table[0x1];
u8 fdb_ipv4_ttl_modify[0x1];
u8 flow_source[0x1];
......@@ -1922,7 +1934,14 @@ struct mlx5_ifc_cmd_hca_cap_bits {
u8 reserved_at_750[0x4];
u8 max_dynamic_vf_msix_table_size[0xc];
u8 reserved_at_760[0x20];
u8 reserved_at_760[0x3];
u8 log_max_num_header_modify_argument[0x5];
u8 reserved_at_768[0x4];
u8 log_header_modify_argument_granularity[0x4];
u8 reserved_at_770[0x3];
u8 log_header_modify_argument_max_alloc[0x5];
u8 reserved_at_778[0x8];
u8 vhca_tunnel_commands[0x40];
u8 match_definer_format_supported[0x40];
};
......@@ -6356,6 +6375,18 @@ struct mlx5_ifc_general_obj_out_cmd_hdr_bits {
u8 reserved_at_60[0x20];
};
struct mlx5_ifc_modify_header_arg_bits {
u8 reserved_at_0[0x80];
u8 reserved_at_80[0x8];
u8 access_pd[0x18];
};
struct mlx5_ifc_create_modify_header_arg_in_bits {
struct mlx5_ifc_general_obj_in_cmd_hdr_bits hdr;
struct mlx5_ifc_modify_header_arg_bits arg;
};
struct mlx5_ifc_create_match_definer_in_bits {
struct mlx5_ifc_general_obj_in_cmd_hdr_bits general_obj_in_cmd_hdr;
......
......@@ -499,6 +499,16 @@ struct mlx5_stride_block_ctrl_seg {
__be16 num_entries;
};
struct mlx5_wqe_flow_update_ctrl_seg {
__be32 flow_idx_update;
__be32 dest_handle;
u8 reserved0[40];
};
struct mlx5_wqe_header_modify_argument_update_seg {
u8 argument_list[64];
};
struct mlx5_core_qp {
struct mlx5_core_rsc_common common; /* must be first */
void (*event) (struct mlx5_core_qp *, int);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment