Skip to content
Permalink
Browse files
mlxsw: spectrum_router_xm: Introduce basic XM cache flushing
Upon router 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.

Signed-off-by: Jiri Pirko <jiri@nvidia.com>
  • Loading branch information
Jiri Pirko committed Sep 17, 2020
1 parent e474e04 commit a515b03fc6ba3290c56f4582bf433a03def0f798
Showing 1 changed file with 209 additions and 0 deletions.
@@ -22,6 +22,9 @@ struct mlxsw_sp_router_xm {
bool ipv6_supported;
unsigned int entries_size;
struct rhashtable ltable_ht;
struct rhashtable flush_ht; /* Stores items about to be flushed from cache */
unsigned int flush_count;
bool flush_all_mode;
};

struct mlxsw_sp_router_xm_ltable_node {
@@ -39,11 +42,20 @@ static const struct rhashtable_params mlxsw_sp_router_xm_ltable_ht_params = {
.automatic_shrinking = true,
};

struct mlxsw_sp_router_xm_flush_info {
bool all;
enum mlxsw_sp_l3proto proto;
u16 virtual_router;
u8 prefix_len;
unsigned char addr[sizeof(struct in6_addr)];
};

struct mlxsw_sp_router_xm_fib_entry {
bool committed;
struct mlxsw_sp_router_xm_ltable_node *ltable_node; /* Parent node */
u16 mindex; /* Store for processing from commit op */
u8 lvalue;
struct mlxsw_sp_router_xm_flush_info flush_info;
};

#define MLXSW_SP_ROUTE_LL_XM_ENTRIES_MAX \
@@ -111,6 +123,7 @@ static void mlxsw_sp_router_ll_xm_fib_entry_pack(struct mlxsw_sp_fib_entry_op_ct
{
struct mlxsw_sp_fib_entry_op_ctx_xm *op_ctx_xm = (void *) op_ctx->ll_priv;
struct mlxsw_sp_router_xm_fib_entry *fib_entry = (void *) priv->priv;
struct mlxsw_sp_router_xm_flush_info *flush_info;
enum mlxsw_reg_xmdr_c_ltr_op xmdr_c_ltr_op;
unsigned int len;

@@ -155,6 +168,15 @@ static void mlxsw_sp_router_ll_xm_fib_entry_pack(struct mlxsw_sp_fib_entry_op_ct
fib_entry->lvalue = prefix_len > mlxsw_sp_router_xm_m_val[proto] ?
prefix_len - mlxsw_sp_router_xm_m_val[proto] : 0;
fib_entry->mindex = mlxsw_sp_router_ll_xm_mindex_get(addr);

flush_info = &fib_entry->flush_info;
flush_info->proto = proto;
flush_info->virtual_router = virtual_router;
flush_info->prefix_len = prefix_len;
if (addr)
memcpy(flush_info->addr, addr, sizeof(flush_info->addr));
else
memset(flush_info->addr, 0, sizeof(flush_info->addr));
}

static void
@@ -244,17 +266,182 @@ static int mlxsw_sp_router_xm_ltable_lvalue_set(struct mlxsw_sp *mlxsw_sp,
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(xrmt), xrmt_pl);
}

struct mlxsw_sp_router_xm_flush_node {
struct rhash_head ht_node; /* Member of router_xm->flush_ht */
struct list_head list;
struct mlxsw_sp_router_xm_flush_info flush_info;
struct delayed_work dw;
struct mlxsw_sp *mlxsw_sp;
unsigned long start_jiffies;
unsigned int reuses; /* By how many flush calls this was reused. */
};

static const struct rhashtable_params mlxsw_sp_router_xm_flush_ht_params = {
.key_offset = offsetof(struct mlxsw_sp_router_xm_flush_node, flush_info),
.head_offset = offsetof(struct mlxsw_sp_router_xm_flush_node, ht_node),
.key_len = sizeof(struct mlxsw_sp_router_xm_flush_info),
.automatic_shrinking = true,
};

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)
{
struct mlxsw_sp_router_xm *router_xm = mlxsw_sp->router->xm;
struct mlxsw_sp_router_xm_flush_node *flush_node;
int err;

flush_node = kzalloc(sizeof(*flush_node), GFP_KERNEL);
if (!flush_node)
return ERR_PTR(-ENOMEM);

flush_node->flush_info = *flush_info;
err = rhashtable_insert_fast(&router_xm->flush_ht, &flush_node->ht_node,
mlxsw_sp_router_xm_flush_ht_params);
if (err) {
kfree(flush_node);
return ERR_PTR(err);
}
router_xm->flush_count++;
flush_node->mlxsw_sp = mlxsw_sp;
flush_node->start_jiffies = jiffies;
return flush_node;
}

void
mlxsw_sp_router_xm_cache_flush_node_destroy(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_router_xm_flush_node *flush_node)
{
struct mlxsw_sp_router_xm *router_xm = mlxsw_sp->router->xm;

router_xm->flush_count--;
rhashtable_remove_fast(&router_xm->flush_ht, &flush_node->ht_node,
mlxsw_sp_router_xm_flush_ht_params);
kfree(flush_node);
}

#define MLXSW_SP_ROUTER_XM_CACHE_PARALLEL_FLUSHES_LIMIT 15
#define MLXSW_SP_ROUTER_XM_CACHE_FLUSH_ALL_MIN_REUSES 15
#define MLXSW_SP_ROUTER_XM_CACHE_DELAY 50 /* usecs */
#define MLXSW_SP_ROUTER_XM_CACHE_MAX_WAIT (MLXSW_SP_ROUTER_XM_CACHE_DELAY * 10)

static void mlxsw_sp_router_xm_cache_flush_work(struct work_struct *work)
{
struct mlxsw_sp_router_xm_flush_info *flush_info;
struct mlxsw_sp_router_xm_flush_node *flush_node;
char rlcmld_pl[MLXSW_REG_RLCMLD_LEN];
struct mlxsw_sp *mlxsw_sp;
int err;

flush_node = container_of(work, struct mlxsw_sp_router_xm_flush_node,
dw.work);
mlxsw_sp = flush_node->mlxsw_sp;
flush_info = &flush_node->flush_info;

if (flush_info->all) {
char xmdr_pl[MLXSW_REG_XMDR_LEN];

/* Use XMDR for immediate cache flush. */
mlxsw_reg_xmdr_pack(xmdr_pl, false);
err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(xmdr), xmdr_pl);
if (err)
dev_err(mlxsw_sp->bus_info->dev, "Failed to flush XM cache\n");

if (flush_node->reuses < MLXSW_SP_ROUTER_XM_CACHE_FLUSH_ALL_MIN_REUSES)
/* Leaving flush-all mode. */
mlxsw_sp->router->xm->flush_all_mode = false;
goto out;
}

switch (flush_info->proto) {
case MLXSW_SP_L3_PROTO_IPV4:
mlxsw_reg_rlcmld_pack4(rlcmld_pl, flush_info->virtual_router,
(u32 *) flush_info->addr);
break;
case MLXSW_SP_L3_PROTO_IPV6:
mlxsw_reg_rlcmld_pack6(rlcmld_pl, flush_info->virtual_router,
flush_info->addr);
break;
default:
WARN_ON(true);
goto out;
}
err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rlcmld), rlcmld_pl);
if (err)
dev_err(mlxsw_sp->bus_info->dev, "Failed to flush XM cache\n");

