| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049 |
- // 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.mr_vif))
- return MLXSW_SP_MR_ROUTE_ACTION_TRAP;
- /* The kernel does not match a (*,G) route that the ingress interface is
- * not one of the egress interfaces, so trap these kind of routes.
- */
- if (mr_route->mr_table->ops->is_route_starg(mr_route->mr_table,
- mr_route) &&
- !mlxsw_sp_mr_route_ivif_in_evifs(mr_route))
- return MLXSW_SP_MR_ROUTE_ACTION_TRAP;
- /* If the route has no valid eVIFs, trap it. */
- if (!mlxsw_sp_mr_route_valid_evifs_num(mr_route))
- return MLXSW_SP_MR_ROUTE_ACTION_TRAP;
- /* If one of the eVIFs has no RIF, trap-and-forward the route as there
- * is some more routing to do in software too.
- */
- list_for_each_entry(rve, &mr_route->evif_list, route_node)
- if (mlxsw_sp_mr_vif_exists(rve->mr_vif) && !rve->mr_vif->rif)
- return MLXSW_SP_MR_ROUTE_ACTION_TRAP_AND_FORWARD;
- return MLXSW_SP_MR_ROUTE_ACTION_FORWARD;
- }
- static enum mlxsw_sp_mr_route_prio
- mlxsw_sp_mr_route_prio(const struct mlxsw_sp_mr_route *mr_route)
- {
- return mr_route->mr_table->ops->is_route_starg(mr_route->mr_table,
- mr_route) ?
- MLXSW_SP_MR_ROUTE_PRIO_STARG : MLXSW_SP_MR_ROUTE_PRIO_SG;
- }
- static int mlxsw_sp_mr_route_evif_link(struct mlxsw_sp_mr_route *mr_route,
- struct mlxsw_sp_mr_vif *mr_vif)
- {
- struct mlxsw_sp_mr_route_vif_entry *rve;
- rve = kzalloc(sizeof(*rve), GFP_KERNEL);
- if (!rve)
- return -ENOMEM;
- rve->mr_route = mr_route;
- rve->mr_vif = mr_vif;
- list_add_tail(&rve->route_node, &mr_route->evif_list);
- list_add_tail(&rve->vif_node, &mr_vif->route_evif_list);
- return 0;
- }
- static void
- mlxsw_sp_mr_route_evif_unlink(struct mlxsw_sp_mr_route_vif_entry *rve)
- {
- list_del(&rve->route_node);
- list_del(&rve->vif_node);
- kfree(rve);
- }
- static void mlxsw_sp_mr_route_ivif_link(struct mlxsw_sp_mr_route *mr_route,
- struct mlxsw_sp_mr_vif *mr_vif)
- {
- mr_route->ivif.mr_route = mr_route;
- mr_route->ivif.mr_vif = mr_vif;
- list_add_tail(&mr_route->ivif.vif_node, &mr_vif->route_ivif_list);
- }
- static void mlxsw_sp_mr_route_ivif_unlink(struct mlxsw_sp_mr_route *mr_route)
- {
- list_del(&mr_route->ivif.vif_node);
- }
- static int
- mlxsw_sp_mr_route_info_create(struct mlxsw_sp_mr_table *mr_table,
- struct mlxsw_sp_mr_route *mr_route,
- struct mlxsw_sp_mr_route_info *route_info)
- {
- struct mlxsw_sp_mr_route_vif_entry *rve;
- u16 *erif_indices;
- u16 irif_index;
- u16 erif = 0;
- erif_indices = kmalloc_array(MAXVIFS, sizeof(*erif_indices),
- GFP_KERNEL);
- if (!erif_indices)
- return -ENOMEM;
- list_for_each_entry(rve, &mr_route->evif_list, route_node) {
- if (mlxsw_sp_mr_vif_valid(rve->mr_vif)) {
- u16 rifi = mlxsw_sp_rif_index(rve->mr_vif->rif);
- erif_indices[erif++] = rifi;
- }
- }
- if (mlxsw_sp_mr_vif_valid(mr_route->ivif.mr_vif))
- irif_index = mlxsw_sp_rif_index(mr_route->ivif.mr_vif->rif);
- else
- irif_index = 0;
- route_info->irif_index = irif_index;
- route_info->erif_indices = erif_indices;
- route_info->min_mtu = mr_route->min_mtu;
- route_info->route_action = mr_route->route_action;
- route_info->erif_num = erif;
- return 0;
- }
- static void
- mlxsw_sp_mr_route_info_destroy(struct mlxsw_sp_mr_route_info *route_info)
- {
- kfree(route_info->erif_indices);
- }
- static int mlxsw_sp_mr_route_write(struct mlxsw_sp_mr_table *mr_table,
- struct mlxsw_sp_mr_route *mr_route,
- bool replace)
- {
- struct mlxsw_sp *mlxsw_sp = mr_table->mlxsw_sp;
- struct mlxsw_sp_mr_route_info route_info;
- struct mlxsw_sp_mr *mr = mlxsw_sp->mr;
- int err;
- err = mlxsw_sp_mr_route_info_create(mr_table, mr_route, &route_info);
- if (err)
- return err;
- if (!replace) {
- struct mlxsw_sp_mr_route_params route_params;
- mr_route->route_priv = kzalloc(mr->mr_ops->route_priv_size,
- GFP_KERNEL);
- if (!mr_route->route_priv) {
- err = -ENOMEM;
- goto out;
- }
- route_params.key = mr_route->key;
- route_params.value = route_info;
- route_params.prio = mlxsw_sp_mr_route_prio(mr_route);
- err = mr->mr_ops->route_create(mlxsw_sp, mr->priv,
- mr_route->route_priv,
- &route_params);
- if (err)
- kfree(mr_route->route_priv);
- } else {
- err = mr->mr_ops->route_update(mlxsw_sp, mr_route->route_priv,
- &route_info);
- }
- out:
- mlxsw_sp_mr_route_info_destroy(&route_info);
- return err;
- }
- static void mlxsw_sp_mr_route_erase(struct mlxsw_sp_mr_table *mr_table,
- struct mlxsw_sp_mr_route *mr_route)
- {
- struct mlxsw_sp *mlxsw_sp = mr_table->mlxsw_sp;
- struct mlxsw_sp_mr *mr = mlxsw_sp->mr;
- mr->mr_ops->route_destroy(mlxsw_sp, mr->priv, mr_route->route_priv);
- kfree(mr_route->route_priv);
- }
- static struct mlxsw_sp_mr_route *
- mlxsw_sp_mr_route_create(struct mlxsw_sp_mr_table *mr_table,
- struct mr_mfc *mfc)
- {
- struct mlxsw_sp_mr_route_vif_entry *rve, *tmp;
- struct mlxsw_sp_mr_route *mr_route;
- int err = 0;
- int i;
- /* Allocate and init a new route and fill it with parameters */
- mr_route = kzalloc(sizeof(*mr_route), GFP_KERNEL);
- if (!mr_route)
- return ERR_PTR(-ENOMEM);
- INIT_LIST_HEAD(&mr_route->evif_list);
- /* Find min_mtu and link iVIF and eVIFs */
- mr_route->min_mtu = ETH_MAX_MTU;
- mr_cache_hold(mfc);
- mr_route->mfc = mfc;
- mr_table->ops->key_create(mr_table, &mr_route->key, mr_route->mfc);
- mr_route->mr_table = mr_table;
- for (i = 0; i < MAXVIFS; i++) {
- if (mfc->mfc_un.res.ttls[i] != 255) {
- err = mlxsw_sp_mr_route_evif_link(mr_route,
- &mr_table->vifs[i]);
- if (err)
- goto err;
- if (mr_table->vifs[i].dev &&
- mr_table->vifs[i].dev->mtu < mr_route->min_mtu)
- mr_route->min_mtu = mr_table->vifs[i].dev->mtu;
- }
- }
- mlxsw_sp_mr_route_ivif_link(mr_route,
- &mr_table->vifs[mfc->mfc_parent]);
- mr_route->route_action = mlxsw_sp_mr_route_action(mr_route);
- return mr_route;
- err:
- mr_cache_put(mfc);
- list_for_each_entry_safe(rve, tmp, &mr_route->evif_list, route_node)
- mlxsw_sp_mr_route_evif_unlink(rve);
- kfree(mr_route);
- return ERR_PTR(err);
- }
- static void mlxsw_sp_mr_route_destroy(struct mlxsw_sp_mr_table *mr_table,
- struct mlxsw_sp_mr_route *mr_route)
- {
- struct mlxsw_sp_mr_route_vif_entry *rve, *tmp;
- mlxsw_sp_mr_route_ivif_unlink(mr_route);
- mr_cache_put(mr_route->mfc);
- list_for_each_entry_safe(rve, tmp, &mr_route->evif_list, route_node)
- mlxsw_sp_mr_route_evif_unlink(rve);
- kfree(mr_route);
- }
- static void mlxsw_sp_mr_mfc_offload_set(struct mlxsw_sp_mr_route *mr_route,
- bool offload)
- {
- if (offload)
- mr_route->mfc->mfc_flags |= MFC_OFFLOAD;
- else
- mr_route->mfc->mfc_flags &= ~MFC_OFFLOAD;
- }
- static void mlxsw_sp_mr_mfc_offload_update(struct mlxsw_sp_mr_route *mr_route)
- {
- bool offload;
- offload = mr_route->route_action != MLXSW_SP_MR_ROUTE_ACTION_TRAP;
- mlxsw_sp_mr_mfc_offload_set(mr_route, offload);
- }
- static void __mlxsw_sp_mr_route_del(struct mlxsw_sp_mr_table *mr_table,
- struct mlxsw_sp_mr_route *mr_route)
- {
- mlxsw_sp_mr_mfc_offload_set(mr_route, false);
- mlxsw_sp_mr_route_erase(mr_table, mr_route);
- rhashtable_remove_fast(&mr_table->route_ht, &mr_route->ht_node,
- mlxsw_sp_mr_route_ht_params);
- list_del(&mr_route->node);
- mlxsw_sp_mr_route_destroy(mr_table, mr_route);
- }
- int mlxsw_sp_mr_route_add(struct mlxsw_sp_mr_table *mr_table,
- struct mr_mfc *mfc, bool replace)
- {
- struct mlxsw_sp_mr_route *mr_orig_route = NULL;
- struct mlxsw_sp_mr_route *mr_route;
- int err;
- if (!mr_table->ops->is_route_valid(mr_table, mfc))
- return -EINVAL;
- /* Create a new route */
- mr_route = mlxsw_sp_mr_route_create(mr_table, mfc);
- if (IS_ERR(mr_route))
- return PTR_ERR(mr_route);
- /* Find any route with a matching key */
- mr_orig_route = rhashtable_lookup_fast(&mr_table->route_ht,
- &mr_route->key,
- mlxsw_sp_mr_route_ht_params);
- if (replace) {
- /* On replace case, make the route point to the new route_priv.
- */
- if (WARN_ON(!mr_orig_route)) {
- err = -ENOENT;
- goto err_no_orig_route;
- }
- mr_route->route_priv = mr_orig_route->route_priv;
- } else if (mr_orig_route) {
- /* On non replace case, if another route with the same key was
- * found, abort, as duplicate routes are used for proxy routes.
- */
- dev_warn(mr_table->mlxsw_sp->bus_info->dev,
- "Offloading proxy routes is not supported.\n");
- err = -EINVAL;
- goto err_duplicate_route;
- }
- /* Put it in the table data-structures */
- list_add_tail(&mr_route->node, &mr_table->route_list);
- err = rhashtable_insert_fast(&mr_table->route_ht,
- &mr_route->ht_node,
- mlxsw_sp_mr_route_ht_params);
- if (err)
- goto err_rhashtable_insert;
- /* Write the route to the hardware */
- err = mlxsw_sp_mr_route_write(mr_table, mr_route, replace);
- if (err)
- goto err_mr_route_write;
- /* Destroy the original route */
- if (replace) {
- rhashtable_remove_fast(&mr_table->route_ht,
- &mr_orig_route->ht_node,
- mlxsw_sp_mr_route_ht_params);
- list_del(&mr_orig_route->node);
- mlxsw_sp_mr_route_destroy(mr_table, mr_orig_route);
- }
- mlxsw_sp_mr_mfc_offload_update(mr_route);
- return 0;
- err_mr_route_write:
- rhashtable_remove_fast(&mr_table->route_ht, &mr_route->ht_node,
- mlxsw_sp_mr_route_ht_params);
- err_rhashtable_insert:
- list_del(&mr_route->node);
- err_no_orig_route:
- err_duplicate_route:
- mlxsw_sp_mr_route_destroy(mr_table, mr_route);
- return err;
- }
- void mlxsw_sp_mr_route_del(struct mlxsw_sp_mr_table *mr_table,
- struct mr_mfc *mfc)
- {
- struct mlxsw_sp_mr_route *mr_route;
- struct mlxsw_sp_mr_route_key key;
- mr_table->ops->key_create(mr_table, &key, mfc);
- mr_route = rhashtable_lookup_fast(&mr_table->route_ht, &key,
- mlxsw_sp_mr_route_ht_params);
- if (mr_route)
- __mlxsw_sp_mr_route_del(mr_table, mr_route);
- }
- /* Should be called after the VIF struct is updated */
- static int
- mlxsw_sp_mr_route_ivif_resolve(struct mlxsw_sp_mr_table *mr_table,
- struct mlxsw_sp_mr_route_vif_entry *rve)
- {
- struct mlxsw_sp *mlxsw_sp = mr_table->mlxsw_sp;
- enum mlxsw_sp_mr_route_action route_action;
- struct mlxsw_sp_mr *mr = mlxsw_sp->mr;
- u16 irif_index;
- int err;
- route_action = mlxsw_sp_mr_route_action(rve->mr_route);
- if (route_action == MLXSW_SP_MR_ROUTE_ACTION_TRAP)
- return 0;
- /* rve->mr_vif->rif is guaranteed to be valid at this stage */
- irif_index = mlxsw_sp_rif_index(rve->mr_vif->rif);
- err = mr->mr_ops->route_irif_update(mlxsw_sp, rve->mr_route->route_priv,
- irif_index);
- if (err)
- return err;
- err = mr->mr_ops->route_action_update(mlxsw_sp,
- rve->mr_route->route_priv,
- route_action);
- if (err)
- /* No need to rollback here because the iRIF change only takes
- * place after the action has been updated.
- */
- return err;
- rve->mr_route->route_action = route_action;
- mlxsw_sp_mr_mfc_offload_update(rve->mr_route);
- return 0;
- }
- static void
- mlxsw_sp_mr_route_ivif_unresolve(struct mlxsw_sp_mr_table *mr_table,
- struct mlxsw_sp_mr_route_vif_entry *rve)
- {
- struct mlxsw_sp *mlxsw_sp = mr_table->mlxsw_sp;
- struct mlxsw_sp_mr *mr = mlxsw_sp->mr;
- mr->mr_ops->route_action_update(mlxsw_sp, rve->mr_route->route_priv,
- MLXSW_SP_MR_ROUTE_ACTION_TRAP);
- rve->mr_route->route_action = MLXSW_SP_MR_ROUTE_ACTION_TRAP;
- mlxsw_sp_mr_mfc_offload_update(rve->mr_route);
- }
- /* Should be called after the RIF struct is updated */
- static int
- mlxsw_sp_mr_route_evif_resolve(struct mlxsw_sp_mr_table *mr_table,
- struct mlxsw_sp_mr_route_vif_entry *rve)
- {
- struct mlxsw_sp *mlxsw_sp = mr_table->mlxsw_sp;
- enum mlxsw_sp_mr_route_action route_action;
- struct mlxsw_sp_mr *mr = mlxsw_sp->mr;
- u16 erif_index = 0;
- int err;
- /* Update the route action, as the new eVIF can be a tunnel or a pimreg
- * device which will require updating the action.
- */
- route_action = mlxsw_sp_mr_route_action(rve->mr_route);
- if (route_action != rve->mr_route->route_action) {
- err = mr->mr_ops->route_action_update(mlxsw_sp,
- rve->mr_route->route_priv,
- route_action);
- if (err)
- return err;
- }
- /* Add the eRIF */
- if (mlxsw_sp_mr_vif_valid(rve->mr_vif)) {
- erif_index = mlxsw_sp_rif_index(rve->mr_vif->rif);
- err = mr->mr_ops->route_erif_add(mlxsw_sp,
- rve->mr_route->route_priv,
- erif_index);
- if (err)
- goto err_route_erif_add;
- }
- /* Update the minimum MTU */
- if (rve->mr_vif->dev->mtu < rve->mr_route->min_mtu) {
- rve->mr_route->min_mtu = rve->mr_vif->dev->mtu;
- err = mr->mr_ops->route_min_mtu_update(mlxsw_sp,
- rve->mr_route->route_priv,
- rve->mr_route->min_mtu);
- if (err)
- goto err_route_min_mtu_update;
- }
- rve->mr_route->route_action = route_action;
- mlxsw_sp_mr_mfc_offload_update(rve->mr_route);
- return 0;
- err_route_min_mtu_update:
- if (mlxsw_sp_mr_vif_valid(rve->mr_vif))
- mr->mr_ops->route_erif_del(mlxsw_sp, rve->mr_route->route_priv,
- erif_index);
- err_route_erif_add:
- if (route_action != rve->mr_route->route_action)
- mr->mr_ops->route_action_update(mlxsw_sp,
- rve->mr_route->route_priv,
- rve->mr_route->route_action);
- return err;
- }
- /* Should be called before the RIF struct is updated */
- static void
- mlxsw_sp_mr_route_evif_unresolve(struct mlxsw_sp_mr_table *mr_table,
- struct mlxsw_sp_mr_route_vif_entry *rve)
- {
- struct mlxsw_sp *mlxsw_sp = mr_table->mlxsw_sp;
- enum mlxsw_sp_mr_route_action route_action;
- struct mlxsw_sp_mr *mr = mlxsw_sp->mr;
- u16 rifi;
- /* If the unresolved RIF was not valid, no need to delete it */
- if (!mlxsw_sp_mr_vif_valid(rve->mr_vif))
- return;
- /* Update the route action: if there is only one valid eVIF in the
- * route, set the action to trap as the VIF deletion will lead to zero
- * valid eVIFs. On any other case, use the mlxsw_sp_mr_route_action to
- * determine the route action.
- */
- if (mlxsw_sp_mr_route_valid_evifs_num(rve->mr_route) == 1)
- route_action = MLXSW_SP_MR_ROUTE_ACTION_TRAP;
- else
- route_action = mlxsw_sp_mr_route_action(rve->mr_route);
- if (route_action != rve->mr_route->route_action)
- mr->mr_ops->route_action_update(mlxsw_sp,
- rve->mr_route->route_priv,
- route_action);
- /* Delete the erif from the route */
- rifi = mlxsw_sp_rif_index(rve->mr_vif->rif);
- mr->mr_ops->route_erif_del(mlxsw_sp, rve->mr_route->route_priv, rifi);
- rve->mr_route->route_action = route_action;
- mlxsw_sp_mr_mfc_offload_update(rve->mr_route);
- }
- static int mlxsw_sp_mr_vif_resolve(struct mlxsw_sp_mr_table *mr_table,
- struct net_device *dev,
- struct mlxsw_sp_mr_vif *mr_vif,
- unsigned long vif_flags,
- const struct mlxsw_sp_rif *rif)
- {
- struct mlxsw_sp_mr_route_vif_entry *irve, *erve;
- int err;
- /* Update the VIF */
- mr_vif->dev = dev;
- mr_vif->rif = rif;
- mr_vif->vif_flags = vif_flags;
- /* Update all routes where this VIF is used as an unresolved iRIF */
- list_for_each_entry(irve, &mr_vif->route_ivif_list, vif_node) {
- err = mlxsw_sp_mr_route_ivif_resolve(mr_table, irve);
- if (err)
- goto err_irif_unresolve;
- }
- /* Update all routes where this VIF is used as an unresolved eRIF */
- list_for_each_entry(erve, &mr_vif->route_evif_list, vif_node) {
- err = mlxsw_sp_mr_route_evif_resolve(mr_table, erve);
- if (err)
- goto err_erif_unresolve;
- }
- return 0;
- err_erif_unresolve:
- list_for_each_entry_from_reverse(erve, &mr_vif->route_evif_list,
- vif_node)
- mlxsw_sp_mr_route_evif_unresolve(mr_table, erve);
- err_irif_unresolve:
- list_for_each_entry_from_reverse(irve, &mr_vif->route_ivif_list,
- vif_node)
- mlxsw_sp_mr_route_ivif_unresolve(mr_table, irve);
- mr_vif->rif = NULL;
- return err;
- }
- static void mlxsw_sp_mr_vif_unresolve(struct mlxsw_sp_mr_table *mr_table,
- struct net_device *dev,
- struct mlxsw_sp_mr_vif *mr_vif)
- {
- struct mlxsw_sp_mr_route_vif_entry *rve;
- /* Update all routes where this VIF is used as an unresolved eRIF */
- list_for_each_entry(rve, &mr_vif->route_evif_list, vif_node)
- mlxsw_sp_mr_route_evif_unresolve(mr_table, rve);
- /* Update all routes where this VIF is used as an unresolved iRIF */
- list_for_each_entry(rve, &mr_vif->route_ivif_list, vif_node)
- mlxsw_sp_mr_route_ivif_unresolve(mr_table, rve);
- /* Update the VIF */
- mr_vif->dev = dev;
- mr_vif->rif = NULL;
- }
- int mlxsw_sp_mr_vif_add(struct mlxsw_sp_mr_table *mr_table,
- struct net_device *dev, vifi_t vif_index,
- unsigned long vif_flags, const struct mlxsw_sp_rif *rif)
- {
- struct mlxsw_sp_mr_vif *mr_vif = &mr_table->vifs[vif_index];
- if (WARN_ON(vif_index >= MAXVIFS))
- return -EINVAL;
- if (mr_vif->dev)
- return -EEXIST;
- return mlxsw_sp_mr_vif_resolve(mr_table, dev, mr_vif, vif_flags, rif);
- }
- void mlxsw_sp_mr_vif_del(struct mlxsw_sp_mr_table *mr_table, vifi_t vif_index)
- {
- struct mlxsw_sp_mr_vif *mr_vif = &mr_table->vifs[vif_index];
- if (WARN_ON(vif_index >= MAXVIFS))
- return;
- if (WARN_ON(!mr_vif->dev))
- return;
- mlxsw_sp_mr_vif_unresolve(mr_table, NULL, mr_vif);
- }
- static struct mlxsw_sp_mr_vif *
- mlxsw_sp_mr_dev_vif_lookup(struct mlxsw_sp_mr_table *mr_table,
- const struct net_device *dev)
- {
- vifi_t vif_index;
- for (vif_index = 0; vif_index < MAXVIFS; vif_index++)
- if (mr_table->vifs[vif_index].dev == dev)
- return &mr_table->vifs[vif_index];
- return NULL;
- }
- int mlxsw_sp_mr_rif_add(struct mlxsw_sp_mr_table *mr_table,
- const struct mlxsw_sp_rif *rif)
- {
- const struct net_device *rif_dev = mlxsw_sp_rif_dev(rif);
- struct mlxsw_sp_mr_vif *mr_vif;
- if (!rif_dev)
- return 0;
- mr_vif = mlxsw_sp_mr_dev_vif_lookup(mr_table, rif_dev);
- if (!mr_vif)
- return 0;
- return mlxsw_sp_mr_vif_resolve(mr_table, mr_vif->dev, mr_vif,
- mr_vif->vif_flags, rif);
- }
- void mlxsw_sp_mr_rif_del(struct mlxsw_sp_mr_table *mr_table,
- const struct mlxsw_sp_rif *rif)
- {
- const struct net_device *rif_dev = mlxsw_sp_rif_dev(rif);
- struct mlxsw_sp_mr_vif *mr_vif;
- if (!rif_dev)
- return;
- mr_vif = mlxsw_sp_mr_dev_vif_lookup(mr_table, rif_dev);
- if (!mr_vif)
- return;
- mlxsw_sp_mr_vif_unresolve(mr_table, mr_vif->dev, mr_vif);
- }
- void mlxsw_sp_mr_rif_mtu_update(struct mlxsw_sp_mr_table *mr_table,
- const struct mlxsw_sp_rif *rif, int mtu)
- {
- const struct net_device *rif_dev = mlxsw_sp_rif_dev(rif);
- struct mlxsw_sp *mlxsw_sp = mr_table->mlxsw_sp;
- struct mlxsw_sp_mr_route_vif_entry *rve;
- struct mlxsw_sp_mr *mr = mlxsw_sp->mr;
- struct mlxsw_sp_mr_vif *mr_vif;
- if (!rif_dev)
- return;
- /* Search for a VIF that use that RIF */
- mr_vif = mlxsw_sp_mr_dev_vif_lookup(mr_table, rif_dev);
- if (!mr_vif)
- return;
- /* Update all the routes that uses that VIF as eVIF */
- list_for_each_entry(rve, &mr_vif->route_evif_list, vif_node) {
- if (mtu < rve->mr_route->min_mtu) {
- rve->mr_route->min_mtu = mtu;
- mr->mr_ops->route_min_mtu_update(mlxsw_sp,
- rve->mr_route->route_priv,
- mtu);
- }
- }
- }
- /* Protocol specific functions */
- static bool
- mlxsw_sp_mr_route4_validate(const struct mlxsw_sp_mr_table *mr_table,
- const struct mr_mfc *c)
- {
- struct mfc_cache *mfc = (struct mfc_cache *) c;
- /* If the route is a (*,*) route, abort, as these kind of routes are
- * used for proxy routes.
- */
- if (mfc->mfc_origin == htonl(INADDR_ANY) &&
- mfc->mfc_mcastgrp == htonl(INADDR_ANY)) {
- dev_warn(mr_table->mlxsw_sp->bus_info->dev,
- "Offloading proxy routes is not supported.\n");
- return false;
- }
- return true;
- }
- static void mlxsw_sp_mr_route4_key(struct mlxsw_sp_mr_table *mr_table,
- struct mlxsw_sp_mr_route_key *key,
- struct mr_mfc *c)
- {
- const struct mfc_cache *mfc = (struct mfc_cache *) c;
- bool starg;
- starg = (mfc->mfc_origin == htonl(INADDR_ANY));
- memset(key, 0, sizeof(*key));
- key->vrid = mr_table->vr_id;
- key->proto = MLXSW_SP_L3_PROTO_IPV4;
- key->group.addr4 = mfc->mfc_mcastgrp;
- key->group_mask.addr4 = htonl(0xffffffff);
- key->source.addr4 = mfc->mfc_origin;
- key->source_mask.addr4 = htonl(starg ? 0 : 0xffffffff);
- }
- static bool mlxsw_sp_mr_route4_starg(const struct mlxsw_sp_mr_table *mr_table,
- const struct mlxsw_sp_mr_route *mr_route)
- {
- return mr_route->key.source_mask.addr4 == htonl(INADDR_ANY);
- }
- static bool mlxsw_sp_mr_vif4_is_regular(const struct mlxsw_sp_mr_vif *vif)
- {
- return !(vif->vif_flags & (VIFF_TUNNEL | VIFF_REGISTER));
- }
- static bool
- mlxsw_sp_mr_route6_validate(const struct mlxsw_sp_mr_table *mr_table,
- const struct mr_mfc *c)
- {
- struct mfc6_cache *mfc = (struct mfc6_cache *) c;
- /* If the route is a (*,*) route, abort, as these kind of routes are
- * used for proxy routes.
- */
- if (ipv6_addr_any(&mfc->mf6c_origin) &&
- ipv6_addr_any(&mfc->mf6c_mcastgrp)) {
- dev_warn(mr_table->mlxsw_sp->bus_info->dev,
- "Offloading proxy routes is not supported.\n");
- return false;
- }
- return true;
- }
- static void mlxsw_sp_mr_route6_key(struct mlxsw_sp_mr_table *mr_table,
- struct mlxsw_sp_mr_route_key *key,
- struct mr_mfc *c)
- {
- const struct mfc6_cache *mfc = (struct mfc6_cache *) c;
- memset(key, 0, sizeof(*key));
- key->vrid = mr_table->vr_id;
- key->proto = MLXSW_SP_L3_PROTO_IPV6;
- key->group.addr6 = mfc->mf6c_mcastgrp;
- memset(&key->group_mask.addr6, 0xff, sizeof(key->group_mask.addr6));
- key->source.addr6 = mfc->mf6c_origin;
- if (!ipv6_addr_any(&mfc->mf6c_origin))
- memset(&key->source_mask.addr6, 0xff,
- sizeof(key->source_mask.addr6));
- }
- static bool mlxsw_sp_mr_route6_starg(const struct mlxsw_sp_mr_table *mr_table,
- const struct mlxsw_sp_mr_route *mr_route)
- {
- return ipv6_addr_any(&mr_route->key.source_mask.addr6);
- }
- static bool mlxsw_sp_mr_vif6_is_regular(const struct mlxsw_sp_mr_vif *vif)
- {
- return !(vif->vif_flags & MIFF_REGISTER);
- }
- static struct
- mlxsw_sp_mr_vif_ops mlxsw_sp_mr_vif_ops_arr[] = {
- {
- .is_regular = mlxsw_sp_mr_vif4_is_regular,
- },
- {
- .is_regular = mlxsw_sp_mr_vif6_is_regular,
- },
- };
- static struct
- mlxsw_sp_mr_table_ops mlxsw_sp_mr_table_ops_arr[] = {
- {
- .is_route_valid = mlxsw_sp_mr_route4_validate,
- .key_create = mlxsw_sp_mr_route4_key,
- .is_route_starg = mlxsw_sp_mr_route4_starg,
- },
- {
- .is_route_valid = mlxsw_sp_mr_route6_validate,
- .key_create = mlxsw_sp_mr_route6_key,
- .is_route_starg = mlxsw_sp_mr_route6_starg,
- },
- };
- struct mlxsw_sp_mr_table *mlxsw_sp_mr_table_create(struct mlxsw_sp *mlxsw_sp,
- u32 vr_id,
- enum mlxsw_sp_l3proto proto)
- {
- struct mlxsw_sp_mr_route_params catchall_route_params = {
- .prio = MLXSW_SP_MR_ROUTE_PRIO_CATCHALL,
- .key = {
- .vrid = vr_id,
- .proto = proto,
- },
- .value = {
- .route_action = MLXSW_SP_MR_ROUTE_ACTION_TRAP,
- }
- };
- struct mlxsw_sp_mr *mr = mlxsw_sp->mr;
- struct mlxsw_sp_mr_table *mr_table;
- int err;
- int i;
- mr_table = kzalloc(sizeof(*mr_table) + mr->mr_ops->route_priv_size,
- GFP_KERNEL);
- if (!mr_table)
- return ERR_PTR(-ENOMEM);
- mr_table->vr_id = vr_id;
- mr_table->mlxsw_sp = mlxsw_sp;
- mr_table->proto = proto;
- mr_table->ops = &mlxsw_sp_mr_table_ops_arr[proto];
- INIT_LIST_HEAD(&mr_table->route_list);
- err = rhashtable_init(&mr_table->route_ht,
- &mlxsw_sp_mr_route_ht_params);
- if (err)
- goto err_route_rhashtable_init;
- for (i = 0; i < MAXVIFS; i++) {
- INIT_LIST_HEAD(&mr_table->vifs[i].route_evif_list);
- INIT_LIST_HEAD(&mr_table->vifs[i].route_ivif_list);
- mr_table->vifs[i].ops = &mlxsw_sp_mr_vif_ops_arr[proto];
- }
- err = mr->mr_ops->route_create(mlxsw_sp, mr->priv,
- mr_table->catchall_route_priv,
- &catchall_route_params);
- if (err)
- goto err_ops_route_create;
- list_add_tail(&mr_table->node, &mr->table_list);
- return mr_table;
- err_ops_route_create:
- rhashtable_destroy(&mr_table->route_ht);
- err_route_rhashtable_init:
- kfree(mr_table);
- return ERR_PTR(err);
- }
- void mlxsw_sp_mr_table_destroy(struct mlxsw_sp_mr_table *mr_table)
- {
- struct mlxsw_sp *mlxsw_sp = mr_table->mlxsw_sp;
- struct mlxsw_sp_mr *mr = mlxsw_sp->mr;
- WARN_ON(!mlxsw_sp_mr_table_empty(mr_table));
- list_del(&mr_table->node);
- mr->mr_ops->route_destroy(mlxsw_sp, mr->priv,
- &mr_table->catchall_route_priv);
- rhashtable_destroy(&mr_table->route_ht);
- kfree(mr_table);
- }
- void mlxsw_sp_mr_table_flush(struct mlxsw_sp_mr_table *mr_table)
- {
- struct mlxsw_sp_mr_route *mr_route, *tmp;
- int i;
- list_for_each_entry_safe(mr_route, tmp, &mr_table->route_list, node)
- __mlxsw_sp_mr_route_del(mr_table, mr_route);
- for (i = 0; i < MAXVIFS; i++) {
- mr_table->vifs[i].dev = NULL;
- mr_table->vifs[i].rif = NULL;
- }
- }
- bool mlxsw_sp_mr_table_empty(const struct mlxsw_sp_mr_table *mr_table)
- {
- int i;
- for (i = 0; i < MAXVIFS; i++)
- if (mr_table->vifs[i].dev)
- return false;
- return list_empty(&mr_table->route_list);
- }
- static void mlxsw_sp_mr_route_stats_update(struct mlxsw_sp *mlxsw_sp,
- struct mlxsw_sp_mr_route *mr_route)
- {
- struct mlxsw_sp_mr *mr = mlxsw_sp->mr;
- u64 packets, bytes;
- if (mr_route->route_action == MLXSW_SP_MR_ROUTE_ACTION_TRAP)
- return;
- mr->mr_ops->route_stats(mlxsw_sp, mr_route->route_priv, &packets,
- &bytes);
- if (mr_route->mfc->mfc_un.res.pkt != packets)
- mr_route->mfc->mfc_un.res.lastuse = jiffies;
- mr_route->mfc->mfc_un.res.pkt = packets;
- mr_route->mfc->mfc_un.res.bytes = bytes;
- }
- static void mlxsw_sp_mr_stats_update(struct work_struct *work)
- {
- struct mlxsw_sp_mr *mr = container_of(work, struct mlxsw_sp_mr,
- stats_update_dw.work);
- struct mlxsw_sp_mr_table *mr_table;
- struct mlxsw_sp_mr_route *mr_route;
- unsigned long interval;
- rtnl_lock();
- list_for_each_entry(mr_table, &mr->table_list, node)
- list_for_each_entry(mr_route, &mr_table->route_list, node)
- mlxsw_sp_mr_route_stats_update(mr_table->mlxsw_sp,
- mr_route);
- rtnl_unlock();
- interval = msecs_to_jiffies(MLXSW_SP_MR_ROUTES_COUNTER_UPDATE_INTERVAL);
- mlxsw_core_schedule_dw(&mr->stats_update_dw, interval);
- }
- int mlxsw_sp_mr_init(struct mlxsw_sp *mlxsw_sp,
- const struct mlxsw_sp_mr_ops *mr_ops)
- {
- struct mlxsw_sp_mr *mr;
- unsigned long interval;
- int err;
- mr = kzalloc(sizeof(*mr) + mr_ops->priv_size, GFP_KERNEL);
- if (!mr)
- return -ENOMEM;
- mr->mr_ops = mr_ops;
- mlxsw_sp->mr = mr;
- INIT_LIST_HEAD(&mr->table_list);
- err = mr_ops->init(mlxsw_sp, mr->priv);
- if (err)
- goto err;
- /* Create the delayed work for counter updates */
- INIT_DELAYED_WORK(&mr->stats_update_dw, mlxsw_sp_mr_stats_update);
- interval = msecs_to_jiffies(MLXSW_SP_MR_ROUTES_COUNTER_UPDATE_INTERVAL);
- mlxsw_core_schedule_dw(&mr->stats_update_dw, interval);
- return 0;
- err:
- kfree(mr);
- return err;
- }
- void mlxsw_sp_mr_fini(struct mlxsw_sp *mlxsw_sp)
- {
- struct mlxsw_sp_mr *mr = mlxsw_sp->mr;
- cancel_delayed_work_sync(&mr->stats_update_dw);
- mr->mr_ops->fini(mlxsw_sp, mr->priv);
- kfree(mr);
- }
|