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(<able_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(<able_node->refcnt, 1);
255
256 err = rhashtable_insert_fast(&router_xm->ltable_ht, <able_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(<able_node->refcnt))
272 return;
273 rhashtable_remove_fast(&router_xm->ltable_ht, <able_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