diff options
Diffstat (limited to 'drivers/net/ethernet/mellanox/mlx5/core/esw/bridge.c')
-rw-r--r-- | drivers/net/ethernet/mellanox/mlx5/core/esw/bridge.c | 359 |
1 files changed, 236 insertions, 123 deletions
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/esw/bridge.c b/drivers/net/ethernet/mellanox/mlx5/core/esw/bridge.c index 69a3630818d7..7e221038df8d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/esw/bridge.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/esw/bridge.c @@ -5,6 +5,7 @@ #include <linux/notifier.h> #include <net/netevent.h> #include <net/switchdev.h> +#include "lib/devcom.h" #include "bridge.h" #include "eswitch.h" #include "bridge_priv.h" @@ -56,7 +57,6 @@ struct mlx5_esw_bridge { struct list_head fdb_list; struct rhashtable fdb_ht; - struct xarray vports; struct mlx5_flow_table *egress_ft; struct mlx5_flow_group *egress_vlan_fg; @@ -77,6 +77,15 @@ mlx5_esw_bridge_fdb_offload_notify(struct net_device *dev, const unsigned char * call_switchdev_notifiers(val, dev, &send_info.info, NULL); } +static void +mlx5_esw_bridge_fdb_del_notify(struct mlx5_esw_bridge_fdb_entry *entry) +{ + if (!(entry->flags & (MLX5_ESW_BRIDGE_FLAG_ADDED_BY_USER | MLX5_ESW_BRIDGE_FLAG_PEER))) + mlx5_esw_bridge_fdb_offload_notify(entry->dev, entry->key.addr, + entry->key.vid, + SWITCHDEV_FDB_DEL_TO_BRIDGE); +} + static struct mlx5_flow_table * mlx5_esw_bridge_table_create(int max_fte, u32 level, struct mlx5_eswitch *esw) { @@ -400,9 +409,10 @@ mlx5_esw_bridge_egress_table_cleanup(struct mlx5_esw_bridge *bridge) } static struct mlx5_flow_handle * -mlx5_esw_bridge_ingress_flow_create(u16 vport_num, const unsigned char *addr, - struct mlx5_esw_bridge_vlan *vlan, u32 counter_id, - struct mlx5_esw_bridge *bridge) +mlx5_esw_bridge_ingress_flow_with_esw_create(u16 vport_num, const unsigned char *addr, + struct mlx5_esw_bridge_vlan *vlan, u32 counter_id, + struct mlx5_esw_bridge *bridge, + struct mlx5_eswitch *esw) { struct mlx5_esw_bridge_offloads *br_offloads = bridge->br_offloads; struct mlx5_flow_act flow_act = { @@ -430,7 +440,7 @@ mlx5_esw_bridge_ingress_flow_create(u16 vport_num, const unsigned char *addr, 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(br_offloads->esw, vport_num)); + mlx5_eswitch_get_vport_metadata_for_match(esw, vport_num)); if (vlan && vlan->pkt_reformat_push) { flow_act.action |= MLX5_FLOW_CONTEXT_ACTION_PACKET_REFORMAT; @@ -459,6 +469,35 @@ mlx5_esw_bridge_ingress_flow_create(u16 vport_num, const unsigned char *addr, } static struct mlx5_flow_handle * +mlx5_esw_bridge_ingress_flow_create(u16 vport_num, const unsigned char *addr, + struct mlx5_esw_bridge_vlan *vlan, u32 counter_id, + struct mlx5_esw_bridge *bridge) +{ + return mlx5_esw_bridge_ingress_flow_with_esw_create(vport_num, addr, vlan, counter_id, + bridge, bridge->br_offloads->esw); +} + +static struct mlx5_flow_handle * +mlx5_esw_bridge_ingress_flow_peer_create(u16 vport_num, const unsigned char *addr, + struct mlx5_esw_bridge_vlan *vlan, u32 counter_id, + struct mlx5_esw_bridge *bridge) +{ + struct mlx5_devcom *devcom = 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_ingress_flow_with_esw_create(vport_num, addr, vlan, counter_id, + bridge, peer_esw); + + mlx5_devcom_release_peer_data(devcom, MLX5_DEVCOM_ESW_OFFLOADS); + return handle; +} + +static struct mlx5_flow_handle * mlx5_esw_bridge_ingress_filter_flow_create(u16 vport_num, const unsigned char *addr, struct mlx5_esw_bridge *bridge) { @@ -505,7 +544,7 @@ mlx5_esw_bridge_ingress_filter_flow_create(u16 vport_num, const unsigned char *a } static struct mlx5_flow_handle * -mlx5_esw_bridge_egress_flow_create(u16 vport_num, const unsigned char *addr, +mlx5_esw_bridge_egress_flow_create(u16 vport_num, u16 esw_owner_vhca_id, const unsigned char *addr, struct mlx5_esw_bridge_vlan *vlan, struct mlx5_esw_bridge *bridge) { @@ -550,6 +589,10 @@ mlx5_esw_bridge_egress_flow_create(u16 vport_num, const unsigned char *addr, 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 = esw_owner_vhca_id; + } handle = mlx5_add_flow_rules(bridge->egress_ft, rule_spec, &flow_act, &dest, 1); kvfree(rule_spec); @@ -576,7 +619,6 @@ static struct mlx5_esw_bridge *mlx5_esw_bridge_create(int ifindex, goto err_fdb_ht; INIT_LIST_HEAD(&bridge->fdb_list); - xa_init(&bridge->vports); bridge->ifindex = ifindex; bridge->refcnt = 1; bridge->ageing_time = clock_t_to_jiffies(BR_DEFAULT_AGEING_TIME); @@ -603,7 +645,6 @@ static void mlx5_esw_bridge_put(struct mlx5_esw_bridge_offloads *br_offloads, return; mlx5_esw_bridge_egress_table_cleanup(bridge); - WARN_ON(!xa_empty(&bridge->vports)); list_del(&bridge->list); rhashtable_destroy(&bridge->fdb_ht); kvfree(bridge); @@ -639,30 +680,40 @@ mlx5_esw_bridge_lookup(int ifindex, struct mlx5_esw_bridge_offloads *br_offloads return bridge; } +static unsigned long mlx5_esw_bridge_port_key_from_data(u16 vport_num, u16 esw_owner_vhca_id) +{ + 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) +{ + return mlx5_esw_bridge_port_key_from_data(port->vport_num, port->esw_owner_vhca_id); +} + static int mlx5_esw_bridge_port_insert(struct mlx5_esw_bridge_port *port, - struct mlx5_esw_bridge *bridge) + struct mlx5_esw_bridge_offloads *br_offloads) { - return xa_insert(&bridge->vports, port->vport_num, port, GFP_KERNEL); + return xa_insert(&br_offloads->ports, mlx5_esw_bridge_port_key(port), port, GFP_KERNEL); } static struct mlx5_esw_bridge_port * -mlx5_esw_bridge_port_lookup(u16 vport_num, struct mlx5_esw_bridge *bridge) +mlx5_esw_bridge_port_lookup(u16 vport_num, u16 esw_owner_vhca_id, + struct mlx5_esw_bridge_offloads *br_offloads) { - return xa_load(&bridge->vports, vport_num); + return xa_load(&br_offloads->ports, mlx5_esw_bridge_port_key_from_data(vport_num, + esw_owner_vhca_id)); } static void mlx5_esw_bridge_port_erase(struct mlx5_esw_bridge_port *port, - struct mlx5_esw_bridge *bridge) + struct mlx5_esw_bridge_offloads *br_offloads) { - xa_erase(&bridge->vports, port->vport_num); + xa_erase(&br_offloads->ports, mlx5_esw_bridge_port_key(port)); } -static void mlx5_esw_bridge_fdb_entry_refresh(unsigned long lastuse, - struct mlx5_esw_bridge_fdb_entry *entry) +static void mlx5_esw_bridge_fdb_entry_refresh(struct mlx5_esw_bridge_fdb_entry *entry) { trace_mlx5_esw_bridge_fdb_entry_refresh(entry); - entry->lastuse = lastuse; mlx5_esw_bridge_fdb_offload_notify(entry->dev, entry->key.addr, entry->key.vid, SWITCHDEV_FDB_ADD_TO_BRIDGE); @@ -690,10 +741,7 @@ static void mlx5_esw_bridge_fdb_flush(struct mlx5_esw_bridge *bridge) struct mlx5_esw_bridge_fdb_entry *entry, *tmp; list_for_each_entry_safe(entry, tmp, &bridge->fdb_list, list) { - if (!(entry->flags & MLX5_ESW_BRIDGE_FLAG_ADDED_BY_USER)) - mlx5_esw_bridge_fdb_offload_notify(entry->dev, entry->key.addr, - entry->key.vid, - SWITCHDEV_FDB_DEL_TO_BRIDGE); + mlx5_esw_bridge_fdb_del_notify(entry); mlx5_esw_bridge_fdb_entry_cleanup(entry, bridge); } } @@ -841,10 +889,7 @@ static void mlx5_esw_bridge_vlan_flush(struct mlx5_esw_bridge_vlan *vlan, struct mlx5_esw_bridge_fdb_entry *entry, *tmp; list_for_each_entry_safe(entry, tmp, &vlan->fdb_list, vlan_list) { - if (!(entry->flags & MLX5_ESW_BRIDGE_FLAG_ADDED_BY_USER)) - mlx5_esw_bridge_fdb_offload_notify(entry->dev, entry->key.addr, - entry->key.vid, - SWITCHDEV_FDB_DEL_TO_BRIDGE); + mlx5_esw_bridge_fdb_del_notify(entry); mlx5_esw_bridge_fdb_entry_cleanup(entry, bridge); } @@ -875,13 +920,13 @@ static void mlx5_esw_bridge_port_vlans_flush(struct mlx5_esw_bridge_port *port, } static struct mlx5_esw_bridge_vlan * -mlx5_esw_bridge_port_vlan_lookup(u16 vid, u16 vport_num, struct mlx5_esw_bridge *bridge, - struct mlx5_eswitch *esw) +mlx5_esw_bridge_port_vlan_lookup(u16 vid, u16 vport_num, u16 esw_owner_vhca_id, + struct mlx5_esw_bridge *bridge, struct mlx5_eswitch *esw) { struct mlx5_esw_bridge_port *port; struct mlx5_esw_bridge_vlan *vlan; - port = mlx5_esw_bridge_port_lookup(vport_num, bridge); + port = mlx5_esw_bridge_port_lookup(vport_num, esw_owner_vhca_id, bridge->br_offloads); if (!port) { /* FDB is added asynchronously on wq while port might have been deleted * concurrently. Report on 'info' logging level and skip the FDB offload. @@ -904,24 +949,23 @@ mlx5_esw_bridge_port_vlan_lookup(u16 vid, u16 vport_num, struct mlx5_esw_bridge } static struct mlx5_esw_bridge_fdb_entry * -mlx5_esw_bridge_fdb_entry_init(struct net_device *dev, u16 vport_num, const unsigned char *addr, - u16 vid, bool added_by_user, struct mlx5_eswitch *esw, - struct mlx5_esw_bridge *bridge) +mlx5_esw_bridge_fdb_entry_init(struct net_device *dev, u16 vport_num, u16 esw_owner_vhca_id, + const unsigned char *addr, u16 vid, bool added_by_user, bool peer, + struct mlx5_eswitch *esw, struct mlx5_esw_bridge *bridge) { struct mlx5_esw_bridge_vlan *vlan = NULL; struct mlx5_esw_bridge_fdb_entry *entry; struct mlx5_flow_handle *handle; struct mlx5_fc *counter; - struct mlx5e_priv *priv; int err; if (bridge->flags & MLX5_ESW_BRIDGE_VLAN_FILTERING_FLAG && vid) { - vlan = mlx5_esw_bridge_port_vlan_lookup(vid, vport_num, bridge, esw); + vlan = mlx5_esw_bridge_port_vlan_lookup(vid, vport_num, esw_owner_vhca_id, bridge, + esw); if (IS_ERR(vlan)) return ERR_CAST(vlan); } - priv = netdev_priv(dev); entry = kvzalloc(sizeof(*entry), GFP_KERNEL); if (!entry) return ERR_PTR(-ENOMEM); @@ -930,19 +974,25 @@ mlx5_esw_bridge_fdb_entry_init(struct net_device *dev, u16 vport_num, const unsi entry->key.vid = vid; entry->dev = dev; entry->vport_num = vport_num; + entry->esw_owner_vhca_id = esw_owner_vhca_id; entry->lastuse = jiffies; if (added_by_user) entry->flags |= MLX5_ESW_BRIDGE_FLAG_ADDED_BY_USER; + if (peer) + entry->flags |= MLX5_ESW_BRIDGE_FLAG_PEER; - counter = mlx5_fc_create(priv->mdev, true); + counter = mlx5_fc_create(esw->dev, true); if (IS_ERR(counter)) { err = PTR_ERR(counter); goto err_ingress_fc_create; } entry->ingress_counter = counter; - handle = mlx5_esw_bridge_ingress_flow_create(vport_num, addr, vlan, mlx5_fc_id(counter), - bridge); + handle = peer ? + mlx5_esw_bridge_ingress_flow_peer_create(vport_num, addr, vlan, + mlx5_fc_id(counter), bridge) : + mlx5_esw_bridge_ingress_flow_create(vport_num, addr, vlan, + mlx5_fc_id(counter), bridge); if (IS_ERR(handle)) { err = PTR_ERR(handle); esw_warn(esw->dev, "Failed to create ingress flow(vport=%u,err=%d)\n", @@ -962,7 +1012,8 @@ mlx5_esw_bridge_fdb_entry_init(struct net_device *dev, u16 vport_num, const unsi entry->filter_handle = handle; } - handle = mlx5_esw_bridge_egress_flow_create(vport_num, addr, vlan, bridge); + handle = mlx5_esw_bridge_egress_flow_create(vport_num, esw_owner_vhca_id, addr, vlan, + bridge); if (IS_ERR(handle)) { err = PTR_ERR(handle); esw_warn(esw->dev, "Failed to create egress flow(vport=%u,err=%d)\n", @@ -994,32 +1045,37 @@ err_egress_flow_create: err_ingress_filter_flow_create: mlx5_del_flow_rules(entry->ingress_handle); err_ingress_flow_create: - mlx5_fc_destroy(priv->mdev, entry->ingress_counter); + mlx5_fc_destroy(esw->dev, entry->ingress_counter); err_ingress_fc_create: kvfree(entry); return ERR_PTR(err); } -int mlx5_esw_bridge_ageing_time_set(unsigned long ageing_time, struct mlx5_eswitch *esw, - struct mlx5_vport *vport) +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) { - if (!vport->bridge) + struct mlx5_esw_bridge_port *port; + + port = mlx5_esw_bridge_port_lookup(vport_num, esw_owner_vhca_id, br_offloads); + if (!port) return -EINVAL; - vport->bridge->ageing_time = clock_t_to_jiffies(ageing_time); + port->bridge->ageing_time = clock_t_to_jiffies(ageing_time); return 0; } -int mlx5_esw_bridge_vlan_filtering_set(bool enable, struct mlx5_eswitch *esw, - struct mlx5_vport *vport) +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; - if (!vport->bridge) + port = mlx5_esw_bridge_port_lookup(vport_num, esw_owner_vhca_id, br_offloads); + if (!port) return -EINVAL; - bridge = vport->bridge; + bridge = port->bridge; filtering = bridge->flags & MLX5_ESW_BRIDGE_VLAN_FILTERING_FLAG; if (filtering == enable) return 0; @@ -1033,114 +1089,143 @@ int mlx5_esw_bridge_vlan_filtering_set(bool enable, struct mlx5_eswitch *esw, return 0; } -static int mlx5_esw_bridge_vport_init(struct mlx5_esw_bridge_offloads *br_offloads, - struct mlx5_esw_bridge *bridge, - struct mlx5_vport *vport) +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) { struct mlx5_eswitch *esw = br_offloads->esw; struct mlx5_esw_bridge_port *port; int err; port = kvzalloc(sizeof(*port), GFP_KERNEL); - if (!port) { - err = -ENOMEM; - goto err_port_alloc; - } + if (!port) + return -ENOMEM; - port->vport_num = vport->vport; + port->vport_num = vport_num; + port->esw_owner_vhca_id = esw_owner_vhca_id; + port->bridge = bridge; + port->flags |= flags; xa_init(&port->vlans); - err = mlx5_esw_bridge_port_insert(port, bridge); + err = mlx5_esw_bridge_port_insert(port, br_offloads); if (err) { - esw_warn(esw->dev, "Failed to insert port metadata (vport=%u,err=%d)\n", - vport->vport, err); + esw_warn(esw->dev, + "Failed to insert port metadata (vport=%u,esw_owner_vhca_id=%u,err=%d)\n", + port->vport_num, port->esw_owner_vhca_id, err); goto err_port_insert; } trace_mlx5_esw_bridge_vport_init(port); - vport->bridge = bridge; return 0; err_port_insert: kvfree(port); -err_port_alloc: - mlx5_esw_bridge_put(br_offloads, bridge); return err; } static int mlx5_esw_bridge_vport_cleanup(struct mlx5_esw_bridge_offloads *br_offloads, - struct mlx5_vport *vport) + struct mlx5_esw_bridge_port *port) { - struct mlx5_esw_bridge *bridge = vport->bridge; + u16 vport_num = port->vport_num, esw_owner_vhca_id = port->esw_owner_vhca_id; + struct mlx5_esw_bridge *bridge = port->bridge; struct mlx5_esw_bridge_fdb_entry *entry, *tmp; - struct mlx5_esw_bridge_port *port; list_for_each_entry_safe(entry, tmp, &bridge->fdb_list, list) - if (entry->vport_num == vport->vport) + if (entry->vport_num == vport_num && entry->esw_owner_vhca_id == esw_owner_vhca_id) mlx5_esw_bridge_fdb_entry_cleanup(entry, bridge); - port = mlx5_esw_bridge_port_lookup(vport->vport, bridge); - if (!port) { - WARN(1, "Vport %u metadata not found on bridge", vport->vport); - return -EINVAL; - } - trace_mlx5_esw_bridge_vport_cleanup(port); mlx5_esw_bridge_port_vlans_flush(port, bridge); - mlx5_esw_bridge_port_erase(port, bridge); + mlx5_esw_bridge_port_erase(port, br_offloads); kvfree(port); mlx5_esw_bridge_put(br_offloads, bridge); - vport->bridge = NULL; return 0; } -int mlx5_esw_bridge_vport_link(int ifindex, struct mlx5_esw_bridge_offloads *br_offloads, - struct mlx5_vport *vport, struct netlink_ext_ack *extack) +static int mlx5_esw_bridge_vport_link_with_flags(int ifindex, u16 vport_num, u16 esw_owner_vhca_id, + u16 flags, + struct mlx5_esw_bridge_offloads *br_offloads, + struct netlink_ext_ack *extack) { struct mlx5_esw_bridge *bridge; int err; - WARN_ON(vport->bridge); - bridge = mlx5_esw_bridge_lookup(ifindex, br_offloads); if (IS_ERR(bridge)) { NL_SET_ERR_MSG_MOD(extack, "Error checking for existing bridge with same ifindex"); return PTR_ERR(bridge); } - err = mlx5_esw_bridge_vport_init(br_offloads, bridge, vport); - if (err) + err = mlx5_esw_bridge_vport_init(vport_num, esw_owner_vhca_id, flags, br_offloads, bridge); + if (err) { NL_SET_ERR_MSG_MOD(extack, "Error initializing port"); + goto err_vport; + } + return 0; + +err_vport: + mlx5_esw_bridge_put(br_offloads, bridge); return err; } -int mlx5_esw_bridge_vport_unlink(int ifindex, struct mlx5_esw_bridge_offloads *br_offloads, - struct mlx5_vport *vport, struct netlink_ext_ack *extack) +int mlx5_esw_bridge_vport_link(int ifindex, u16 vport_num, u16 esw_owner_vhca_id, + struct mlx5_esw_bridge_offloads *br_offloads, + struct netlink_ext_ack *extack) { - struct mlx5_esw_bridge *bridge = vport->bridge; + return mlx5_esw_bridge_vport_link_with_flags(ifindex, vport_num, esw_owner_vhca_id, 0, + br_offloads, extack); +} + +int mlx5_esw_bridge_vport_unlink(int ifindex, u16 vport_num, u16 esw_owner_vhca_id, + struct mlx5_esw_bridge_offloads *br_offloads, + struct netlink_ext_ack *extack) +{ + struct mlx5_esw_bridge_port *port; int err; - if (!bridge) { + port = mlx5_esw_bridge_port_lookup(vport_num, esw_owner_vhca_id, br_offloads); + if (!port) { NL_SET_ERR_MSG_MOD(extack, "Port is not attached to any bridge"); return -EINVAL; } - if (bridge->ifindex != ifindex) { + if (port->bridge->ifindex != ifindex) { NL_SET_ERR_MSG_MOD(extack, "Port is attached to another bridge"); return -EINVAL; } - err = mlx5_esw_bridge_vport_cleanup(br_offloads, vport); + err = mlx5_esw_bridge_vport_cleanup(br_offloads, port); if (err) NL_SET_ERR_MSG_MOD(extack, "Port cleanup failed"); return err; } -int mlx5_esw_bridge_port_vlan_add(u16 vid, u16 flags, struct mlx5_eswitch *esw, - struct mlx5_vport *vport, struct netlink_ext_ack *extack) +int mlx5_esw_bridge_vport_peer_link(int ifindex, u16 vport_num, u16 esw_owner_vhca_id, + struct mlx5_esw_bridge_offloads *br_offloads, + struct netlink_ext_ack *extack) +{ + if (!MLX5_CAP_ESW(br_offloads->esw->dev, merged_eswitch)) + return 0; + + return mlx5_esw_bridge_vport_link_with_flags(ifindex, vport_num, esw_owner_vhca_id, + MLX5_ESW_BRIDGE_PORT_FLAG_PEER, + br_offloads, extack); +} + +int mlx5_esw_bridge_vport_peer_unlink(int ifindex, u16 vport_num, u16 esw_owner_vhca_id, + struct mlx5_esw_bridge_offloads *br_offloads, + struct netlink_ext_ack *extack) +{ + return mlx5_esw_bridge_vport_unlink(ifindex, vport_num, esw_owner_vhca_id, br_offloads, + extack); +} + +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) { struct mlx5_esw_bridge_port *port; struct mlx5_esw_bridge_vlan *vlan; - port = mlx5_esw_bridge_port_lookup(vport->vport, vport->bridge); + port = mlx5_esw_bridge_port_lookup(vport_num, esw_owner_vhca_id, br_offloads); if (!port) return -EINVAL; @@ -1148,10 +1233,10 @@ int mlx5_esw_bridge_port_vlan_add(u16 vid, u16 flags, struct mlx5_eswitch *esw, if (vlan) { if (vlan->flags == flags) return 0; - mlx5_esw_bridge_vlan_cleanup(port, vlan, vport->bridge); + mlx5_esw_bridge_vlan_cleanup(port, vlan, port->bridge); } - vlan = mlx5_esw_bridge_vlan_create(vid, flags, port, esw); + vlan = mlx5_esw_bridge_vlan_create(vid, flags, port, br_offloads->esw); if (IS_ERR(vlan)) { NL_SET_ERR_MSG_MOD(extack, "Failed to create VLAN entry"); return PTR_ERR(vlan); @@ -1159,62 +1244,93 @@ int mlx5_esw_bridge_port_vlan_add(u16 vid, u16 flags, struct mlx5_eswitch *esw, return 0; } -void mlx5_esw_bridge_port_vlan_del(u16 vid, struct mlx5_eswitch *esw, struct mlx5_vport *vport) +void mlx5_esw_bridge_port_vlan_del(u16 vport_num, u16 esw_owner_vhca_id, u16 vid, + struct mlx5_esw_bridge_offloads *br_offloads) { struct mlx5_esw_bridge_port *port; struct mlx5_esw_bridge_vlan *vlan; - port = mlx5_esw_bridge_port_lookup(vport->vport, vport->bridge); + port = mlx5_esw_bridge_port_lookup(vport_num, esw_owner_vhca_id, br_offloads); if (!port) return; vlan = mlx5_esw_bridge_vlan_lookup(vid, port); if (!vlan) return; - mlx5_esw_bridge_vlan_cleanup(port, vlan, vport->bridge); + mlx5_esw_bridge_vlan_cleanup(port, vlan, port->bridge); } -void mlx5_esw_bridge_fdb_create(struct net_device *dev, struct mlx5_eswitch *esw, - struct mlx5_vport *vport, - struct switchdev_notifier_fdb_info *fdb_info) +void mlx5_esw_bridge_fdb_update_used(struct net_device *dev, u16 vport_num, u16 esw_owner_vhca_id, + struct mlx5_esw_bridge_offloads *br_offloads, + struct switchdev_notifier_fdb_info *fdb_info) { - struct mlx5_esw_bridge *bridge = vport->bridge; struct mlx5_esw_bridge_fdb_entry *entry; - u16 vport_num = vport->vport; + struct mlx5_esw_bridge_fdb_key key; + struct mlx5_esw_bridge_port *port; + struct mlx5_esw_bridge *bridge; - if (!bridge) { - esw_info(esw->dev, "Vport is not assigned to bridge (vport=%u)\n", vport_num); + port = mlx5_esw_bridge_port_lookup(vport_num, esw_owner_vhca_id, br_offloads); + if (!port || port->flags & MLX5_ESW_BRIDGE_PORT_FLAG_PEER) + return; + + bridge = port->bridge; + ether_addr_copy(key.addr, fdb_info->addr); + key.vid = fdb_info->vid; + entry = rhashtable_lookup_fast(&bridge->fdb_ht, &key, fdb_ht_params); + if (!entry) { + esw_debug(br_offloads->esw->dev, + "FDB entry with specified key not found (MAC=%pM,vid=%u,vport=%u)\n", + key.addr, key.vid, vport_num); return; } - entry = mlx5_esw_bridge_fdb_entry_init(dev, vport_num, fdb_info->addr, fdb_info->vid, - fdb_info->added_by_user, esw, bridge); + entry->lastuse = jiffies; +} + +void mlx5_esw_bridge_fdb_create(struct net_device *dev, u16 vport_num, u16 esw_owner_vhca_id, + struct mlx5_esw_bridge_offloads *br_offloads, + 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) + return; + + bridge = port->bridge; + entry = mlx5_esw_bridge_fdb_entry_init(dev, vport_num, esw_owner_vhca_id, fdb_info->addr, + fdb_info->vid, fdb_info->added_by_user, + port->flags & MLX5_ESW_BRIDGE_PORT_FLAG_PEER, + br_offloads->esw, bridge); if (IS_ERR(entry)) return; if (entry->flags & MLX5_ESW_BRIDGE_FLAG_ADDED_BY_USER) mlx5_esw_bridge_fdb_offload_notify(dev, entry->key.addr, entry->key.vid, SWITCHDEV_FDB_OFFLOADED); - else + else if (!(entry->flags & MLX5_ESW_BRIDGE_FLAG_PEER)) /* Take over dynamic entries to prevent kernel bridge from aging them out. */ mlx5_esw_bridge_fdb_offload_notify(dev, entry->key.addr, entry->key.vid, SWITCHDEV_FDB_ADD_TO_BRIDGE); } -void mlx5_esw_bridge_fdb_remove(struct net_device *dev, struct mlx5_eswitch *esw, - struct mlx5_vport *vport, +void mlx5_esw_bridge_fdb_remove(struct net_device *dev, u16 vport_num, u16 esw_owner_vhca_id, + struct mlx5_esw_bridge_offloads *br_offloads, struct switchdev_notifier_fdb_info *fdb_info) { - struct mlx5_esw_bridge *bridge = vport->bridge; + struct mlx5_eswitch *esw = br_offloads->esw; struct mlx5_esw_bridge_fdb_entry *entry; struct mlx5_esw_bridge_fdb_key key; - u16 vport_num = vport->vport; + struct mlx5_esw_bridge_port *port; + struct mlx5_esw_bridge *bridge; - if (!bridge) { - esw_warn(esw->dev, "Vport is not assigned to bridge (vport=%u)\n", vport_num); + port = mlx5_esw_bridge_port_lookup(vport_num, esw_owner_vhca_id, br_offloads); + if (!port) return; - } + bridge = port->bridge; ether_addr_copy(key.addr, fdb_info->addr); key.vid = fdb_info->vid; entry = rhashtable_lookup_fast(&bridge->fdb_ht, &key, fdb_ht_params); @@ -1225,9 +1341,7 @@ void mlx5_esw_bridge_fdb_remove(struct net_device *dev, struct mlx5_eswitch *esw return; } - if (!(entry->flags & MLX5_ESW_BRIDGE_FLAG_ADDED_BY_USER)) - mlx5_esw_bridge_fdb_offload_notify(dev, entry->key.addr, entry->key.vid, - SWITCHDEV_FDB_DEL_TO_BRIDGE); + mlx5_esw_bridge_fdb_del_notify(entry); mlx5_esw_bridge_fdb_entry_cleanup(entry, bridge); } @@ -1245,11 +1359,10 @@ void mlx5_esw_bridge_update(struct mlx5_esw_bridge_offloads *br_offloads) continue; if (time_after(lastuse, entry->lastuse)) { - mlx5_esw_bridge_fdb_entry_refresh(lastuse, entry); - } else if (time_is_before_jiffies(entry->lastuse + bridge->ageing_time)) { - mlx5_esw_bridge_fdb_offload_notify(entry->dev, entry->key.addr, - entry->key.vid, - SWITCHDEV_FDB_DEL_TO_BRIDGE); + mlx5_esw_bridge_fdb_entry_refresh(entry); + } else if (!(entry->flags & MLX5_ESW_BRIDGE_FLAG_PEER) && + time_is_before_jiffies(entry->lastuse + bridge->ageing_time)) { + mlx5_esw_bridge_fdb_del_notify(entry); mlx5_esw_bridge_fdb_entry_cleanup(entry, bridge); } } @@ -1258,13 +1371,11 @@ void mlx5_esw_bridge_update(struct mlx5_esw_bridge_offloads *br_offloads) static void mlx5_esw_bridge_flush(struct mlx5_esw_bridge_offloads *br_offloads) { - struct mlx5_eswitch *esw = br_offloads->esw; - struct mlx5_vport *vport; + struct mlx5_esw_bridge_port *port; unsigned long i; - mlx5_esw_for_each_vport(esw, i, vport) - if (vport->bridge) - mlx5_esw_bridge_vport_cleanup(br_offloads, vport); + xa_for_each(&br_offloads->ports, i, port) + mlx5_esw_bridge_vport_cleanup(br_offloads, port); WARN_ONCE(!list_empty(&br_offloads->bridges), "Cleaning up bridge offloads while still having bridges attached\n"); @@ -1279,6 +1390,7 @@ struct mlx5_esw_bridge_offloads *mlx5_esw_bridge_init(struct mlx5_eswitch *esw) return ERR_PTR(-ENOMEM); INIT_LIST_HEAD(&br_offloads->bridges); + xa_init(&br_offloads->ports); br_offloads->esw = esw; esw->br_offloads = br_offloads; @@ -1293,6 +1405,7 @@ void mlx5_esw_bridge_cleanup(struct mlx5_eswitch *esw) return; mlx5_esw_bridge_flush(br_offloads); + WARN_ON(!xa_empty(&br_offloads->ports)); esw->br_offloads = NULL; kvfree(br_offloads); |