out:
mlxsw_sp_router_xm_cache_flush_node_destroy(mlxsw_sp, flush_node);
}

static bool
mlxsw_sp_router_xm_cache_flush_may_cancel(struct mlxsw_sp_router_xm_flush_node *flush_node)
{
unsigned long max_wait = usecs_to_jiffies(MLXSW_SP_ROUTER_XM_CACHE_MAX_WAIT);
unsigned long delay = usecs_to_jiffies(MLXSW_SP_ROUTER_XM_CACHE_DELAY);

/* In case there is the same flushing work pending, check
* if we can consolidate with it. We can do it up to MAX_WAIT.
* Cancel the delayed work. If the work was still pending.
*/
if (flush_node->start_jiffies + max_wait - delay < jiffies &&
cancel_delayed_work_sync(&flush_node->dw))
return true;
return false;
}

static int
mlxsw_sp_router_xm_cache_flush_schedule(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_router_xm_flush_info *flush_info)
{
unsigned long delay = usecs_to_jiffies(MLXSW_SP_ROUTER_XM_CACHE_DELAY);
struct mlxsw_sp_router_xm_flush_info flush_all_info = {.all = true};
struct mlxsw_sp_router_xm *router_xm = mlxsw_sp->router->xm;
struct mlxsw_sp_router_xm_flush_node *flush_node;

/* Check if the number of flushes happening in parallel reached critical
* amount after which it is better to just flush the whole cache.
*/
if (router_xm->flush_count == MLXSW_SP_ROUTER_XM_CACHE_PARALLEL_FLUSHES_LIMIT)
/* Entering flush-all mode. */
router_xm->flush_all_mode = true;

if (router_xm->flush_all_mode)
flush_info = &flush_all_info;

flush_node = rhashtable_lookup_fast(&router_xm->flush_ht, flush_info,
mlxsw_sp_router_xm_flush_ht_params);
if (flush_node && mlxsw_sp_router_xm_cache_flush_may_cancel(flush_node)) {
/* Original work was withing wait period and was canceled.
* Reschedule it it with fresh delay.
*/
flush_node->reuses++;
goto schedule_work;
}

flush_node = mlxsw_sp_router_xm_cache_flush_node_create(mlxsw_sp, flush_info);
if (IS_ERR(flush_node))
return PTR_ERR(flush_node);
INIT_DELAYED_WORK(&flush_node->dw, mlxsw_sp_router_xm_cache_flush_work);

schedule_work:
mlxsw_core_schedule_dw(&flush_node->dw, delay);
return 0;
}

