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 
15 static 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 
22 struct 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 
32 struct 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 
40 static 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 
47 struct 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 
55 struct 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 
66 struct 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 
mlxsw_sp_router_ll_xm_init(struct mlxsw_sp * mlxsw_sp,u16 vr_id,enum mlxsw_sp_l3proto proto)80 static 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 
mlxsw_sp_router_ll_xm_ralta_write(struct mlxsw_sp * mlxsw_sp,char * xralta_pl)90 static 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 
mlxsw_sp_router_ll_xm_ralst_write(struct mlxsw_sp * mlxsw_sp,char * xralst_pl)95 static 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 
mlxsw_sp_router_ll_xm_raltb_write(struct mlxsw_sp * mlxsw_sp,char * xraltb_pl)100 static 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 
mlxsw_sp_router_ll_xm_mindex_get4(const u32 addr)105 static 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 
mlxsw_sp_router_ll_xm_mindex_get6(const unsigned char * addr)113 static 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 
mlxsw_sp_router_ll_xm_op_ctx_check_init(struct mlxsw_sp_fib_entry_op_ctx * op_ctx,struct mlxsw_sp_fib_entry_op_ctx_xm * op_ctx_xm)119 static 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 
mlxsw_sp_router_ll_xm_fib_entry_pack(struct mlxsw_sp_fib_entry_op_ctx * op_ctx,enum mlxsw_sp_l3proto proto,enum mlxsw_sp_fib_entry_op op,u16 virtual_router,u8 prefix_len,unsigned char * addr,struct mlxsw_sp_fib_entry_priv * priv)131 static 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 
198 static void
mlxsw_sp_router_ll_xm_fib_entry_act_remote_pack(struct mlxsw_sp_fib_entry_op_ctx * op_ctx,enum mlxsw_reg_ralue_trap_action trap_action,u16 trap_id,u32 adjacency_index,u16 ecmp_size)199 mlxsw_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 
209 static void
mlxsw_sp_router_ll_xm_fib_entry_act_local_pack(struct mlxsw_sp_fib_entry_op_ctx * op_ctx,enum mlxsw_reg_ralue_trap_action trap_action,u16 trap_id,u16 local_erif)210 mlxsw_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 
220 static void
mlxsw_sp_router_ll_xm_fib_entry_act_ip2me_pack(struct mlxsw_sp_fib_entry_op_ctx * op_ctx)221 mlxsw_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 
228 static void
mlxsw_sp_router_ll_xm_fib_entry_act_ip2me_tun_pack(struct mlxsw_sp_fib_entry_op_ctx * op_ctx,u32 tunnel_ptr)229 mlxsw_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 
238 static struct mlxsw_sp_router_xm_ltable_node *
mlxsw_sp_router_xm_ltable_node_get(struct mlxsw_sp_router_xm * router_xm,u16 mindex)239 mlxsw_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 
263 err_insert:
264 	kfree(ltable_node);
265 	return ERR_PTR(err);
266 }
267 
mlxsw_sp_router_xm_ltable_node_put(struct mlxsw_sp_router_xm * router_xm,struct mlxsw_sp_router_xm_ltable_node * ltable_node)268 static 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 
mlxsw_sp_router_xm_ltable_lvalue_set(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_router_xm_ltable_node * ltable_node)278 static 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 
287 struct 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 
298 static 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 
305 static struct mlxsw_sp_router_xm_flush_node *
mlxsw_sp_router_xm_cache_flush_node_create(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_router_xm_flush_info * flush_info)306 mlxsw_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 
331 static void
mlxsw_sp_router_xm_cache_flush_node_hold(struct mlxsw_sp_router_xm_flush_node * flush_node)332 mlxsw_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 
339 static void
mlxsw_sp_router_xm_cache_flush_node_put(struct mlxsw_sp_router_xm_flush_node * flush_node)340 mlxsw_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 
347 static void
mlxsw_sp_router_xm_cache_flush_node_destroy(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_router_xm_flush_node * flush_node)348 mlxsw_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 
mlxsw_sp_router_xm_flush_mask4(u8 prefix_len)359 static u32 mlxsw_sp_router_xm_flush_mask4(u8 prefix_len)
360 {
361 	return GENMASK(31, 32 - prefix_len);
362 }
363 
mlxsw_sp_router_xm_flush_mask6(u8 prefix_len)364 static 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 
mlxsw_sp_router_xm_cache_flush_work(struct work_struct * work)379 static 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 
441 out:
442 	mlxsw_sp_router_xm_cache_flush_node_destroy(mlxsw_sp, flush_node);
443 }
444 
445 static bool
mlxsw_sp_router_xm_cache_flush_may_cancel(struct mlxsw_sp_router_xm_flush_node * flush_node)446 mlxsw_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 
461 static int
mlxsw_sp_router_xm_cache_flush_schedule(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_router_xm_flush_info * flush_info)462 mlxsw_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 
507 schedule_work:
508 	mlxsw_core_schedule_dw(&flush_node->dw, delay);
509 	return 0;
510 }
511 
512 static int
mlxsw_sp_router_xm_ml_entry_add(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_router_xm_fib_entry * fib_entry)513 mlxsw_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 
546 err_lvalue_set:
547 	mlxsw_sp_router_xm_ltable_node_put(router_xm, ltable_node);
548 	return err;
549 }
550 
551 static void
mlxsw_sp_router_xm_ml_entry_del(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_router_xm_fib_entry * fib_entry)552 mlxsw_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 
582 static int
mlxsw_sp_router_xm_ml_entries_add(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_fib_entry_op_ctx_xm * op_ctx_xm)583 mlxsw_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 
598 rollback:
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 
606 static void
mlxsw_sp_router_xm_ml_entries_del(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_fib_entry_op_ctx_xm * op_ctx_xm)607 mlxsw_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 
619 static void
mlxsw_sp_router_xm_ml_entries_cache_flush(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_fib_entry_op_ctx_xm * op_ctx_xm)620 mlxsw_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 
mlxsw_sp_router_ll_xm_fib_entry_commit(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_fib_entry_op_ctx * op_ctx,bool * postponed_for_bulk)636 static 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 
701 out:
702 	/* Next pack call is going to do reinitialization */
703 	op_ctx->initialized = false;
704 	return err;
705 }
706 
mlxsw_sp_router_ll_xm_fib_entry_is_committed(struct mlxsw_sp_fib_entry_priv * priv)707 static 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 
714 const 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 
mlxsw_sp_router_xm_init(struct mlxsw_sp * mlxsw_sp)732 int 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 
784 err_flush_ht_init:
785 	rhashtable_destroy(&router_xm->ltable_ht);
786 err_ltable_ht_init:
787 err_rxltm_write:
788 err_mindex_size_check:
789 err_device_id_check:
790 err_xltq_query:
791 	kfree(router_xm);
792 	return err;
793 }
794 
mlxsw_sp_router_xm_fini(struct mlxsw_sp * mlxsw_sp)795 void 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 
mlxsw_sp_router_xm_ipv4_is_supported(const struct mlxsw_sp * mlxsw_sp)807 bool 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