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;
 
        return bridge;
 
+err_mdb_ht:
+       rhashtable_destroy(&bridge->fdb_ht);
 err_fdb_ht:
        mlx5_esw_bridge_egress_table_cleanup(bridge);
 err_egress_tbl:
        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);
 
        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);
 }
        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;
 
        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);
                                         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);
 }
        int err;
 
        xa_for_each(&port->vlans, i, vlan) {
-               mlx5_esw_bridge_vlan_flush(vlan, bridge);
+               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) {
                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
        }
 
        mlx5_esw_bridge_fdb_flush(bridge);
+       mlx5_esw_bridge_mdb_flush(bridge);
        bridge->vlan_proto = proto;
        mlx5_esw_bridge_vlans_recreate(bridge);
 
        }
 }
 
+int mlx5_esw_bridge_port_mdb_add(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(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(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(port, addr, vid);
+}
+
 static void mlx5_esw_bridge_flush(struct mlx5_esw_bridge_offloads *br_offloads)
 {
        struct mlx5_esw_bridge_port *port;
 
 #include "eswitch.h"
 #include "bridge_priv.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 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);
+       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 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;
+       }
+
+       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)
 {
 
 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);