linux/drivers/net/ethernet/mellanox/mlxsw/spectrum_mr_tcam.c
<<
>>
Prefs
   1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
   2/* Copyright (c) 2017-2018 Mellanox Technologies. All rights reserved */
   3
   4#include <linux/kernel.h>
   5#include <linux/list.h>
   6#include <linux/netdevice.h>
   7
   8#include "spectrum_mr_tcam.h"
   9#include "reg.h"
  10#include "spectrum.h"
  11#include "core_acl_flex_actions.h"
  12#include "spectrum_mr.h"
  13
  14struct mlxsw_sp_mr_tcam {
  15        void *priv;
  16};
  17
  18/* This struct maps to one RIGR2 register entry */
  19struct mlxsw_sp_mr_erif_sublist {
  20        struct list_head list;
  21        u32 rigr2_kvdl_index;
  22        int num_erifs;
  23        u16 erif_indices[MLXSW_REG_RIGR2_MAX_ERIFS];
  24        bool synced;
  25};
  26
  27struct mlxsw_sp_mr_tcam_erif_list {
  28        struct list_head erif_sublists;
  29        u32 kvdl_index;
  30};
  31
  32static bool
  33mlxsw_sp_mr_erif_sublist_full(struct mlxsw_sp *mlxsw_sp,
  34                              struct mlxsw_sp_mr_erif_sublist *erif_sublist)
  35{
  36        int erif_list_entries = MLXSW_CORE_RES_GET(mlxsw_sp->core,
  37                                                   MC_ERIF_LIST_ENTRIES);
  38
  39        return erif_sublist->num_erifs == erif_list_entries;
  40}
  41
  42static void
  43mlxsw_sp_mr_erif_list_init(struct mlxsw_sp_mr_tcam_erif_list *erif_list)
  44{
  45        INIT_LIST_HEAD(&erif_list->erif_sublists);
  46}
  47
  48static struct mlxsw_sp_mr_erif_sublist *
  49mlxsw_sp_mr_erif_sublist_create(struct mlxsw_sp *mlxsw_sp,
  50                                struct mlxsw_sp_mr_tcam_erif_list *erif_list)
  51{
  52        struct mlxsw_sp_mr_erif_sublist *erif_sublist;
  53        int err;
  54
  55        erif_sublist = kzalloc(sizeof(*erif_sublist), GFP_KERNEL);
  56        if (!erif_sublist)
  57                return ERR_PTR(-ENOMEM);
  58        err = mlxsw_sp_kvdl_alloc(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_MCRIGR,
  59                                  1, &erif_sublist->rigr2_kvdl_index);
  60        if (err) {
  61                kfree(erif_sublist);
  62                return ERR_PTR(err);
  63        }
  64
  65        list_add_tail(&erif_sublist->list, &erif_list->erif_sublists);
  66        return erif_sublist;
  67}
  68
  69static void
  70mlxsw_sp_mr_erif_sublist_destroy(struct mlxsw_sp *mlxsw_sp,
  71                                 struct mlxsw_sp_mr_erif_sublist *erif_sublist)
  72{
  73        list_del(&erif_sublist->list);
  74        mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_MCRIGR,
  75                           1, erif_sublist->rigr2_kvdl_index);
  76        kfree(erif_sublist);
  77}
  78
  79static int
  80mlxsw_sp_mr_erif_list_add(struct mlxsw_sp *mlxsw_sp,
  81                          struct mlxsw_sp_mr_tcam_erif_list *erif_list,
  82                          u16 erif_index)
  83{
  84        struct mlxsw_sp_mr_erif_sublist *sublist;
  85
  86        /* If either there is no erif_entry or the last one is full, allocate a
  87         * new one.
  88         */
  89        if (list_empty(&erif_list->erif_sublists)) {
  90                sublist = mlxsw_sp_mr_erif_sublist_create(mlxsw_sp, erif_list);
  91                if (IS_ERR(sublist))
  92                        return PTR_ERR(sublist);
  93                erif_list->kvdl_index = sublist->rigr2_kvdl_index;
  94        } else {
  95                sublist = list_last_entry(&erif_list->erif_sublists,
  96                                          struct mlxsw_sp_mr_erif_sublist,
  97                                          list);
  98                sublist->synced = false;
  99                if (mlxsw_sp_mr_erif_sublist_full(mlxsw_sp, sublist)) {
 100                        sublist = mlxsw_sp_mr_erif_sublist_create(mlxsw_sp,
 101                                                                  erif_list);
 102                        if (IS_ERR(sublist))
 103                                return PTR_ERR(sublist);
 104                }
 105        }
 106
 107        /* Add the eRIF to the last entry's last index */
 108        sublist->erif_indices[sublist->num_erifs++] = erif_index;
 109        return 0;
 110}
 111
 112static void
 113mlxsw_sp_mr_erif_list_flush(struct mlxsw_sp *mlxsw_sp,
 114                            struct mlxsw_sp_mr_tcam_erif_list *erif_list)
 115{
 116        struct mlxsw_sp_mr_erif_sublist *erif_sublist, *tmp;
 117
 118        list_for_each_entry_safe(erif_sublist, tmp, &erif_list->erif_sublists,
 119                                 list)
 120                mlxsw_sp_mr_erif_sublist_destroy(mlxsw_sp, erif_sublist);
 121}
 122
 123static int
 124mlxsw_sp_mr_erif_list_commit(struct mlxsw_sp *mlxsw_sp,
 125                             struct mlxsw_sp_mr_tcam_erif_list *erif_list)
 126{
 127        struct mlxsw_sp_mr_erif_sublist *curr_sublist;
 128        char rigr2_pl[MLXSW_REG_RIGR2_LEN];
 129        int err;
 130        int i;
 131
 132        list_for_each_entry(curr_sublist, &erif_list->erif_sublists, list) {
 133                if (curr_sublist->synced)
 134                        continue;
 135
 136                /* If the sublist is not the last one, pack the next index */
 137                if (list_is_last(&curr_sublist->list,
 138                                 &erif_list->erif_sublists)) {
 139                        mlxsw_reg_rigr2_pack(rigr2_pl,
 140                                             curr_sublist->rigr2_kvdl_index,
 141                                             false, 0);
 142                } else {
 143                        struct mlxsw_sp_mr_erif_sublist *next_sublist;
 144
 145                        next_sublist = list_next_entry(curr_sublist, list);
 146                        mlxsw_reg_rigr2_pack(rigr2_pl,
 147                                             curr_sublist->rigr2_kvdl_index,
 148                                             true,
 149                                             next_sublist->rigr2_kvdl_index);
 150                }
 151
 152                /* Pack all the erifs */
 153                for (i = 0; i < curr_sublist->num_erifs; i++) {
 154                        u16 erif_index = curr_sublist->erif_indices[i];
 155
 156                        mlxsw_reg_rigr2_erif_entry_pack(rigr2_pl, i, true,
 157                                                        erif_index);
 158                }
 159
 160                /* Write the entry */
 161                err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rigr2),
 162                                      rigr2_pl);
 163                if (err)
 164                        /* No need of a rollback here because this
 165                         * hardware entry should not be pointed yet.
 166                         */
 167                        return err;
 168                curr_sublist->synced = true;
 169        }
 170        return 0;
 171}
 172
 173static void mlxsw_sp_mr_erif_list_move(struct mlxsw_sp_mr_tcam_erif_list *to,
 174                                       struct mlxsw_sp_mr_tcam_erif_list *from)
 175{
 176        list_splice(&from->erif_sublists, &to->erif_sublists);
 177        to->kvdl_index = from->kvdl_index;
 178}
 179
 180struct mlxsw_sp_mr_tcam_route {
 181        struct mlxsw_sp_mr_tcam_erif_list erif_list;
 182        struct mlxsw_afa_block *afa_block;
 183        u32 counter_index;
 184        enum mlxsw_sp_mr_route_action action;
 185        struct mlxsw_sp_mr_route_key key;
 186        u16 irif_index;
 187        u16 min_mtu;
 188        void *priv;
 189};
 190
 191static struct mlxsw_afa_block *
 192mlxsw_sp_mr_tcam_afa_block_create(struct mlxsw_sp *mlxsw_sp,
 193                                  enum mlxsw_sp_mr_route_action route_action,
 194                                  u16 irif_index, u32 counter_index,
 195                                  u16 min_mtu,
 196                                  struct mlxsw_sp_mr_tcam_erif_list *erif_list)
 197{
 198        struct mlxsw_afa_block *afa_block;
 199        int err;
 200
 201        afa_block = mlxsw_afa_block_create(mlxsw_sp->afa);
 202        if (IS_ERR(afa_block))
 203                return afa_block;
 204
 205        err = mlxsw_afa_block_append_allocated_counter(afa_block,
 206                                                       counter_index);
 207        if (err)
 208                goto err;
 209
 210        switch (route_action) {
 211        case MLXSW_SP_MR_ROUTE_ACTION_TRAP:
 212                err = mlxsw_afa_block_append_trap(afa_block,
 213                                                  MLXSW_TRAP_ID_ACL1);
 214                if (err)
 215                        goto err;
 216                break;
 217        case MLXSW_SP_MR_ROUTE_ACTION_TRAP_AND_FORWARD:
 218        case MLXSW_SP_MR_ROUTE_ACTION_FORWARD:
 219                /* If we are about to append a multicast router action, commit
 220                 * the erif_list.
 221                 */
 222                err = mlxsw_sp_mr_erif_list_commit(mlxsw_sp, erif_list);
 223                if (err)
 224                        goto err;
 225
 226                err = mlxsw_afa_block_append_mcrouter(afa_block, irif_index,
 227                                                      min_mtu, false,
 228                                                      erif_list->kvdl_index);
 229                if (err)
 230                        goto err;
 231
 232                if (route_action == MLXSW_SP_MR_ROUTE_ACTION_TRAP_AND_FORWARD) {
 233                        err = mlxsw_afa_block_append_trap_and_forward(afa_block,
 234                                                                      MLXSW_TRAP_ID_ACL2);
 235                        if (err)
 236                                goto err;
 237                }
 238                break;
 239        default:
 240                err = -EINVAL;
 241                goto err;
 242        }
 243
 244        err = mlxsw_afa_block_commit(afa_block);
 245        if (err)
 246                goto err;
 247        return afa_block;
 248err:
 249        mlxsw_afa_block_destroy(afa_block);
 250        return ERR_PTR(err);
 251}
 252
 253static void
 254mlxsw_sp_mr_tcam_afa_block_destroy(struct mlxsw_afa_block *afa_block)
 255{
 256        mlxsw_afa_block_destroy(afa_block);
 257}
 258
 259static int
 260mlxsw_sp_mr_tcam_erif_populate(struct mlxsw_sp *mlxsw_sp,
 261                               struct mlxsw_sp_mr_tcam_erif_list *erif_list,
 262                               struct mlxsw_sp_mr_route_info *route_info)
 263{
 264        int err;
 265        int i;
 266
 267        for (i = 0; i < route_info->erif_num; i++) {
 268                u16 erif_index = route_info->erif_indices[i];
 269
 270                err = mlxsw_sp_mr_erif_list_add(mlxsw_sp, erif_list,
 271                                                erif_index);
 272                if (err)
 273                        return err;
 274        }
 275        return 0;
 276}
 277
 278static int
 279mlxsw_sp_mr_tcam_route_create(struct mlxsw_sp *mlxsw_sp, void *priv,
 280                              void *route_priv,
 281                              struct mlxsw_sp_mr_route_params *route_params)
 282{
 283        const struct mlxsw_sp_mr_tcam_ops *ops = mlxsw_sp->mr_tcam_ops;
 284        struct mlxsw_sp_mr_tcam_route *route = route_priv;
 285        struct mlxsw_sp_mr_tcam *mr_tcam = priv;
 286        int err;
 287
 288        route->key = route_params->key;
 289        route->irif_index = route_params->value.irif_index;
 290        route->min_mtu = route_params->value.min_mtu;
 291        route->action = route_params->value.route_action;
 292
 293        /* Create the egress RIFs list */
 294        mlxsw_sp_mr_erif_list_init(&route->erif_list);
 295        err = mlxsw_sp_mr_tcam_erif_populate(mlxsw_sp, &route->erif_list,
 296                                             &route_params->value);
 297        if (err)
 298                goto err_erif_populate;
 299
 300        /* Create the flow counter */
 301        err = mlxsw_sp_flow_counter_alloc(mlxsw_sp, &route->counter_index);
 302        if (err)
 303                goto err_counter_alloc;
 304
 305        /* Create the flexible action block */
 306        route->afa_block = mlxsw_sp_mr_tcam_afa_block_create(mlxsw_sp,
 307                                                             route->action,
 308                                                             route->irif_index,
 309                                                             route->counter_index,
 310                                                             route->min_mtu,
 311                                                             &route->erif_list);
 312        if (IS_ERR(route->afa_block)) {
 313                err = PTR_ERR(route->afa_block);
 314                goto err_afa_block_create;
 315        }
 316
 317        route->priv = kzalloc(ops->route_priv_size, GFP_KERNEL);
 318        if (!route->priv) {
 319                err = -ENOMEM;
 320                goto err_route_priv_alloc;
 321        }
 322
 323        /* Write the route to the TCAM */
 324        err = ops->route_create(mlxsw_sp, mr_tcam->priv, route->priv,
 325                                &route->key, route->afa_block,
 326                                route_params->prio);
 327        if (err)
 328                goto err_route_create;
 329        return 0;
 330
 331err_route_create:
 332        kfree(route->priv);
 333err_route_priv_alloc:
 334        mlxsw_sp_mr_tcam_afa_block_destroy(route->afa_block);
 335err_afa_block_create:
 336        mlxsw_sp_flow_counter_free(mlxsw_sp, route->counter_index);
 337err_erif_populate:
 338err_counter_alloc:
 339        mlxsw_sp_mr_erif_list_flush(mlxsw_sp, &route->erif_list);
 340        return err;
 341}
 342
 343static void mlxsw_sp_mr_tcam_route_destroy(struct mlxsw_sp *mlxsw_sp,
 344                                           void *priv, void *route_priv)
 345{
 346        const struct mlxsw_sp_mr_tcam_ops *ops = mlxsw_sp->mr_tcam_ops;
 347        struct mlxsw_sp_mr_tcam_route *route = route_priv;
 348        struct mlxsw_sp_mr_tcam *mr_tcam = priv;
 349
 350        ops->route_destroy(mlxsw_sp, mr_tcam->priv, route->priv, &route->key);
 351        kfree(route->priv);
 352        mlxsw_sp_mr_tcam_afa_block_destroy(route->afa_block);
 353        mlxsw_sp_flow_counter_free(mlxsw_sp, route->counter_index);
 354        mlxsw_sp_mr_erif_list_flush(mlxsw_sp, &route->erif_list);
 355}
 356
 357static int mlxsw_sp_mr_tcam_route_stats(struct mlxsw_sp *mlxsw_sp,
 358                                        void *route_priv, u64 *packets,
 359                                        u64 *bytes)
 360{
 361        struct mlxsw_sp_mr_tcam_route *route = route_priv;
 362
 363        return mlxsw_sp_flow_counter_get(mlxsw_sp, route->counter_index,
 364                                         packets, bytes);
 365}
 366
 367static int
 368mlxsw_sp_mr_tcam_route_action_update(struct mlxsw_sp *mlxsw_sp,
 369                                     void *route_priv,
 370                                     enum mlxsw_sp_mr_route_action route_action)
 371{
 372        const struct mlxsw_sp_mr_tcam_ops *ops = mlxsw_sp->mr_tcam_ops;
 373        struct mlxsw_sp_mr_tcam_route *route = route_priv;
 374        struct mlxsw_afa_block *afa_block;
 375        int err;
 376
 377        /* Create a new flexible action block */
 378        afa_block = mlxsw_sp_mr_tcam_afa_block_create(mlxsw_sp, route_action,
 379                                                      route->irif_index,
 380                                                      route->counter_index,
 381                                                      route->min_mtu,
 382                                                      &route->erif_list);
 383        if (IS_ERR(afa_block))
 384                return PTR_ERR(afa_block);
 385
 386        /* Update the TCAM route entry */
 387        err = ops->route_update(mlxsw_sp, route->priv, &route->key, afa_block);
 388        if (err)
 389                goto err;
 390
 391        /* Delete the old one */
 392        mlxsw_sp_mr_tcam_afa_block_destroy(route->afa_block);
 393        route->afa_block = afa_block;
 394        route->action = route_action;
 395        return 0;
 396err:
 397        mlxsw_sp_mr_tcam_afa_block_destroy(afa_block);
 398        return err;
 399}
 400
 401static int mlxsw_sp_mr_tcam_route_min_mtu_update(struct mlxsw_sp *mlxsw_sp,
 402                                                 void *route_priv, u16 min_mtu)
 403{
 404        const struct mlxsw_sp_mr_tcam_ops *ops = mlxsw_sp->mr_tcam_ops;
 405        struct mlxsw_sp_mr_tcam_route *route = route_priv;
 406        struct mlxsw_afa_block *afa_block;
 407        int err;
 408
 409        /* Create a new flexible action block */
 410        afa_block = mlxsw_sp_mr_tcam_afa_block_create(mlxsw_sp,
 411                                                      route->action,
 412                                                      route->irif_index,
 413                                                      route->counter_index,
 414                                                      min_mtu,
 415                                                      &route->erif_list);
 416        if (IS_ERR(afa_block))
 417                return PTR_ERR(afa_block);
 418
 419        /* Update the TCAM route entry */
 420        err = ops->route_update(mlxsw_sp, route->priv, &route->key, afa_block);
 421        if (err)
 422                goto err;
 423
 424        /* Delete the old one */
 425        mlxsw_sp_mr_tcam_afa_block_destroy(route->afa_block);
 426        route->afa_block = afa_block;
 427        route->min_mtu = min_mtu;
 428        return 0;
 429err:
 430        mlxsw_sp_mr_tcam_afa_block_destroy(afa_block);
 431        return err;
 432}
 433
 434static int mlxsw_sp_mr_tcam_route_irif_update(struct mlxsw_sp *mlxsw_sp,
 435                                              void *route_priv, u16 irif_index)
 436{
 437        struct mlxsw_sp_mr_tcam_route *route = route_priv;
 438
 439        if (route->action != MLXSW_SP_MR_ROUTE_ACTION_TRAP)
 440                return -EINVAL;
 441        route->irif_index = irif_index;
 442        return 0;
 443}
 444
 445static int mlxsw_sp_mr_tcam_route_erif_add(struct mlxsw_sp *mlxsw_sp,
 446                                           void *route_priv, u16 erif_index)
 447{
 448        struct mlxsw_sp_mr_tcam_route *route = route_priv;
 449        int err;
 450
 451        err = mlxsw_sp_mr_erif_list_add(mlxsw_sp, &route->erif_list,
 452                                        erif_index);
 453        if (err)
 454                return err;
 455
 456        /* Commit the action only if the route action is not TRAP */
 457        if (route->action != MLXSW_SP_MR_ROUTE_ACTION_TRAP)
 458                return mlxsw_sp_mr_erif_list_commit(mlxsw_sp,
 459                                                    &route->erif_list);
 460        return 0;
 461}
 462
 463static int mlxsw_sp_mr_tcam_route_erif_del(struct mlxsw_sp *mlxsw_sp,
 464                                           void *route_priv, u16 erif_index)
 465{
 466        const struct mlxsw_sp_mr_tcam_ops *ops = mlxsw_sp->mr_tcam_ops;
 467        struct mlxsw_sp_mr_tcam_route *route = route_priv;
 468        struct mlxsw_sp_mr_erif_sublist *erif_sublist;
 469        struct mlxsw_sp_mr_tcam_erif_list erif_list;
 470        struct mlxsw_afa_block *afa_block;
 471        int err;
 472        int i;
 473
 474        /* Create a copy of the original erif_list without the deleted entry */
 475        mlxsw_sp_mr_erif_list_init(&erif_list);
 476        list_for_each_entry(erif_sublist, &route->erif_list.erif_sublists, list) {
 477                for (i = 0; i < erif_sublist->num_erifs; i++) {
 478                        u16 curr_erif = erif_sublist->erif_indices[i];
 479
 480                        if (curr_erif == erif_index)
 481                                continue;
 482                        err = mlxsw_sp_mr_erif_list_add(mlxsw_sp, &erif_list,
 483                                                        curr_erif);
 484                        if (err)
 485                                goto err_erif_list_add;
 486                }
 487        }
 488
 489        /* Create the flexible action block pointing to the new erif_list */
 490        afa_block = mlxsw_sp_mr_tcam_afa_block_create(mlxsw_sp, route->action,
 491                                                      route->irif_index,
 492                                                      route->counter_index,
 493                                                      route->min_mtu,
 494                                                      &erif_list);
 495        if (IS_ERR(afa_block)) {
 496                err = PTR_ERR(afa_block);
 497                goto err_afa_block_create;
 498        }
 499
 500        /* Update the TCAM route entry */
 501        err = ops->route_update(mlxsw_sp, route->priv, &route->key, afa_block);
 502        if (err)
 503                goto err_route_write;
 504
 505        mlxsw_sp_mr_tcam_afa_block_destroy(route->afa_block);
 506        mlxsw_sp_mr_erif_list_flush(mlxsw_sp, &route->erif_list);
 507        route->afa_block = afa_block;
 508        mlxsw_sp_mr_erif_list_move(&route->erif_list, &erif_list);
 509        return 0;
 510
 511err_route_write:
 512        mlxsw_sp_mr_tcam_afa_block_destroy(afa_block);
 513err_afa_block_create:
 514err_erif_list_add:
 515        mlxsw_sp_mr_erif_list_flush(mlxsw_sp, &erif_list);
 516        return err;
 517}
 518
 519static int
 520mlxsw_sp_mr_tcam_route_update(struct mlxsw_sp *mlxsw_sp, void *route_priv,
 521                              struct mlxsw_sp_mr_route_info *route_info)
 522{
 523        const struct mlxsw_sp_mr_tcam_ops *ops = mlxsw_sp->mr_tcam_ops;
 524        struct mlxsw_sp_mr_tcam_route *route = route_priv;
 525        struct mlxsw_sp_mr_tcam_erif_list erif_list;
 526        struct mlxsw_afa_block *afa_block;
 527        int err;
 528
 529        /* Create a new erif_list */
 530        mlxsw_sp_mr_erif_list_init(&erif_list);
 531        err = mlxsw_sp_mr_tcam_erif_populate(mlxsw_sp, &erif_list, route_info);
 532        if (err)
 533                goto err_erif_populate;
 534
 535        /* Create the flexible action block pointing to the new erif_list */
 536        afa_block = mlxsw_sp_mr_tcam_afa_block_create(mlxsw_sp,
 537                                                      route_info->route_action,
 538                                                      route_info->irif_index,
 539                                                      route->counter_index,
 540                                                      route_info->min_mtu,
 541                                                      &erif_list);
 542        if (IS_ERR(afa_block)) {
 543                err = PTR_ERR(afa_block);
 544                goto err_afa_block_create;
 545        }
 546
 547        /* Update the TCAM route entry */
 548        err = ops->route_update(mlxsw_sp, route->priv, &route->key, afa_block);
 549        if (err)
 550                goto err_route_write;
 551
 552        mlxsw_sp_mr_tcam_afa_block_destroy(route->afa_block);
 553        mlxsw_sp_mr_erif_list_flush(mlxsw_sp, &route->erif_list);
 554        route->afa_block = afa_block;
 555        mlxsw_sp_mr_erif_list_move(&route->erif_list, &erif_list);
 556        route->action = route_info->route_action;
 557        route->irif_index = route_info->irif_index;
 558        route->min_mtu = route_info->min_mtu;
 559        return 0;
 560
 561err_route_write:
 562        mlxsw_sp_mr_tcam_afa_block_destroy(afa_block);
 563err_afa_block_create:
 564err_erif_populate:
 565        mlxsw_sp_mr_erif_list_flush(mlxsw_sp, &erif_list);
 566        return err;
 567}
 568
 569static int mlxsw_sp_mr_tcam_init(struct mlxsw_sp *mlxsw_sp, void *priv)
 570{
 571        const struct mlxsw_sp_mr_tcam_ops *ops = mlxsw_sp->mr_tcam_ops;
 572        struct mlxsw_sp_mr_tcam *mr_tcam = priv;
 573        int err;
 574
 575        if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MC_ERIF_LIST_ENTRIES))
 576                return -EIO;
 577
 578        mr_tcam->priv = kzalloc(ops->priv_size, GFP_KERNEL);
 579        if (!mr_tcam->priv)
 580                return -ENOMEM;
 581
 582        err = ops->init(mlxsw_sp, mr_tcam->priv);
 583        if (err)
 584                goto err_init;
 585        return 0;
 586
 587err_init:
 588        kfree(mr_tcam->priv);
 589        return err;
 590}
 591
 592static void mlxsw_sp_mr_tcam_fini(struct mlxsw_sp *mlxsw_sp, void *priv)
 593{
 594        const struct mlxsw_sp_mr_tcam_ops *ops = mlxsw_sp->mr_tcam_ops;
 595        struct mlxsw_sp_mr_tcam *mr_tcam = priv;
 596
 597        ops->fini(mr_tcam->priv);
 598        kfree(mr_tcam->priv);
 599}
 600
 601const struct mlxsw_sp_mr_ops mlxsw_sp_mr_tcam_ops = {
 602        .priv_size = sizeof(struct mlxsw_sp_mr_tcam),
 603        .route_priv_size = sizeof(struct mlxsw_sp_mr_tcam_route),
 604        .init = mlxsw_sp_mr_tcam_init,
 605        .route_create = mlxsw_sp_mr_tcam_route_create,
 606        .route_update = mlxsw_sp_mr_tcam_route_update,
 607        .route_stats = mlxsw_sp_mr_tcam_route_stats,
 608        .route_action_update = mlxsw_sp_mr_tcam_route_action_update,
 609        .route_min_mtu_update = mlxsw_sp_mr_tcam_route_min_mtu_update,
 610        .route_irif_update = mlxsw_sp_mr_tcam_route_irif_update,
 611        .route_erif_add = mlxsw_sp_mr_tcam_route_erif_add,
 612        .route_erif_del = mlxsw_sp_mr_tcam_route_erif_del,
 613        .route_destroy = mlxsw_sp_mr_tcam_route_destroy,
 614        .fini = mlxsw_sp_mr_tcam_fini,
 615};
 616