static int mlxsw_sp_router_xm_ml_entry_add(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_router_xm_fib_entry *fib_entry)
{
struct mlxsw_sp_router_xm *router_xm = mlxsw_sp->router->xm;
struct mlxsw_sp_router_xm_ltable_node *ltable_node;
u8 lvalue = fib_entry->lvalue;
u8 orig_lvalue;
int err;

ltable_node = mlxsw_sp_router_xm_ltable_node_get(router_xm, fib_entry->mindex);
if (IS_ERR(ltable_node))
return PTR_ERR(ltable_node);
orig_lvalue = ltable_node->current_lvalue;
if (lvalue > ltable_node->current_lvalue) {
/* The L-value is biggest then the one currently set, update. */
ltable_node->current_lvalue = lvalue;
@@ -265,8 +452,18 @@ static int mlxsw_sp_router_xm_ml_entry_add(struct mlxsw_sp *mlxsw_sp,

ltable_node->lvalue_ref[lvalue]++;
fib_entry->ltable_node = ltable_node;

err = mlxsw_sp_router_xm_cache_flush_schedule(mlxsw_sp,
&fib_entry->flush_info);
if (err)
goto err_cache_flush;
return 0;

err_cache_flush:
if (ltable_node->current_lvalue != orig_lvalue) {
ltable_node->current_lvalue = orig_lvalue;
mlxsw_sp_router_xm_ltable_lvalue_set(mlxsw_sp, ltable_node);
}
err_lvalue_set:
mlxsw_sp_router_xm_ltable_node_put(router_xm, ltable_node);
return err;
@@ -278,6 +475,7 @@ static void mlxsw_sp_router_xm_ml_entry_del(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_router_xm_ltable_node *ltable_node = fib_entry->ltable_node;
struct mlxsw_sp_router_xm *router_xm = mlxsw_sp->router->xm;
u8 lvalue = fib_entry->lvalue;
int err;

if (!fib_entry || !fib_entry->ltable_node)
return;
@@ -295,6 +493,10 @@ static void mlxsw_sp_router_xm_ml_entry_del(struct mlxsw_sp *mlxsw_sp,
mlxsw_sp_router_xm_ltable_lvalue_set(mlxsw_sp, ltable_node);
}

err = mlxsw_sp_router_xm_cache_flush_schedule(mlxsw_sp,
&fib_entry->flush_info);
if (err)
dev_err(mlxsw_sp->bus_info->dev, "Failed to flush XM cache\n");
mlxsw_sp_router_xm_ltable_node_put(router_xm, ltable_node);
}

@@ -450,9 +652,15 @@ int mlxsw_sp_router_xm_init(struct mlxsw_sp *mlxsw_sp)
if (err)
goto err_ltable_ht_init;

err = rhashtable_init(&router_xm->flush_ht, &mlxsw_sp_router_xm_flush_ht_params);
if (err)
goto err_flush_ht_init;

mlxsw_sp->router->xm = router_xm;
return 0;

err_flush_ht_init:
rhashtable_destroy(&router_xm->ltable_ht);
err_ltable_ht_init:
err_xltq_query:
err_mindex_size_check:
@@ -469,6 +677,7 @@ void mlxsw_sp_router_xm_fini(struct mlxsw_sp *mlxsw_sp)
if (!mlxsw_sp->bus_info->xm_exists)
return;

rhashtable_destroy(&router_xm->flush_ht);
rhashtable_destroy(&router_xm->ltable_ht);
kfree(router_xm);
}

0 comments on commit a515b03

Please sign in to comment.