// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/* Copyright (c) 2017-2018 Mellanox Technologies. All rights reserved */
#include <linux/rhashtable.h>
#include <net/ipv6.h>
#include "spectrum_mr.h"
#include "spectrum_router.h"
struct mlxsw_sp_mr {
const struct mlxsw_sp_mr_ops *mr_ops;
void *catchall_route_priv;
struct delayed_work stats_update_dw;
struct list_head table_list;
#define MLXSW_SP_MR_ROUTES_COUNTER_UPDATE_INTERVAL 5000 /* ms */
unsigned long priv[0];
/* priv has to be always the last item */
};
struct mlxsw_sp_mr_vif;
struct mlxsw_sp_mr_vif_ops {
bool (*is_regular)(const struct mlxsw_sp_mr_vif *vif);
};
struct mlxsw_sp_mr_vif {
struct net_device *dev;
const struct mlxsw_sp_rif *rif;
unsigned long vif_flags;
/* A list of route_vif_entry structs that point to routes that the VIF
* instance is used as one of the egress VIFs
*/
struct list_head route_evif_list;
/* A list of route_vif_entry structs that point to routes that the VIF
* instance is used as an ingress VIF
*/
struct list_head route_ivif_list;
/* Protocol specific operations for a VIF */
const struct mlxsw_sp_mr_vif_ops *ops;
};
struct mlxsw_sp_mr_route_vif_entry {
struct list_head vif_node;
struct list_head route_node;
struct mlxsw_sp_mr_vif *mr_vif;
struct mlxsw_sp_mr_route *mr_route;
};
struct mlxsw_sp_mr_table;
struct mlxsw_sp_mr_table_ops {
bool (*is_route_valid)(const struct mlxsw_sp_mr_table *mr_table,
const struct mr_mfc *mfc);
void (*key_create)(struct mlxsw_sp_mr_table *mr_table,
struct mlxsw_sp_mr_route_key *key,
struct mr_mfc *mfc);
bool (*is_route_starg)(const struct mlxsw_sp_mr_table *mr_table,
const struct mlxsw_sp_mr_route *mr_route);
};
struct mlxsw_sp_mr_table {
struct list_head node;
enum mlxsw_sp_l3proto proto;
struct mlxsw_sp *mlxsw_sp;
u32 vr_id;
struct mlxsw_sp_mr_vif vifs[MAXVIFS];
struct list_head route_list;
struct rhashtable route_ht;
const struct mlxsw_sp_mr_table_ops *ops;
char catchall_route_priv[0];
/* catchall_route_priv has to be always the last item */
};
struct mlxsw_sp_mr_route {
struct list_head node;
struct rhash_head ht_node;
struct mlxsw_sp_mr_route_key key;
enum mlxsw_sp_mr_route_action route_action;
u16 min_mtu;
struct mr_mfc *mfc;
void *route_priv;
const struct mlxsw_sp_mr_table *mr_table;
/* A list of route_vif_entry structs that point to the egress VIFs */
struct list_head evif_list;
/* A route_vif_entry struct that point to the ingress VIF */
struct mlxsw_sp_mr_route_vif_entry ivif;
};
static const struct rhashtable_params mlxsw_sp_mr_route_ht_params = {
.key_len = sizeof(struct mlxsw_sp_mr_route_key),
.key_offset = offsetof(struct mlxsw_sp_mr_route, key),
.head_offset = offsetof(struct mlxsw_sp_mr_route, ht_node),
.automatic_shrinking = true,
};
static bool mlxsw_sp_mr_vif_valid(const struct mlxsw_sp_mr_vif *vif)
{
return vif->ops->is_regular(vif) && vif->dev && vif->rif;
}
static bool mlxsw_sp_mr_vif_exists(const struct mlxsw_sp_mr_vif *vif)
{
return vif->dev;
}
static bool
mlxsw_sp_mr_route_ivif_in_evifs(const struct mlxsw_sp_mr_route *mr_route)
{
vifi_t ivif = mr_route->mfc->mfc_parent;
return mr_route->mfc->mfc_un.res.ttls[ivif] != 255;
}
static int
mlxsw_sp_mr_route_valid_evifs_num(const struct mlxsw_sp_mr_route *mr_route)
{
struct mlxsw_sp_mr_route_vif_entry *rve;
int valid_evifs;
valid_evifs = 0;
list_for_each_entry(rve, &mr_route->evif_list, route_node)
if (mlxsw_sp_mr_vif_valid(rve->mr_vif))
valid_evifs++;
return valid_evifs;
}
static enum mlxsw_sp_mr_route_action
mlxsw_sp_mr_route_action(const struct mlxsw_sp_mr_route *mr_route)
{
struct mlxsw_sp_mr_route_vif_entry *rve;
/* If the ingress port is not regular and resolved, trap the route */
if (!mlxsw_sp_mr_vif_valid(mr_route->ivif.