1073 lines · c
1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.02/* Copyright (c) 2017-2018 Mellanox Technologies. All rights reserved */3 4#include <linux/mutex.h>5#include <linux/rhashtable.h>6#include <net/ipv6.h>7 8#include "spectrum_mr.h"9#include "spectrum_router.h"10 11struct mlxsw_sp_mr {12 const struct mlxsw_sp_mr_ops *mr_ops;13 void *catchall_route_priv;14 struct delayed_work stats_update_dw;15 struct list_head table_list;16 struct mutex table_list_lock; /* Protects table_list */17#define MLXSW_SP_MR_ROUTES_COUNTER_UPDATE_INTERVAL 5000 /* ms */18 unsigned long priv[];19 /* priv has to be always the last item */20};21 22struct mlxsw_sp_mr_vif;23struct mlxsw_sp_mr_vif_ops {24 bool (*is_regular)(const struct mlxsw_sp_mr_vif *vif);25};26 27struct mlxsw_sp_mr_vif {28 struct net_device *dev;29 const struct mlxsw_sp_rif *rif;30 unsigned long vif_flags;31 32 /* A list of route_vif_entry structs that point to routes that the VIF33 * instance is used as one of the egress VIFs34 */35 struct list_head route_evif_list;36 37 /* A list of route_vif_entry structs that point to routes that the VIF38 * instance is used as an ingress VIF39 */40 struct list_head route_ivif_list;41 42 /* Protocol specific operations for a VIF */43 const struct mlxsw_sp_mr_vif_ops *ops;44};45 46struct mlxsw_sp_mr_route_vif_entry {47 struct list_head vif_node;48 struct list_head route_node;49 struct mlxsw_sp_mr_vif *mr_vif;50 struct mlxsw_sp_mr_route *mr_route;51};52 53struct mlxsw_sp_mr_table;54struct mlxsw_sp_mr_table_ops {55 bool (*is_route_valid)(const struct mlxsw_sp_mr_table *mr_table,56 const struct mr_mfc *mfc);57 void (*key_create)(struct mlxsw_sp_mr_table *mr_table,58 struct mlxsw_sp_mr_route_key *key,59 struct mr_mfc *mfc);60 bool (*is_route_starg)(const struct mlxsw_sp_mr_table *mr_table,61 const struct mlxsw_sp_mr_route *mr_route);62};63 64struct mlxsw_sp_mr_table {65 struct list_head node;66 enum mlxsw_sp_l3proto proto;67 struct mlxsw_sp *mlxsw_sp;68 u32 vr_id;69 struct mlxsw_sp_mr_vif vifs[MAXVIFS];70 struct list_head route_list;71 struct mutex route_list_lock; /* Protects route_list */72 struct rhashtable route_ht;73 const struct mlxsw_sp_mr_table_ops *ops;74 char catchall_route_priv[];75 /* catchall_route_priv has to be always the last item */76};77 78struct mlxsw_sp_mr_route {79 struct list_head node;80 struct rhash_head ht_node;81 struct mlxsw_sp_mr_route_key key;82 enum mlxsw_sp_mr_route_action route_action;83 u16 min_mtu;84 struct mr_mfc *mfc;85 void *route_priv;86 const struct mlxsw_sp_mr_table *mr_table;87 /* A list of route_vif_entry structs that point to the egress VIFs */88 struct list_head evif_list;89 /* A route_vif_entry struct that point to the ingress VIF */90 struct mlxsw_sp_mr_route_vif_entry ivif;91};92 93static const struct rhashtable_params mlxsw_sp_mr_route_ht_params = {94 .key_len = sizeof(struct mlxsw_sp_mr_route_key),95 .key_offset = offsetof(struct mlxsw_sp_mr_route, key),96 .head_offset = offsetof(struct mlxsw_sp_mr_route, ht_node),97 .automatic_shrinking = true,98};99 100static bool mlxsw_sp_mr_vif_valid(const struct mlxsw_sp_mr_vif *vif)101{102 return vif->ops->is_regular(vif) && vif->dev && vif->rif;103}104 105static bool mlxsw_sp_mr_vif_exists(const struct mlxsw_sp_mr_vif *vif)106{107 return vif->dev;108}109 110static bool111mlxsw_sp_mr_route_ivif_in_evifs(const struct mlxsw_sp_mr_route *mr_route)112{113 vifi_t ivif = mr_route->mfc->mfc_parent;114 115 return mr_route->mfc->mfc_un.res.ttls[ivif] != 255;116}117 118static int119mlxsw_sp_mr_route_valid_evifs_num(const struct mlxsw_sp_mr_route *mr_route)120{121 struct mlxsw_sp_mr_route_vif_entry *rve;122 int valid_evifs;123 124 valid_evifs = 0;125 list_for_each_entry(rve, &mr_route->evif_list, route_node)126 if (mlxsw_sp_mr_vif_valid(rve->mr_vif))127 valid_evifs++;128 return valid_evifs;129}130 131static enum mlxsw_sp_mr_route_action132mlxsw_sp_mr_route_action(const struct mlxsw_sp_mr_route *mr_route)133{134 struct mlxsw_sp_mr_route_vif_entry *rve;135 136 /* If the ingress port is not regular and resolved, trap the route */137 if (!mlxsw_sp_mr_vif_valid(mr_route->ivif.mr_vif))138 return MLXSW_SP_MR_ROUTE_ACTION_TRAP;139 140 /* The kernel does not match a (*,G) route that the ingress interface is141 * not one of the egress interfaces, so trap these kind of routes.142 */143 if (mr_route->mr_table->ops->is_route_starg(mr_route->mr_table,144 mr_route) &&145 !mlxsw_sp_mr_route_ivif_in_evifs(mr_route))146 return MLXSW_SP_MR_ROUTE_ACTION_TRAP;147 148 /* If the route has no valid eVIFs, trap it. */149 if (!mlxsw_sp_mr_route_valid_evifs_num(mr_route))150 return MLXSW_SP_MR_ROUTE_ACTION_TRAP;151 152 /* If one of the eVIFs has no RIF, trap-and-forward the route as there153 * is some more routing to do in software too.154 */155 list_for_each_entry(rve, &mr_route->evif_list, route_node)156 if (mlxsw_sp_mr_vif_exists(rve->mr_vif) && !rve->mr_vif->rif)157 return MLXSW_SP_MR_ROUTE_ACTION_TRAP_AND_FORWARD;158 159 return MLXSW_SP_MR_ROUTE_ACTION_FORWARD;160}161 162static enum mlxsw_sp_mr_route_prio163mlxsw_sp_mr_route_prio(const struct mlxsw_sp_mr_route *mr_route)164{165 return mr_route->mr_table->ops->is_route_starg(mr_route->mr_table,166 mr_route) ?167 MLXSW_SP_MR_ROUTE_PRIO_STARG : MLXSW_SP_MR_ROUTE_PRIO_SG;168}169 170static int mlxsw_sp_mr_route_evif_link(struct mlxsw_sp_mr_route *mr_route,171 struct mlxsw_sp_mr_vif *mr_vif)172{173 struct mlxsw_sp_mr_route_vif_entry *rve;174 175 rve = kzalloc(sizeof(*rve), GFP_KERNEL);176 if (!rve)177 return -ENOMEM;178 rve->mr_route = mr_route;179 rve->mr_vif = mr_vif;180 list_add_tail(&rve->route_node, &mr_route->evif_list);181 list_add_tail(&rve->vif_node, &mr_vif->route_evif_list);182 return 0;183}184 185static void186mlxsw_sp_mr_route_evif_unlink(struct mlxsw_sp_mr_route_vif_entry *rve)187{188 list_del(&rve->route_node);189 list_del(&rve->vif_node);190 kfree(rve);191}192 193static void mlxsw_sp_mr_route_ivif_link(struct mlxsw_sp_mr_route *mr_route,194 struct mlxsw_sp_mr_vif *mr_vif)195{196 mr_route->ivif.mr_route = mr_route;197 mr_route->ivif.mr_vif = mr_vif;198 list_add_tail(&mr_route->ivif.vif_node, &mr_vif->route_ivif_list);199}200 201static void mlxsw_sp_mr_route_ivif_unlink(struct mlxsw_sp_mr_route *mr_route)202{203 list_del(&mr_route->ivif.vif_node);204}205 206static int207mlxsw_sp_mr_route_info_create(struct mlxsw_sp_mr_table *mr_table,208 struct mlxsw_sp_mr_route *mr_route,209 struct mlxsw_sp_mr_route_info *route_info)210{211 struct mlxsw_sp_mr_route_vif_entry *rve;212 u16 *erif_indices;213 u16 irif_index;214 u16 erif = 0;215 216 erif_indices = kmalloc_array(MAXVIFS, sizeof(*erif_indices),217 GFP_KERNEL);218 if (!erif_indices)219 return -ENOMEM;220 221 list_for_each_entry(rve, &mr_route->evif_list, route_node) {222 if (mlxsw_sp_mr_vif_valid(rve->mr_vif)) {223 u16 rifi = mlxsw_sp_rif_index(rve->mr_vif->rif);224 225 erif_indices[erif++] = rifi;226 }227 }228 229 if (mlxsw_sp_mr_vif_valid(mr_route->ivif.mr_vif))230 irif_index = mlxsw_sp_rif_index(mr_route->ivif.mr_vif->rif);231 else232 irif_index = 0;233 234 route_info->irif_index = irif_index;235 route_info->erif_indices = erif_indices;236 route_info->min_mtu = mr_route->min_mtu;237 route_info->route_action = mr_route->route_action;238 route_info->erif_num = erif;239 return 0;240}241 242static void243mlxsw_sp_mr_route_info_destroy(struct mlxsw_sp_mr_route_info *route_info)244{245 kfree(route_info->erif_indices);246}247 248static int mlxsw_sp_mr_route_write(struct mlxsw_sp_mr_table *mr_table,249 struct mlxsw_sp_mr_route *mr_route,250 bool replace)251{252 struct mlxsw_sp *mlxsw_sp = mr_table->mlxsw_sp;253 struct mlxsw_sp_mr_route_info route_info;254 struct mlxsw_sp_mr *mr = mlxsw_sp->mr;255 int err;256 257 err = mlxsw_sp_mr_route_info_create(mr_table, mr_route, &route_info);258 if (err)259 return err;260 261 if (!replace) {262 struct mlxsw_sp_mr_route_params route_params;263 264 mr_route->route_priv = kzalloc(mr->mr_ops->route_priv_size,265 GFP_KERNEL);266 if (!mr_route->route_priv) {267 err = -ENOMEM;268 goto out;269 }270 271 route_params.key = mr_route->key;272 route_params.value = route_info;273 route_params.prio = mlxsw_sp_mr_route_prio(mr_route);274 err = mr->mr_ops->route_create(mlxsw_sp, mr->priv,275 mr_route->route_priv,276 &route_params);277 if (err)278 kfree(mr_route->route_priv);279 } else {280 err = mr->mr_ops->route_update(mlxsw_sp, mr_route->route_priv,281 &route_info);282 }283out:284 mlxsw_sp_mr_route_info_destroy(&route_info);285 return err;286}287 288static void mlxsw_sp_mr_route_erase(struct mlxsw_sp_mr_table *mr_table,289 struct mlxsw_sp_mr_route *mr_route)290{291 struct mlxsw_sp *mlxsw_sp = mr_table->mlxsw_sp;292 struct mlxsw_sp_mr *mr = mlxsw_sp->mr;293 294 mr->mr_ops->route_destroy(mlxsw_sp, mr->priv, mr_route->route_priv);295 kfree(mr_route->route_priv);296}297 298static struct mlxsw_sp_mr_route *299mlxsw_sp_mr_route_create(struct mlxsw_sp_mr_table *mr_table,300 struct mr_mfc *mfc)301{302 struct mlxsw_sp_mr_route_vif_entry *rve, *tmp;303 struct mlxsw_sp_mr_route *mr_route;304 int err = 0;305 int i;306 307 /* Allocate and init a new route and fill it with parameters */308 mr_route = kzalloc(sizeof(*mr_route), GFP_KERNEL);309 if (!mr_route)310 return ERR_PTR(-ENOMEM);311 INIT_LIST_HEAD(&mr_route->evif_list);312 313 /* Find min_mtu and link iVIF and eVIFs */314 mr_route->min_mtu = ETH_MAX_MTU;315 mr_cache_hold(mfc);316 mr_route->mfc = mfc;317 mr_table->ops->key_create(mr_table, &mr_route->key, mr_route->mfc);318 319 mr_route->mr_table = mr_table;320 for (i = 0; i < MAXVIFS; i++) {321 if (mfc->mfc_un.res.ttls[i] != 255) {322 err = mlxsw_sp_mr_route_evif_link(mr_route,323 &mr_table->vifs[i]);324 if (err)325 goto err;326 if (mr_table->vifs[i].dev &&327 mr_table->vifs[i].dev->mtu < mr_route->min_mtu)328 mr_route->min_mtu = mr_table->vifs[i].dev->mtu;329 }330 }331 mlxsw_sp_mr_route_ivif_link(mr_route,332 &mr_table->vifs[mfc->mfc_parent]);333 334 mr_route->route_action = mlxsw_sp_mr_route_action(mr_route);335 return mr_route;336err:337 mr_cache_put(mfc);338 list_for_each_entry_safe(rve, tmp, &mr_route->evif_list, route_node)339 mlxsw_sp_mr_route_evif_unlink(rve);340 kfree(mr_route);341 return ERR_PTR(err);342}343 344static void mlxsw_sp_mr_route_destroy(struct mlxsw_sp_mr_table *mr_table,345 struct mlxsw_sp_mr_route *mr_route)346{347 struct mlxsw_sp_mr_route_vif_entry *rve, *tmp;348 349 mlxsw_sp_mr_route_ivif_unlink(mr_route);350 mr_cache_put(mr_route->mfc);351 list_for_each_entry_safe(rve, tmp, &mr_route->evif_list, route_node)352 mlxsw_sp_mr_route_evif_unlink(rve);353 kfree(mr_route);354}355 356static void mlxsw_sp_mr_mfc_offload_set(struct mlxsw_sp_mr_route *mr_route,357 bool offload)358{359 if (offload)360 mr_route->mfc->mfc_flags |= MFC_OFFLOAD;361 else362 mr_route->mfc->mfc_flags &= ~MFC_OFFLOAD;363}364 365static void mlxsw_sp_mr_mfc_offload_update(struct mlxsw_sp_mr_route *mr_route)366{367 bool offload;368 369 offload = mr_route->route_action != MLXSW_SP_MR_ROUTE_ACTION_TRAP;370 mlxsw_sp_mr_mfc_offload_set(mr_route, offload);371}372 373static void __mlxsw_sp_mr_route_del(struct mlxsw_sp_mr_table *mr_table,374 struct mlxsw_sp_mr_route *mr_route)375{376 WARN_ON_ONCE(!mutex_is_locked(&mr_table->route_list_lock));377 378 mlxsw_sp_mr_mfc_offload_set(mr_route, false);379 rhashtable_remove_fast(&mr_table->route_ht, &mr_route->ht_node,380 mlxsw_sp_mr_route_ht_params);381 list_del(&mr_route->node);382 mlxsw_sp_mr_route_erase(mr_table, mr_route);383 mlxsw_sp_mr_route_destroy(mr_table, mr_route);384}385 386int mlxsw_sp_mr_route_add(struct mlxsw_sp_mr_table *mr_table,387 struct mr_mfc *mfc, bool replace)388{389 struct mlxsw_sp_mr_route *mr_orig_route = NULL;390 struct mlxsw_sp_mr_route *mr_route;391 int err;392 393 if (!mr_table->ops->is_route_valid(mr_table, mfc))394 return -EINVAL;395 396 /* Create a new route */397 mr_route = mlxsw_sp_mr_route_create(mr_table, mfc);398 if (IS_ERR(mr_route))399 return PTR_ERR(mr_route);400 401 /* Find any route with a matching key */402 mr_orig_route = rhashtable_lookup_fast(&mr_table->route_ht,403 &mr_route->key,404 mlxsw_sp_mr_route_ht_params);405 if (replace) {406 /* On replace case, make the route point to the new route_priv.407 */408 if (WARN_ON(!mr_orig_route)) {409 err = -ENOENT;410 goto err_no_orig_route;411 }412 mr_route->route_priv = mr_orig_route->route_priv;413 } else if (mr_orig_route) {414 /* On non replace case, if another route with the same key was415 * found, abort, as duplicate routes are used for proxy routes.416 */417 dev_warn(mr_table->mlxsw_sp->bus_info->dev,418 "Offloading proxy routes is not supported.\n");419 err = -EINVAL;420 goto err_duplicate_route;421 }422 423 /* Write the route to the hardware */424 err = mlxsw_sp_mr_route_write(mr_table, mr_route, replace);425 if (err)426 goto err_mr_route_write;427 428 /* Put it in the table data-structures */429 mutex_lock(&mr_table->route_list_lock);430 list_add_tail(&mr_route->node, &mr_table->route_list);431 mutex_unlock(&mr_table->route_list_lock);432 err = rhashtable_insert_fast(&mr_table->route_ht,433 &mr_route->ht_node,434 mlxsw_sp_mr_route_ht_params);435 if (err)436 goto err_rhashtable_insert;437 438 /* Destroy the original route */439 if (replace) {440 rhashtable_remove_fast(&mr_table->route_ht,441 &mr_orig_route->ht_node,442 mlxsw_sp_mr_route_ht_params);443 list_del(&mr_orig_route->node);444 mlxsw_sp_mr_route_destroy(mr_table, mr_orig_route);445 }446 447 mlxsw_sp_mr_mfc_offload_update(mr_route);448 return 0;449 450err_rhashtable_insert:451 mutex_lock(&mr_table->route_list_lock);452 list_del(&mr_route->node);453 mutex_unlock(&mr_table->route_list_lock);454 mlxsw_sp_mr_route_erase(mr_table, mr_route);455err_mr_route_write:456err_no_orig_route:457err_duplicate_route:458 mlxsw_sp_mr_route_destroy(mr_table, mr_route);459 return err;460}461 462void mlxsw_sp_mr_route_del(struct mlxsw_sp_mr_table *mr_table,463 struct mr_mfc *mfc)464{465 struct mlxsw_sp_mr_route *mr_route;466 struct mlxsw_sp_mr_route_key key;467 468 mr_table->ops->key_create(mr_table, &key, mfc);469 mr_route = rhashtable_lookup_fast(&mr_table->route_ht, &key,470 mlxsw_sp_mr_route_ht_params);471 if (mr_route) {472 mutex_lock(&mr_table->route_list_lock);473 __mlxsw_sp_mr_route_del(mr_table, mr_route);474 mutex_unlock(&mr_table->route_list_lock);475 }476}477 478/* Should be called after the VIF struct is updated */479static int480mlxsw_sp_mr_route_ivif_resolve(struct mlxsw_sp_mr_table *mr_table,481 struct mlxsw_sp_mr_route_vif_entry *rve)482{483 struct mlxsw_sp *mlxsw_sp = mr_table->mlxsw_sp;484 enum mlxsw_sp_mr_route_action route_action;485 struct mlxsw_sp_mr *mr = mlxsw_sp->mr;486 u16 irif_index;487 int err;488 489 route_action = mlxsw_sp_mr_route_action(rve->mr_route);490 if (route_action == MLXSW_SP_MR_ROUTE_ACTION_TRAP)491 return 0;492 493 /* rve->mr_vif->rif is guaranteed to be valid at this stage */494 irif_index = mlxsw_sp_rif_index(rve->mr_vif->rif);495 err = mr->mr_ops->route_irif_update(mlxsw_sp, rve->mr_route->route_priv,496 irif_index);497 if (err)498 return err;499 500 err = mr->mr_ops->route_action_update(mlxsw_sp,501 rve->mr_route->route_priv,502 route_action);503 if (err)504 /* No need to rollback here because the iRIF change only takes505 * place after the action has been updated.506 */507 return err;508 509 rve->mr_route->route_action = route_action;510 mlxsw_sp_mr_mfc_offload_update(rve->mr_route);511 return 0;512}513 514static void515mlxsw_sp_mr_route_ivif_unresolve(struct mlxsw_sp_mr_table *mr_table,516 struct mlxsw_sp_mr_route_vif_entry *rve)517{518 struct mlxsw_sp *mlxsw_sp = mr_table->mlxsw_sp;519 struct mlxsw_sp_mr *mr = mlxsw_sp->mr;520 521 mr->mr_ops->route_action_update(mlxsw_sp, rve->mr_route->route_priv,522 MLXSW_SP_MR_ROUTE_ACTION_TRAP);523 rve->mr_route->route_action = MLXSW_SP_MR_ROUTE_ACTION_TRAP;524 mlxsw_sp_mr_mfc_offload_update(rve->mr_route);525}526 527/* Should be called after the RIF struct is updated */528static int529mlxsw_sp_mr_route_evif_resolve(struct mlxsw_sp_mr_table *mr_table,530 struct mlxsw_sp_mr_route_vif_entry *rve)531{532 struct mlxsw_sp *mlxsw_sp = mr_table->mlxsw_sp;533 enum mlxsw_sp_mr_route_action route_action;534 struct mlxsw_sp_mr *mr = mlxsw_sp->mr;535 u16 erif_index = 0;536 int err;537 538 /* Add the eRIF */539 if (mlxsw_sp_mr_vif_valid(rve->mr_vif)) {540 erif_index = mlxsw_sp_rif_index(rve->mr_vif->rif);541 err = mr->mr_ops->route_erif_add(mlxsw_sp,542 rve->mr_route->route_priv,543 erif_index);544 if (err)545 return err;546 }547 548 /* Update the route action, as the new eVIF can be a tunnel or a pimreg549 * device which will require updating the action.550 */551 route_action = mlxsw_sp_mr_route_action(rve->mr_route);552 if (route_action != rve->mr_route->route_action) {553 err = mr->mr_ops->route_action_update(mlxsw_sp,554 rve->mr_route->route_priv,555 route_action);556 if (err)557 goto err_route_action_update;558 }559 560 /* Update the minimum MTU */561 if (rve->mr_vif->dev->mtu < rve->mr_route->min_mtu) {562 rve->mr_route->min_mtu = rve->mr_vif->dev->mtu;563 err = mr->mr_ops->route_min_mtu_update(mlxsw_sp,564 rve->mr_route->route_priv,565 rve->mr_route->min_mtu);566 if (err)567 goto err_route_min_mtu_update;568 }569 570 rve->mr_route->route_action = route_action;571 mlxsw_sp_mr_mfc_offload_update(rve->mr_route);572 return 0;573 574err_route_min_mtu_update:575 if (route_action != rve->mr_route->route_action)576 mr->mr_ops->route_action_update(mlxsw_sp,577 rve->mr_route->route_priv,578 rve->mr_route->route_action);579err_route_action_update:580 if (mlxsw_sp_mr_vif_valid(rve->mr_vif))581 mr->mr_ops->route_erif_del(mlxsw_sp, rve->mr_route->route_priv,582 erif_index);583 return err;584}585 586/* Should be called before the RIF struct is updated */587static void588mlxsw_sp_mr_route_evif_unresolve(struct mlxsw_sp_mr_table *mr_table,589 struct mlxsw_sp_mr_route_vif_entry *rve)590{591 struct mlxsw_sp *mlxsw_sp = mr_table->mlxsw_sp;592 enum mlxsw_sp_mr_route_action route_action;593 struct mlxsw_sp_mr *mr = mlxsw_sp->mr;594 u16 rifi;595 596 /* If the unresolved RIF was not valid, no need to delete it */597 if (!mlxsw_sp_mr_vif_valid(rve->mr_vif))598 return;599 600 /* Update the route action: if there is only one valid eVIF in the601 * route, set the action to trap as the VIF deletion will lead to zero602 * valid eVIFs. On any other case, use the mlxsw_sp_mr_route_action to603 * determine the route action.604 */605 if (mlxsw_sp_mr_route_valid_evifs_num(rve->mr_route) == 1)606 route_action = MLXSW_SP_MR_ROUTE_ACTION_TRAP;607 else608 route_action = mlxsw_sp_mr_route_action(rve->mr_route);609 if (route_action != rve->mr_route->route_action)610 mr->mr_ops->route_action_update(mlxsw_sp,611 rve->mr_route->route_priv,612 route_action);613 614 /* Delete the erif from the route */615 rifi = mlxsw_sp_rif_index(rve->mr_vif->rif);616 mr->mr_ops->route_erif_del(mlxsw_sp, rve->mr_route->route_priv, rifi);617 rve->mr_route->route_action = route_action;618 mlxsw_sp_mr_mfc_offload_update(rve->mr_route);619}620 621static int mlxsw_sp_mr_vif_resolve(struct mlxsw_sp_mr_table *mr_table,622 struct net_device *dev,623 struct mlxsw_sp_mr_vif *mr_vif,624 unsigned long vif_flags,625 const struct mlxsw_sp_rif *rif)626{627 struct mlxsw_sp_mr_route_vif_entry *irve, *erve;628 int err;629 630 /* Update the VIF */631 mr_vif->dev = dev;632 mr_vif->rif = rif;633 mr_vif->vif_flags = vif_flags;634 635 /* Update all routes where this VIF is used as an unresolved iRIF */636 list_for_each_entry(irve, &mr_vif->route_ivif_list, vif_node) {637 err = mlxsw_sp_mr_route_ivif_resolve(mr_table, irve);638 if (err)639 goto err_irif_unresolve;640 }641 642 /* Update all routes where this VIF is used as an unresolved eRIF */643 list_for_each_entry(erve, &mr_vif->route_evif_list, vif_node) {644 err = mlxsw_sp_mr_route_evif_resolve(mr_table, erve);645 if (err)646 goto err_erif_unresolve;647 }648 return 0;649 650err_erif_unresolve:651 list_for_each_entry_continue_reverse(erve, &mr_vif->route_evif_list,652 vif_node)653 mlxsw_sp_mr_route_evif_unresolve(mr_table, erve);654err_irif_unresolve:655 list_for_each_entry_continue_reverse(irve, &mr_vif->route_ivif_list,656 vif_node)657 mlxsw_sp_mr_route_ivif_unresolve(mr_table, irve);658 mr_vif->rif = NULL;659 return err;660}661 662static void mlxsw_sp_mr_vif_unresolve(struct mlxsw_sp_mr_table *mr_table,663 struct net_device *dev,664 struct mlxsw_sp_mr_vif *mr_vif)665{666 struct mlxsw_sp_mr_route_vif_entry *rve;667 668 /* Update all routes where this VIF is used as an unresolved eRIF */669 list_for_each_entry(rve, &mr_vif->route_evif_list, vif_node)670 mlxsw_sp_mr_route_evif_unresolve(mr_table, rve);671 672 /* Update all routes where this VIF is used as an unresolved iRIF */673 list_for_each_entry(rve, &mr_vif->route_ivif_list, vif_node)674 mlxsw_sp_mr_route_ivif_unresolve(mr_table, rve);675 676 /* Update the VIF */677 mr_vif->dev = dev;678 mr_vif->rif = NULL;679}680 681int mlxsw_sp_mr_vif_add(struct mlxsw_sp_mr_table *mr_table,682 struct net_device *dev, vifi_t vif_index,683 unsigned long vif_flags, const struct mlxsw_sp_rif *rif)684{685 struct mlxsw_sp_mr_vif *mr_vif = &mr_table->vifs[vif_index];686 687 if (WARN_ON(vif_index >= MAXVIFS))688 return -EINVAL;689 if (mr_vif->dev)690 return -EEXIST;691 return mlxsw_sp_mr_vif_resolve(mr_table, dev, mr_vif, vif_flags, rif);692}693 694void mlxsw_sp_mr_vif_del(struct mlxsw_sp_mr_table *mr_table, vifi_t vif_index)695{696 struct mlxsw_sp_mr_vif *mr_vif = &mr_table->vifs[vif_index];697 698 if (WARN_ON(vif_index >= MAXVIFS))699 return;700 if (WARN_ON(!mr_vif->dev))701 return;702 mlxsw_sp_mr_vif_unresolve(mr_table, NULL, mr_vif);703}704 705static struct mlxsw_sp_mr_vif *706mlxsw_sp_mr_dev_vif_lookup(struct mlxsw_sp_mr_table *mr_table,707 const struct mlxsw_sp_rif *rif)708{709 vifi_t vif_index;710 711 for (vif_index = 0; vif_index < MAXVIFS; vif_index++)712 if (mlxsw_sp_rif_dev_is(rif, mr_table->vifs[vif_index].dev))713 return &mr_table->vifs[vif_index];714 return NULL;715}716 717int mlxsw_sp_mr_rif_add(struct mlxsw_sp_mr_table *mr_table,718 const struct mlxsw_sp_rif *rif)719{720 struct mlxsw_sp_mr_vif *mr_vif;721 722 if (!mlxsw_sp_rif_has_dev(rif))723 return 0;724 725 mr_vif = mlxsw_sp_mr_dev_vif_lookup(mr_table, rif);726 if (!mr_vif)727 return 0;728 return mlxsw_sp_mr_vif_resolve(mr_table, mr_vif->dev, mr_vif,729 mr_vif->vif_flags, rif);730}731 732void mlxsw_sp_mr_rif_del(struct mlxsw_sp_mr_table *mr_table,733 const struct mlxsw_sp_rif *rif)734{735 struct mlxsw_sp_mr_vif *mr_vif;736 737 if (!mlxsw_sp_rif_has_dev(rif))738 return;739 740 mr_vif = mlxsw_sp_mr_dev_vif_lookup(mr_table, rif);741 if (!mr_vif)742 return;743 mlxsw_sp_mr_vif_unresolve(mr_table, mr_vif->dev, mr_vif);744}745 746void mlxsw_sp_mr_rif_mtu_update(struct mlxsw_sp_mr_table *mr_table,747 const struct mlxsw_sp_rif *rif, int mtu)748{749 struct mlxsw_sp *mlxsw_sp = mr_table->mlxsw_sp;750 struct mlxsw_sp_mr_route_vif_entry *rve;751 struct mlxsw_sp_mr *mr = mlxsw_sp->mr;752 struct mlxsw_sp_mr_vif *mr_vif;753 754 if (!mlxsw_sp_rif_has_dev(rif))755 return;756 757 /* Search for a VIF that use that RIF */758 mr_vif = mlxsw_sp_mr_dev_vif_lookup(mr_table, rif);759 if (!mr_vif)760 return;761 762 /* Update all the routes that uses that VIF as eVIF */763 list_for_each_entry(rve, &mr_vif->route_evif_list, vif_node) {764 if (mtu < rve->mr_route->min_mtu) {765 rve->mr_route->min_mtu = mtu;766 mr->mr_ops->route_min_mtu_update(mlxsw_sp,767 rve->mr_route->route_priv,768 mtu);769 }770 }771}772 773/* Protocol specific functions */774static bool775mlxsw_sp_mr_route4_validate(const struct mlxsw_sp_mr_table *mr_table,776 const struct mr_mfc *c)777{778 struct mfc_cache *mfc = (struct mfc_cache *) c;779 780 /* If the route is a (*,*) route, abort, as these kind of routes are781 * used for proxy routes.782 */783 if (mfc->mfc_origin == htonl(INADDR_ANY) &&784 mfc->mfc_mcastgrp == htonl(INADDR_ANY)) {785 dev_warn(mr_table->mlxsw_sp->bus_info->dev,786 "Offloading proxy routes is not supported.\n");787 return false;788 }789 return true;790}791 792static void mlxsw_sp_mr_route4_key(struct mlxsw_sp_mr_table *mr_table,793 struct mlxsw_sp_mr_route_key *key,794 struct mr_mfc *c)795{796 const struct mfc_cache *mfc = (struct mfc_cache *) c;797 bool starg;798 799 starg = (mfc->mfc_origin == htonl(INADDR_ANY));800 801 memset(key, 0, sizeof(*key));802 key->vrid = mr_table->vr_id;803 key->proto = MLXSW_SP_L3_PROTO_IPV4;804 key->group.addr4 = mfc->mfc_mcastgrp;805 key->group_mask.addr4 = htonl(0xffffffff);806 key->source.addr4 = mfc->mfc_origin;807 key->source_mask.addr4 = htonl(starg ? 0 : 0xffffffff);808}809 810static bool mlxsw_sp_mr_route4_starg(const struct mlxsw_sp_mr_table *mr_table,811 const struct mlxsw_sp_mr_route *mr_route)812{813 return mr_route->key.source_mask.addr4 == htonl(INADDR_ANY);814}815 816static bool mlxsw_sp_mr_vif4_is_regular(const struct mlxsw_sp_mr_vif *vif)817{818 return !(vif->vif_flags & (VIFF_TUNNEL | VIFF_REGISTER));819}820 821static bool822mlxsw_sp_mr_route6_validate(const struct mlxsw_sp_mr_table *mr_table,823 const struct mr_mfc *c)824{825 struct mfc6_cache *mfc = (struct mfc6_cache *) c;826 827 /* If the route is a (*,*) route, abort, as these kind of routes are828 * used for proxy routes.829 */830 if (ipv6_addr_any(&mfc->mf6c_origin) &&831 ipv6_addr_any(&mfc->mf6c_mcastgrp)) {832 dev_warn(mr_table->mlxsw_sp->bus_info->dev,833 "Offloading proxy routes is not supported.\n");834 return false;835 }836 return true;837}838 839static void mlxsw_sp_mr_route6_key(struct mlxsw_sp_mr_table *mr_table,840 struct mlxsw_sp_mr_route_key *key,841 struct mr_mfc *c)842{843 const struct mfc6_cache *mfc = (struct mfc6_cache *) c;844 845 memset(key, 0, sizeof(*key));846 key->vrid = mr_table->vr_id;847 key->proto = MLXSW_SP_L3_PROTO_IPV6;848 key->group.addr6 = mfc->mf6c_mcastgrp;849 memset(&key->group_mask.addr6, 0xff, sizeof(key->group_mask.addr6));850 key->source.addr6 = mfc->mf6c_origin;851 if (!ipv6_addr_any(&mfc->mf6c_origin))852 memset(&key->source_mask.addr6, 0xff,853 sizeof(key->source_mask.addr6));854}855 856static bool mlxsw_sp_mr_route6_starg(const struct mlxsw_sp_mr_table *mr_table,857 const struct mlxsw_sp_mr_route *mr_route)858{859 return ipv6_addr_any(&mr_route->key.source_mask.addr6);860}861 862static bool mlxsw_sp_mr_vif6_is_regular(const struct mlxsw_sp_mr_vif *vif)863{864 return !(vif->vif_flags & MIFF_REGISTER);865}866 867static struct868mlxsw_sp_mr_vif_ops mlxsw_sp_mr_vif_ops_arr[] = {869 {870 .is_regular = mlxsw_sp_mr_vif4_is_regular,871 },872 {873 .is_regular = mlxsw_sp_mr_vif6_is_regular,874 },875};876 877static struct878mlxsw_sp_mr_table_ops mlxsw_sp_mr_table_ops_arr[] = {879 {880 .is_route_valid = mlxsw_sp_mr_route4_validate,881 .key_create = mlxsw_sp_mr_route4_key,882 .is_route_starg = mlxsw_sp_mr_route4_starg,883 },884 {885 .is_route_valid = mlxsw_sp_mr_route6_validate,886 .key_create = mlxsw_sp_mr_route6_key,887 .is_route_starg = mlxsw_sp_mr_route6_starg,888 },889 890};891 892struct mlxsw_sp_mr_table *mlxsw_sp_mr_table_create(struct mlxsw_sp *mlxsw_sp,893 u32 vr_id,894 enum mlxsw_sp_l3proto proto)895{896 struct mlxsw_sp_mr_route_params catchall_route_params = {897 .prio = MLXSW_SP_MR_ROUTE_PRIO_CATCHALL,898 .key = {899 .vrid = vr_id,900 .proto = proto,901 },902 .value = {903 .route_action = MLXSW_SP_MR_ROUTE_ACTION_TRAP,904 }905 };906 struct mlxsw_sp_mr *mr = mlxsw_sp->mr;907 struct mlxsw_sp_mr_table *mr_table;908 int err;909 int i;910 911 mr_table = kzalloc(sizeof(*mr_table) + mr->mr_ops->route_priv_size,912 GFP_KERNEL);913 if (!mr_table)914 return ERR_PTR(-ENOMEM);915 916 mr_table->vr_id = vr_id;917 mr_table->mlxsw_sp = mlxsw_sp;918 mr_table->proto = proto;919 mr_table->ops = &mlxsw_sp_mr_table_ops_arr[proto];920 INIT_LIST_HEAD(&mr_table->route_list);921 mutex_init(&mr_table->route_list_lock);922 923 err = rhashtable_init(&mr_table->route_ht,924 &mlxsw_sp_mr_route_ht_params);925 if (err)926 goto err_route_rhashtable_init;927 928 for (i = 0; i < MAXVIFS; i++) {929 INIT_LIST_HEAD(&mr_table->vifs[i].route_evif_list);930 INIT_LIST_HEAD(&mr_table->vifs[i].route_ivif_list);931 mr_table->vifs[i].ops = &mlxsw_sp_mr_vif_ops_arr[proto];932 }933 934 err = mr->mr_ops->route_create(mlxsw_sp, mr->priv,935 mr_table->catchall_route_priv,936 &catchall_route_params);937 if (err)938 goto err_ops_route_create;939 mutex_lock(&mr->table_list_lock);940 list_add_tail(&mr_table->node, &mr->table_list);941 mutex_unlock(&mr->table_list_lock);942 return mr_table;943 944err_ops_route_create:945 rhashtable_destroy(&mr_table->route_ht);946err_route_rhashtable_init:947 mutex_destroy(&mr_table->route_list_lock);948 kfree(mr_table);949 return ERR_PTR(err);950}951 952void mlxsw_sp_mr_table_destroy(struct mlxsw_sp_mr_table *mr_table)953{954 struct mlxsw_sp *mlxsw_sp = mr_table->mlxsw_sp;955 struct mlxsw_sp_mr *mr = mlxsw_sp->mr;956 957 WARN_ON(!mlxsw_sp_mr_table_empty(mr_table));958 mutex_lock(&mr->table_list_lock);959 list_del(&mr_table->node);960 mutex_unlock(&mr->table_list_lock);961 mr->mr_ops->route_destroy(mlxsw_sp, mr->priv,962 &mr_table->catchall_route_priv);963 rhashtable_destroy(&mr_table->route_ht);964 mutex_destroy(&mr_table->route_list_lock);965 kfree(mr_table);966}967 968void mlxsw_sp_mr_table_flush(struct mlxsw_sp_mr_table *mr_table)969{970 struct mlxsw_sp_mr_route *mr_route, *tmp;971 int i;972 973 mutex_lock(&mr_table->route_list_lock);974 list_for_each_entry_safe(mr_route, tmp, &mr_table->route_list, node)975 __mlxsw_sp_mr_route_del(mr_table, mr_route);976 mutex_unlock(&mr_table->route_list_lock);977 978 for (i = 0; i < MAXVIFS; i++) {979 mr_table->vifs[i].dev = NULL;980 mr_table->vifs[i].rif = NULL;981 }982}983 984bool mlxsw_sp_mr_table_empty(const struct mlxsw_sp_mr_table *mr_table)985{986 int i;987 988 for (i = 0; i < MAXVIFS; i++)989 if (mr_table->vifs[i].dev)990 return false;991 return list_empty(&mr_table->route_list);992}993 994static void mlxsw_sp_mr_route_stats_update(struct mlxsw_sp *mlxsw_sp,995 struct mlxsw_sp_mr_route *mr_route)996{997 struct mlxsw_sp_mr *mr = mlxsw_sp->mr;998 u64 packets, bytes;999 1000 if (mr_route->route_action == MLXSW_SP_MR_ROUTE_ACTION_TRAP)1001 return;1002 1003 mr->mr_ops->route_stats(mlxsw_sp, mr_route->route_priv, &packets,1004 &bytes);1005 1006 if (mr_route->mfc->mfc_un.res.pkt != packets)1007 mr_route->mfc->mfc_un.res.lastuse = jiffies;1008 mr_route->mfc->mfc_un.res.pkt = packets;1009 mr_route->mfc->mfc_un.res.bytes = bytes;1010}1011 1012static void mlxsw_sp_mr_stats_update(struct work_struct *work)1013{1014 struct mlxsw_sp_mr *mr = container_of(work, struct mlxsw_sp_mr,1015 stats_update_dw.work);1016 struct mlxsw_sp_mr_table *mr_table;1017 struct mlxsw_sp_mr_route *mr_route;1018 unsigned long interval;1019 1020 mutex_lock(&mr->table_list_lock);1021 list_for_each_entry(mr_table, &mr->table_list, node) {1022 mutex_lock(&mr_table->route_list_lock);1023 list_for_each_entry(mr_route, &mr_table->route_list, node)1024 mlxsw_sp_mr_route_stats_update(mr_table->mlxsw_sp,1025 mr_route);1026 mutex_unlock(&mr_table->route_list_lock);1027 }1028 mutex_unlock(&mr->table_list_lock);1029 1030 interval = msecs_to_jiffies(MLXSW_SP_MR_ROUTES_COUNTER_UPDATE_INTERVAL);1031 mlxsw_core_schedule_dw(&mr->stats_update_dw, interval);1032}1033 1034int mlxsw_sp_mr_init(struct mlxsw_sp *mlxsw_sp,1035 const struct mlxsw_sp_mr_ops *mr_ops)1036{1037 struct mlxsw_sp_mr *mr;1038 unsigned long interval;1039 int err;1040 1041 mr = kzalloc(sizeof(*mr) + mr_ops->priv_size, GFP_KERNEL);1042 if (!mr)1043 return -ENOMEM;1044 mr->mr_ops = mr_ops;1045 mlxsw_sp->mr = mr;1046 INIT_LIST_HEAD(&mr->table_list);1047 mutex_init(&mr->table_list_lock);1048 1049 err = mr_ops->init(mlxsw_sp, mr->priv);1050 if (err)1051 goto err;1052 1053 /* Create the delayed work for counter updates */1054 INIT_DELAYED_WORK(&mr->stats_update_dw, mlxsw_sp_mr_stats_update);1055 interval = msecs_to_jiffies(MLXSW_SP_MR_ROUTES_COUNTER_UPDATE_INTERVAL);1056 mlxsw_core_schedule_dw(&mr->stats_update_dw, interval);1057 return 0;1058err:1059 mutex_destroy(&mr->table_list_lock);1060 kfree(mr);1061 return err;1062}1063 1064void mlxsw_sp_mr_fini(struct mlxsw_sp *mlxsw_sp)1065{1066 struct mlxsw_sp_mr *mr = mlxsw_sp->mr;1067 1068 cancel_delayed_work_sync(&mr->stats_update_dw);1069 mr->mr_ops->fini(mlxsw_sp, mr->priv);1070 mutex_destroy(&mr->table_list_lock);1071 kfree(mr);1072}1073