root/drivers/net/ethernet/mellanox/mlxsw/spectrum_nve.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. mlxsw_sp_nve_mc_record_ipv4_entry_add
  2. mlxsw_sp_nve_mc_record_ipv4_entry_del
  3. mlxsw_sp_nve_mc_record_ipv4_entry_set
  4. mlxsw_sp_nve_mc_record_ipv4_entry_compare
  5. mlxsw_sp_nve_mc_record_ipv6_entry_add
  6. mlxsw_sp_nve_mc_record_ipv6_entry_del
  7. mlxsw_sp_nve_mc_record_ipv6_entry_set
  8. mlxsw_sp_nve_mc_record_ipv6_entry_compare
  9. mlxsw_sp_nve_learned_ip_resolve
  10. mlxsw_sp_nve_mc_list_find
  11. mlxsw_sp_nve_mc_list_create
  12. mlxsw_sp_nve_mc_list_destroy
  13. mlxsw_sp_nve_mc_list_get
  14. mlxsw_sp_nve_mc_list_put
  15. mlxsw_sp_nve_mc_record_create
  16. mlxsw_sp_nve_mc_record_destroy
  17. mlxsw_sp_nve_mc_record_get
  18. mlxsw_sp_nve_mc_record_put
  19. mlxsw_sp_nve_mc_free_entry_find
  20. mlxsw_sp_nve_mc_record_refresh
  21. mlxsw_sp_nve_mc_record_is_first
  22. mlxsw_sp_nve_mc_entry_find
  23. mlxsw_sp_nve_mc_record_ip_add
  24. mlxsw_sp_nve_mc_record_entry_del
  25. mlxsw_sp_nve_mc_record_find
  26. mlxsw_sp_nve_mc_list_ip_add
  27. mlxsw_sp_nve_mc_list_ip_del
  28. mlxsw_sp_nve_fid_flood_index_set
  29. mlxsw_sp_nve_fid_flood_index_clear
  30. mlxsw_sp_nve_flood_ip_add
  31. mlxsw_sp_nve_flood_ip_del
  32. mlxsw_sp_nve_mc_record_delete
  33. mlxsw_sp_nve_flood_ip_flush
  34. mlxsw_sp_nve_decap_tunnel_index_get
  35. mlxsw_sp_nve_ipv4_route_is_decap
  36. mlxsw_sp_nve_tunnel_init
  37. mlxsw_sp_nve_tunnel_fini
  38. mlxsw_sp_nve_fdb_flush_by_fid
  39. mlxsw_sp_nve_fdb_clear_offload
  40. mlxsw_sp_nve_fid_enable
  41. mlxsw_sp_nve_fid_disable
  42. mlxsw_sp_port_nve_init
  43. mlxsw_sp_port_nve_fini
  44. mlxsw_sp_nve_qos_init
  45. mlxsw_sp_nve_ecn_encap_init
  46. __mlxsw_sp_nve_ecn_decap_init
  47. mlxsw_sp_nve_ecn_decap_init
  48. mlxsw_sp_nve_ecn_init
  49. mlxsw_sp_nve_resources_query
  50. mlxsw_sp_nve_init
  51. mlxsw_sp_nve_fini

   1 // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
   2 /* Copyright (c) 2018 Mellanox Technologies. All rights reserved */
   3 
   4 #include <linux/err.h>
   5 #include <linux/gfp.h>
   6 #include <linux/kernel.h>
   7 #include <linux/list.h>
   8 #include <linux/netlink.h>
   9 #include <linux/rtnetlink.h>
  10 #include <linux/slab.h>
  11 #include <net/inet_ecn.h>
  12 #include <net/ipv6.h>
  13 
  14 #include "reg.h"
  15 #include "spectrum.h"
  16 #include "spectrum_nve.h"
  17 
  18 const struct mlxsw_sp_nve_ops *mlxsw_sp1_nve_ops_arr[] = {
  19         [MLXSW_SP_NVE_TYPE_VXLAN]       = &mlxsw_sp1_nve_vxlan_ops,
  20 };
  21 
  22 const struct mlxsw_sp_nve_ops *mlxsw_sp2_nve_ops_arr[] = {
  23         [MLXSW_SP_NVE_TYPE_VXLAN]       = &mlxsw_sp2_nve_vxlan_ops,
  24 };
  25 
  26 struct mlxsw_sp_nve_mc_entry;
  27 struct mlxsw_sp_nve_mc_record;
  28 struct mlxsw_sp_nve_mc_list;
  29 
  30 struct mlxsw_sp_nve_mc_record_ops {
  31         enum mlxsw_reg_tnumt_record_type type;
  32         int (*entry_add)(struct mlxsw_sp_nve_mc_record *mc_record,
  33                          struct mlxsw_sp_nve_mc_entry *mc_entry,
  34                          const union mlxsw_sp_l3addr *addr);
  35         void (*entry_del)(const struct mlxsw_sp_nve_mc_record *mc_record,
  36                           const struct mlxsw_sp_nve_mc_entry *mc_entry);
  37         void (*entry_set)(const struct mlxsw_sp_nve_mc_record *mc_record,
  38                           const struct mlxsw_sp_nve_mc_entry *mc_entry,
  39                           char *tnumt_pl, unsigned int entry_index);
  40         bool (*entry_compare)(const struct mlxsw_sp_nve_mc_record *mc_record,
  41                               const struct mlxsw_sp_nve_mc_entry *mc_entry,
  42                               const union mlxsw_sp_l3addr *addr);
  43 };
  44 
  45 struct mlxsw_sp_nve_mc_list_key {
  46         u16 fid_index;
  47 };
  48 
  49 struct mlxsw_sp_nve_mc_ipv6_entry {
  50         struct in6_addr addr6;
  51         u32 addr6_kvdl_index;
  52 };
  53 
  54 struct mlxsw_sp_nve_mc_entry {
  55         union {
  56                 __be32 addr4;
  57                 struct mlxsw_sp_nve_mc_ipv6_entry ipv6_entry;
  58         };
  59         u8 valid:1;
  60 };
  61 
  62 struct mlxsw_sp_nve_mc_record {
  63         struct list_head list;
  64         enum mlxsw_sp_l3proto proto;
  65         unsigned int num_entries;
  66         struct mlxsw_sp *mlxsw_sp;
  67         struct mlxsw_sp_nve_mc_list *mc_list;
  68         const struct mlxsw_sp_nve_mc_record_ops *ops;
  69         u32 kvdl_index;
  70         struct mlxsw_sp_nve_mc_entry entries[0];
  71 };
  72 
  73 struct mlxsw_sp_nve_mc_list {
  74         struct list_head records_list;
  75         struct rhash_head ht_node;
  76         struct mlxsw_sp_nve_mc_list_key key;
  77 };
  78 
  79 static const struct rhashtable_params mlxsw_sp_nve_mc_list_ht_params = {
  80         .key_len = sizeof(struct mlxsw_sp_nve_mc_list_key),
  81         .key_offset = offsetof(struct mlxsw_sp_nve_mc_list, key),
  82         .head_offset = offsetof(struct mlxsw_sp_nve_mc_list, ht_node),
  83 };
  84 
  85 static int
  86 mlxsw_sp_nve_mc_record_ipv4_entry_add(struct mlxsw_sp_nve_mc_record *mc_record,
  87                                       struct mlxsw_sp_nve_mc_entry *mc_entry,
  88                                       const union mlxsw_sp_l3addr *addr)
  89 {
  90         mc_entry->addr4 = addr->addr4;
  91 
  92         return 0;
  93 }
  94 
  95 static void
  96 mlxsw_sp_nve_mc_record_ipv4_entry_del(const struct mlxsw_sp_nve_mc_record *mc_record,
  97                                       const struct mlxsw_sp_nve_mc_entry *mc_entry)
  98 {
  99 }
 100 
 101 static void
 102 mlxsw_sp_nve_mc_record_ipv4_entry_set(const struct mlxsw_sp_nve_mc_record *mc_record,
 103                                       const struct mlxsw_sp_nve_mc_entry *mc_entry,
 104                                       char *tnumt_pl, unsigned int entry_index)
 105 {
 106         u32 udip = be32_to_cpu(mc_entry->addr4);
 107 
 108         mlxsw_reg_tnumt_udip_set(tnumt_pl, entry_index, udip);
 109 }
 110 
 111 static bool
 112 mlxsw_sp_nve_mc_record_ipv4_entry_compare(const struct mlxsw_sp_nve_mc_record *mc_record,
 113                                           const struct mlxsw_sp_nve_mc_entry *mc_entry,
 114                                           const union mlxsw_sp_l3addr *addr)
 115 {
 116         return mc_entry->addr4 == addr->addr4;
 117 }
 118 
 119 static const struct mlxsw_sp_nve_mc_record_ops
 120 mlxsw_sp_nve_mc_record_ipv4_ops = {
 121         .type           = MLXSW_REG_TNUMT_RECORD_TYPE_IPV4,
 122         .entry_add      = &mlxsw_sp_nve_mc_record_ipv4_entry_add,
 123         .entry_del      = &mlxsw_sp_nve_mc_record_ipv4_entry_del,
 124         .entry_set      = &mlxsw_sp_nve_mc_record_ipv4_entry_set,
 125         .entry_compare  = &mlxsw_sp_nve_mc_record_ipv4_entry_compare,
 126 };
 127 
 128 static int
 129 mlxsw_sp_nve_mc_record_ipv6_entry_add(struct mlxsw_sp_nve_mc_record *mc_record,
 130                                       struct mlxsw_sp_nve_mc_entry *mc_entry,
 131                                       const union mlxsw_sp_l3addr *addr)
 132 {
 133         WARN_ON(1);
 134 
 135         return -EINVAL;
 136 }
 137 
 138 static void
 139 mlxsw_sp_nve_mc_record_ipv6_entry_del(const struct mlxsw_sp_nve_mc_record *mc_record,
 140                                       const struct mlxsw_sp_nve_mc_entry *mc_entry)
 141 {
 142 }
 143 
 144 static void
 145 mlxsw_sp_nve_mc_record_ipv6_entry_set(const struct mlxsw_sp_nve_mc_record *mc_record,
 146                                       const struct mlxsw_sp_nve_mc_entry *mc_entry,
 147                                       char *tnumt_pl, unsigned int entry_index)
 148 {
 149         u32 udip_ptr = mc_entry->ipv6_entry.addr6_kvdl_index;
 150 
 151         mlxsw_reg_tnumt_udip_ptr_set(tnumt_pl, entry_index, udip_ptr);
 152 }
 153 
 154 static bool
 155 mlxsw_sp_nve_mc_record_ipv6_entry_compare(const struct mlxsw_sp_nve_mc_record *mc_record,
 156                                           const struct mlxsw_sp_nve_mc_entry *mc_entry,
 157                                           const union mlxsw_sp_l3addr *addr)
 158 {
 159         return ipv6_addr_equal(&mc_entry->ipv6_entry.addr6, &addr->addr6);
 160 }
 161 
 162 static const struct mlxsw_sp_nve_mc_record_ops
 163 mlxsw_sp_nve_mc_record_ipv6_ops = {
 164         .type           = MLXSW_REG_TNUMT_RECORD_TYPE_IPV6,
 165         .entry_add      = &mlxsw_sp_nve_mc_record_ipv6_entry_add,
 166         .entry_del      = &mlxsw_sp_nve_mc_record_ipv6_entry_del,
 167         .entry_set      = &mlxsw_sp_nve_mc_record_ipv6_entry_set,
 168         .entry_compare  = &mlxsw_sp_nve_mc_record_ipv6_entry_compare,
 169 };
 170 
 171 static const struct mlxsw_sp_nve_mc_record_ops *
 172 mlxsw_sp_nve_mc_record_ops_arr[] = {
 173         [MLXSW_SP_L3_PROTO_IPV4] = &mlxsw_sp_nve_mc_record_ipv4_ops,
 174         [MLXSW_SP_L3_PROTO_IPV6] = &mlxsw_sp_nve_mc_record_ipv6_ops,
 175 };
 176 
 177 int mlxsw_sp_nve_learned_ip_resolve(struct mlxsw_sp *mlxsw_sp, u32 uip,
 178                                     enum mlxsw_sp_l3proto proto,
 179                                     union mlxsw_sp_l3addr *addr)
 180 {
 181         switch (proto) {
 182         case MLXSW_SP_L3_PROTO_IPV4:
 183                 addr->addr4 = cpu_to_be32(uip);
 184                 return 0;
 185         default:
 186                 WARN_ON(1);
 187                 return -EINVAL;
 188         }
 189 }
 190 
 191 static struct mlxsw_sp_nve_mc_list *
 192 mlxsw_sp_nve_mc_list_find(struct mlxsw_sp *mlxsw_sp,
 193                           const struct mlxsw_sp_nve_mc_list_key *key)
 194 {
 195         struct mlxsw_sp_nve *nve = mlxsw_sp->nve;
 196 
 197         return rhashtable_lookup_fast(&nve->mc_list_ht, key,
 198                                       mlxsw_sp_nve_mc_list_ht_params);
 199 }
 200 
 201 static struct mlxsw_sp_nve_mc_list *
 202 mlxsw_sp_nve_mc_list_create(struct mlxsw_sp *mlxsw_sp,
 203                             const struct mlxsw_sp_nve_mc_list_key *key)
 204 {
 205         struct mlxsw_sp_nve *nve = mlxsw_sp->nve;
 206         struct mlxsw_sp_nve_mc_list *mc_list;
 207         int err;
 208 
 209         mc_list = kmalloc(sizeof(*mc_list), GFP_KERNEL);
 210         if (!mc_list)
 211                 return ERR_PTR(-ENOMEM);
 212 
 213         INIT_LIST_HEAD(&mc_list->records_list);
 214         mc_list->key = *key;
 215 
 216         err = rhashtable_insert_fast(&nve->mc_list_ht, &mc_list->ht_node,
 217                                      mlxsw_sp_nve_mc_list_ht_params);
 218         if (err)
 219                 goto err_rhashtable_insert;
 220 
 221         return mc_list;
 222 
 223 err_rhashtable_insert:
 224         kfree(mc_list);
 225         return ERR_PTR(err);
 226 }
 227 
 228 static void mlxsw_sp_nve_mc_list_destroy(struct mlxsw_sp *mlxsw_sp,
 229                                          struct mlxsw_sp_nve_mc_list *mc_list)
 230 {
 231         struct mlxsw_sp_nve *nve = mlxsw_sp->nve;
 232 
 233         rhashtable_remove_fast(&nve->mc_list_ht, &mc_list->ht_node,
 234                                mlxsw_sp_nve_mc_list_ht_params);
 235         WARN_ON(!list_empty(&mc_list->records_list));
 236         kfree(mc_list);
 237 }
 238 
 239 static struct mlxsw_sp_nve_mc_list *
 240 mlxsw_sp_nve_mc_list_get(struct mlxsw_sp *mlxsw_sp,
 241                          const struct mlxsw_sp_nve_mc_list_key *key)
 242 {
 243         struct mlxsw_sp_nve_mc_list *mc_list;
 244 
 245         mc_list = mlxsw_sp_nve_mc_list_find(mlxsw_sp, key);
 246         if (mc_list)
 247                 return mc_list;
 248 
 249         return mlxsw_sp_nve_mc_list_create(mlxsw_sp, key);
 250 }
 251 
 252 static void
 253 mlxsw_sp_nve_mc_list_put(struct mlxsw_sp *mlxsw_sp,
 254                          struct mlxsw_sp_nve_mc_list *mc_list)
 255 {
 256         if (!list_empty(&mc_list->records_list))
 257                 return;
 258         mlxsw_sp_nve_mc_list_destroy(mlxsw_sp, mc_list);
 259 }
 260 
 261 static struct mlxsw_sp_nve_mc_record *
 262 mlxsw_sp_nve_mc_record_create(struct mlxsw_sp *mlxsw_sp,
 263                               struct mlxsw_sp_nve_mc_list *mc_list,
 264                               enum mlxsw_sp_l3proto proto)
 265 {
 266         unsigned int num_max_entries = mlxsw_sp->nve->num_max_mc_entries[proto];
 267         struct mlxsw_sp_nve_mc_record *mc_record;
 268         int err;
 269 
 270         mc_record = kzalloc(struct_size(mc_record, entries, num_max_entries),
 271                             GFP_KERNEL);
 272         if (!mc_record)
 273                 return ERR_PTR(-ENOMEM);
 274 
 275         err = mlxsw_sp_kvdl_alloc(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_TNUMT, 1,
 276                                   &mc_record->kvdl_index);
 277         if (err)
 278                 goto err_kvdl_alloc;
 279 
 280         mc_record->ops = mlxsw_sp_nve_mc_record_ops_arr[proto];
 281         mc_record->mlxsw_sp = mlxsw_sp;
 282         mc_record->mc_list = mc_list;
 283         mc_record->proto = proto;
 284         list_add_tail(&mc_record->list, &mc_list->records_list);
 285 
 286         return mc_record;
 287 
 288 err_kvdl_alloc:
 289         kfree(mc_record);
 290         return ERR_PTR(err);
 291 }
 292 
 293 static void
 294 mlxsw_sp_nve_mc_record_destroy(struct mlxsw_sp_nve_mc_record *mc_record)
 295 {
 296         struct mlxsw_sp *mlxsw_sp = mc_record->mlxsw_sp;
 297 
 298         list_del(&mc_record->list);
 299         mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_TNUMT, 1,
 300                            mc_record->kvdl_index);
 301         WARN_ON(mc_record->num_entries);
 302         kfree(mc_record);
 303 }
 304 
 305 static struct mlxsw_sp_nve_mc_record *
 306 mlxsw_sp_nve_mc_record_get(struct mlxsw_sp *mlxsw_sp,
 307                            struct mlxsw_sp_nve_mc_list *mc_list,
 308                            enum mlxsw_sp_l3proto proto)
 309 {
 310         struct mlxsw_sp_nve_mc_record *mc_record;
 311 
 312         list_for_each_entry_reverse(mc_record, &mc_list->records_list, list) {
 313                 unsigned int num_entries = mc_record->num_entries;
 314                 struct mlxsw_sp_nve *nve = mlxsw_sp->nve;
 315 
 316                 if (mc_record->proto == proto &&
 317                     num_entries < nve->num_max_mc_entries[proto])
 318                         return mc_record;
 319         }
 320 
 321         return mlxsw_sp_nve_mc_record_create(mlxsw_sp, mc_list, proto);
 322 }
 323 
 324 static void
 325 mlxsw_sp_nve_mc_record_put(struct mlxsw_sp_nve_mc_record *mc_record)
 326 {
 327         if (mc_record->num_entries != 0)
 328                 return;
 329 
 330         mlxsw_sp_nve_mc_record_destroy(mc_record);
 331 }
 332 
 333 static struct mlxsw_sp_nve_mc_entry *
 334 mlxsw_sp_nve_mc_free_entry_find(struct mlxsw_sp_nve_mc_record *mc_record)
 335 {
 336         struct mlxsw_sp_nve *nve = mc_record->mlxsw_sp->nve;
 337         unsigned int num_max_entries;
 338         int i;
 339 
 340         num_max_entries = nve->num_max_mc_entries[mc_record->proto];
 341         for (i = 0; i < num_max_entries; i++) {
 342                 if (mc_record->entries[i].valid)
 343                         continue;
 344                 return &mc_record->entries[i];
 345         }
 346 
 347         return NULL;
 348 }
 349 
 350 static int
 351 mlxsw_sp_nve_mc_record_refresh(struct mlxsw_sp_nve_mc_record *mc_record)
 352 {
 353         enum mlxsw_reg_tnumt_record_type type = mc_record->ops->type;
 354         struct mlxsw_sp_nve_mc_list *mc_list = mc_record->mc_list;
 355         struct mlxsw_sp *mlxsw_sp = mc_record->mlxsw_sp;
 356         char tnumt_pl[MLXSW_REG_TNUMT_LEN];
 357         unsigned int num_max_entries;
 358         unsigned int num_entries = 0;
 359         u32 next_kvdl_index = 0;
 360         bool next_valid = false;
 361         int i;
 362 
 363         if (!list_is_last(&mc_record->list, &mc_list->records_list)) {
 364                 struct mlxsw_sp_nve_mc_record *next_record;
 365 
 366                 next_record = list_next_entry(mc_record, list);
 367                 next_kvdl_index = next_record->kvdl_index;
 368                 next_valid = true;
 369         }
 370 
 371         mlxsw_reg_tnumt_pack(tnumt_pl, type, MLXSW_REG_TNUMT_TUNNEL_PORT_NVE,
 372                              mc_record->kvdl_index, next_valid,
 373                              next_kvdl_index, mc_record->num_entries);
 374 
 375         num_max_entries = mlxsw_sp->nve->num_max_mc_entries[mc_record->proto];
 376         for (i = 0; i < num_max_entries; i++) {
 377                 struct mlxsw_sp_nve_mc_entry *mc_entry;
 378 
 379                 mc_entry = &mc_record->entries[i];
 380                 if (!mc_entry->valid)
 381                         continue;
 382                 mc_record->ops->entry_set(mc_record, mc_entry, tnumt_pl,
 383                                           num_entries++);
 384         }
 385 
 386         WARN_ON(num_entries != mc_record->num_entries);
 387 
 388         return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(tnumt), tnumt_pl);
 389 }
 390 
 391 static bool
 392 mlxsw_sp_nve_mc_record_is_first(struct mlxsw_sp_nve_mc_record *mc_record)
 393 {
 394         struct mlxsw_sp_nve_mc_list *mc_list = mc_record->mc_list;
 395         struct mlxsw_sp_nve_mc_record *first_record;
 396 
 397         first_record = list_first_entry(&mc_list->records_list,
 398                                         struct mlxsw_sp_nve_mc_record, list);
 399 
 400         return mc_record == first_record;
 401 }
 402 
 403 static struct mlxsw_sp_nve_mc_entry *
 404 mlxsw_sp_nve_mc_entry_find(struct mlxsw_sp_nve_mc_record *mc_record,
 405                            union mlxsw_sp_l3addr *addr)
 406 {
 407         struct mlxsw_sp_nve *nve = mc_record->mlxsw_sp->nve;
 408         unsigned int num_max_entries;
 409         int i;
 410 
 411         num_max_entries = nve->num_max_mc_entries[mc_record->proto];
 412         for (i = 0; i < num_max_entries; i++) {
 413                 struct mlxsw_sp_nve_mc_entry *mc_entry;
 414 
 415                 mc_entry = &mc_record->entries[i];
 416                 if (!mc_entry->valid)
 417                         continue;
 418                 if (mc_record->ops->entry_compare(mc_record, mc_entry, addr))
 419                         return mc_entry;
 420         }
 421 
 422         return NULL;
 423 }
 424 
 425 static int
 426 mlxsw_sp_nve_mc_record_ip_add(struct mlxsw_sp_nve_mc_record *mc_record,
 427                               union mlxsw_sp_l3addr *addr)
 428 {
 429         struct mlxsw_sp_nve_mc_entry *mc_entry = NULL;
 430         int err;
 431 
 432         mc_entry = mlxsw_sp_nve_mc_free_entry_find(mc_record);
 433         if (WARN_ON(!mc_entry))
 434                 return -EINVAL;
 435 
 436         err = mc_record->ops->entry_add(mc_record, mc_entry, addr);
 437         if (err)
 438                 return err;
 439         mc_record->num_entries++;
 440         mc_entry->valid = true;
 441 
 442         err = mlxsw_sp_nve_mc_record_refresh(mc_record);
 443         if (err)
 444                 goto err_record_refresh;
 445 
 446         /* If this is a new record and not the first one, then we need to
 447          * update the next pointer of the previous entry
 448          */
 449         if (mc_record->num_entries != 1 ||
 450             mlxsw_sp_nve_mc_record_is_first(mc_record))
 451                 return 0;
 452 
 453         err = mlxsw_sp_nve_mc_record_refresh(list_prev_entry(mc_record, list));
 454         if (err)
 455                 goto err_prev_record_refresh;
 456 
 457         return 0;
 458 
 459 err_prev_record_refresh:
 460 err_record_refresh:
 461         mc_entry->valid = false;
 462         mc_record->num_entries--;
 463         mc_record->ops->entry_del(mc_record, mc_entry);
 464         return err;
 465 }
 466 
 467 static void
 468 mlxsw_sp_nve_mc_record_entry_del(struct mlxsw_sp_nve_mc_record *mc_record,
 469                                  struct mlxsw_sp_nve_mc_entry *mc_entry)
 470 {
 471         struct mlxsw_sp_nve_mc_list *mc_list = mc_record->mc_list;
 472 
 473         mc_entry->valid = false;
 474         mc_record->num_entries--;
 475 
 476         /* When the record continues to exist we only need to invalidate
 477          * the requested entry
 478          */
 479         if (mc_record->num_entries != 0) {
 480                 mlxsw_sp_nve_mc_record_refresh(mc_record);
 481                 mc_record->ops->entry_del(mc_record, mc_entry);
 482                 return;
 483         }
 484 
 485         /* If the record needs to be deleted, but it is not the first,
 486          * then we need to make sure that the previous record no longer
 487          * points to it. Remove deleted record from the list to reflect
 488          * that and then re-add it at the end, so that it could be
 489          * properly removed by the record destruction code
 490          */
 491         if (!mlxsw_sp_nve_mc_record_is_first(mc_record)) {
 492                 struct mlxsw_sp_nve_mc_record *prev_record;
 493 
 494                 prev_record = list_prev_entry(mc_record, list);
 495                 list_del(&mc_record->list);
 496                 mlxsw_sp_nve_mc_record_refresh(prev_record);
 497                 list_add_tail(&mc_record->list, &mc_list->records_list);
 498                 mc_record->ops->entry_del(mc_record, mc_entry);
 499                 return;
 500         }
 501 
 502         /* If the first record needs to be deleted, but the list is not
 503          * singular, then the second record needs to be written in the
 504          * first record's address, as this address is stored as a property
 505          * of the FID
 506          */
 507         if (mlxsw_sp_nve_mc_record_is_first(mc_record) &&
 508             !list_is_singular(&mc_list->records_list)) {
 509                 struct mlxsw_sp_nve_mc_record *next_record;
 510 
 511                 next_record = list_next_entry(mc_record, list);
 512                 swap(mc_record->kvdl_index, next_record->kvdl_index);
 513                 mlxsw_sp_nve_mc_record_refresh(next_record);
 514                 mc_record->ops->entry_del(mc_record, mc_entry);
 515                 return;
 516         }
 517 
 518         /* This is the last case where the last remaining record needs to
 519          * be deleted. Simply delete the entry
 520          */
 521         mc_record->ops->entry_del(mc_record, mc_entry);
 522 }
 523 
 524 static struct mlxsw_sp_nve_mc_record *
 525 mlxsw_sp_nve_mc_record_find(struct mlxsw_sp_nve_mc_list *mc_list,
 526                             enum mlxsw_sp_l3proto proto,
 527                             union mlxsw_sp_l3addr *addr,
 528                             struct mlxsw_sp_nve_mc_entry **mc_entry)
 529 {
 530         struct mlxsw_sp_nve_mc_record *mc_record;
 531 
 532         list_for_each_entry(mc_record, &mc_list->records_list, list) {
 533                 if (mc_record->proto != proto)
 534                         continue;
 535 
 536                 *mc_entry = mlxsw_sp_nve_mc_entry_find(mc_record, addr);
 537                 if (*mc_entry)
 538                         return mc_record;
 539         }
 540 
 541         return NULL;
 542 }
 543 
 544 static int mlxsw_sp_nve_mc_list_ip_add(struct mlxsw_sp *mlxsw_sp,
 545                                        struct mlxsw_sp_nve_mc_list *mc_list,
 546                                        enum mlxsw_sp_l3proto proto,
 547                                        union mlxsw_sp_l3addr *addr)
 548 {
 549         struct mlxsw_sp_nve_mc_record *mc_record;
 550         int err;
 551 
 552         mc_record = mlxsw_sp_nve_mc_record_get(mlxsw_sp, mc_list, proto);
 553         if (IS_ERR(mc_record))
 554                 return PTR_ERR(mc_record);
 555 
 556         err = mlxsw_sp_nve_mc_record_ip_add(mc_record, addr);
 557         if (err)
 558                 goto err_ip_add;
 559 
 560         return 0;
 561 
 562 err_ip_add:
 563         mlxsw_sp_nve_mc_record_put(mc_record);
 564         return err;
 565 }
 566 
 567 static void mlxsw_sp_nve_mc_list_ip_del(struct mlxsw_sp *mlxsw_sp,
 568                                         struct mlxsw_sp_nve_mc_list *mc_list,
 569                                         enum mlxsw_sp_l3proto proto,
 570                                         union mlxsw_sp_l3addr *addr)
 571 {
 572         struct mlxsw_sp_nve_mc_record *mc_record;
 573         struct mlxsw_sp_nve_mc_entry *mc_entry;
 574 
 575         mc_record = mlxsw_sp_nve_mc_record_find(mc_list, proto, addr,
 576                                                 &mc_entry);
 577         if (!mc_record)
 578                 return;
 579 
 580         mlxsw_sp_nve_mc_record_entry_del(mc_record, mc_entry);
 581         mlxsw_sp_nve_mc_record_put(mc_record);
 582 }
 583 
 584 static int
 585 mlxsw_sp_nve_fid_flood_index_set(struct mlxsw_sp_fid *fid,
 586                                  struct mlxsw_sp_nve_mc_list *mc_list)
 587 {
 588         struct mlxsw_sp_nve_mc_record *mc_record;
 589 
 590         /* The address of the first record in the list is a property of
 591          * the FID and we never change it. It only needs to be set when
 592          * a new list is created
 593          */
 594         if (mlxsw_sp_fid_nve_flood_index_is_set(fid))
 595                 return 0;
 596 
 597         mc_record = list_first_entry(&mc_list->records_list,
 598                                      struct mlxsw_sp_nve_mc_record, list);
 599 
 600         return mlxsw_sp_fid_nve_flood_index_set(fid, mc_record->kvdl_index);
 601 }
 602 
 603 static void
 604 mlxsw_sp_nve_fid_flood_index_clear(struct mlxsw_sp_fid *fid,
 605                                    struct mlxsw_sp_nve_mc_list *mc_list)
 606 {
 607         struct mlxsw_sp_nve_mc_record *mc_record;
 608 
 609         /* The address of the first record needs to be invalidated only when
 610          * the last record is about to be removed
 611          */
 612         if (!list_is_singular(&mc_list->records_list))
 613                 return;
 614 
 615         mc_record = list_first_entry(&mc_list->records_list,
 616                                      struct mlxsw_sp_nve_mc_record, list);
 617         if (mc_record->num_entries != 1)
 618                 return;
 619 
 620         return mlxsw_sp_fid_nve_flood_index_clear(fid);
 621 }
 622 
 623 int mlxsw_sp_nve_flood_ip_add(struct mlxsw_sp *mlxsw_sp,
 624                               struct mlxsw_sp_fid *fid,
 625                               enum mlxsw_sp_l3proto proto,
 626                               union mlxsw_sp_l3addr *addr)
 627 {
 628         struct mlxsw_sp_nve_mc_list_key key = { 0 };
 629         struct mlxsw_sp_nve_mc_list *mc_list;
 630         int err;
 631 
 632         key.fid_index = mlxsw_sp_fid_index(fid);
 633         mc_list = mlxsw_sp_nve_mc_list_get(mlxsw_sp, &key);
 634         if (IS_ERR(mc_list))
 635                 return PTR_ERR(mc_list);
 636 
 637         err = mlxsw_sp_nve_mc_list_ip_add(mlxsw_sp, mc_list, proto, addr);
 638         if (err)
 639                 goto err_add_ip;
 640 
 641         err = mlxsw_sp_nve_fid_flood_index_set(fid, mc_list);
 642         if (err)
 643                 goto err_fid_flood_index_set;
 644 
 645         return 0;
 646 
 647 err_fid_flood_index_set:
 648         mlxsw_sp_nve_mc_list_ip_del(mlxsw_sp, mc_list, proto, addr);
 649 err_add_ip:
 650         mlxsw_sp_nve_mc_list_put(mlxsw_sp, mc_list);
 651         return err;
 652 }
 653 
 654 void mlxsw_sp_nve_flood_ip_del(struct mlxsw_sp *mlxsw_sp,
 655                                struct mlxsw_sp_fid *fid,
 656                                enum mlxsw_sp_l3proto proto,
 657                                union mlxsw_sp_l3addr *addr)
 658 {
 659         struct mlxsw_sp_nve_mc_list_key key = { 0 };
 660         struct mlxsw_sp_nve_mc_list *mc_list;
 661 
 662         key.fid_index = mlxsw_sp_fid_index(fid);
 663         mc_list = mlxsw_sp_nve_mc_list_find(mlxsw_sp, &key);
 664         if (!mc_list)
 665                 return;
 666 
 667         mlxsw_sp_nve_fid_flood_index_clear(fid, mc_list);
 668         mlxsw_sp_nve_mc_list_ip_del(mlxsw_sp, mc_list, proto, addr);
 669         mlxsw_sp_nve_mc_list_put(mlxsw_sp, mc_list);
 670 }
 671 
 672 static void
 673 mlxsw_sp_nve_mc_record_delete(struct mlxsw_sp_nve_mc_record *mc_record)
 674 {
 675         struct mlxsw_sp_nve *nve = mc_record->mlxsw_sp->nve;
 676         unsigned int num_max_entries;
 677         int i;
 678 
 679         num_max_entries = nve->num_max_mc_entries[mc_record->proto];
 680         for (i = 0; i < num_max_entries; i++) {
 681                 struct mlxsw_sp_nve_mc_entry *mc_entry = &mc_record->entries[i];
 682 
 683                 if (!mc_entry->valid)
 684                         continue;
 685                 mlxsw_sp_nve_mc_record_entry_del(mc_record, mc_entry);
 686         }
 687 
 688         WARN_ON(mc_record->num_entries);
 689         mlxsw_sp_nve_mc_record_put(mc_record);
 690 }
 691 
 692 static void mlxsw_sp_nve_flood_ip_flush(struct mlxsw_sp *mlxsw_sp,
 693                                         struct mlxsw_sp_fid *fid)
 694 {
 695         struct mlxsw_sp_nve_mc_record *mc_record, *tmp;
 696         struct mlxsw_sp_nve_mc_list_key key = { 0 };
 697         struct mlxsw_sp_nve_mc_list *mc_list;
 698 
 699         if (!mlxsw_sp_fid_nve_flood_index_is_set(fid))
 700                 return;
 701 
 702         mlxsw_sp_fid_nve_flood_index_clear(fid);
 703 
 704         key.fid_index = mlxsw_sp_fid_index(fid);
 705         mc_list = mlxsw_sp_nve_mc_list_find(mlxsw_sp, &key);
 706         if (WARN_ON(!mc_list))
 707                 return;
 708 
 709         list_for_each_entry_safe(mc_record, tmp, &mc_list->records_list, list)
 710                 mlxsw_sp_nve_mc_record_delete(mc_record);
 711 
 712         WARN_ON(!list_empty(&mc_list->records_list));
 713         mlxsw_sp_nve_mc_list_put(mlxsw_sp, mc_list);
 714 }
 715 
 716 u32 mlxsw_sp_nve_decap_tunnel_index_get(const struct mlxsw_sp *mlxsw_sp)
 717 {
 718         WARN_ON(mlxsw_sp->nve->num_nve_tunnels == 0);
 719 
 720         return mlxsw_sp->nve->tunnel_index;
 721 }
 722 
 723 bool mlxsw_sp_nve_ipv4_route_is_decap(const struct mlxsw_sp *mlxsw_sp,
 724                                       u32 tb_id, __be32 addr)
 725 {
 726         struct mlxsw_sp_nve *nve = mlxsw_sp->nve;
 727         struct mlxsw_sp_nve_config *config = &nve->config;
 728 
 729         if (nve->num_nve_tunnels &&
 730             config->ul_proto == MLXSW_SP_L3_PROTO_IPV4 &&
 731             config->ul_sip.addr4 == addr && config->ul_tb_id == tb_id)
 732                 return true;
 733 
 734         return false;
 735 }
 736 
 737 static int mlxsw_sp_nve_tunnel_init(struct mlxsw_sp *mlxsw_sp,
 738                                     struct mlxsw_sp_nve_config *config)
 739 {
 740         struct mlxsw_sp_nve *nve = mlxsw_sp->nve;
 741         const struct mlxsw_sp_nve_ops *ops;
 742         int err;
 743 
 744         if (nve->num_nve_tunnels++ != 0)
 745                 return 0;
 746 
 747         err = mlxsw_sp_kvdl_alloc(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ADJ, 1,
 748                                   &nve->tunnel_index);
 749         if (err)
 750                 goto err_kvdl_alloc;
 751 
 752         ops = nve->nve_ops_arr[config->type];
 753         err = ops->init(nve, config);
 754         if (err)
 755                 goto err_ops_init;
 756 
 757         return 0;
 758 
 759 err_ops_init:
 760         mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ADJ, 1,
 761                            nve->tunnel_index);
 762 err_kvdl_alloc:
 763         nve->num_nve_tunnels--;
 764         return err;
 765 }
 766 
 767 static void mlxsw_sp_nve_tunnel_fini(struct mlxsw_sp *mlxsw_sp)
 768 {
 769         struct mlxsw_sp_nve *nve = mlxsw_sp->nve;
 770         const struct mlxsw_sp_nve_ops *ops;
 771 
 772         ops = nve->nve_ops_arr[nve->config.type];
 773 
 774         if (mlxsw_sp->nve->num_nve_tunnels == 1) {
 775                 ops->fini(nve);
 776                 mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ADJ, 1,
 777                                    nve->tunnel_index);
 778                 memset(&nve->config, 0, sizeof(nve->config));
 779         }
 780         nve->num_nve_tunnels--;
 781 }
 782 
 783 static void mlxsw_sp_nve_fdb_flush_by_fid(struct mlxsw_sp *mlxsw_sp,
 784                                           u16 fid_index)
 785 {
 786         char sfdf_pl[MLXSW_REG_SFDF_LEN];
 787 
 788         mlxsw_reg_sfdf_pack(sfdf_pl, MLXSW_REG_SFDF_FLUSH_PER_NVE_AND_FID);
 789         mlxsw_reg_sfdf_fid_set(sfdf_pl, fid_index);
 790         mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfdf), sfdf_pl);
 791 }
 792 
 793 static void mlxsw_sp_nve_fdb_clear_offload(struct mlxsw_sp *mlxsw_sp,
 794                                            const struct mlxsw_sp_fid *fid,
 795                                            const struct net_device *nve_dev,
 796                                            __be32 vni)
 797 {
 798         const struct mlxsw_sp_nve_ops *ops;
 799         enum mlxsw_sp_nve_type type;
 800 
 801         if (WARN_ON(mlxsw_sp_fid_nve_type(fid, &type)))
 802                 return;
 803 
 804         ops = mlxsw_sp->nve->nve_ops_arr[type];
 805         ops->fdb_clear_offload(nve_dev, vni);
 806 }
 807 
 808 int mlxsw_sp_nve_fid_enable(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fid *fid,
 809                             struct mlxsw_sp_nve_params *params,
 810                             struct netlink_ext_ack *extack)
 811 {
 812         struct mlxsw_sp_nve *nve = mlxsw_sp->nve;
 813         const struct mlxsw_sp_nve_ops *ops;
 814         struct mlxsw_sp_nve_config config;
 815         int err;
 816 
 817         ops = nve->nve_ops_arr[params->type];
 818 
 819         if (!ops->can_offload(nve, params->dev, extack))
 820                 return -EINVAL;
 821 
 822         memset(&config, 0, sizeof(config));
 823         ops->nve_config(nve, params->dev, &config);
 824         if (nve->num_nve_tunnels &&
 825             memcmp(&config, &nve->config, sizeof(config))) {
 826                 NL_SET_ERR_MSG_MOD(extack, "Conflicting NVE tunnels configuration");
 827                 return -EINVAL;
 828         }
 829 
 830         err = mlxsw_sp_nve_tunnel_init(mlxsw_sp, &config);
 831         if (err) {
 832                 NL_SET_ERR_MSG_MOD(extack, "Failed to initialize NVE tunnel");
 833                 return err;
 834         }
 835 
 836         err = mlxsw_sp_fid_vni_set(fid, params->type, params->vni,
 837                                    params->dev->ifindex);
 838         if (err) {
 839                 NL_SET_ERR_MSG_MOD(extack, "Failed to set VNI on FID");
 840                 goto err_fid_vni_set;
 841         }
 842 
 843         nve->config = config;
 844 
 845         err = ops->fdb_replay(params->dev, params->vni, extack);
 846         if (err)
 847                 goto err_fdb_replay;
 848 
 849         return 0;
 850 
 851 err_fdb_replay:
 852         mlxsw_sp_fid_vni_clear(fid);
 853 err_fid_vni_set:
 854         mlxsw_sp_nve_tunnel_fini(mlxsw_sp);
 855         return err;
 856 }
 857 
 858 void mlxsw_sp_nve_fid_disable(struct mlxsw_sp *mlxsw_sp,
 859                               struct mlxsw_sp_fid *fid)
 860 {
 861         u16 fid_index = mlxsw_sp_fid_index(fid);
 862         struct net_device *nve_dev;
 863         int nve_ifindex;
 864         __be32 vni;
 865 
 866         mlxsw_sp_nve_flood_ip_flush(mlxsw_sp, fid);
 867         mlxsw_sp_nve_fdb_flush_by_fid(mlxsw_sp, fid_index);
 868 
 869         if (WARN_ON(mlxsw_sp_fid_nve_ifindex(fid, &nve_ifindex) ||
 870                     mlxsw_sp_fid_vni(fid, &vni)))
 871                 goto out;
 872 
 873         nve_dev = dev_get_by_index(&init_net, nve_ifindex);
 874         if (!nve_dev)
 875                 goto out;
 876 
 877         mlxsw_sp_nve_fdb_clear_offload(mlxsw_sp, fid, nve_dev, vni);
 878         mlxsw_sp_fid_fdb_clear_offload(fid, nve_dev);
 879 
 880         dev_put(nve_dev);
 881 
 882 out:
 883         mlxsw_sp_fid_vni_clear(fid);
 884         mlxsw_sp_nve_tunnel_fini(mlxsw_sp);
 885 }
 886 
 887 int mlxsw_sp_port_nve_init(struct mlxsw_sp_port *mlxsw_sp_port)
 888 {
 889         struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
 890         char tnqdr_pl[MLXSW_REG_TNQDR_LEN];
 891 
 892         mlxsw_reg_tnqdr_pack(tnqdr_pl, mlxsw_sp_port->local_port);
 893         return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(tnqdr), tnqdr_pl);
 894 }
 895 
 896 void mlxsw_sp_port_nve_fini(struct mlxsw_sp_port *mlxsw_sp_port)
 897 {
 898 }
 899 
 900 static int mlxsw_sp_nve_qos_init(struct mlxsw_sp *mlxsw_sp)
 901 {
 902         char tnqcr_pl[MLXSW_REG_TNQCR_LEN];
 903 
 904         mlxsw_reg_tnqcr_pack(tnqcr_pl);
 905         return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(tnqcr), tnqcr_pl);
 906 }
 907 
 908 static int mlxsw_sp_nve_ecn_encap_init(struct mlxsw_sp *mlxsw_sp)
 909 {
 910         int i;
 911 
 912         /* Iterate over inner ECN values */
 913         for (i = INET_ECN_NOT_ECT; i <= INET_ECN_CE; i++) {
 914                 u8 outer_ecn = INET_ECN_encapsulate(0, i);
 915                 char tneem_pl[MLXSW_REG_TNEEM_LEN];
 916                 int err;
 917 
 918                 mlxsw_reg_tneem_pack(tneem_pl, i, outer_ecn);
 919                 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(tneem),
 920                                       tneem_pl);
 921                 if (err)
 922                         return err;
 923         }
 924 
 925         return 0;
 926 }
 927 
 928 static int __mlxsw_sp_nve_ecn_decap_init(struct mlxsw_sp *mlxsw_sp,
 929                                          u8 inner_ecn, u8 outer_ecn)
 930 {
 931         char tndem_pl[MLXSW_REG_TNDEM_LEN];
 932         bool trap_en, set_ce = false;
 933         u8 new_inner_ecn;
 934 
 935         trap_en = !!__INET_ECN_decapsulate(outer_ecn, inner_ecn, &set_ce);
 936         new_inner_ecn = set_ce ? INET_ECN_CE : inner_ecn;
 937 
 938         mlxsw_reg_tndem_pack(tndem_pl, outer_ecn, inner_ecn, new_inner_ecn,
 939                              trap_en, trap_en ? MLXSW_TRAP_ID_DECAP_ECN0 : 0);
 940         return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(tndem), tndem_pl);
 941 }
 942 
 943 static int mlxsw_sp_nve_ecn_decap_init(struct mlxsw_sp *mlxsw_sp)
 944 {
 945         int i;
 946 
 947         /* Iterate over inner ECN values */
 948         for (i = INET_ECN_NOT_ECT; i <= INET_ECN_CE; i++) {
 949                 int j;
 950 
 951                 /* Iterate over outer ECN values */
 952                 for (j = INET_ECN_NOT_ECT; j <= INET_ECN_CE; j++) {
 953                         int err;
 954 
 955                         err = __mlxsw_sp_nve_ecn_decap_init(mlxsw_sp, i, j);
 956                         if (err)
 957                                 return err;
 958                 }
 959         }
 960 
 961         return 0;
 962 }
 963 
 964 static int mlxsw_sp_nve_ecn_init(struct mlxsw_sp *mlxsw_sp)
 965 {
 966         int err;
 967 
 968         err = mlxsw_sp_nve_ecn_encap_init(mlxsw_sp);
 969         if (err)
 970                 return err;
 971 
 972         return mlxsw_sp_nve_ecn_decap_init(mlxsw_sp);
 973 }
 974 
 975 static int mlxsw_sp_nve_resources_query(struct mlxsw_sp *mlxsw_sp)
 976 {
 977         unsigned int max;
 978 
 979         if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_NVE_MC_ENTRIES_IPV4) ||
 980             !MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_NVE_MC_ENTRIES_IPV6))
 981                 return -EIO;
 982         max = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_NVE_MC_ENTRIES_IPV4);
 983         mlxsw_sp->nve->num_max_mc_entries[MLXSW_SP_L3_PROTO_IPV4] = max;
 984         max = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_NVE_MC_ENTRIES_IPV6);
 985         mlxsw_sp->nve->num_max_mc_entries[MLXSW_SP_L3_PROTO_IPV6] = max;
 986 
 987         return 0;
 988 }
 989 
 990 int mlxsw_sp_nve_init(struct mlxsw_sp *mlxsw_sp)
 991 {
 992         struct mlxsw_sp_nve *nve;
 993         int err;
 994 
 995         nve = kzalloc(sizeof(*mlxsw_sp->nve), GFP_KERNEL);
 996         if (!nve)
 997                 return -ENOMEM;
 998         mlxsw_sp->nve = nve;
 999         nve->mlxsw_sp = mlxsw_sp;
1000         nve->nve_ops_arr = mlxsw_sp->nve_ops_arr;
1001 
1002         err = rhashtable_init(&nve->mc_list_ht,
1003                               &mlxsw_sp_nve_mc_list_ht_params);
1004         if (err)
1005                 goto err_rhashtable_init;
1006 
1007         err = mlxsw_sp_nve_qos_init(mlxsw_sp);
1008         if (err)
1009                 goto err_nve_qos_init;
1010 
1011         err = mlxsw_sp_nve_ecn_init(mlxsw_sp);
1012         if (err)
1013                 goto err_nve_ecn_init;
1014 
1015         err = mlxsw_sp_nve_resources_query(mlxsw_sp);
1016         if (err)
1017                 goto err_nve_resources_query;
1018 
1019         return 0;
1020 
1021 err_nve_resources_query:
1022 err_nve_ecn_init:
1023 err_nve_qos_init:
1024         rhashtable_destroy(&nve->mc_list_ht);
1025 err_rhashtable_init:
1026         mlxsw_sp->nve = NULL;
1027         kfree(nve);
1028         return err;
1029 }
1030 
1031 void mlxsw_sp_nve_fini(struct mlxsw_sp *mlxsw_sp)
1032 {
1033         WARN_ON(mlxsw_sp->nve->num_nve_tunnels);
1034         rhashtable_destroy(&mlxsw_sp->nve->mc_list_ht);
1035         kfree(mlxsw_sp->nve);
1036         mlxsw_sp->nve = NULL;
1037 }

/* [<][>][^][v][top][bottom][index][help] */