linux/drivers/net/ethernet/mellanox/mlxsw/spectrum_router_xm.c
<<
>>
Prefs
   1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
   2/* Copyright (c) 2020 Mellanox Technologies. All rights reserved */
   3
   4#include <linux/kernel.h>
   5#include <linux/types.h>
   6#include <linux/rhashtable.h>
   7
   8#include "spectrum.h"
   9#include "core.h"
  10#include "reg.h"
  11#include "spectrum_router.h"
  12
  13#define MLXSW_SP_ROUTER_XM_M_VAL 16
  14
  15static const u8 mlxsw_sp_router_xm_m_val[] = {
  16        [MLXSW_SP_L3_PROTO_IPV4] = MLXSW_SP_ROUTER_XM_M_VAL,
  17        [MLXSW_SP_L3_PROTO_IPV6] = 0, /* Currently unused. */
  18};
  19
  20#define MLXSW_SP_ROUTER_XM_L_VAL_MAX 16
  21
  22struct mlxsw_sp_router_xm {
  23        bool ipv4_supported;
  24        bool ipv6_supported;
  25        unsigned int entries_size;
  26        struct rhashtable ltable_ht;
  27        struct rhashtable flush_ht; /* Stores items about to be flushed from cache */
  28        unsigned int flush_count;
  29        bool flush_all_mode;
  30};
  31
  32struct mlxsw_sp_router_xm_ltable_node {
  33        struct rhash_head ht_node; /* Member of router_xm->ltable_ht */
  34        u16 mindex;
  35        u8 current_lvalue;
  36        refcount_t refcnt;
  37        unsigned int lvalue_ref[MLXSW_SP_ROUTER_XM_L_VAL_MAX + 1];
  38};
  39
  40static const struct rhashtable_params mlxsw_sp_router_xm_ltable_ht_params = {
  41        .key_offset = offsetof(struct mlxsw_sp_router_xm_ltable_node, mindex),
  42        .head_offset = offsetof(struct mlxsw_sp_router_xm_ltable_node, ht_node),
  43        .key_len = sizeof(u16),
  44        .automatic_shrinking = true,
  45};
  46
  47struct mlxsw_sp_router_xm_flush_info {
  48        bool all;
  49        enum mlxsw_sp_l3proto proto;
  50        u16 virtual_router;
  51        u8 prefix_len;
  52        unsigned char addr[sizeof(struct in6_addr)];
  53};
  54
  55struct mlxsw_sp_router_xm_fib_entry {
  56        bool committed;
  57        struct mlxsw_sp_router_xm_ltable_node *ltable_node; /* Parent node */
  58        u16 mindex; /* Store for processing from commit op */
  59        u8 lvalue;
  60        struct mlxsw_sp_router_xm_flush_info flush_info;
  61};
  62
  63#define MLXSW_SP_ROUTE_LL_XM_ENTRIES_MAX \
  64        (MLXSW_REG_XMDR_TRANS_LEN / MLXSW_REG_XMDR_C_LT_ROUTE_V4_LEN)
  65
  66struct mlxsw_sp_fib_entry_op_ctx_xm {
  67        bool initialized;
  68        char xmdr_pl[MLXSW_REG_XMDR_LEN];
  69        unsigned int trans_offset; /* Offset of the current command within one
  70                                    * transaction of XMDR register.
  71                                    */
  72        unsigned int trans_item_len; /* The current command length. This is used
  73                                      * to advance 'trans_offset' when the next
  74                                      * command is appended.
  75                                      */
  76        unsigned int entries_count;
  77        struct mlxsw_sp_router_xm_fib_entry *entries[MLXSW_SP_ROUTE_LL_XM_ENTRIES_MAX];
  78};
  79
  80static int mlxsw_sp_router_ll_xm_init(struct mlxsw_sp *mlxsw_sp, u16 vr_id,
  81                                      enum mlxsw_sp_l3proto proto)
  82{
  83        char rxlte_pl[MLXSW_REG_RXLTE_LEN];
  84
  85        mlxsw_reg_rxlte_pack(rxlte_pl, vr_id,
  86                             (enum mlxsw_reg_rxlte_protocol) proto, true);
  87        return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rxlte), rxlte_pl);
  88}
  89
  90static int mlxsw_sp_router_ll_xm_ralta_write(struct mlxsw_sp *mlxsw_sp, char *xralta_pl)
  91{
  92        return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(xralta), xralta_pl);
  93}
  94
  95static int mlxsw_sp_router_ll_xm_ralst_write(struct mlxsw_sp *mlxsw_sp, char *xralst_pl)
  96{
  97        return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(xralst), xralst_pl);
  98}
  99
 100static int mlxsw_sp_router_ll_xm_raltb_write(struct mlxsw_sp *mlxsw_sp, char *xraltb_pl)
 101{
 102        return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(xraltb), xraltb_pl);
 103}
 104
 105static u16 mlxsw_sp_router_ll_xm_mindex_get4(const u32 addr)
 106{
 107        /* Currently the M-index is set to linear mode. That means it is defined
 108         * as 16 MSB of IP address.
 109         */
 110        return addr >> MLXSW_SP_ROUTER_XM_L_VAL_MAX;
 111}
 112
 113static u16 mlxsw_sp_router_ll_xm_mindex_get6(const unsigned char *addr)
 114{
 115        WARN_ON_ONCE(1);
 116        return 0; /* currently unused */
 117}
 118
 119static void mlxsw_sp_router_ll_xm_op_ctx_check_init(struct mlxsw_sp_fib_entry_op_ctx *op_ctx,
 120                                                    struct mlxsw_sp_fib_entry_op_ctx_xm *op_ctx_xm)
 121{
 122        if (op_ctx->initialized)
 123                return;
 124        op_ctx->initialized = true;
 125
 126        mlxsw_reg_xmdr_pack(op_ctx_xm->xmdr_pl, true);
 127        op_ctx_xm->trans_offset = 0;
 128        op_ctx_xm->entries_count = 0;
 129}
 130
 131static void mlxsw_sp_router_ll_xm_fib_entry_pack(struct mlxsw_sp_fib_entry_op_ctx *op_ctx,
 132                                                 enum mlxsw_sp_l3proto proto,
 133                                                 enum mlxsw_sp_fib_entry_op op,
 134                                                 u16 virtual_router, u8 prefix_len,
 135                                                 unsigned char *addr,
 136                                                 struct mlxsw_sp_fib_entry_priv *priv)
 137{
 138        struct mlxsw_sp_fib_entry_op_ctx_xm *op_ctx_xm = (void *) op_ctx->ll_priv;
 139        struct mlxsw_sp_router_xm_fib_entry *fib_entry = (void *) priv->priv;
 140        struct mlxsw_sp_router_xm_flush_info *flush_info;
 141        enum mlxsw_reg_xmdr_c_ltr_op xmdr_c_ltr_op;
 142        unsigned int len;
 143
 144        mlxsw_sp_router_ll_xm_op_ctx_check_init(op_ctx, op_ctx_xm);
 145
 146        switch (op) {
 147        case MLXSW_SP_FIB_ENTRY_OP_WRITE:
 148                xmdr_c_ltr_op = MLXSW_REG_XMDR_C_LTR_OP_WRITE;
 149                break;
 150        case MLXSW_SP_FIB_ENTRY_OP_UPDATE:
 151                xmdr_c_ltr_op = MLXSW_REG_XMDR_C_LTR_OP_UPDATE;
 152                break;
 153        case MLXSW_SP_FIB_ENTRY_OP_DELETE:
 154                xmdr_c_ltr_op = MLXSW_REG_XMDR_C_LTR_OP_DELETE;
 155                break;
 156        default:
 157                WARN_ON_ONCE(1);
 158                return;
 159        }
 160
 161        switch (proto) {
 162        case MLXSW_SP_L3_PROTO_IPV4:
 163                len = mlxsw_reg_xmdr_c_ltr_pack4(op_ctx_xm->xmdr_pl, op_ctx_xm->trans_offset,
 164                                                 op_ctx_xm->entries_count, xmdr_c_ltr_op,
 165                                                 virtual_router, prefix_len, (u32 *) addr);
 166                fib_entry->mindex = mlxsw_sp_router_ll_xm_mindex_get4(*((u32 *) addr));
 167                break;
 168        case MLXSW_SP_L3_PROTO_IPV6:
 169                len = mlxsw_reg_xmdr_c_ltr_pack6(op_ctx_xm->xmdr_pl, op_ctx_xm->trans_offset,
 170                                                 op_ctx_xm->entries_count, xmdr_c_ltr_op,
 171                                                 virtual_router, prefix_len, addr);
 172                fib_entry->mindex = mlxsw_sp_router_ll_xm_mindex_get6(addr);
 173                break;
 174        default:
 175                WARN_ON_ONCE(1);
 176                return;
 177        }
 178        if (!op_ctx_xm->trans_offset)
 179                op_ctx_xm->trans_item_len = len;
 180        else
 181                WARN_ON_ONCE(op_ctx_xm->trans_item_len != len);
 182
 183        op_ctx_xm->entries[op_ctx_xm->entries_count] = fib_entry;
 184
 185        fib_entry->lvalue = prefix_len > mlxsw_sp_router_xm_m_val[proto] ?
 186                               prefix_len - mlxsw_sp_router_xm_m_val[proto] : 0;
 187
 188        flush_info = &fib_entry->flush_info;
 189        flush_info->proto = proto;
 190        flush_info->virtual_router = virtual_router;
 191        flush_info->prefix_len = prefix_len;
 192        if (addr)
 193                memcpy(flush_info->addr, addr, sizeof(flush_info->addr));
 194        else
 195                memset(flush_info->addr, 0, sizeof(flush_info->addr));
 196}
 197
 198static void
 199mlxsw_sp_router_ll_xm_fib_entry_act_remote_pack(struct mlxsw_sp_fib_entry_op_ctx *op_ctx,
 200                                                enum mlxsw_reg_ralue_trap_action trap_action,
 201                                                u16 trap_id, u32 adjacency_index, u16 ecmp_size)
 202{
 203        struct mlxsw_sp_fib_entry_op_ctx_xm *op_ctx_xm = (void *) op_ctx->ll_priv;
 204
 205        mlxsw_reg_xmdr_c_ltr_act_remote_pack(op_ctx_xm->xmdr_pl, op_ctx_xm->trans_offset,
 206                                             trap_action, trap_id, adjacency_index, ecmp_size);
 207}
 208
 209static void
 210mlxsw_sp_router_ll_xm_fib_entry_act_local_pack(struct mlxsw_sp_fib_entry_op_ctx *op_ctx,
 211                                              enum mlxsw_reg_ralue_trap_action trap_action,
 212                                               u16 trap_id, u16 local_erif)
 213{
 214        struct mlxsw_sp_fib_entry_op_ctx_xm *op_ctx_xm = (void *) op_ctx->ll_priv;
 215
 216        mlxsw_reg_xmdr_c_ltr_act_local_pack(op_ctx_xm->xmdr_pl, op_ctx_xm->trans_offset,
 217                                            trap_action, trap_id, local_erif);
 218}
 219
 220static void
 221mlxsw_sp_router_ll_xm_fib_entry_act_ip2me_pack(struct mlxsw_sp_fib_entry_op_ctx *op_ctx)
 222{
 223        struct mlxsw_sp_fib_entry_op_ctx_xm *op_ctx_xm = (void *) op_ctx->ll_priv;
 224
 225        mlxsw_reg_xmdr_c_ltr_act_ip2me_pack(op_ctx_xm->xmdr_pl, op_ctx_xm->trans_offset);
 226}
 227
 228static void
 229mlxsw_sp_router_ll_xm_fib_entry_act_ip2me_tun_pack(struct mlxsw_sp_fib_entry_op_ctx *op_ctx,
 230                                                   u32 tunnel_ptr)
 231{
 232        struct mlxsw_sp_fib_entry_op_ctx_xm *op_ctx_xm = (void *) op_ctx->ll_priv;
 233
 234        mlxsw_reg_xmdr_c_ltr_act_ip2me_tun_pack(op_ctx_xm->xmdr_pl, op_ctx_xm->trans_offset,
 235                                                tunnel_ptr);
 236}
 237
 238static struct mlxsw_sp_router_xm_ltable_node *
 239mlxsw_sp_router_xm_ltable_node_get(struct mlxsw_sp_router_xm *router_xm, u16 mindex)
 240{
 241        struct mlxsw_sp_router_xm_ltable_node *ltable_node;
 242        int err;
 243
 244        ltable_node = rhashtable_lookup_fast(&router_xm->ltable_ht, &mindex,
 245                                             mlxsw_sp_router_xm_ltable_ht_params);
 246        if (ltable_node) {
 247                refcount_inc(&ltable_node->refcnt);
 248                return ltable_node;
 249        }
 250        ltable_node = kzalloc(sizeof(*ltable_node), GFP_KERNEL);
 251        if (!ltable_node)
 252                return ERR_PTR(-ENOMEM);
 253        ltable_node->mindex = mindex;
 254        refcount_set(&ltable_node->refcnt, 1);
 255
 256        err = rhashtable_insert_fast(&router_xm->ltable_ht, &ltable_node->ht_node,
 257                                     mlxsw_sp_router_xm_ltable_ht_params);
 258        if (err)
 259                goto err_insert;
 260
 261        return ltable_node;
 262
 263err_insert:
 264        kfree(ltable_node);
 265        return ERR_PTR(err);
 266}
 267
 268static void mlxsw_sp_router_xm_ltable_node_put(struct mlxsw_sp_router_xm *router_xm,
 269                                               struct mlxsw_sp_router_xm_ltable_node *ltable_node)
 270{
 271        if (!refcount_dec_and_test(&ltable_node->refcnt))
 272                return;
 273        rhashtable_remove_fast(&router_xm->ltable_ht, &ltable_node->ht_node,
 274                               mlxsw_sp_router_xm_ltable_ht_params);
 275        kfree(ltable_node);
 276}
 277
 278static int mlxsw_sp_router_xm_ltable_lvalue_set(struct mlxsw_sp *mlxsw_sp,
 279                                                struct mlxsw_sp_router_xm_ltable_node *ltable_node)
 280{
 281        char xrmt_pl[MLXSW_REG_XRMT_LEN];
 282
 283        mlxsw_reg_xrmt_pack(xrmt_pl, ltable_node->mindex, ltable_node->current_lvalue);
 284        return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(xrmt), xrmt_pl);
 285}
 286
 287struct mlxsw_sp_router_xm_flush_node {
 288        struct rhash_head ht_node; /* Member of router_xm->flush_ht */
 289        struct list_head list;
 290        struct mlxsw_sp_router_xm_flush_info flush_info;
 291        struct delayed_work dw;
 292        struct mlxsw_sp *mlxsw_sp;
 293        unsigned long start_jiffies;
 294        unsigned int reuses; /* By how many flush calls this was reused. */
 295        refcount_t refcnt;
 296};
 297
 298static const struct rhashtable_params mlxsw_sp_router_xm_flush_ht_params = {
 299        .key_offset = offsetof(struct mlxsw_sp_router_xm_flush_node, flush_info),
 300        .head_offset = offsetof(struct mlxsw_sp_router_xm_flush_node, ht_node),
 301        .key_len = sizeof(struct mlxsw_sp_router_xm_flush_info),
 302        .automatic_shrinking = true,
 303};
 304
 305static struct mlxsw_sp_router_xm_flush_node *
 306mlxsw_sp_router_xm_cache_flush_node_create(struct mlxsw_sp *mlxsw_sp,
 307                                           struct mlxsw_sp_router_xm_flush_info *flush_info)
 308{
 309        struct mlxsw_sp_router_xm *router_xm = mlxsw_sp->router->xm;
 310        struct mlxsw_sp_router_xm_flush_node *flush_node;
 311        int err;
 312
 313        flush_node = kzalloc(sizeof(*flush_node), GFP_KERNEL);
 314        if (!flush_node)
 315                return ERR_PTR(-ENOMEM);
 316
 317        flush_node->flush_info = *flush_info;
 318        err = rhashtable_insert_fast(&router_xm->flush_ht, &flush_node->ht_node,
 319                                     mlxsw_sp_router_xm_flush_ht_params);
 320        if (err) {
 321                kfree(flush_node);
 322                return ERR_PTR(err);
 323        }
 324        router_xm->flush_count++;
 325        flush_node->mlxsw_sp = mlxsw_sp;
 326        flush_node->start_jiffies = jiffies;
 327        refcount_set(&flush_node->refcnt, 1);
 328        return flush_node;
 329}
 330
 331static void
 332mlxsw_sp_router_xm_cache_flush_node_hold(struct mlxsw_sp_router_xm_flush_node *flush_node)
 333{
 334        if (!flush_node)
 335                return;
 336        refcount_inc(&flush_node->refcnt);
 337}
 338
 339static void
 340mlxsw_sp_router_xm_cache_flush_node_put(struct mlxsw_sp_router_xm_flush_node *flush_node)
 341{
 342        if (!flush_node || !refcount_dec_and_test(&flush_node->refcnt))
 343                return;
 344        kfree(flush_node);
 345}
 346
 347static void
 348mlxsw_sp_router_xm_cache_flush_node_destroy(struct mlxsw_sp *mlxsw_sp,
 349                                            struct mlxsw_sp_router_xm_flush_node *flush_node)
 350{
 351        struct mlxsw_sp_router_xm *router_xm = mlxsw_sp->router->xm;
 352
 353        router_xm->flush_count--;
 354        rhashtable_remove_fast(&router_xm->flush_ht, &flush_node->ht_node,
 355                               mlxsw_sp_router_xm_flush_ht_params);
 356        mlxsw_sp_router_xm_cache_flush_node_put(flush_node);
 357}
 358
 359static u32 mlxsw_sp_router_xm_flush_mask4(u8 prefix_len)
 360{
 361        return GENMASK(31, 32 - prefix_len);
 362}
 363
 364static unsigned char *mlxsw_sp_router_xm_flush_mask6(u8 prefix_len)
 365{
 366        static unsigned char mask[sizeof(struct in6_addr)];
 367
 368        memset(mask, 0, sizeof(mask));
 369        memset(mask, 0xff, prefix_len / 8);
 370        mask[prefix_len / 8] = GENMASK(8, 8 - prefix_len % 8);
 371        return mask;
 372}
 373
 374#define MLXSW_SP_ROUTER_XM_CACHE_PARALLEL_FLUSHES_LIMIT 15
 375#define MLXSW_SP_ROUTER_XM_CACHE_FLUSH_ALL_MIN_REUSES 15
 376#define MLXSW_SP_ROUTER_XM_CACHE_DELAY 50 /* usecs */
 377#define MLXSW_SP_ROUTER_XM_CACHE_MAX_WAIT (MLXSW_SP_ROUTER_XM_CACHE_DELAY * 10)
 378
 379static void mlxsw_sp_router_xm_cache_flush_work(struct work_struct *work)
 380{
 381        struct mlxsw_sp_router_xm_flush_info *flush_info;
 382        struct mlxsw_sp_router_xm_flush_node *flush_node;
 383        char rlcmld_pl[MLXSW_REG_RLCMLD_LEN];
 384        enum mlxsw_reg_rlcmld_select select;
 385        struct mlxsw_sp *mlxsw_sp;
 386        u32 addr4;
 387        int err;
 388
 389        flush_node = container_of(work, struct mlxsw_sp_router_xm_flush_node,
 390                                  dw.work);
 391        mlxsw_sp = flush_node->mlxsw_sp;
 392        flush_info = &flush_node->flush_info;
 393
 394        if (flush_info->all) {
 395                char rlpmce_pl[MLXSW_REG_RLPMCE_LEN];
 396
 397                mlxsw_reg_rlpmce_pack(rlpmce_pl, true, false);
 398                err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rlpmce),
 399                                      rlpmce_pl);
 400                if (err)
 401                        dev_err(mlxsw_sp->bus_info->dev, "Failed to flush XM cache\n");
 402
 403                if (flush_node->reuses <
 404                    MLXSW_SP_ROUTER_XM_CACHE_FLUSH_ALL_MIN_REUSES)
 405                        /* Leaving flush-all mode. */
 406                        mlxsw_sp->router->xm->flush_all_mode = false;
 407                goto out;
 408        }
 409
 410        select = MLXSW_REG_RLCMLD_SELECT_M_AND_ML_ENTRIES;
 411
 412        switch (flush_info->proto) {
 413        case MLXSW_SP_L3_PROTO_IPV4:
 414                addr4 = *((u32 *) flush_info->addr);
 415                addr4 &= mlxsw_sp_router_xm_flush_mask4(flush_info->prefix_len);
 416
 417                /* In case the flush prefix length is bigger than M-value,
 418                 * it makes no sense to flush M entries. So just flush
 419                 * the ML entries.
 420                 */
 421                if (flush_info->prefix_len > MLXSW_SP_ROUTER_XM_M_VAL)
 422                        select = MLXSW_REG_RLCMLD_SELECT_ML_ENTRIES;
 423
 424                mlxsw_reg_rlcmld_pack4(rlcmld_pl, select,
 425                                       flush_info->virtual_router, addr4,
 426                                       mlxsw_sp_router_xm_flush_mask4(flush_info->prefix_len));
 427                break;
 428        case MLXSW_SP_L3_PROTO_IPV6:
 429                mlxsw_reg_rlcmld_pack6(rlcmld_pl, select,
 430                                       flush_info->virtual_router, flush_info->addr,
 431                                       mlxsw_sp_router_xm_flush_mask6(flush_info->prefix_len));
 432                break;
 433        default:
 434                WARN_ON(true);
 435                goto out;
 436        }
 437        err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rlcmld), rlcmld_pl);
 438        if (err)
 439                dev_err(mlxsw_sp->bus_info->dev, "Failed to flush XM cache\n");
 440
 441out:
 442        mlxsw_sp_router_xm_cache_flush_node_destroy(mlxsw_sp, flush_node);
 443}
 444
 445static bool
 446mlxsw_sp_router_xm_cache_flush_may_cancel(struct mlxsw_sp_router_xm_flush_node *flush_node)
 447{
 448        unsigned long max_wait = usecs_to_jiffies(MLXSW_SP_ROUTER_XM_CACHE_MAX_WAIT);
 449        unsigned long delay = usecs_to_jiffies(MLXSW_SP_ROUTER_XM_CACHE_DELAY);
 450
 451        /* In case there is the same flushing work pending, check
 452         * if we can consolidate with it. We can do it up to MAX_WAIT.
 453         * Cancel the delayed work. If the work was still pending.
 454         */
 455        if (time_is_before_jiffies(flush_node->start_jiffies + max_wait - delay) &&
 456            cancel_delayed_work_sync(&flush_node->dw))
 457                return true;
 458        return false;
 459}
 460
 461static int
 462mlxsw_sp_router_xm_cache_flush_schedule(struct mlxsw_sp *mlxsw_sp,
 463                                        struct mlxsw_sp_router_xm_flush_info *flush_info)
 464{
 465        unsigned long delay = usecs_to_jiffies(MLXSW_SP_ROUTER_XM_CACHE_DELAY);
 466        struct mlxsw_sp_router_xm_flush_info flush_all_info = {.all = true};
 467        struct mlxsw_sp_router_xm *router_xm = mlxsw_sp->router->xm;
 468        struct mlxsw_sp_router_xm_flush_node *flush_node;
 469
 470        /* Check if the queued number of flushes reached critical amount after
 471         * which it is better to just flush the whole cache.
 472         */
 473        if (router_xm->flush_count == MLXSW_SP_ROUTER_XM_CACHE_PARALLEL_FLUSHES_LIMIT)
 474                /* Entering flush-all mode. */
 475                router_xm->flush_all_mode = true;
 476
 477        if (router_xm->flush_all_mode)
 478                flush_info = &flush_all_info;
 479
 480        rcu_read_lock();
 481        flush_node = rhashtable_lookup_fast(&router_xm->flush_ht, flush_info,
 482                                            mlxsw_sp_router_xm_flush_ht_params);
 483        /* Take a reference so the object is not freed before possible
 484         * delayed work cancel could be done.
 485         */
 486        mlxsw_sp_router_xm_cache_flush_node_hold(flush_node);
 487        rcu_read_unlock();
 488
 489        if (flush_node && mlxsw_sp_router_xm_cache_flush_may_cancel(flush_node)) {
 490                flush_node->reuses++;
 491                mlxsw_sp_router_xm_cache_flush_node_put(flush_node);
 492                 /* Original work was within wait period and was canceled.
 493                  * That means that the reference is still held and the
 494                  * flush_node_put() call above did not free the flush_node.
 495                  * Reschedule it with fresh delay.
 496                  */
 497                goto schedule_work;
 498        } else {
 499                mlxsw_sp_router_xm_cache_flush_node_put(flush_node);
 500        }
 501
 502        flush_node = mlxsw_sp_router_xm_cache_flush_node_create(mlxsw_sp, flush_info);
 503        if (IS_ERR(flush_node))
 504                return PTR_ERR(flush_node);
 505        INIT_DELAYED_WORK(&flush_node->dw, mlxsw_sp_router_xm_cache_flush_work);
 506
 507schedule_work:
 508        mlxsw_core_schedule_dw(&flush_node->dw, delay);
 509        return 0;
 510}
 511
 512static int
 513mlxsw_sp_router_xm_ml_entry_add(struct mlxsw_sp *mlxsw_sp,
 514                                struct mlxsw_sp_router_xm_fib_entry *fib_entry)
 515{
 516        struct mlxsw_sp_router_xm *router_xm = mlxsw_sp->router->xm;
 517        struct mlxsw_sp_router_xm_ltable_node *ltable_node;
 518        u8 lvalue = fib_entry->lvalue;
 519        int err;
 520
 521        ltable_node = mlxsw_sp_router_xm_ltable_node_get(router_xm,
 522                                                         fib_entry->mindex);
 523        if (IS_ERR(ltable_node))
 524                return PTR_ERR(ltable_node);
 525        if (lvalue > ltable_node->current_lvalue) {
 526                /* The L-value is bigger then the one currently set, update. */
 527                ltable_node->current_lvalue = lvalue;
 528                err = mlxsw_sp_router_xm_ltable_lvalue_set(mlxsw_sp,
 529                                                           ltable_node);
 530                if (err)
 531                        goto err_lvalue_set;
 532
 533                /* The L value for prefix/M is increased.
 534                 * Therefore, all entries in M and ML caches matching
 535                 * {prefix/M, proto, VR} need to be flushed. Set the flush
 536                 * prefix length to M to achieve that.
 537                 */
 538                fib_entry->flush_info.prefix_len = MLXSW_SP_ROUTER_XM_M_VAL;
 539        }
 540
 541        ltable_node->lvalue_ref[lvalue]++;
 542        fib_entry->ltable_node = ltable_node;
 543
 544        return 0;
 545
 546err_lvalue_set:
 547        mlxsw_sp_router_xm_ltable_node_put(router_xm, ltable_node);
 548        return err;
 549}
 550
 551static void
 552mlxsw_sp_router_xm_ml_entry_del(struct mlxsw_sp *mlxsw_sp,
 553                                struct mlxsw_sp_router_xm_fib_entry *fib_entry)
 554{
 555        struct mlxsw_sp_router_xm_ltable_node *ltable_node =
 556                                                        fib_entry->ltable_node;
 557        struct mlxsw_sp_router_xm *router_xm = mlxsw_sp->router->xm;
 558        u8 lvalue = fib_entry->lvalue;
 559
 560        ltable_node->lvalue_ref[lvalue]--;
 561        if (lvalue == ltable_node->current_lvalue && lvalue &&
 562            !ltable_node->lvalue_ref[lvalue]) {
 563                u8 new_lvalue = lvalue - 1;
 564
 565                /* Find the biggest L-value left out there. */
 566                while (new_lvalue > 0 && !ltable_node->lvalue_ref[lvalue])
 567                        new_lvalue--;
 568
 569                ltable_node->current_lvalue = new_lvalue;
 570                mlxsw_sp_router_xm_ltable_lvalue_set(mlxsw_sp, ltable_node);
 571
 572                /* The L value for prefix/M is decreased.
 573                 * Therefore, all entries in M and ML caches matching
 574                 * {prefix/M, proto, VR} need to be flushed. Set the flush
 575                 * prefix length to M to achieve that.
 576                 */
 577                fib_entry->flush_info.prefix_len = MLXSW_SP_ROUTER_XM_M_VAL;
 578        }
 579        mlxsw_sp_router_xm_ltable_node_put(router_xm, ltable_node);
 580}
 581
 582static int
 583mlxsw_sp_router_xm_ml_entries_add(struct mlxsw_sp *mlxsw_sp,
 584                                  struct mlxsw_sp_fib_entry_op_ctx_xm *op_ctx_xm)
 585{
 586        struct mlxsw_sp_router_xm_fib_entry *fib_entry;
 587        int err;
 588        int i;
 589
 590        for (i = 0; i < op_ctx_xm->entries_count; i++) {
 591                fib_entry = op_ctx_xm->entries[i];
 592                err = mlxsw_sp_router_xm_ml_entry_add(mlxsw_sp, fib_entry);
 593                if (err)
 594                        goto rollback;
 595        }
 596        return 0;
 597
 598rollback:
 599        for (i--; i >= 0; i--) {
 600                fib_entry = op_ctx_xm->entries[i];
 601                mlxsw_sp_router_xm_ml_entry_del(mlxsw_sp, fib_entry);
 602        }
 603        return err;
 604}
 605
 606static void
 607mlxsw_sp_router_xm_ml_entries_del(struct mlxsw_sp *mlxsw_sp,
 608                                  struct mlxsw_sp_fib_entry_op_ctx_xm *op_ctx_xm)
 609{
 610        struct mlxsw_sp_router_xm_fib_entry *fib_entry;
 611        int i;
 612
 613        for (i = 0; i < op_ctx_xm->entries_count; i++) {
 614                fib_entry = op_ctx_xm->entries[i];
 615                mlxsw_sp_router_xm_ml_entry_del(mlxsw_sp, fib_entry);
 616        }
 617}
 618
 619static void
 620mlxsw_sp_router_xm_ml_entries_cache_flush(struct mlxsw_sp *mlxsw_sp,
 621                                          struct mlxsw_sp_fib_entry_op_ctx_xm *op_ctx_xm)
 622{
 623        struct mlxsw_sp_router_xm_fib_entry *fib_entry;
 624        int err;
 625        int i;
 626
 627        for (i = 0; i < op_ctx_xm->entries_count; i++) {
 628                fib_entry = op_ctx_xm->entries[i];
 629                err = mlxsw_sp_router_xm_cache_flush_schedule(mlxsw_sp,
 630                                                              &fib_entry->flush_info);
 631                if (err)
 632                        dev_err(mlxsw_sp->bus_info->dev, "Failed to flush XM cache\n");
 633        }
 634}
 635
 636static int mlxsw_sp_router_ll_xm_fib_entry_commit(struct mlxsw_sp *mlxsw_sp,
 637                                                  struct mlxsw_sp_fib_entry_op_ctx *op_ctx,
 638                                                  bool *postponed_for_bulk)
 639{
 640        struct mlxsw_sp_fib_entry_op_ctx_xm *op_ctx_xm = (void *) op_ctx->ll_priv;
 641        struct mlxsw_sp_router_xm_fib_entry *fib_entry;
 642        u8 num_rec;
 643        int err;
 644        int i;
 645
 646        op_ctx_xm->trans_offset += op_ctx_xm->trans_item_len;
 647        op_ctx_xm->entries_count++;
 648
 649        /* Check if bulking is possible and there is still room for another
 650         * FIB entry record. The size of 'trans_item_len' is either size of IPv4
 651         * command or size of IPv6 command. Not possible to mix those in a
 652         * single XMDR write.
 653         */
 654        if (op_ctx->bulk_ok &&
 655            op_ctx_xm->trans_offset + op_ctx_xm->trans_item_len <= MLXSW_REG_XMDR_TRANS_LEN) {
 656                if (postponed_for_bulk)
 657                        *postponed_for_bulk = true;
 658                return 0;
 659        }
 660
 661        if (op_ctx->event == FIB_EVENT_ENTRY_REPLACE) {
 662                /* The L-table is updated inside. It has to be done before
 663                 * the prefix is inserted.
 664                 */
 665                err = mlxsw_sp_router_xm_ml_entries_add(mlxsw_sp, op_ctx_xm);
 666                if (err)
 667                        goto out;
 668        }
 669
 670        err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(xmdr), op_ctx_xm->xmdr_pl);
 671        if (err)
 672                goto out;
 673        num_rec = mlxsw_reg_xmdr_num_rec_get(op_ctx_xm->xmdr_pl);
 674        if (num_rec > op_ctx_xm->entries_count) {
 675                dev_err(mlxsw_sp->bus_info->dev, "Invalid XMDR number of records\n");
 676                err = -EIO;
 677                goto out;
 678        }
 679        for (i = 0; i < num_rec; i++) {
 680                if (!mlxsw_reg_xmdr_reply_vect_get(op_ctx_xm->xmdr_pl, i)) {
 681                        dev_err(mlxsw_sp->bus_info->dev, "Command send over XMDR failed\n");
 682                        err = -EIO;
 683                        goto out;
 684                } else {
 685                        fib_entry = op_ctx_xm->entries[i];
 686                        fib_entry->committed = true;
 687                }
 688        }
 689
 690        if (op_ctx->event == FIB_EVENT_ENTRY_DEL)
 691                /* The L-table is updated inside. It has to be done after
 692                 * the prefix was removed.
 693                 */
 694                mlxsw_sp_router_xm_ml_entries_del(mlxsw_sp, op_ctx_xm);
 695
 696        /* At the very end, do the XLT cache flushing to evict stale
 697         * M and ML cache entries after prefixes were inserted/removed.
 698         */
 699        mlxsw_sp_router_xm_ml_entries_cache_flush(mlxsw_sp, op_ctx_xm);
 700
 701out:
 702        /* Next pack call is going to do reinitialization */
 703        op_ctx->initialized = false;
 704        return err;
 705}
 706
 707static bool mlxsw_sp_router_ll_xm_fib_entry_is_committed(struct mlxsw_sp_fib_entry_priv *priv)
 708{
 709        struct mlxsw_sp_router_xm_fib_entry *fib_entry = (void *) priv->priv;
 710
 711        return fib_entry->committed;
 712}
 713
 714const struct mlxsw_sp_router_ll_ops mlxsw_sp_router_ll_xm_ops = {
 715        .init = mlxsw_sp_router_ll_xm_init,
 716        .ralta_write = mlxsw_sp_router_ll_xm_ralta_write,
 717        .ralst_write = mlxsw_sp_router_ll_xm_ralst_write,
 718        .raltb_write = mlxsw_sp_router_ll_xm_raltb_write,
 719        .fib_entry_op_ctx_size = sizeof(struct mlxsw_sp_fib_entry_op_ctx_xm),
 720        .fib_entry_priv_size = sizeof(struct mlxsw_sp_router_xm_fib_entry),
 721        .fib_entry_pack = mlxsw_sp_router_ll_xm_fib_entry_pack,
 722        .fib_entry_act_remote_pack = mlxsw_sp_router_ll_xm_fib_entry_act_remote_pack,
 723        .fib_entry_act_local_pack = mlxsw_sp_router_ll_xm_fib_entry_act_local_pack,
 724        .fib_entry_act_ip2me_pack = mlxsw_sp_router_ll_xm_fib_entry_act_ip2me_pack,
 725        .fib_entry_act_ip2me_tun_pack = mlxsw_sp_router_ll_xm_fib_entry_act_ip2me_tun_pack,
 726        .fib_entry_commit = mlxsw_sp_router_ll_xm_fib_entry_commit,
 727        .fib_entry_is_committed = mlxsw_sp_router_ll_xm_fib_entry_is_committed,
 728};
 729
 730#define MLXSW_SP_ROUTER_XM_MINDEX_SIZE (64 * 1024)
 731
 732int mlxsw_sp_router_xm_init(struct mlxsw_sp *mlxsw_sp)
 733{
 734        struct mlxsw_sp_router_xm *router_xm;
 735        char rxltm_pl[MLXSW_REG_RXLTM_LEN];
 736        char xltq_pl[MLXSW_REG_XLTQ_LEN];
 737        u32 mindex_size;
 738        u16 device_id;
 739        int err;
 740
 741        if (!mlxsw_sp->bus_info->xm_exists)
 742                return 0;
 743
 744        router_xm = kzalloc(sizeof(*router_xm), GFP_KERNEL);
 745        if (!router_xm)
 746                return -ENOMEM;
 747
 748        mlxsw_reg_xltq_pack(xltq_pl);
 749        err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(xltq), xltq_pl);
 750        if (err)
 751                goto err_xltq_query;
 752        mlxsw_reg_xltq_unpack(xltq_pl, &device_id, &router_xm->ipv4_supported,
 753                              &router_xm->ipv6_supported, &router_xm->entries_size, &mindex_size);
 754
 755        if (device_id != MLXSW_REG_XLTQ_XM_DEVICE_ID_XLT) {
 756                dev_err(mlxsw_sp->bus_info->dev, "Invalid XM device id\n");
 757                err = -EINVAL;
 758                goto err_device_id_check;
 759        }
 760
 761        if (mindex_size != MLXSW_SP_ROUTER_XM_MINDEX_SIZE) {
 762                dev_err(mlxsw_sp->bus_info->dev, "Unexpected M-index size\n");
 763                err = -EINVAL;
 764                goto err_mindex_size_check;
 765        }
 766
 767        mlxsw_reg_rxltm_pack(rxltm_pl, mlxsw_sp_router_xm_m_val[MLXSW_SP_L3_PROTO_IPV4],
 768                             mlxsw_sp_router_xm_m_val[MLXSW_SP_L3_PROTO_IPV6]);
 769        err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rxltm), rxltm_pl);
 770        if (err)
 771                goto err_rxltm_write;
 772
 773        err = rhashtable_init(&router_xm->ltable_ht, &mlxsw_sp_router_xm_ltable_ht_params);
 774        if (err)
 775                goto err_ltable_ht_init;
 776
 777        err = rhashtable_init(&router_xm->flush_ht, &mlxsw_sp_router_xm_flush_ht_params);
 778        if (err)
 779                goto err_flush_ht_init;
 780
 781        mlxsw_sp->router->xm = router_xm;
 782        return 0;
 783
 784err_flush_ht_init:
 785        rhashtable_destroy(&router_xm->ltable_ht);
 786err_ltable_ht_init:
 787err_rxltm_write:
 788err_mindex_size_check:
 789err_device_id_check:
 790err_xltq_query:
 791        kfree(router_xm);
 792        return err;
 793}
 794
 795void mlxsw_sp_router_xm_fini(struct mlxsw_sp *mlxsw_sp)
 796{
 797        struct mlxsw_sp_router_xm *router_xm = mlxsw_sp->router->xm;
 798
 799        if (!mlxsw_sp->bus_info->xm_exists)
 800                return;
 801
 802        rhashtable_destroy(&router_xm->flush_ht);
 803        rhashtable_destroy(&router_xm->ltable_ht);
 804        kfree(router_xm);
 805}
 806
 807bool mlxsw_sp_router_xm_ipv4_is_supported(const struct mlxsw_sp *mlxsw_sp)
 808{
 809        struct mlxsw_sp_router_xm *router_xm = mlxsw_sp->router->xm;
 810
 811        return router_xm && router_xm->ipv4_supported;
 812}
 813