Skip to content

Commit 2dfad87

Browse files
Jiri Pirkokuba-moo
authored andcommitted
mlxsw: spectrum_router_xm: Introduce basic XM cache flushing
Upon route insertion and removal, it is needed to flush possibly cached entries from the XM cache. Extend XM op context to carry information needed for the flush. Implement the flush in delayed work since for HW design reasons there is a need to wait 50usec before the flush can be done. If during this time comes the same flush request, consolidate it to the first one. Implement this queued flushes by a hashtable. v2: * Fix GENMASK() high bit Signed-off-by: Jiri Pirko <[email protected]> Signed-off-by: Ido Schimmel <[email protected]> Signed-off-by: Jakub Kicinski <[email protected]>
1 parent 0692546 commit 2dfad87

File tree

1 file changed

+291
-0
lines changed

1 file changed

+291
-0
lines changed

drivers/net/ethernet/mellanox/mlxsw/spectrum_router_xm.c

Lines changed: 291 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ struct mlxsw_sp_router_xm {
2424
bool ipv6_supported;
2525
unsigned int entries_size;
2626
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;
2730
};
2831

2932
struct mlxsw_sp_router_xm_ltable_node {
@@ -41,11 +44,20 @@ static const struct rhashtable_params mlxsw_sp_router_xm_ltable_ht_params = {
4144
.automatic_shrinking = true,
4245
};
4346

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+
4455
struct mlxsw_sp_router_xm_fib_entry {
4556
bool committed;
4657
struct mlxsw_sp_router_xm_ltable_node *ltable_node; /* Parent node */
4758
u16 mindex; /* Store for processing from commit op */
4859
u8 lvalue;
60+
struct mlxsw_sp_router_xm_flush_info flush_info;
4961
};
5062

5163
#define MLXSW_SP_ROUTE_LL_XM_ENTRIES_MAX \
@@ -125,6 +137,7 @@ static void mlxsw_sp_router_ll_xm_fib_entry_pack(struct mlxsw_sp_fib_entry_op_ct
125137
{
126138
struct mlxsw_sp_fib_entry_op_ctx_xm *op_ctx_xm = (void *) op_ctx->ll_priv;
127139
struct mlxsw_sp_router_xm_fib_entry *fib_entry = (void *) priv->priv;
140+
struct mlxsw_sp_router_xm_flush_info *flush_info;
128141
enum mlxsw_reg_xmdr_c_ltr_op xmdr_c_ltr_op;
129142
unsigned int len;
130143

@@ -171,6 +184,15 @@ static void mlxsw_sp_router_ll_xm_fib_entry_pack(struct mlxsw_sp_fib_entry_op_ct
171184

172185
fib_entry->lvalue = prefix_len > mlxsw_sp_router_xm_m_val[proto] ?
173186
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));
174196
}
175197

176198
static void
@@ -262,6 +284,231 @@ static int mlxsw_sp_router_xm_ltable_lvalue_set(struct mlxsw_sp *mlxsw_sp,
262284
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(xrmt), xrmt_pl);
263285
}
264286

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 *
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
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
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
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+
359+
static u32 mlxsw_sp_router_xm_flush_mask4(u8 prefix_len)
360+
{
361+
return GENMASK(31, 32 - prefix_len);
362+
}
363+
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+
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
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
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+
265512
static int
266513
mlxsw_sp_router_xm_ml_entry_add(struct mlxsw_sp *mlxsw_sp,
267514
struct mlxsw_sp_router_xm_fib_entry *fib_entry)
@@ -282,10 +529,18 @@ mlxsw_sp_router_xm_ml_entry_add(struct mlxsw_sp *mlxsw_sp,
282529
ltable_node);
283530
if (err)
284531
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;
285539
}
286540

287541
ltable_node->lvalue_ref[lvalue]++;
288542
fib_entry->ltable_node = ltable_node;
543+
289544
return 0;
290545

291546
err_lvalue_set:
@@ -313,6 +568,13 @@ mlxsw_sp_router_xm_ml_entry_del(struct mlxsw_sp *mlxsw_sp,
313568

314569
ltable_node->current_lvalue = new_lvalue;
315570
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;
316578
}
317579
mlxsw_sp_router_xm_ltable_node_put(router_xm, ltable_node);
318580
}
@@ -354,6 +616,23 @@ mlxsw_sp_router_xm_ml_entries_del(struct mlxsw_sp *mlxsw_sp,
354616
}
355617
}
356618

619+
static void
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+
357636
static int mlxsw_sp_router_ll_xm_fib_entry_commit(struct mlxsw_sp *mlxsw_sp,
358637
struct mlxsw_sp_fib_entry_op_ctx *op_ctx,
359638
bool *postponed_for_bulk)
@@ -414,6 +693,11 @@ static int mlxsw_sp_router_ll_xm_fib_entry_commit(struct mlxsw_sp *mlxsw_sp,
414693
*/
415694
mlxsw_sp_router_xm_ml_entries_del(mlxsw_sp, op_ctx_xm);
416695

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+
417701
out:
418702
/* Next pack call is going to do reinitialization */
419703
op_ctx->initialized = false;
@@ -490,9 +774,15 @@ int mlxsw_sp_router_xm_init(struct mlxsw_sp *mlxsw_sp)
490774
if (err)
491775
goto err_ltable_ht_init;
492776

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+
493781
mlxsw_sp->router->xm = router_xm;
494782
return 0;
495783

784+
err_flush_ht_init:
785+
rhashtable_destroy(&router_xm->ltable_ht);
496786
err_ltable_ht_init:
497787
err_rxltm_write:
498788
err_mindex_size_check:
@@ -509,6 +799,7 @@ void mlxsw_sp_router_xm_fini(struct mlxsw_sp *mlxsw_sp)
509799
if (!mlxsw_sp->bus_info->xm_exists)
510800
return;
511801

802+
rhashtable_destroy(&router_xm->flush_ht);
512803
rhashtable_destroy(&router_xm->ltable_ht);
513804
kfree(router_xm);
514805
}

0 commit comments

Comments
 (0)