]> git.baikalelectronics.ru Git - kernel.git/commitdiff
mlxsw: spectrum_switchdev: Implement mlxsw_sp_mc_mdb_entry_{init, fini}()
authorAmit Cohen <amcohen@nvidia.com>
Wed, 29 Jun 2022 09:40:04 +0000 (12:40 +0300)
committerDavid S. Miller <davem@davemloft.net>
Wed, 29 Jun 2022 12:35:46 +0000 (13:35 +0100)
The next patches will convert MDB code to use PGT APIs. The change will
move the responsibility of allocating MID indexes and writing PGT
configurations to hardware to PGT code. As part of this change, most of the
MDB code will be changed and improved.

As a preparation for the above mentioned change, implement
mlxsw_sp_mc_mdb_entry_{init, fini}(). Currently, there is a function
__mlxsw_sp_mc_alloc(), which does not only allocate MID. In addition,
there is no an equivalent function to free the MID. When
mlxsw_sp_port_remove_from_mid() removes the last port, it handles MID
removal. Instead, add init() and fini() functions, which use PGT APIs.

The differences between the existing and the new functions are as follows:
1. Today MDB code does not update SMID when port is added/removed while
   multicast is disabled. It maintains a bitmap of ports and once multicast
   is enabled, it writes the entry to hardware. Instead, using PGT APIs,
   the entry will be updated also when multicast is disabled, but the
   mapping between {MAC, FID}->{MID} (is configured using SFD) will be
   updated according to multicast state. It means that SMID will be updated
   all the time and disable/enable multicast will impact only SFD
   configuration.

2. Today the allocation of MID index is done as part of
   mlxsw_sp_mc_write_mdb_entry(). The fact that the entry will be
   written in hardware all the time, moves the allocation of the index to
   be as part of the MDB entry initialization. PGT API is used for the
   allocation.

3. Today the update of multicast router ports is done as part of
   mlxsw_sp_mc_write_mdb_entry(). Instead, add functions to add/remove
   all multicast router ports when entry is first added or removed. When
   new multicast router port will be added/removed, the dedicated API will
   be used to add/remove it from the existing entries.

4. A list of ports will be stored per MDB entry instead of the exiting
   bitmap. The list will contain the multicast router ports and maintain
   reference counter per port.

Add mlxsw_sp_mdb_entry_write() which is almost identical to
mlxsw_sp_port_mdb_op(). Use more clear name and align the MID index to
bridge model using PGT API. The existing function will be removed in the
next patches.

Note that PGT APIs configure the firmware using SMID register, like the
driver already does today for MDB entries, so PGT APIs can be used also
using legacy bridge model.

Signed-off-by: Amit Cohen <amcohen@nvidia.com>
Signed-off-by: Ido Schimmel <idosch@nvidia.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c

index d1b0eddad5041020c4dbc4567680f03bac2cce2e..bb2694ef6220b2cd84957263cf62bc11d36b7727 100644 (file)
@@ -1018,7 +1018,7 @@ mlxsw_sp_mdb_entry_port_lookup(struct mlxsw_sp_mdb_entry *mdb_entry,
        return NULL;
 }
 
-static __always_unused struct mlxsw_sp_mdb_entry_port *
+static struct mlxsw_sp_mdb_entry_port *
 mlxsw_sp_mdb_entry_port_get(struct mlxsw_sp *mlxsw_sp,
                            struct mlxsw_sp_mdb_entry *mdb_entry,
                            u16 local_port)
@@ -1060,7 +1060,7 @@ err_mdb_entry_port_alloc:
        return ERR_PTR(err);
 }
 
-static __always_unused void
+static void
 mlxsw_sp_mdb_entry_port_put(struct mlxsw_sp *mlxsw_sp,
                            struct mlxsw_sp_mdb_entry *mdb_entry,
                            u16 local_port, bool force)
@@ -1801,6 +1801,37 @@ mlxsw_sp_port_fdb_set(struct mlxsw_sp_port *mlxsw_sp_port,
                                                   vid, adding, false);
 }
 
