Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
0002 /* Copyright (c) 2017-2018 Mellanox Technologies. All rights reserved */
0003 
0004 #include <linux/kernel.h>
0005 #include <linux/list.h>
0006 #include <linux/netdevice.h>
0007 
0008 #include "spectrum_mr_tcam.h"
0009 #include "reg.h"
0010 #include "spectrum.h"
0011 #include "core_acl_flex_actions.h"
0012 #include "spectrum_mr.h"
0013 
0014 struct mlxsw_sp_mr_tcam {
0015     void *priv;
0016 };
0017 
0018 /* This struct maps to one RIGR2 register entry */
0019 struct mlxsw_sp_mr_erif_sublist {
0020     struct list_head list;
0021     u32 rigr2_kvdl_index;
0022     int num_erifs;
0023     u16 erif_indices[MLXSW_REG_RIGR2_MAX_ERIFS];
0024     bool synced;
0025 };
0026 
0027 struct mlxsw_sp_mr_tcam_erif_list {
0028     struct list_head erif_sublists;
0029     u32 kvdl_index;
0030 };
0031 
0032 static bool
0033 mlxsw_sp_mr_erif_sublist_full(struct mlxsw_sp *mlxsw_sp,
0034                   struct mlxsw_sp_mr_erif_sublist *erif_sublist)
0035 {
0036     int erif_list_entries = MLXSW_CORE_RES_GET(mlxsw_sp->core,
0037                            MC_ERIF_LIST_ENTRIES);
0038 
0039     return erif_sublist->num_erifs == erif_list_entries;
0040 }
0041 
0042 static void
0043 mlxsw_sp_mr_erif_list_init(struct mlxsw_sp_mr_tcam_erif_list *erif_list)
0044 {
0045     INIT_LIST_HEAD(&erif_list->erif_sublists);
0046 }
0047 
0048 static struct mlxsw_sp_mr_erif_sublist *
0049 mlxsw_sp_mr_erif_sublist_create(struct mlxsw_sp *mlxsw_sp,
0050                 struct mlxsw_sp_mr_tcam_erif_list *erif_list)
0051 {
0052     struct mlxsw_sp_mr_erif_sublist *erif_sublist;
0053     int err;
0054 
0055     erif_sublist = kzalloc(sizeof(*erif_sublist), GFP_KERNEL);
0056     if (!erif_sublist)
0057         return ERR_PTR(-ENOMEM);
0058     err = mlxsw_sp_kvdl_alloc(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_MCRIGR,
0059                   1, &erif_sublist->rigr2_kvdl_index);
0060     if (err) {
0061         kfree(erif_sublist);
0062         return ERR_PTR(err);
0063     }
0064 
0065     list_add_tail(&erif_sublist->list, &erif_list->erif_sublists);
0066     return erif_sublist;
0067 }
0068 
0069 static void
0070 mlxsw_sp_mr_erif_sublist_destroy(struct mlxsw_sp *mlxsw_sp,
0071                  struct mlxsw_sp_mr_erif_sublist *erif_sublist)
0072 {
0073     list_del(&erif_sublist->list);
0074     mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_MCRIGR,
0075                1, erif_sublist->rigr2_kvdl_index);
0076     kfree(erif_sublist);
0077 }
0078 
0079 static int
0080 mlxsw_sp_mr_erif_list_add(struct mlxsw_sp *mlxsw_sp,
0081               struct mlxsw_sp_mr_tcam_erif_list *erif_list,
0082               u16 erif_index)
0083 {
0084     struct mlxsw_sp_mr_erif_sublist *sublist;
0085 
0086     /* If either there is no erif_entry or the last one is full, allocate a
0087      * new one.
0088      */
0089     if (list_empty(&erif_list->erif_sublists)) {
0090         sublist = mlxsw_sp_mr_erif_sublist_create(mlxsw_sp, erif_list);
0091         if (IS_ERR(sublist))
0092             return PTR_ERR(sublist);
0093         erif_list->kvdl_index = sublist->rigr2_kvdl_index;
0094     } else {
0095         sublist = list_last_entry(&erif_list->erif_sublists,
0096                       struct mlxsw_sp_mr_erif_sublist,
0097                       list);
0098         sublist->synced = false;
0099         if (mlxsw_sp_mr_erif_sublist_full(mlxsw_sp, sublist)) {
0100             sublist = mlxsw_sp_mr_erif_sublist_create(mlxsw_sp,
0101                                   erif_list);
0102             if (IS_ERR(sublist))
0103                 return PTR_ERR(sublist);
0104         }
0105     }
0106 
0107     /* Add the eRIF to the last entry's last index */
0108     sublist->erif_indices[sublist->num_erifs++] = erif_index;
0109     return 0;
0110 }
0111 
0112 static void
0113 mlxsw_sp_mr_erif_list_flush(struct mlxsw_sp *mlxsw_sp,
0114                 struct mlxsw_sp_mr_tcam_erif_list *erif_list)
0115 {
0116     struct mlxsw_sp_mr_erif_sublist *erif_sublist, *tmp;
0117 
0118     list_for_each_entry_safe(erif_sublist, tmp, &erif_list->erif_sublists,
0119                  list)
0120         mlxsw_sp_mr_erif_sublist_destroy(mlxsw_sp, erif_sublist);
0121 }
0122 
0123 static int
0124 mlxsw_sp_mr_erif_list_commit(struct mlxsw_sp *mlxsw_sp,
0125                  struct mlxsw_sp_mr_tcam_erif_list *erif_list)
0126 {
0127     struct mlxsw_sp_mr_erif_sublist *curr_sublist;
0128     char rigr2_pl[MLXSW_REG_RIGR2_LEN];
0129     int err;
0130     int i;
0131 
0132     list_for_each_entry(curr_sublist, &erif_list->erif_sublists, list) {
0133         if (curr_sublist->synced)
0134             continue;
0135 
0136         /* If the sublist is not the last one, pack the next index */
0137         if (list_is_last(&curr_sublist->list,
0138                  &erif_list->erif_sublists)) {
0139             mlxsw_reg_rigr2_pack(rigr2_pl,
0140                          curr_sublist->rigr2_kvdl_index,
0141                          false, 0);
0142         } else {
0143             struct mlxsw_sp_mr_erif_sublist *next_sublist;
0144 
0145             next_sublist = list_next_entry(curr_sublist, list);
0146             mlxsw_reg_rigr2_pack(rigr2_pl,
0147                          curr_sublist->rigr2_kvdl_index,
0148                          true,
0149                          next_sublist->rigr2_kvdl_index);
0150         }
0151 
0152         /* Pack all the erifs */
0153         for (i = 0; i < curr_sublist->num_erifs; i++) {
0154             u16 erif_index = curr_sublist->erif_indices[i];
0155 
0156             mlxsw_reg_rigr2_erif_entry_pack(rigr2_pl, i, true,
0157                             erif_index);
0158         }
0159 
0160         /* Write the entry */
0161         err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rigr2),
0162                       rigr2_pl);
0163         if (err)
0164             /* No need of a rollback here because this
0165              * hardware entry should not be pointed yet.
0166              */
0167             return err;
0168         curr_sublist->synced = true;
0169     }
0170     return 0;
0171 }
0172 
0173 static void mlxsw_sp_mr_erif_list_move(struct mlxsw_sp_mr_tcam_erif_list *to,
0174                        struct mlxsw_sp_mr_tcam_erif_list *from)
0175 {
0176     list_splice(&from->erif_sublists, &to->erif_sublists);
0177     to->kvdl_index = from->kvdl_index;
0178 }
0179 
0180 struct mlxsw_sp_mr_tcam_route {
0181     struct mlxsw_sp_mr_tcam_erif_list erif_list;
0182     struct mlxsw_afa_block *afa_block;
0183     u32 counter_index;
0184     enum mlxsw_sp_mr_route_action action;
0185     struct mlxsw_sp_mr_route_key key;
0186     u16 irif_index;
0187     u16 min_mtu;
0188     void *priv;
0189 };
0190 
0191 static struct mlxsw_afa_block *
0192 mlxsw_sp_mr_tcam_afa_block_create(struct mlxsw_sp *mlxsw_sp,
0193                   enum mlxsw_sp_mr_route_action route_action,
0194                   u16 irif_index, u32 counter_index,
0195                   u16 min_mtu,
0196                   struct mlxsw_sp_mr_tcam_erif_list *erif_list)
0197 {
0198     struct mlxsw_afa_block *afa_block;
0199     int err;
0200 
0201     afa_block = mlxsw_afa_block_create(mlxsw_sp->afa);
0202     if (IS_ERR(afa_block))
0203         return afa_block;
0204 
0205     err = mlxsw_afa_block_append_allocated_counter(afa_block,
0206                                counter_index);
0207     if (err)
0208         goto err;
0209 
0210     switch (route_action) {
0211     case MLXSW_SP_MR_ROUTE_ACTION_TRAP:
0212         err = mlxsw_afa_block_append_trap(afa_block,
0213                           MLXSW_TRAP_ID_ACL1);
0214         if (err)
0215             goto err;
0216         break;
0217     case MLXSW_SP_MR_ROUTE_ACTION_TRAP_AND_FORWARD:
0218     case MLXSW_SP_MR_ROUTE_ACTION_FORWARD:
0219         /* If we are about to append a multicast router action, commit
0220          * the erif_list.
0221          */
0222         err = mlxsw_sp_mr_erif_list_commit(mlxsw_sp, erif_list);
0223         if (err)
0224             goto err;
0225 
0226         err = mlxsw_afa_block_append_mcrouter(afa_block, irif_index,
0227                               min_mtu, false,
0228                               erif_list->kvdl_index);
0229         if (err)
0230             goto err;
0231 
0232         if (route_action == MLXSW_SP_MR_ROUTE_ACTION_TRAP_AND_FORWARD) {
0233             err = mlxsw_afa_block_append_trap_and_forward(afa_block,
0234                                       MLXSW_TRAP_ID_ACL2);
0235             if (err)
0236                 goto err;
0237         }
0238         break;
0239     default:
0240         err = -EINVAL;
0241         goto err;
0242     }
0243 
0244     err = mlxsw_afa_block_commit(afa_block);
0245     if (err)
0246         goto err;
0247     return afa_block;
0248 err:
0249     mlxsw_afa_block_destroy(afa_block);
0250     return ERR_PTR(err);
0251 }
0252 
0253 static void
0254 mlxsw_sp_mr_tcam_afa_block_destroy(struct mlxsw_afa_block *afa_block)
0255 {
0256     mlxsw_afa_block_destroy(afa_block);
0257 }
0258 
0259 static int
0260 mlxsw_sp_mr_tcam_erif_populate(struct mlxsw_sp *mlxsw_sp,
0261                    struct mlxsw_sp_mr_tcam_erif_list *erif_list,
0262                    struct mlxsw_sp_mr_route_info *route_info)
0263 {
0264     int err;
0265     int i;
0266 
0267     for (i = 0; i < route_info->erif_num; i++) {
0268         u16 erif_index = route_info->erif_indices[i];
0269 
0270         err = mlxsw_sp_mr_erif_list_add(mlxsw_sp, erif_list,
0271                         erif_index);
0272         if (err)
0273             return err;
0274     }
0275     return 0;
0276 }
0277 
0278 static int
0279 mlxsw_sp_mr_tcam_route_create(struct mlxsw_sp *mlxsw_sp, void *priv,
0280                   void *route_priv,
0281                   struct mlxsw_sp_mr_route_params *route_params)
0282 {
0283     const struct mlxsw_sp_mr_tcam_ops *ops = mlxsw_sp->mr_tcam_ops;
0284     struct mlxsw_sp_mr_tcam_route *route = route_priv;
0285     struct mlxsw_sp_mr_tcam *mr_tcam = priv;
0286     int err;
0287 
0288     route->key = route_params->key;
0289     route->irif_index = route_params->value.irif_index;
0290     route->min_mtu = route_params->value.min_mtu;
0291     route->action = route_params->value.route_action;
0292 
0293     /* Create the egress RIFs list */
0294     mlxsw_sp_mr_erif_list_init(&route->erif_list);
0295     err = mlxsw_sp_mr_tcam_erif_populate(mlxsw_sp, &route->erif_list,
0296                          &route_params->value);
0297     if (err)
0298         goto err_erif_populate;
0299 
0300     /* Create the flow counter */
0301     err = mlxsw_sp_flow_counter_alloc(mlxsw_sp, &route->counter_index);
0302     if (err)
0303         goto err_counter_alloc;
0304 
0305     /* Create the flexible action block */
0306     route->afa_block = mlxsw_sp_mr_tcam_afa_block_create(mlxsw_sp,
0307                                  route->action,
0308                                  route->irif_index,
0309                                  route->counter_index,
0310                                  route->min_mtu,
0311                                  &route->erif_list);
0312     if (IS_ERR(route->afa_block)) {
0313         err = PTR_ERR(route->afa_block);
0314         goto err_afa_block_create;
0315     }
0316 
0317     route->priv = kzalloc(ops->route_priv_size, GFP_KERNEL);
0318     if (!route->priv) {
0319         err = -ENOMEM;
0320         goto err_route_priv_alloc;
0321     }
0322 
0323     /* Write the route to the TCAM */
0324     err = ops->route_create(mlxsw_sp, mr_tcam->priv, route->priv,
0325                 &route->key, route->afa_block,
0326                 route_params->prio);
0327     if (err)
0328         goto err_route_create;
0329     return 0;
0330 
0331 err_route_create:
0332     kfree(route->priv);
0333 err_route_priv_alloc:
0334     mlxsw_sp_mr_tcam_afa_block_destroy(route->afa_block);
0335 err_afa_block_create:
0336     mlxsw_sp_flow_counter_free(mlxsw_sp, route->counter_index);
0337 err_erif_populate:
0338 err_counter_alloc:
0339     mlxsw_sp_mr_erif_list_flush(mlxsw_sp, &route->erif_list);
0340     return err;
0341 }
0342 
0343 static void mlxsw_sp_mr_tcam_route_destroy(struct mlxsw_sp *mlxsw_sp,
0344                        void *priv, void *route_priv)
0345 {
0346     const struct mlxsw_sp_mr_tcam_ops *ops = mlxsw_sp->mr_tcam_ops;
0347     struct mlxsw_sp_mr_tcam_route *route = route_priv;
0348     struct mlxsw_sp_mr_tcam *mr_tcam = priv;
0349 
0350     ops->route_destroy(mlxsw_sp, mr_tcam->priv, route->priv, &route->key);
0351     kfree(route->priv);
0352     mlxsw_sp_mr_tcam_afa_block_destroy(route->afa_block);
0353     mlxsw_sp_flow_counter_free(mlxsw_sp, route->counter_index);
0354     mlxsw_sp_mr_erif_list_flush(mlxsw_sp, &route->erif_list);
0355 }
0356 
0357 static int mlxsw_sp_mr_tcam_route_stats(struct mlxsw_sp *mlxsw_sp,
0358                     void *route_priv, u64 *packets,
0359                     u64 *bytes)
0360 {
0361     struct mlxsw_sp_mr_tcam_route *route = route_priv;
0362 
0363     return mlxsw_sp_flow_counter_get(mlxsw_sp, route->counter_index,
0364                      packets, bytes);
0365 }
0366 
0367 static int
0368 mlxsw_sp_mr_tcam_route_action_update(struct mlxsw_sp *mlxsw_sp,
0369                      void *route_priv,
0370                      enum mlxsw_sp_mr_route_action route_action)
0371 {
0372     const struct mlxsw_sp_mr_tcam_ops *ops = mlxsw_sp->mr_tcam_ops;
0373     struct mlxsw_sp_mr_tcam_route *route = route_priv;
0374     struct mlxsw_afa_block *afa_block;
0375     int err;
0376 
0377     /* Create a new flexible action block */
0378     afa_block = mlxsw_sp_mr_tcam_afa_block_create(mlxsw_sp, route_action,
0379                               route->irif_index,
0380                               route->counter_index,
0381                               route->min_mtu,
0382                               &route->erif_list);
0383     if (IS_ERR(afa_block))
0384         return PTR_ERR(afa_block);
0385 
0386     /* Update the TCAM route entry */
0387     err = ops->route_update(mlxsw_sp, route->priv, &route->key, afa_block);
0388     if (err)
0389         goto err;
0390 
0391     /* Delete the old one */
0392     mlxsw_sp_mr_tcam_afa_block_destroy(route->afa_block);
0393     route->afa_block = afa_block;
0394     route->action = route_action;
0395     return 0;
0396 err:
0397     mlxsw_sp_mr_tcam_afa_block_destroy(afa_block);
0398     return err;
0399 }
0400 
0401 static int mlxsw_sp_mr_tcam_route_min_mtu_update(struct mlxsw_sp *mlxsw_sp,
0402                          void *route_priv, u16 min_mtu)
0403 {
0404     const struct mlxsw_sp_mr_tcam_ops *ops = mlxsw_sp->mr_tcam_ops;
0405     struct mlxsw_sp_mr_tcam_route *route = route_priv;
0406     struct mlxsw_afa_block *afa_block;
0407     int err;
0408 
0409     /* Create a new flexible action block */
0410     afa_block = mlxsw_sp_mr_tcam_afa_block_create(mlxsw_sp,
0411                               route->action,
0412                               route->irif_index,
0413                               route->counter_index,
0414                               min_mtu,
0415                               &route->erif_list);
0416     if (IS_ERR(afa_block))
0417         return PTR_ERR(afa_block);
0418 
0419     /* Update the TCAM route entry */
0420     err = ops->route_update(mlxsw_sp, route->priv, &route->key, afa_block);
0421     if (err)
0422         goto err;
0423 
0424     /* Delete the old one */
0425     mlxsw_sp_mr_tcam_afa_block_destroy(route->afa_block);
0426     route->afa_block = afa_block;
0427     route->min_mtu = min_mtu;
0428     return 0;
0429 err:
0430     mlxsw_sp_mr_tcam_afa_block_destroy(afa_block);
0431     return err;
0432 }
0433 
0434 static int mlxsw_sp_mr_tcam_route_irif_update(struct mlxsw_sp *mlxsw_sp,
0435                           void *route_priv, u16 irif_index)
0436 {
0437     struct mlxsw_sp_mr_tcam_route *route = route_priv;
0438 
0439     if (route->action != MLXSW_SP_MR_ROUTE_ACTION_TRAP)
0440         return -EINVAL;
0441     route->irif_index = irif_index;
0442     return 0;
0443 }
0444 
0445 static int mlxsw_sp_mr_tcam_route_erif_add(struct mlxsw_sp *mlxsw_sp,
0446                        void *route_priv, u16 erif_index)
0447 {
0448     struct mlxsw_sp_mr_tcam_route *route = route_priv;
0449     int err;
0450 
0451     err = mlxsw_sp_mr_erif_list_add(mlxsw_sp, &route->erif_list,
0452                     erif_index);
0453     if (err)
0454         return err;
0455 
0456     /* Commit the action only if the route action is not TRAP */
0457     if (route->action != MLXSW_SP_MR_ROUTE_ACTION_TRAP)
0458         return mlxsw_sp_mr_erif_list_commit(mlxsw_sp,
0459                             &route->erif_list);
0460     return 0;
0461 }
0462 
0463 static int mlxsw_sp_mr_tcam_route_erif_del(struct mlxsw_sp *mlxsw_sp,
0464                        void *route_priv, u16 erif_index)
0465 {
0466     const struct mlxsw_sp_mr_tcam_ops *ops = mlxsw_sp->mr_tcam_ops;
0467     struct mlxsw_sp_mr_tcam_route *route = route_priv;
0468     struct mlxsw_sp_mr_erif_sublist *erif_sublist;
0469     struct mlxsw_sp_mr_tcam_erif_list erif_list;
0470     struct mlxsw_afa_block *afa_block;
0471     int err;
0472     int i;
0473 
0474     /* Create a copy of the original erif_list without the deleted entry */
0475     mlxsw_sp_mr_erif_list_init(&erif_list);
0476     list_for_each_entry(erif_sublist, &route->erif_list.erif_sublists, list) {
0477         for (i = 0; i < erif_sublist->num_erifs; i++) {
0478             u16 curr_erif = erif_sublist->erif_indices[i];
0479 
0480             if (curr_erif == erif_index)
0481                 continue;
0482             err = mlxsw_sp_mr_erif_list_add(mlxsw_sp, &erif_list,
0483                             curr_erif);
0484             if (err)
0485                 goto err_erif_list_add;
0486         }
0487     }
0488 
0489     /* Create the flexible action block pointing to the new erif_list */
0490     afa_block = mlxsw_sp_mr_tcam_afa_block_create(mlxsw_sp, route->action,
0491                               route->irif_index,
0492                               route->counter_index,
0493                               route->min_mtu,
0494                               &erif_list);
0495     if (IS_ERR(afa_block)) {
0496         err = PTR_ERR(afa_block);
0497         goto err_afa_block_create;
0498     }
0499 
0500     /* Update the TCAM route entry */
0501     err = ops->route_update(mlxsw_sp, route->priv, &route->key, afa_block);
0502     if (err)
0503         goto err_route_write;
0504 
0505     mlxsw_sp_mr_tcam_afa_block_destroy(route->afa_block);
0506     mlxsw_sp_mr_erif_list_flush(mlxsw_sp, &route->erif_list);
0507     route->afa_block = afa_block;
0508     mlxsw_sp_mr_erif_list_move(&route->erif_list, &erif_list);
0509     return 0;
0510 
0511 err_route_write:
0512     mlxsw_sp_mr_tcam_afa_block_destroy(afa_block);
0513 err_afa_block_create:
0514 err_erif_list_add:
0515     mlxsw_sp_mr_erif_list_flush(mlxsw_sp, &erif_list);
0516     return err;
0517 }
0518 
0519 static int
0520 mlxsw_sp_mr_tcam_route_update(struct mlxsw_sp *mlxsw_sp, void *route_priv,
0521                   struct mlxsw_sp_mr_route_info *route_info)
0522 {
0523     const struct mlxsw_sp_mr_tcam_ops *ops = mlxsw_sp->mr_tcam_ops;
0524     struct mlxsw_sp_mr_tcam_route *route = route_priv;
0525     struct mlxsw_sp_mr_tcam_erif_list erif_list;
0526     struct mlxsw_afa_block *afa_block;
0527     int err;
0528 
0529     /* Create a new erif_list */
0530     mlxsw_sp_mr_erif_list_init(&erif_list);
0531     err = mlxsw_sp_mr_tcam_erif_populate(mlxsw_sp, &erif_list, route_info);
0532     if (err)
0533         goto err_erif_populate;
0534 
0535     /* Create the flexible action block pointing to the new erif_list */
0536     afa_block = mlxsw_sp_mr_tcam_afa_block_create(mlxsw_sp,
0537                               route_info->route_action,
0538                               route_info->irif_index,
0539                               route->counter_index,
0540                               route_info->min_mtu,
0541                               &erif_list);
0542     if (IS_ERR(afa_block)) {
0543         err = PTR_ERR(afa_block);
0544         goto err_afa_block_create;
0545     }
0546 
0547     /* Update the TCAM route entry */
0548     err = ops->route_update(mlxsw_sp, route->priv, &route->key, afa_block);
0549     if (err)
0550         goto err_route_write;
0551 
0552     mlxsw_sp_mr_tcam_afa_block_destroy(route->afa_block);
0553     mlxsw_sp_mr_erif_list_flush(mlxsw_sp, &route->erif_list);
0554     route->afa_block = afa_block;
0555     mlxsw_sp_mr_erif_list_move(&route->erif_list, &erif_list);
0556     route->action = route_info->route_action;
0557     route->irif_index = route_info->irif_index;
0558     route->min_mtu = route_info->min_mtu;
0559     return 0;
0560 
0561 err_route_write:
0562     mlxsw_sp_mr_tcam_afa_block_destroy(afa_block);
0563 err_afa_block_create:
0564 err_erif_populate:
0565     mlxsw_sp_mr_erif_list_flush(mlxsw_sp, &erif_list);
0566     return err;
0567 }
0568 
0569 static int mlxsw_sp_mr_tcam_init(struct mlxsw_sp *mlxsw_sp, void *priv)
0570 {
0571     const struct mlxsw_sp_mr_tcam_ops *ops = mlxsw_sp->mr_tcam_ops;
0572     struct mlxsw_sp_mr_tcam *mr_tcam = priv;
0573     int err;
0574 
0575     if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MC_ERIF_LIST_ENTRIES))
0576         return -EIO;
0577 
0578     mr_tcam->priv = kzalloc(ops->priv_size, GFP_KERNEL);
0579     if (!mr_tcam->priv)
0580         return -ENOMEM;
0581 
0582     err = ops->init(mlxsw_sp, mr_tcam->priv);
0583     if (err)
0584         goto err_init;
0585     return 0;
0586 
0587 err_init:
0588     kfree(mr_tcam->priv);
0589     return err;
0590 }
0591 
0592 static void mlxsw_sp_mr_tcam_fini(struct mlxsw_sp *mlxsw_sp, void *priv)
0593 {
0594     const struct mlxsw_sp_mr_tcam_ops *ops = mlxsw_sp->mr_tcam_ops;
0595     struct mlxsw_sp_mr_tcam *mr_tcam = priv;
0596 
0597     ops->fini(mr_tcam->priv);
0598     kfree(mr_tcam->priv);
0599 }
0600 
0601 const struct mlxsw_sp_mr_ops mlxsw_sp_mr_tcam_ops = {
0602     .priv_size = sizeof(struct mlxsw_sp_mr_tcam),
0603     .route_priv_size = sizeof(struct mlxsw_sp_mr_tcam_route),
0604     .init = mlxsw_sp_mr_tcam_init,
0605     .route_create = mlxsw_sp_mr_tcam_route_create,
0606     .route_update = mlxsw_sp_mr_tcam_route_update,
0607     .route_stats = mlxsw_sp_mr_tcam_route_stats,
0608     .route_action_update = mlxsw_sp_mr_tcam_route_action_update,
0609     .route_min_mtu_update = mlxsw_sp_mr_tcam_route_min_mtu_update,
0610     .route_irif_update = mlxsw_sp_mr_tcam_route_irif_update,
0611     .route_erif_add = mlxsw_sp_mr_tcam_route_erif_add,
0612     .route_erif_del = mlxsw_sp_mr_tcam_route_erif_del,
0613     .route_destroy = mlxsw_sp_mr_tcam_route_destroy,
0614     .fini = mlxsw_sp_mr_tcam_fini,
0615 };