linux/drivers/net/ethernet/mellanox/mlxsw/spectrum_nve.c
<<
>>
Prefs
   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
  18const struct mlxsw_sp_nve_ops *mlxsw_sp1_nve_ops_arr[] = {
  19        [MLXSW_SP_NVE_TYPE_VXLAN]       = &mlxsw_sp1_nve_vxlan_ops,
  20};
  21
  22const struct mlxsw_sp_nve_ops *mlxsw_sp2_nve_ops_arr[] = {
  23        [MLXSW_SP_NVE_TYPE_VXLAN]       = &mlxsw_sp2_nve_vxlan_ops,
  24};
  25
  26struct mlxsw_sp_nve_mc_entry;
  27struct mlxsw_sp_nve_mc_record;
  28struct mlxsw_sp_nve_mc_list;
  29
  30struct 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
  45struct mlxsw_sp_nve_mc_list_key {
  46        u16 fid_index;
  47};
  48
  49struct mlxsw_sp_nve_mc_ipv6_entry {
  50        struct in6_addr addr6;
  51        u32 addr6_kvdl_index;
  52};
  53
  54struct 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
  62struct 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[];
  71};
  72
  73struct 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
  79static 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
  85static int
  86mlxsw_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
  95static void
  96mlxsw_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
 101static void
 102mlxsw_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
 111static bool
 112mlxsw_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
 119static const struct mlxsw_sp_nve_mc_record_ops
 120mlxsw_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
 128static int
 129mlxsw_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
 138static void
 139mlxsw_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
 144static void
 145mlxsw_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
 154static bool
 155mlxsw_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
 162static const struct mlxsw_sp_nve_mc_record_ops
 163mlxsw_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
 171static const struct mlxsw_sp_nve_mc_record_ops *
 172mlxsw_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
 177int 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
 191static struct mlxsw_sp_nve_mc_list *
 192mlxsw_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
 201static struct mlxsw_sp_nve_mc_list *
 202mlxsw_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
 223err_rhashtable_insert:
 224        kfree(mc_list);
 225        return ERR_PTR(err);
 226}
 227
 228static 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
 239static struct mlxsw_sp_nve_mc_list *
 240mlxsw_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
 252static void
 253mlxsw_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
 261static struct mlxsw_sp_nve_mc_record *
 262mlxsw_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
 288err_kvdl_alloc:
 289        kfree(mc_record);
 290        return ERR_PTR(err);
 291}
 292
 293static void
 294mlxsw_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
 305static struct mlxsw_sp_nve_mc_record *
 306mlxsw_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
 324static void
 325mlxsw_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
 333static struct mlxsw_sp_nve_mc_entry *
 334mlxsw_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
 350static int
 351mlxsw_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_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
 391static bool
 392mlxsw_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
 403static struct mlxsw_sp_nve_mc_entry *
 404mlxsw_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
 425static int
 426mlxsw_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
 459err_prev_record_refresh:
 460err_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
 467static void
 468mlxsw_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
 524static struct mlxsw_sp_nve_mc_record *
 525mlxsw_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
 544static 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
 562err_ip_add:
 563        mlxsw_sp_nve_mc_record_put(mc_record);
 564        return err;
 565}
 566
 567static 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
 584static int
 585mlxsw_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
 603static void
 604mlxsw_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
 623int 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
 647err_fid_flood_index_set:
 648        mlxsw_sp_nve_mc_list_ip_del(mlxsw_sp, mc_list, proto, addr);
 649err_add_ip:
 650        mlxsw_sp_nve_mc_list_put(mlxsw_sp, mc_list);
 651        return err;
 652}
 653
 654void 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
 672static void
 673mlxsw_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
 692static 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
 716static int mlxsw_sp_nve_tunnel_init(struct mlxsw_sp *mlxsw_sp,
 717                                    struct mlxsw_sp_nve_config *config)
 718{
 719        struct mlxsw_sp_nve *nve = mlxsw_sp->nve;
 720        const struct mlxsw_sp_nve_ops *ops;
 721        int err;
 722
 723        if (nve->num_nve_tunnels++ != 0)
 724                return 0;
 725
 726        nve->config = *config;
 727
 728        err = mlxsw_sp_kvdl_alloc(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ADJ, 1,
 729                                  &nve->tunnel_index);
 730        if (err)
 731                goto err_kvdl_alloc;
 732
 733        ops = nve->nve_ops_arr[config->type];
 734        err = ops->init(nve, config);
 735        if (err)
 736                goto err_ops_init;
 737
 738        return 0;
 739
 740err_ops_init:
 741        mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ADJ, 1,
 742                           nve->tunnel_index);
 743err_kvdl_alloc:
 744        memset(&nve->config, 0, sizeof(nve->config));
 745        nve->num_nve_tunnels--;
 746        return err;
 747}
 748
 749static void mlxsw_sp_nve_tunnel_fini(struct mlxsw_sp *mlxsw_sp)
 750{
 751        struct mlxsw_sp_nve *nve = mlxsw_sp->nve;
 752        const struct mlxsw_sp_nve_ops *ops;
 753
 754        ops = nve->nve_ops_arr[nve->config.type];
 755
 756        if (mlxsw_sp->nve->num_nve_tunnels == 1) {
 757                ops->fini(nve);
 758                mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ADJ, 1,
 759                                   nve->tunnel_index);
 760                memset(&nve->config, 0, sizeof(nve->config));
 761        }
 762        nve->num_nve_tunnels--;
 763}
 764
 765static void mlxsw_sp_nve_fdb_flush_by_fid(struct mlxsw_sp *mlxsw_sp,
 766                                          u16 fid_index)
 767{
 768        char sfdf_pl[MLXSW_REG_SFDF_LEN];
 769
 770        mlxsw_reg_sfdf_pack(sfdf_pl, MLXSW_REG_SFDF_FLUSH_PER_NVE_AND_FID);
 771        mlxsw_reg_sfdf_fid_set(sfdf_pl, fid_index);
 772        mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfdf), sfdf_pl);
 773}
 774
 775static void mlxsw_sp_nve_fdb_clear_offload(struct mlxsw_sp *mlxsw_sp,
 776                                           const struct mlxsw_sp_fid *fid,
 777                                           const struct net_device *nve_dev,
 778                                           __be32 vni)
 779{
 780        const struct mlxsw_sp_nve_ops *ops;
 781        enum mlxsw_sp_nve_type type;
 782
 783        if (WARN_ON(mlxsw_sp_fid_nve_type(fid, &type)))
 784                return;
 785
 786        ops = mlxsw_sp->nve->nve_ops_arr[type];
 787        ops->fdb_clear_offload(nve_dev, vni);
 788}
 789
 790int mlxsw_sp_nve_fid_enable(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fid *fid,
 791                            struct mlxsw_sp_nve_params *params,
 792                            struct netlink_ext_ack *extack)
 793{
 794        struct mlxsw_sp_nve *nve = mlxsw_sp->nve;
 795        const struct mlxsw_sp_nve_ops *ops;
 796        struct mlxsw_sp_nve_config config;
 797        int err;
 798
 799        ops = nve->nve_ops_arr[params->type];
 800
 801        if (!ops->can_offload(nve, params, extack))
 802                return -EINVAL;
 803
 804        memset(&config, 0, sizeof(config));
 805        ops->nve_config(nve, params, &config);
 806        if (nve->num_nve_tunnels &&
 807            memcmp(&config, &nve->config, sizeof(config))) {
 808                NL_SET_ERR_MSG_MOD(extack, "Conflicting NVE tunnels configuration");
 809                return -EINVAL;
 810        }
 811
 812        err = mlxsw_sp_nve_tunnel_init(mlxsw_sp, &config);
 813        if (err) {
 814                NL_SET_ERR_MSG_MOD(extack, "Failed to initialize NVE tunnel");
 815                return err;
 816        }
 817
 818        err = mlxsw_sp_fid_vni_set(fid, params->type, params->vni,
 819                                   params->dev->ifindex);
 820        if (err) {
 821                NL_SET_ERR_MSG_MOD(extack, "Failed to set VNI on FID");
 822                goto err_fid_vni_set;
 823        }
 824
 825        err = ops->fdb_replay(params->dev, params->vni, extack);
 826        if (err)
 827                goto err_fdb_replay;
 828
 829        return 0;
 830
 831err_fdb_replay:
 832        mlxsw_sp_fid_vni_clear(fid);
 833err_fid_vni_set:
 834        mlxsw_sp_nve_tunnel_fini(mlxsw_sp);
 835        return err;
 836}
 837
 838void mlxsw_sp_nve_fid_disable(struct mlxsw_sp *mlxsw_sp,
 839                              struct mlxsw_sp_fid *fid)
 840{
 841        u16 fid_index = mlxsw_sp_fid_index(fid);
 842        struct net_device *nve_dev;
 843        int nve_ifindex;
 844        __be32 vni;
 845
 846        mlxsw_sp_nve_flood_ip_flush(mlxsw_sp, fid);
 847        mlxsw_sp_nve_fdb_flush_by_fid(mlxsw_sp, fid_index);
 848
 849        if (WARN_ON(mlxsw_sp_fid_nve_ifindex(fid, &nve_ifindex) ||
 850                    mlxsw_sp_fid_vni(fid, &vni)))
 851                goto out;
 852
 853        nve_dev = dev_get_by_index(mlxsw_sp_net(mlxsw_sp), nve_ifindex);
 854        if (!nve_dev)
 855                goto out;
 856
 857        mlxsw_sp_nve_fdb_clear_offload(mlxsw_sp, fid, nve_dev, vni);
 858        mlxsw_sp_fid_fdb_clear_offload(fid, nve_dev);
 859
 860        dev_put(nve_dev);
 861
 862out:
 863        mlxsw_sp_fid_vni_clear(fid);
 864        mlxsw_sp_nve_tunnel_fini(mlxsw_sp);
 865}
 866
 867int mlxsw_sp_port_nve_init(struct mlxsw_sp_port *mlxsw_sp_port)
 868{
 869        struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
 870        char tnqdr_pl[MLXSW_REG_TNQDR_LEN];
 871
 872        mlxsw_reg_tnqdr_pack(tnqdr_pl, mlxsw_sp_port->local_port);
 873        return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(tnqdr), tnqdr_pl);
 874}
 875
 876void mlxsw_sp_port_nve_fini(struct mlxsw_sp_port *mlxsw_sp_port)
 877{
 878}
 879
 880static int mlxsw_sp_nve_qos_init(struct mlxsw_sp *mlxsw_sp)
 881{
 882        char tnqcr_pl[MLXSW_REG_TNQCR_LEN];
 883
 884        mlxsw_reg_tnqcr_pack(tnqcr_pl);
 885        return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(tnqcr), tnqcr_pl);
 886}
 887
 888static int mlxsw_sp_nve_ecn_encap_init(struct mlxsw_sp *mlxsw_sp)
 889{
 890        int i;
 891
 892        /* Iterate over inner ECN values */
 893        for (i = INET_ECN_NOT_ECT; i <= INET_ECN_CE; i++) {
 894                u8 outer_ecn = INET_ECN_encapsulate(0, i);
 895                char tneem_pl[MLXSW_REG_TNEEM_LEN];
 896                int err;
 897
 898                mlxsw_reg_tneem_pack(tneem_pl, i, outer_ecn);
 899                err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(tneem),
 900                                      tneem_pl);
 901                if (err)
 902                        return err;
 903        }
 904
 905        return 0;
 906}
 907
 908static int __mlxsw_sp_nve_ecn_decap_init(struct mlxsw_sp *mlxsw_sp,
 909                                         u8 inner_ecn, u8 outer_ecn)
 910{
 911        char tndem_pl[MLXSW_REG_TNDEM_LEN];
 912        u8 new_inner_ecn;
 913        bool trap_en;
 914
 915        new_inner_ecn = mlxsw_sp_tunnel_ecn_decap(outer_ecn, inner_ecn,
 916                                                  &trap_en);
 917        mlxsw_reg_tndem_pack(tndem_pl, outer_ecn, inner_ecn, new_inner_ecn,
 918                             trap_en, trap_en ? MLXSW_TRAP_ID_DECAP_ECN0 : 0);
 919        return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(tndem), tndem_pl);
 920}
 921
 922static int mlxsw_sp_nve_ecn_decap_init(struct mlxsw_sp *mlxsw_sp)
 923{
 924        int i;
 925
 926        /* Iterate over inner ECN values */
 927        for (i = INET_ECN_NOT_ECT; i <= INET_ECN_CE; i++) {
 928                int j;
 929
 930                /* Iterate over outer ECN values */
 931                for (j = INET_ECN_NOT_ECT; j <= INET_ECN_CE; j++) {
 932                        int err;
 933
 934                        err = __mlxsw_sp_nve_ecn_decap_init(mlxsw_sp, i, j);
 935                        if (err)
 936                                return err;
 937                }
 938        }
 939
 940        return 0;
 941}
 942
 943static int mlxsw_sp_nve_ecn_init(struct mlxsw_sp *mlxsw_sp)
 944{
 945        int err;
 946
 947        err = mlxsw_sp_nve_ecn_encap_init(mlxsw_sp);
 948        if (err)
 949                return err;
 950
 951        return mlxsw_sp_nve_ecn_decap_init(mlxsw_sp);
 952}
 953
 954static int mlxsw_sp_nve_resources_query(struct mlxsw_sp *mlxsw_sp)
 955{
 956        unsigned int max;
 957
 958        if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_NVE_MC_ENTRIES_IPV4) ||
 959            !MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_NVE_MC_ENTRIES_IPV6))
 960                return -EIO;
 961        max = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_NVE_MC_ENTRIES_IPV4);
 962        mlxsw_sp->nve->num_max_mc_entries[MLXSW_SP_L3_PROTO_IPV4] = max;
 963        max = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_NVE_MC_ENTRIES_IPV6);
 964        mlxsw_sp->nve->num_max_mc_entries[MLXSW_SP_L3_PROTO_IPV6] = max;
 965
 966        return 0;
 967}
 968
 969int mlxsw_sp_nve_init(struct mlxsw_sp *mlxsw_sp)
 970{
 971        struct mlxsw_sp_nve *nve;
 972        int err;
 973
 974        nve = kzalloc(sizeof(*mlxsw_sp->nve), GFP_KERNEL);
 975        if (!nve)
 976                return -ENOMEM;
 977        mlxsw_sp->nve = nve;
 978        nve->mlxsw_sp = mlxsw_sp;
 979        nve->nve_ops_arr = mlxsw_sp->nve_ops_arr;
 980
 981        err = rhashtable_init(&nve->mc_list_ht,
 982                              &mlxsw_sp_nve_mc_list_ht_params);
 983        if (err)
 984                goto err_rhashtable_init;
 985
 986        err = mlxsw_sp_nve_qos_init(mlxsw_sp);
 987        if (err)
 988                goto err_nve_qos_init;
 989
 990        err = mlxsw_sp_nve_ecn_init(mlxsw_sp);
 991        if (err)
 992                goto err_nve_ecn_init;
 993
 994        err = mlxsw_sp_nve_resources_query(mlxsw_sp);
 995        if (err)
 996                goto err_nve_resources_query;
 997
 998        return 0;
 999
1000err_nve_resources_query:
1001err_nve_ecn_init:
1002err_nve_qos_init:
1003        rhashtable_destroy(&nve->mc_list_ht);
1004err_rhashtable_init:
1005        mlxsw_sp->nve = NULL;
1006        kfree(nve);
1007        return err;
1008}
1009
1010void mlxsw_sp_nve_fini(struct mlxsw_sp *mlxsw_sp)
1011{
1012        WARN_ON(mlxsw_sp->nve->num_nve_tunnels);
1013        rhashtable_destroy(&mlxsw_sp->nve->mc_list_ht);
1014        kfree(mlxsw_sp->nve);
1015        mlxsw_sp->nve = NULL;
1016}
1017