+static int mlxsw_sp_mdb_entry_write(struct mlxsw_sp *mlxsw_sp,
+                                   const struct mlxsw_sp_mdb_entry *mdb_entry,
+                                   bool adding)
+{
+       char *sfd_pl;
+       u16 mid_idx;
+       u8 num_rec;
+       int err;
+
+       sfd_pl = kmalloc(MLXSW_REG_SFD_LEN, GFP_KERNEL);
+       if (!sfd_pl)
+               return -ENOMEM;
+
+       mid_idx = mlxsw_sp_pgt_index_to_mid(mlxsw_sp, mdb_entry->mid);
+       mlxsw_reg_sfd_pack(sfd_pl, mlxsw_sp_sfd_op(adding), 0);
+       mlxsw_reg_sfd_mc_pack(sfd_pl, 0, mdb_entry->key.addr,
+                             mdb_entry->key.fid, MLXSW_REG_SFD_REC_ACTION_NOP,
+                             mid_idx);
+       num_rec = mlxsw_reg_sfd_num_rec_get(sfd_pl);
+       err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl);
+       if (err)
+               goto out;
+
+       if (num_rec != mlxsw_reg_sfd_num_rec_get(sfd_pl))
+               err = -EBUSY;
+
+out:
+       kfree(sfd_pl);
+       return err;
+}
+
 static int mlxsw_sp_port_mdb_op(struct mlxsw_sp *mlxsw_sp, const char *addr,
                                u16 fid, u16 mid_idx, bool adding)
 {
@@ -2040,6 +2071,147 @@ static int mlxsw_sp_port_remove_from_mid(struct mlxsw_sp_port *mlxsw_sp_port,
        return err;
 }
 
+static int mlxsw_sp_mc_mdb_mrouters_add(struct mlxsw_sp *mlxsw_sp,
+                                       struct mlxsw_sp_ports_bitmap *ports_bm,
+                                       struct mlxsw_sp_mdb_entry *mdb_entry)
+{
+       struct mlxsw_sp_mdb_entry_port *mdb_entry_port;
+       unsigned int nbits = ports_bm->nbits;
+       int i;
+
+       for_each_set_bit(i, ports_bm->bitmap, nbits) {
+               mdb_entry_port = mlxsw_sp_mdb_entry_mrouter_port_get(mlxsw_sp,
+                                                                    mdb_entry,
+                                                                    i);
+               if (IS_ERR(mdb_entry_port)) {
+                       nbits = i;
+                       goto err_mrouter_port_get;
+               }
+       }
+
+       return 0;
+
+err_mrouter_port_get:
+       for_each_set_bit(i, ports_bm->bitmap, nbits)
+               mlxsw_sp_mdb_entry_mrouter_port_put(mlxsw_sp, mdb_entry, i);
+       return PTR_ERR(mdb_entry_port);
+}
+
+static void mlxsw_sp_mc_mdb_mrouters_del(struct mlxsw_sp *mlxsw_sp,
+                                        struct mlxsw_sp_ports_bitmap *ports_bm,
+                                        struct mlxsw_sp_mdb_entry *mdb_entry)
+{
+       int i;
+
+       for_each_set_bit(i, ports_bm->bitmap, ports_bm->nbits)
+               mlxsw_sp_mdb_entry_mrouter_port_put(mlxsw_sp, mdb_entry, i);
+}
+
+static int
+mlxsw_sp_mc_mdb_mrouters_set(struct mlxsw_sp *mlxsw_sp,
+                            struct mlxsw_sp_bridge_device *bridge_device,
+                            struct mlxsw_sp_mdb_entry *mdb_entry, bool add)
+{
+       struct mlxsw_sp_ports_bitmap ports_bm;
+       int err;
+
+       err = mlxsw_sp_port_bitmap_init(mlxsw_sp, &ports_bm);
+       if (err)
+               return err;
+
+       mlxsw_sp_mc_get_mrouters_bitmap(&ports_bm, bridge_device, mlxsw_sp);
+
+       if (add)
+               err = mlxsw_sp_mc_mdb_mrouters_add(mlxsw_sp, &ports_bm,
+                                                  mdb_entry);
+       else
+               mlxsw_sp_mc_mdb_mrouters_del(mlxsw_sp, &ports_bm, mdb_entry);
+
+       mlxsw_sp_port_bitmap_fini(&ports_bm);
+       return err;
+}
+
+static __always_unused struct mlxsw_sp_mdb_entry *
+mlxsw_sp_mc_mdb_entry_init(struct mlxsw_sp *mlxsw_sp,
+                          struct mlxsw_sp_bridge_device *bridge_device,
+                          const unsigned char *addr, u16 fid, u16 local_port)
+{
+       struct mlxsw_sp_mdb_entry_port *mdb_entry_port;
+       struct mlxsw_sp_mdb_entry *mdb_entry;
+       int err;
+
+       mdb_entry = kzalloc(sizeof(*mdb_entry), GFP_KERNEL);
+       if (!mdb_entry)
+               return ERR_PTR(-ENOMEM);
+
+       ether_addr_copy(mdb_entry->key.addr, addr);
+       mdb_entry->key.fid = fid;
+       err = mlxsw_sp_pgt_mid_alloc(mlxsw_sp, &mdb_entry->mid);
+       if (err)
+               goto err_pgt_mid_alloc;
+
+       INIT_LIST_HEAD(&mdb_entry->ports_list);
+
+       err = mlxsw_sp_mc_mdb_mrouters_set(mlxsw_sp, bridge_device, mdb_entry,
+                                          true);
+       if (err)
+               goto err_mdb_mrouters_set;
+
+       mdb_entry_port = mlxsw_sp_mdb_entry_port_get(mlxsw_sp, mdb_entry,
+                                                    local_port);
+       if (IS_ERR(mdb_entry_port)) {
+               err = PTR_ERR(mdb_entry_port);
+               goto err_mdb_entry_port_get;
+       }
+
+       if (bridge_device->multicast_enabled) {
+               err = mlxsw_sp_mdb_entry_write(mlxsw_sp, mdb_entry, true);
+               if (err)
+                       goto err_mdb_entry_write;
+       }
+
+       err = rhashtable_insert_fast(&bridge_device->mdb_ht,
+                                    &mdb_entry->ht_node,
+                                    mlxsw_sp_mdb_ht_params);
+       if (err)
+               goto err_rhashtable_insert;
+
+       list_add_tail(&mdb_entry->list, &bridge_device->mdb_list);
+
+       return mdb_entry;
+
+err_rhashtable_insert:
+       if (bridge_device->multicast_enabled)
+               mlxsw_sp_mdb_entry_write(mlxsw_sp, mdb_entry, false);
+err_mdb_entry_write:
+       mlxsw_sp_mdb_entry_port_put(mlxsw_sp, mdb_entry, local_port, false);
+err_mdb_entry_port_get:
+       mlxsw_sp_mc_mdb_mrouters_set(mlxsw_sp, bridge_device, mdb_entry, false);
+err_mdb_mrouters_set:
+       mlxsw_sp_pgt_mid_free(mlxsw_sp, mdb_entry->mid);
+err_pgt_mid_alloc:
+       kfree(mdb_entry);
+       return ERR_PTR(err);
+}
+
+static __always_unused void
+mlxsw_sp_mc_mdb_entry_fini(struct mlxsw_sp *mlxsw_sp,
+                          struct mlxsw_sp_mdb_entry *mdb_entry,
+                          struct mlxsw_sp_bridge_device *bridge_device,
+                          u16 local_port, bool force)
+{
+       list_del(&mdb_entry->list);
+       rhashtable_remove_fast(&bridge_device->mdb_ht, &mdb_entry->ht_node,
+                              mlxsw_sp_mdb_ht_params);
+       if (bridge_device->multicast_enabled)
+               mlxsw_sp_mdb_entry_write(mlxsw_sp, mdb_entry, false);
+       mlxsw_sp_mdb_entry_port_put(mlxsw_sp, mdb_entry, local_port, force);
+       mlxsw_sp_mc_mdb_mrouters_set(mlxsw_sp, bridge_device, mdb_entry, false);
+       WARN_ON(!list_empty(&mdb_entry->ports_list));
+       mlxsw_sp_pgt_mid_free(mlxsw_sp, mdb_entry->mid);
+       kfree(mdb_entry);
+}
+
 static int mlxsw_sp_port_mdb_add(struct mlxsw_sp_port *mlxsw_sp_port,
                                 const struct switchdev_obj_port_mdb *mdb)
 {