cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

spectrum_router_xm.c (25213B)


      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}