11834 lines · c
1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.02/* Copyright (c) 2016-2018 Mellanox Technologies. All rights reserved */3 4#include <linux/kernel.h>5#include <linux/types.h>6#include <linux/rhashtable.h>7#include <linux/bitops.h>8#include <linux/in6.h>9#include <linux/notifier.h>10#include <linux/inetdevice.h>11#include <linux/netdevice.h>12#include <linux/if_bridge.h>13#include <linux/socket.h>14#include <linux/route.h>15#include <linux/gcd.h>16#include <linux/if_macvlan.h>17#include <linux/refcount.h>18#include <linux/jhash.h>19#include <linux/net_namespace.h>20#include <linux/mutex.h>21#include <linux/genalloc.h>22#include <linux/xarray.h>23#include <net/netevent.h>24#include <net/neighbour.h>25#include <net/arp.h>26#include <net/inet_dscp.h>27#include <net/ip_fib.h>28#include <net/ip6_fib.h>29#include <net/nexthop.h>30#include <net/fib_rules.h>31#include <net/ip_tunnels.h>32#include <net/l3mdev.h>33#include <net/addrconf.h>34#include <net/ndisc.h>35#include <net/ipv6.h>36#include <net/fib_notifier.h>37#include <net/switchdev.h>38 39#include "spectrum.h"40#include "core.h"41#include "reg.h"42#include "spectrum_cnt.h"43#include "spectrum_dpipe.h"44#include "spectrum_ipip.h"45#include "spectrum_mr.h"46#include "spectrum_mr_tcam.h"47#include "spectrum_router.h"48#include "spectrum_span.h"49 50struct mlxsw_sp_fib;51struct mlxsw_sp_vr;52struct mlxsw_sp_lpm_tree;53struct mlxsw_sp_rif_ops;54 55struct mlxsw_sp_crif_key {56 struct net_device *dev;57};58 59struct mlxsw_sp_crif {60 struct mlxsw_sp_crif_key key;61 struct rhash_head ht_node;62 bool can_destroy;63 struct list_head nexthop_list;64 struct mlxsw_sp_rif *rif;65};66 67static const struct rhashtable_params mlxsw_sp_crif_ht_params = {68 .key_offset = offsetof(struct mlxsw_sp_crif, key),69 .key_len = sizeof_field(struct mlxsw_sp_crif, key),70 .head_offset = offsetof(struct mlxsw_sp_crif, ht_node),71};72 73struct mlxsw_sp_rif {74 struct mlxsw_sp_crif *crif; /* NULL for underlay RIF */75 netdevice_tracker dev_tracker;76 struct list_head neigh_list;77 struct mlxsw_sp_fid *fid;78 unsigned char addr[ETH_ALEN];79 int mtu;80 u16 rif_index;81 u8 mac_profile_id;82 u8 rif_entries;83 u16 vr_id;84 const struct mlxsw_sp_rif_ops *ops;85 struct mlxsw_sp *mlxsw_sp;86 87 unsigned int counter_ingress;88 bool counter_ingress_valid;89 unsigned int counter_egress;90 bool counter_egress_valid;91};92 93static struct net_device *mlxsw_sp_rif_dev(const struct mlxsw_sp_rif *rif)94{95 if (!rif->crif)96 return NULL;97 return rif->crif->key.dev;98}99 100struct mlxsw_sp_rif_params {101 struct net_device *dev;102 union {103 u16 system_port;104 u16 lag_id;105 };106 u16 vid;107 bool lag;108 bool double_entry;109};110 111struct mlxsw_sp_rif_subport {112 struct mlxsw_sp_rif common;113 refcount_t ref_count;114 union {115 u16 system_port;116 u16 lag_id;117 };118 u16 vid;119 bool lag;120};121 122struct mlxsw_sp_rif_ipip_lb {123 struct mlxsw_sp_rif common;124 struct mlxsw_sp_rif_ipip_lb_config lb_config;125 u16 ul_vr_id; /* Spectrum-1. */126 u16 ul_rif_id; /* Spectrum-2+. */127};128 129struct mlxsw_sp_rif_params_ipip_lb {130 struct mlxsw_sp_rif_params common;131 struct mlxsw_sp_rif_ipip_lb_config lb_config;132};133 134struct mlxsw_sp_rif_ops {135 enum mlxsw_sp_rif_type type;136 size_t rif_size;137 138 void (*setup)(struct mlxsw_sp_rif *rif,139 const struct mlxsw_sp_rif_params *params);140 int (*configure)(struct mlxsw_sp_rif *rif,141 struct netlink_ext_ack *extack);142 void (*deconfigure)(struct mlxsw_sp_rif *rif);143 struct mlxsw_sp_fid * (*fid_get)(struct mlxsw_sp_rif *rif,144 const struct mlxsw_sp_rif_params *params,145 struct netlink_ext_ack *extack);146 void (*fdb_del)(struct mlxsw_sp_rif *rif, const char *mac);147};148 149struct mlxsw_sp_rif_mac_profile {150 unsigned char mac_prefix[ETH_ALEN];151 refcount_t ref_count;152 u8 id;153};154 155struct mlxsw_sp_router_ops {156 int (*init)(struct mlxsw_sp *mlxsw_sp);157 int (*ipips_init)(struct mlxsw_sp *mlxsw_sp);158};159 160static struct mlxsw_sp_rif *161mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp,162 const struct net_device *dev);163static void mlxsw_sp_rif_destroy(struct mlxsw_sp_rif *rif);164static void mlxsw_sp_lpm_tree_hold(struct mlxsw_sp_lpm_tree *lpm_tree);165static void mlxsw_sp_lpm_tree_put(struct mlxsw_sp *mlxsw_sp,166 struct mlxsw_sp_lpm_tree *lpm_tree);167static int mlxsw_sp_vr_lpm_tree_bind(struct mlxsw_sp *mlxsw_sp,168 const struct mlxsw_sp_fib *fib,169 u8 tree_id);170static int mlxsw_sp_vr_lpm_tree_unbind(struct mlxsw_sp *mlxsw_sp,171 const struct mlxsw_sp_fib *fib);172 173static unsigned int *174mlxsw_sp_rif_p_counter_get(struct mlxsw_sp_rif *rif,175 enum mlxsw_sp_rif_counter_dir dir)176{177 switch (dir) {178 case MLXSW_SP_RIF_COUNTER_EGRESS:179 return &rif->counter_egress;180 case MLXSW_SP_RIF_COUNTER_INGRESS:181 return &rif->counter_ingress;182 }183 return NULL;184}185 186static bool187mlxsw_sp_rif_counter_valid_get(struct mlxsw_sp_rif *rif,188 enum mlxsw_sp_rif_counter_dir dir)189{190 switch (dir) {191 case MLXSW_SP_RIF_COUNTER_EGRESS:192 return rif->counter_egress_valid;193 case MLXSW_SP_RIF_COUNTER_INGRESS:194 return rif->counter_ingress_valid;195 }196 return false;197}198 199static void200mlxsw_sp_rif_counter_valid_set(struct mlxsw_sp_rif *rif,201 enum mlxsw_sp_rif_counter_dir dir,202 bool valid)203{204 switch (dir) {205 case MLXSW_SP_RIF_COUNTER_EGRESS:206 rif->counter_egress_valid = valid;207 break;208 case MLXSW_SP_RIF_COUNTER_INGRESS:209 rif->counter_ingress_valid = valid;210 break;211 }212}213 214static int mlxsw_sp_rif_counter_edit(struct mlxsw_sp *mlxsw_sp, u16 rif_index,215 unsigned int counter_index, bool enable,216 enum mlxsw_sp_rif_counter_dir dir)217{218 char ritr_pl[MLXSW_REG_RITR_LEN];219 bool is_egress = false;220 int err;221 222 if (dir == MLXSW_SP_RIF_COUNTER_EGRESS)223 is_egress = true;224 mlxsw_reg_ritr_rif_pack(ritr_pl, rif_index);225 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);226 if (err)227 return err;228 229 mlxsw_reg_ritr_counter_pack(ritr_pl, counter_index, enable,230 is_egress);231 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);232}233 234int mlxsw_sp_rif_counter_value_get(struct mlxsw_sp *mlxsw_sp,235 struct mlxsw_sp_rif *rif,236 enum mlxsw_sp_rif_counter_dir dir, u64 *cnt)237{238 char ricnt_pl[MLXSW_REG_RICNT_LEN];239 unsigned int *p_counter_index;240 bool valid;241 int err;242 243 valid = mlxsw_sp_rif_counter_valid_get(rif, dir);244 if (!valid)245 return -EINVAL;246 247 p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir);248 if (!p_counter_index)249 return -EINVAL;250 mlxsw_reg_ricnt_pack(ricnt_pl, *p_counter_index,251 MLXSW_REG_RICNT_OPCODE_NOP);252 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ricnt), ricnt_pl);253 if (err)254 return err;255 *cnt = mlxsw_reg_ricnt_good_unicast_packets_get(ricnt_pl);256 return 0;257}258 259struct mlxsw_sp_rif_counter_set_basic {260 u64 good_unicast_packets;261 u64 good_multicast_packets;262 u64 good_broadcast_packets;263 u64 good_unicast_bytes;264 u64 good_multicast_bytes;265 u64 good_broadcast_bytes;266 u64 error_packets;267 u64 discard_packets;268 u64 error_bytes;269 u64 discard_bytes;270};271 272static int273mlxsw_sp_rif_counter_fetch_clear(struct mlxsw_sp_rif *rif,274 enum mlxsw_sp_rif_counter_dir dir,275 struct mlxsw_sp_rif_counter_set_basic *set)276{277 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;278 char ricnt_pl[MLXSW_REG_RICNT_LEN];279 unsigned int *p_counter_index;280 int err;281 282 if (!mlxsw_sp_rif_counter_valid_get(rif, dir))283 return -EINVAL;284 285 p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir);286 if (!p_counter_index)287 return -EINVAL;288 289 mlxsw_reg_ricnt_pack(ricnt_pl, *p_counter_index,290 MLXSW_REG_RICNT_OPCODE_CLEAR);291 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ricnt), ricnt_pl);292 if (err)293 return err;294 295 if (!set)296 return 0;297 298#define MLXSW_SP_RIF_COUNTER_EXTRACT(NAME) \299 (set->NAME = mlxsw_reg_ricnt_ ## NAME ## _get(ricnt_pl))300 301 MLXSW_SP_RIF_COUNTER_EXTRACT(good_unicast_packets);302 MLXSW_SP_RIF_COUNTER_EXTRACT(good_multicast_packets);303 MLXSW_SP_RIF_COUNTER_EXTRACT(good_broadcast_packets);304 MLXSW_SP_RIF_COUNTER_EXTRACT(good_unicast_bytes);305 MLXSW_SP_RIF_COUNTER_EXTRACT(good_multicast_bytes);306 MLXSW_SP_RIF_COUNTER_EXTRACT(good_broadcast_bytes);307 MLXSW_SP_RIF_COUNTER_EXTRACT(error_packets);308 MLXSW_SP_RIF_COUNTER_EXTRACT(discard_packets);309 MLXSW_SP_RIF_COUNTER_EXTRACT(error_bytes);310 MLXSW_SP_RIF_COUNTER_EXTRACT(discard_bytes);311 312#undef MLXSW_SP_RIF_COUNTER_EXTRACT313 314 return 0;315}316 317static int mlxsw_sp_rif_counter_clear(struct mlxsw_sp *mlxsw_sp,318 unsigned int counter_index)319{320 char ricnt_pl[MLXSW_REG_RICNT_LEN];321 322 mlxsw_reg_ricnt_pack(ricnt_pl, counter_index,323 MLXSW_REG_RICNT_OPCODE_CLEAR);324 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ricnt), ricnt_pl);325}326 327int mlxsw_sp_rif_counter_alloc(struct mlxsw_sp_rif *rif,328 enum mlxsw_sp_rif_counter_dir dir)329{330 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;331 unsigned int *p_counter_index;332 int err;333 334 if (mlxsw_sp_rif_counter_valid_get(rif, dir))335 return 0;336 337 p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir);338 if (!p_counter_index)339 return -EINVAL;340 341 err = mlxsw_sp_counter_alloc(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF,342 p_counter_index);343 if (err)344 return err;345 346 err = mlxsw_sp_rif_counter_clear(mlxsw_sp, *p_counter_index);347 if (err)348 goto err_counter_clear;349 350 err = mlxsw_sp_rif_counter_edit(mlxsw_sp, rif->rif_index,351 *p_counter_index, true, dir);352 if (err)353 goto err_counter_edit;354 mlxsw_sp_rif_counter_valid_set(rif, dir, true);355 return 0;356 357err_counter_edit:358err_counter_clear:359 mlxsw_sp_counter_free(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF,360 *p_counter_index);361 return err;362}363 364void mlxsw_sp_rif_counter_free(struct mlxsw_sp_rif *rif,365 enum mlxsw_sp_rif_counter_dir dir)366{367 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;368 unsigned int *p_counter_index;369 370 if (!mlxsw_sp_rif_counter_valid_get(rif, dir))371 return;372 373 p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir);374 if (WARN_ON(!p_counter_index))375 return;376 mlxsw_sp_rif_counter_edit(mlxsw_sp, rif->rif_index,377 *p_counter_index, false, dir);378 mlxsw_sp_counter_free(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF,379 *p_counter_index);380 mlxsw_sp_rif_counter_valid_set(rif, dir, false);381}382 383static void mlxsw_sp_rif_counters_alloc(struct mlxsw_sp_rif *rif)384{385 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;386 struct devlink *devlink;387 388 devlink = priv_to_devlink(mlxsw_sp->core);389 if (!devlink_dpipe_table_counter_enabled(devlink,390 MLXSW_SP_DPIPE_TABLE_NAME_ERIF))391 return;392 mlxsw_sp_rif_counter_alloc(rif, MLXSW_SP_RIF_COUNTER_EGRESS);393}394 395static void mlxsw_sp_rif_counters_free(struct mlxsw_sp_rif *rif)396{397 mlxsw_sp_rif_counter_free(rif, MLXSW_SP_RIF_COUNTER_EGRESS);398}399 400#define MLXSW_SP_PREFIX_COUNT (sizeof(struct in6_addr) * BITS_PER_BYTE + 1)401 402struct mlxsw_sp_prefix_usage {403 DECLARE_BITMAP(b, MLXSW_SP_PREFIX_COUNT);404};405 406#define mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage) \407 for_each_set_bit(prefix, (prefix_usage)->b, MLXSW_SP_PREFIX_COUNT)408 409static bool410mlxsw_sp_prefix_usage_eq(struct mlxsw_sp_prefix_usage *prefix_usage1,411 struct mlxsw_sp_prefix_usage *prefix_usage2)412{413 return !memcmp(prefix_usage1, prefix_usage2, sizeof(*prefix_usage1));414}415 416static void417mlxsw_sp_prefix_usage_cpy(struct mlxsw_sp_prefix_usage *prefix_usage1,418 struct mlxsw_sp_prefix_usage *prefix_usage2)419{420 memcpy(prefix_usage1, prefix_usage2, sizeof(*prefix_usage1));421}422 423static void424mlxsw_sp_prefix_usage_set(struct mlxsw_sp_prefix_usage *prefix_usage,425 unsigned char prefix_len)426{427 set_bit(prefix_len, prefix_usage->b);428}429 430static void431mlxsw_sp_prefix_usage_clear(struct mlxsw_sp_prefix_usage *prefix_usage,432 unsigned char prefix_len)433{434 clear_bit(prefix_len, prefix_usage->b);435}436 437struct mlxsw_sp_fib_key {438 unsigned char addr[sizeof(struct in6_addr)];439 unsigned char prefix_len;440};441 442enum mlxsw_sp_fib_entry_type {443 MLXSW_SP_FIB_ENTRY_TYPE_REMOTE,444 MLXSW_SP_FIB_ENTRY_TYPE_LOCAL,445 MLXSW_SP_FIB_ENTRY_TYPE_TRAP,446 MLXSW_SP_FIB_ENTRY_TYPE_BLACKHOLE,447 MLXSW_SP_FIB_ENTRY_TYPE_UNREACHABLE,448 449 /* This is a special case of local delivery, where a packet should be450 * decapsulated on reception. Note that there is no corresponding ENCAP,451 * because that's a type of next hop, not of FIB entry. (There can be452 * several next hops in a REMOTE entry, and some of them may be453 * encapsulating entries.)454 */455 MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP,456 MLXSW_SP_FIB_ENTRY_TYPE_NVE_DECAP,457};458 459struct mlxsw_sp_nexthop_group_info;460struct mlxsw_sp_nexthop_group;461struct mlxsw_sp_fib_entry;462 463struct mlxsw_sp_fib_node {464 struct mlxsw_sp_fib_entry *fib_entry;465 struct list_head list;466 struct rhash_head ht_node;467 struct mlxsw_sp_fib *fib;468 struct mlxsw_sp_fib_key key;469};470 471struct mlxsw_sp_fib_entry_decap {472 struct mlxsw_sp_ipip_entry *ipip_entry;473 u32 tunnel_index;474};475 476struct mlxsw_sp_fib_entry {477 struct mlxsw_sp_fib_node *fib_node;478 enum mlxsw_sp_fib_entry_type type;479 struct list_head nexthop_group_node;480 struct mlxsw_sp_nexthop_group *nh_group;481 struct mlxsw_sp_fib_entry_decap decap; /* Valid for decap entries. */482};483 484struct mlxsw_sp_fib4_entry {485 struct mlxsw_sp_fib_entry common;486 struct fib_info *fi;487 u32 tb_id;488 dscp_t dscp;489 u8 type;490};491 492struct mlxsw_sp_fib6_entry {493 struct mlxsw_sp_fib_entry common;494 struct list_head rt6_list;495 unsigned int nrt6;496};497 498struct mlxsw_sp_rt6 {499 struct list_head list;500 struct fib6_info *rt;501};502 503struct mlxsw_sp_lpm_tree {504 u8 id; /* tree ID */505 refcount_t ref_count;506 enum mlxsw_sp_l3proto proto;507 unsigned long prefix_ref_count[MLXSW_SP_PREFIX_COUNT];508 struct mlxsw_sp_prefix_usage prefix_usage;509};510 511struct mlxsw_sp_fib {512 struct rhashtable ht;513 struct list_head node_list;514 struct mlxsw_sp_vr *vr;515 struct mlxsw_sp_lpm_tree *lpm_tree;516 enum mlxsw_sp_l3proto proto;517};518 519struct mlxsw_sp_vr {520 u16 id; /* virtual router ID */521 u32 tb_id; /* kernel fib table id */522 unsigned int rif_count;523 struct mlxsw_sp_fib *fib4;524 struct mlxsw_sp_fib *fib6;525 struct mlxsw_sp_mr_table *mr_table[MLXSW_SP_L3_PROTO_MAX];526 struct mlxsw_sp_rif *ul_rif;527 refcount_t ul_rif_refcnt;528};529 530static const struct rhashtable_params mlxsw_sp_fib_ht_params;531 532static struct mlxsw_sp_fib *mlxsw_sp_fib_create(struct mlxsw_sp *mlxsw_sp,533 struct mlxsw_sp_vr *vr,534 enum mlxsw_sp_l3proto proto)535{536 struct mlxsw_sp_lpm_tree *lpm_tree;537 struct mlxsw_sp_fib *fib;538 int err;539 540 lpm_tree = mlxsw_sp->router->lpm.proto_trees[proto];541 fib = kzalloc(sizeof(*fib), GFP_KERNEL);542 if (!fib)543 return ERR_PTR(-ENOMEM);544 err = rhashtable_init(&fib->ht, &mlxsw_sp_fib_ht_params);545 if (err)546 goto err_rhashtable_init;547 INIT_LIST_HEAD(&fib->node_list);548 fib->proto = proto;549 fib->vr = vr;550 fib->lpm_tree = lpm_tree;551 mlxsw_sp_lpm_tree_hold(lpm_tree);552 err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, fib, lpm_tree->id);553 if (err)554 goto err_lpm_tree_bind;555 return fib;556 557err_lpm_tree_bind:558 mlxsw_sp_lpm_tree_put(mlxsw_sp, lpm_tree);559err_rhashtable_init:560 kfree(fib);561 return ERR_PTR(err);562}563 564static void mlxsw_sp_fib_destroy(struct mlxsw_sp *mlxsw_sp,565 struct mlxsw_sp_fib *fib)566{567 mlxsw_sp_vr_lpm_tree_unbind(mlxsw_sp, fib);568 mlxsw_sp_lpm_tree_put(mlxsw_sp, fib->lpm_tree);569 WARN_ON(!list_empty(&fib->node_list));570 rhashtable_destroy(&fib->ht);571 kfree(fib);572}573 574static struct mlxsw_sp_lpm_tree *575mlxsw_sp_lpm_tree_find_unused(struct mlxsw_sp *mlxsw_sp)576{577 static struct mlxsw_sp_lpm_tree *lpm_tree;578 int i;579 580 for (i = 0; i < mlxsw_sp->router->lpm.tree_count; i++) {581 lpm_tree = &mlxsw_sp->router->lpm.trees[i];582 if (refcount_read(&lpm_tree->ref_count) == 0)583 return lpm_tree;584 }585 return NULL;586}587 588static int mlxsw_sp_lpm_tree_alloc(struct mlxsw_sp *mlxsw_sp,589 struct mlxsw_sp_lpm_tree *lpm_tree)590{591 char ralta_pl[MLXSW_REG_RALTA_LEN];592 593 mlxsw_reg_ralta_pack(ralta_pl, true,594 (enum mlxsw_reg_ralxx_protocol) lpm_tree->proto,595 lpm_tree->id);596 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);597}598 599static void mlxsw_sp_lpm_tree_free(struct mlxsw_sp *mlxsw_sp,600 struct mlxsw_sp_lpm_tree *lpm_tree)601{602 char ralta_pl[MLXSW_REG_RALTA_LEN];603 604 mlxsw_reg_ralta_pack(ralta_pl, false,605 (enum mlxsw_reg_ralxx_protocol) lpm_tree->proto,606 lpm_tree->id);607 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);608}609 610static int611mlxsw_sp_lpm_tree_left_struct_set(struct mlxsw_sp *mlxsw_sp,612 struct mlxsw_sp_prefix_usage *prefix_usage,613 struct mlxsw_sp_lpm_tree *lpm_tree)614{615 char ralst_pl[MLXSW_REG_RALST_LEN];616 u8 root_bin = 0;617 u8 prefix;618 u8 last_prefix = MLXSW_REG_RALST_BIN_NO_CHILD;619 620 mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage)621 root_bin = prefix;622 623 mlxsw_reg_ralst_pack(ralst_pl, root_bin, lpm_tree->id);624 mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage) {625 if (prefix == 0)626 continue;627 mlxsw_reg_ralst_bin_pack(ralst_pl, prefix, last_prefix,628 MLXSW_REG_RALST_BIN_NO_CHILD);629 last_prefix = prefix;630 }631 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralst), ralst_pl);632}633 634static struct mlxsw_sp_lpm_tree *635mlxsw_sp_lpm_tree_create(struct mlxsw_sp *mlxsw_sp,636 struct mlxsw_sp_prefix_usage *prefix_usage,637 enum mlxsw_sp_l3proto proto)638{639 struct mlxsw_sp_lpm_tree *lpm_tree;640 int err;641 642 lpm_tree = mlxsw_sp_lpm_tree_find_unused(mlxsw_sp);643 if (!lpm_tree)644 return ERR_PTR(-EBUSY);645 lpm_tree->proto = proto;646 err = mlxsw_sp_lpm_tree_alloc(mlxsw_sp, lpm_tree);647 if (err)648 return ERR_PTR(err);649 650 err = mlxsw_sp_lpm_tree_left_struct_set(mlxsw_sp, prefix_usage,651 lpm_tree);652 if (err)653 goto err_left_struct_set;654 memcpy(&lpm_tree->prefix_usage, prefix_usage,655 sizeof(lpm_tree->prefix_usage));656 memset(&lpm_tree->prefix_ref_count, 0,657 sizeof(lpm_tree->prefix_ref_count));658 refcount_set(&lpm_tree->ref_count, 1);659 return lpm_tree;660 661err_left_struct_set:662 mlxsw_sp_lpm_tree_free(mlxsw_sp, lpm_tree);663 return ERR_PTR(err);664}665 666static void mlxsw_sp_lpm_tree_destroy(struct mlxsw_sp *mlxsw_sp,667 struct mlxsw_sp_lpm_tree *lpm_tree)668{669 mlxsw_sp_lpm_tree_free(mlxsw_sp, lpm_tree);670}671 672static struct mlxsw_sp_lpm_tree *673mlxsw_sp_lpm_tree_get(struct mlxsw_sp *mlxsw_sp,674 struct mlxsw_sp_prefix_usage *prefix_usage,675 enum mlxsw_sp_l3proto proto)676{677 struct mlxsw_sp_lpm_tree *lpm_tree;678 int i;679 680 for (i = 0; i < mlxsw_sp->router->lpm.tree_count; i++) {681 lpm_tree = &mlxsw_sp->router->lpm.trees[i];682 if (refcount_read(&lpm_tree->ref_count) &&683 lpm_tree->proto == proto &&684 mlxsw_sp_prefix_usage_eq(&lpm_tree->prefix_usage,685 prefix_usage)) {686 mlxsw_sp_lpm_tree_hold(lpm_tree);687 return lpm_tree;688 }689 }690 return mlxsw_sp_lpm_tree_create(mlxsw_sp, prefix_usage, proto);691}692 693static void mlxsw_sp_lpm_tree_hold(struct mlxsw_sp_lpm_tree *lpm_tree)694{695 refcount_inc(&lpm_tree->ref_count);696}697 698static void mlxsw_sp_lpm_tree_put(struct mlxsw_sp *mlxsw_sp,699 struct mlxsw_sp_lpm_tree *lpm_tree)700{701 if (!refcount_dec_and_test(&lpm_tree->ref_count))702 return;703 mlxsw_sp_lpm_tree_destroy(mlxsw_sp, lpm_tree);704}705 706#define MLXSW_SP_LPM_TREE_MIN 1 /* tree 0 is reserved */707 708static int mlxsw_sp_lpm_init(struct mlxsw_sp *mlxsw_sp)709{710 struct mlxsw_sp_prefix_usage req_prefix_usage = {{ 0 } };711 struct mlxsw_sp_lpm_tree *lpm_tree;712 u64 max_trees;713 int err, i;714 715 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_LPM_TREES))716 return -EIO;717 718 max_trees = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_LPM_TREES);719 mlxsw_sp->router->lpm.tree_count = max_trees - MLXSW_SP_LPM_TREE_MIN;720 mlxsw_sp->router->lpm.trees = kcalloc(mlxsw_sp->router->lpm.tree_count,721 sizeof(struct mlxsw_sp_lpm_tree),722 GFP_KERNEL);723 if (!mlxsw_sp->router->lpm.trees)724 return -ENOMEM;725 726 for (i = 0; i < mlxsw_sp->router->lpm.tree_count; i++) {727 lpm_tree = &mlxsw_sp->router->lpm.trees[i];728 lpm_tree->id = i + MLXSW_SP_LPM_TREE_MIN;729 }730 731 lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage,732 MLXSW_SP_L3_PROTO_IPV4);733 if (IS_ERR(lpm_tree)) {734 err = PTR_ERR(lpm_tree);735 goto err_ipv4_tree_get;736 }737 mlxsw_sp->router->lpm.proto_trees[MLXSW_SP_L3_PROTO_IPV4] = lpm_tree;738 739 lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage,740 MLXSW_SP_L3_PROTO_IPV6);741 if (IS_ERR(lpm_tree)) {742 err = PTR_ERR(lpm_tree);743 goto err_ipv6_tree_get;744 }745 mlxsw_sp->router->lpm.proto_trees[MLXSW_SP_L3_PROTO_IPV6] = lpm_tree;746 747 return 0;748 749err_ipv6_tree_get:750 lpm_tree = mlxsw_sp->router->lpm.proto_trees[MLXSW_SP_L3_PROTO_IPV4];751 mlxsw_sp_lpm_tree_put(mlxsw_sp, lpm_tree);752err_ipv4_tree_get:753 kfree(mlxsw_sp->router->lpm.trees);754 return err;755}756 757static void mlxsw_sp_lpm_fini(struct mlxsw_sp *mlxsw_sp)758{759 struct mlxsw_sp_lpm_tree *lpm_tree;760 761 lpm_tree = mlxsw_sp->router->lpm.proto_trees[MLXSW_SP_L3_PROTO_IPV6];762 mlxsw_sp_lpm_tree_put(mlxsw_sp, lpm_tree);763 764 lpm_tree = mlxsw_sp->router->lpm.proto_trees[MLXSW_SP_L3_PROTO_IPV4];765 mlxsw_sp_lpm_tree_put(mlxsw_sp, lpm_tree);766 767 kfree(mlxsw_sp->router->lpm.trees);768}769 770static bool mlxsw_sp_vr_is_used(const struct mlxsw_sp_vr *vr)771{772 return !!vr->fib4 || !!vr->fib6 ||773 !!vr->mr_table[MLXSW_SP_L3_PROTO_IPV4] ||774 !!vr->mr_table[MLXSW_SP_L3_PROTO_IPV6];775}776 777static struct mlxsw_sp_vr *mlxsw_sp_vr_find_unused(struct mlxsw_sp *mlxsw_sp)778{779 int max_vrs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS);780 struct mlxsw_sp_vr *vr;781 int i;782 783 for (i = 0; i < max_vrs; i++) {784 vr = &mlxsw_sp->router->vrs[i];785 if (!mlxsw_sp_vr_is_used(vr))786 return vr;787 }788 return NULL;789}790 791static int mlxsw_sp_vr_lpm_tree_bind(struct mlxsw_sp *mlxsw_sp,792 const struct mlxsw_sp_fib *fib, u8 tree_id)793{794 char raltb_pl[MLXSW_REG_RALTB_LEN];795 796 mlxsw_reg_raltb_pack(raltb_pl, fib->vr->id,797 (enum mlxsw_reg_ralxx_protocol) fib->proto,798 tree_id);799 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl);800}801 802static int mlxsw_sp_vr_lpm_tree_unbind(struct mlxsw_sp *mlxsw_sp,803 const struct mlxsw_sp_fib *fib)804{805 char raltb_pl[MLXSW_REG_RALTB_LEN];806 807 /* Bind to tree 0 which is default */808 mlxsw_reg_raltb_pack(raltb_pl, fib->vr->id,809 (enum mlxsw_reg_ralxx_protocol) fib->proto, 0);810 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl);811}812 813static u32 mlxsw_sp_fix_tb_id(u32 tb_id)814{815 /* For our purpose, squash main, default and local tables into one */816 if (tb_id == RT_TABLE_LOCAL || tb_id == RT_TABLE_DEFAULT)817 tb_id = RT_TABLE_MAIN;818 return tb_id;819}820 821static struct mlxsw_sp_vr *mlxsw_sp_vr_find(struct mlxsw_sp *mlxsw_sp,822 u32 tb_id)823{824 int max_vrs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS);825 struct mlxsw_sp_vr *vr;826 int i;827 828 tb_id = mlxsw_sp_fix_tb_id(tb_id);829 830 for (i = 0; i < max_vrs; i++) {831 vr = &mlxsw_sp->router->vrs[i];832 if (mlxsw_sp_vr_is_used(vr) && vr->tb_id == tb_id)833 return vr;834 }835 return NULL;836}837 838int mlxsw_sp_router_tb_id_vr_id(struct mlxsw_sp *mlxsw_sp, u32 tb_id,839 u16 *vr_id)840{841 struct mlxsw_sp_vr *vr;842 int err = 0;843 844 mutex_lock(&mlxsw_sp->router->lock);845 vr = mlxsw_sp_vr_find(mlxsw_sp, tb_id);846 if (!vr) {847 err = -ESRCH;848 goto out;849 }850 *vr_id = vr->id;851out:852 mutex_unlock(&mlxsw_sp->router->lock);853 return err;854}855 856static struct mlxsw_sp_fib *mlxsw_sp_vr_fib(const struct mlxsw_sp_vr *vr,857 enum mlxsw_sp_l3proto proto)858{859 switch (proto) {860 case MLXSW_SP_L3_PROTO_IPV4:861 return vr->fib4;862 case MLXSW_SP_L3_PROTO_IPV6:863 return vr->fib6;864 }865 return NULL;866}867 868static struct mlxsw_sp_vr *mlxsw_sp_vr_create(struct mlxsw_sp *mlxsw_sp,869 u32 tb_id,870 struct netlink_ext_ack *extack)871{872 struct mlxsw_sp_mr_table *mr4_table, *mr6_table;873 struct mlxsw_sp_fib *fib4;874 struct mlxsw_sp_fib *fib6;875 struct mlxsw_sp_vr *vr;876 int err;877 878 vr = mlxsw_sp_vr_find_unused(mlxsw_sp);879 if (!vr) {880 NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported virtual routers");881 return ERR_PTR(-EBUSY);882 }883 fib4 = mlxsw_sp_fib_create(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV4);884 if (IS_ERR(fib4))885 return ERR_CAST(fib4);886 fib6 = mlxsw_sp_fib_create(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV6);887 if (IS_ERR(fib6)) {888 err = PTR_ERR(fib6);889 goto err_fib6_create;890 }891 mr4_table = mlxsw_sp_mr_table_create(mlxsw_sp, vr->id,892 MLXSW_SP_L3_PROTO_IPV4);893 if (IS_ERR(mr4_table)) {894 err = PTR_ERR(mr4_table);895 goto err_mr4_table_create;896 }897 mr6_table = mlxsw_sp_mr_table_create(mlxsw_sp, vr->id,898 MLXSW_SP_L3_PROTO_IPV6);899 if (IS_ERR(mr6_table)) {900 err = PTR_ERR(mr6_table);901 goto err_mr6_table_create;902 }903 904 vr->fib4 = fib4;905 vr->fib6 = fib6;906 vr->mr_table[MLXSW_SP_L3_PROTO_IPV4] = mr4_table;907 vr->mr_table[MLXSW_SP_L3_PROTO_IPV6] = mr6_table;908 vr->tb_id = tb_id;909 return vr;910 911err_mr6_table_create:912 mlxsw_sp_mr_table_destroy(mr4_table);913err_mr4_table_create:914 mlxsw_sp_fib_destroy(mlxsw_sp, fib6);915err_fib6_create:916 mlxsw_sp_fib_destroy(mlxsw_sp, fib4);917 return ERR_PTR(err);918}919 920static void mlxsw_sp_vr_destroy(struct mlxsw_sp *mlxsw_sp,921 struct mlxsw_sp_vr *vr)922{923 mlxsw_sp_mr_table_destroy(vr->mr_table[MLXSW_SP_L3_PROTO_IPV6]);924 vr->mr_table[MLXSW_SP_L3_PROTO_IPV6] = NULL;925 mlxsw_sp_mr_table_destroy(vr->mr_table[MLXSW_SP_L3_PROTO_IPV4]);926 vr->mr_table[MLXSW_SP_L3_PROTO_IPV4] = NULL;927 mlxsw_sp_fib_destroy(mlxsw_sp, vr->fib6);928 vr->fib6 = NULL;929 mlxsw_sp_fib_destroy(mlxsw_sp, vr->fib4);930 vr->fib4 = NULL;931}932 933static struct mlxsw_sp_vr *mlxsw_sp_vr_get(struct mlxsw_sp *mlxsw_sp, u32 tb_id,934 struct netlink_ext_ack *extack)935{936 struct mlxsw_sp_vr *vr;937 938 tb_id = mlxsw_sp_fix_tb_id(tb_id);939 vr = mlxsw_sp_vr_find(mlxsw_sp, tb_id);940 if (!vr)941 vr = mlxsw_sp_vr_create(mlxsw_sp, tb_id, extack);942 return vr;943}944 945static void mlxsw_sp_vr_put(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_vr *vr)946{947 if (!vr->rif_count && list_empty(&vr->fib4->node_list) &&948 list_empty(&vr->fib6->node_list) &&949 mlxsw_sp_mr_table_empty(vr->mr_table[MLXSW_SP_L3_PROTO_IPV4]) &&950 mlxsw_sp_mr_table_empty(vr->mr_table[MLXSW_SP_L3_PROTO_IPV6]))951 mlxsw_sp_vr_destroy(mlxsw_sp, vr);952}953 954static bool955mlxsw_sp_vr_lpm_tree_should_replace(struct mlxsw_sp_vr *vr,956 enum mlxsw_sp_l3proto proto, u8 tree_id)957{958 struct mlxsw_sp_fib *fib = mlxsw_sp_vr_fib(vr, proto);959 960 if (!mlxsw_sp_vr_is_used(vr))961 return false;962 if (fib->lpm_tree->id == tree_id)963 return true;964 return false;965}966 967static int mlxsw_sp_vr_lpm_tree_replace(struct mlxsw_sp *mlxsw_sp,968 struct mlxsw_sp_fib *fib,969 struct mlxsw_sp_lpm_tree *new_tree)970{971 struct mlxsw_sp_lpm_tree *old_tree = fib->lpm_tree;972 int err;973 974 fib->lpm_tree = new_tree;975 mlxsw_sp_lpm_tree_hold(new_tree);976 err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, fib, new_tree->id);977 if (err)978 goto err_tree_bind;979 mlxsw_sp_lpm_tree_put(mlxsw_sp, old_tree);980 return 0;981 982err_tree_bind:983 mlxsw_sp_lpm_tree_put(mlxsw_sp, new_tree);984 fib->lpm_tree = old_tree;985 return err;986}987 988static int mlxsw_sp_vrs_lpm_tree_replace(struct mlxsw_sp *mlxsw_sp,989 struct mlxsw_sp_fib *fib,990 struct mlxsw_sp_lpm_tree *new_tree)991{992 int max_vrs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS);993 enum mlxsw_sp_l3proto proto = fib->proto;994 struct mlxsw_sp_lpm_tree *old_tree;995 u8 old_id, new_id = new_tree->id;996 struct mlxsw_sp_vr *vr;997 int i, err;998 999 old_tree = mlxsw_sp->router->lpm.proto_trees[proto];1000 old_id = old_tree->id;1001 1002 for (i = 0; i < max_vrs; i++) {1003 vr = &mlxsw_sp->router->vrs[i];1004 if (!mlxsw_sp_vr_lpm_tree_should_replace(vr, proto, old_id))1005 continue;1006 err = mlxsw_sp_vr_lpm_tree_replace(mlxsw_sp,1007 mlxsw_sp_vr_fib(vr, proto),1008 new_tree);1009 if (err)1010 goto err_tree_replace;1011 }1012 1013 memcpy(new_tree->prefix_ref_count, old_tree->prefix_ref_count,1014 sizeof(new_tree->prefix_ref_count));1015 mlxsw_sp->router->lpm.proto_trees[proto] = new_tree;1016 mlxsw_sp_lpm_tree_put(mlxsw_sp, old_tree);1017 1018 return 0;1019 1020err_tree_replace:1021 for (i--; i >= 0; i--) {1022 if (!mlxsw_sp_vr_lpm_tree_should_replace(vr, proto, new_id))1023 continue;1024 mlxsw_sp_vr_lpm_tree_replace(mlxsw_sp,1025 mlxsw_sp_vr_fib(vr, proto),1026 old_tree);1027 }1028 return err;1029}1030 1031static int mlxsw_sp_vrs_init(struct mlxsw_sp *mlxsw_sp)1032{1033 struct mlxsw_sp_vr *vr;1034 u64 max_vrs;1035 int i;1036 1037 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_VRS))1038 return -EIO;1039 1040 max_vrs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS);1041 mlxsw_sp->router->vrs = kcalloc(max_vrs, sizeof(struct mlxsw_sp_vr),1042 GFP_KERNEL);1043 if (!mlxsw_sp->router->vrs)1044 return -ENOMEM;1045 1046 for (i = 0; i < max_vrs; i++) {1047 vr = &mlxsw_sp->router->vrs[i];1048 vr->id = i;1049 }1050 1051 return 0;1052}1053 1054static void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp);1055 1056static void mlxsw_sp_vrs_fini(struct mlxsw_sp *mlxsw_sp)1057{1058 /* At this stage we're guaranteed not to have new incoming1059 * FIB notifications and the work queue is free from FIBs1060 * sitting on top of mlxsw netdevs. However, we can still1061 * have other FIBs queued. Flush the queue before flushing1062 * the device's tables. No need for locks, as we're the only1063 * writer.1064 */1065 mlxsw_core_flush_owq();1066 mlxsw_sp_router_fib_flush(mlxsw_sp);1067 kfree(mlxsw_sp->router->vrs);1068}1069 1070u32 mlxsw_sp_ipip_dev_ul_tb_id(const struct net_device *ol_dev)1071{1072 struct net_device *d;1073 u32 tb_id;1074 1075 rcu_read_lock();1076 d = mlxsw_sp_ipip_netdev_ul_dev_get(ol_dev);1077 if (d)1078 tb_id = l3mdev_fib_table(d) ? : RT_TABLE_MAIN;1079 else1080 tb_id = RT_TABLE_MAIN;1081 rcu_read_unlock();1082 1083 return tb_id;1084}1085 1086static void1087mlxsw_sp_crif_init(struct mlxsw_sp_crif *crif, struct net_device *dev)1088{1089 crif->key.dev = dev;1090 INIT_LIST_HEAD(&crif->nexthop_list);1091}1092 1093static struct mlxsw_sp_crif *1094mlxsw_sp_crif_alloc(struct net_device *dev)1095{1096 struct mlxsw_sp_crif *crif;1097 1098 crif = kzalloc(sizeof(*crif), GFP_KERNEL);1099 if (!crif)1100 return NULL;1101 1102 mlxsw_sp_crif_init(crif, dev);1103 return crif;1104}1105 1106static void mlxsw_sp_crif_free(struct mlxsw_sp_crif *crif)1107{1108 if (WARN_ON(crif->rif))1109 return;1110 1111 WARN_ON(!list_empty(&crif->nexthop_list));1112 kfree(crif);1113}1114 1115static int mlxsw_sp_crif_insert(struct mlxsw_sp_router *router,1116 struct mlxsw_sp_crif *crif)1117{1118 return rhashtable_insert_fast(&router->crif_ht, &crif->ht_node,1119 mlxsw_sp_crif_ht_params);1120}1121 1122static void mlxsw_sp_crif_remove(struct mlxsw_sp_router *router,1123 struct mlxsw_sp_crif *crif)1124{1125 rhashtable_remove_fast(&router->crif_ht, &crif->ht_node,1126 mlxsw_sp_crif_ht_params);1127}1128 1129static struct mlxsw_sp_crif *1130mlxsw_sp_crif_lookup(struct mlxsw_sp_router *router,1131 const struct net_device *dev)1132{1133 struct mlxsw_sp_crif_key key = {1134 .dev = (struct net_device *)dev,1135 };1136 1137 return rhashtable_lookup_fast(&router->crif_ht, &key,1138 mlxsw_sp_crif_ht_params);1139}1140 1141static struct mlxsw_sp_rif *1142mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp,1143 const struct mlxsw_sp_rif_params *params,1144 struct netlink_ext_ack *extack);1145 1146static struct mlxsw_sp_rif_ipip_lb *1147mlxsw_sp_ipip_ol_ipip_lb_create(struct mlxsw_sp *mlxsw_sp,1148 enum mlxsw_sp_ipip_type ipipt,1149 struct net_device *ol_dev,1150 struct netlink_ext_ack *extack)1151{1152 struct mlxsw_sp_rif_params_ipip_lb lb_params;1153 const struct mlxsw_sp_ipip_ops *ipip_ops;1154 struct mlxsw_sp_rif *rif;1155 1156 ipip_ops = mlxsw_sp->router->ipip_ops_arr[ipipt];1157 lb_params = (struct mlxsw_sp_rif_params_ipip_lb) {1158 .common.dev = ol_dev,1159 .common.lag = false,1160 .common.double_entry = ipip_ops->double_rif_entry,1161 .lb_config = ipip_ops->ol_loopback_config(mlxsw_sp, ol_dev),1162 };1163 1164 rif = mlxsw_sp_rif_create(mlxsw_sp, &lb_params.common, extack);1165 if (IS_ERR(rif))1166 return ERR_CAST(rif);1167 return container_of(rif, struct mlxsw_sp_rif_ipip_lb, common);1168}1169 1170static struct mlxsw_sp_ipip_entry *1171mlxsw_sp_ipip_entry_alloc(struct mlxsw_sp *mlxsw_sp,1172 enum mlxsw_sp_ipip_type ipipt,1173 struct net_device *ol_dev)1174{1175 const struct mlxsw_sp_ipip_ops *ipip_ops;1176 struct mlxsw_sp_ipip_entry *ipip_entry;1177 struct mlxsw_sp_ipip_entry *ret = NULL;1178 int err;1179 1180 ipip_ops = mlxsw_sp->router->ipip_ops_arr[ipipt];1181 ipip_entry = kzalloc(sizeof(*ipip_entry), GFP_KERNEL);1182 if (!ipip_entry)1183 return ERR_PTR(-ENOMEM);1184 1185 ipip_entry->ol_lb = mlxsw_sp_ipip_ol_ipip_lb_create(mlxsw_sp, ipipt,1186 ol_dev, NULL);1187 if (IS_ERR(ipip_entry->ol_lb)) {1188 ret = ERR_CAST(ipip_entry->ol_lb);1189 goto err_ol_ipip_lb_create;1190 }1191 1192 ipip_entry->ipipt = ipipt;1193 ipip_entry->ol_dev = ol_dev;1194 ipip_entry->parms = ipip_ops->parms_init(ol_dev);1195 1196 err = ipip_ops->rem_ip_addr_set(mlxsw_sp, ipip_entry);1197 if (err) {1198 ret = ERR_PTR(err);1199 goto err_rem_ip_addr_set;1200 }1201 1202 return ipip_entry;1203 1204err_rem_ip_addr_set:1205 mlxsw_sp_rif_destroy(&ipip_entry->ol_lb->common);1206err_ol_ipip_lb_create:1207 kfree(ipip_entry);1208 return ret;1209}1210 1211static void mlxsw_sp_ipip_entry_dealloc(struct mlxsw_sp *mlxsw_sp,1212 struct mlxsw_sp_ipip_entry *ipip_entry)1213{1214 const struct mlxsw_sp_ipip_ops *ipip_ops =1215 mlxsw_sp->router->ipip_ops_arr[ipip_entry->ipipt];1216 1217 ipip_ops->rem_ip_addr_unset(mlxsw_sp, ipip_entry);1218 mlxsw_sp_rif_destroy(&ipip_entry->ol_lb->common);1219 kfree(ipip_entry);1220}1221 1222static bool1223mlxsw_sp_ipip_entry_saddr_matches(struct mlxsw_sp *mlxsw_sp,1224 const enum mlxsw_sp_l3proto ul_proto,1225 union mlxsw_sp_l3addr saddr,1226 u32 ul_tb_id,1227 struct mlxsw_sp_ipip_entry *ipip_entry)1228{1229 u32 tun_ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(ipip_entry->ol_dev);1230 enum mlxsw_sp_ipip_type ipipt = ipip_entry->ipipt;1231 union mlxsw_sp_l3addr tun_saddr;1232 1233 if (mlxsw_sp->router->ipip_ops_arr[ipipt]->ul_proto != ul_proto)1234 return false;1235 1236 tun_saddr = mlxsw_sp_ipip_netdev_saddr(ul_proto, ipip_entry->ol_dev);1237 return tun_ul_tb_id == ul_tb_id &&1238 mlxsw_sp_l3addr_eq(&tun_saddr, &saddr);1239}1240 1241static int mlxsw_sp_ipip_decap_parsing_depth_inc(struct mlxsw_sp *mlxsw_sp,1242 enum mlxsw_sp_ipip_type ipipt)1243{1244 const struct mlxsw_sp_ipip_ops *ipip_ops;1245 1246 ipip_ops = mlxsw_sp->router->ipip_ops_arr[ipipt];1247 1248 /* Not all tunnels require to increase the default pasing depth1249 * (96 bytes).1250 */1251 if (ipip_ops->inc_parsing_depth)1252 return mlxsw_sp_parsing_depth_inc(mlxsw_sp);1253 1254 return 0;1255}1256 1257static void mlxsw_sp_ipip_decap_parsing_depth_dec(struct mlxsw_sp *mlxsw_sp,1258 enum mlxsw_sp_ipip_type ipipt)1259{1260 const struct mlxsw_sp_ipip_ops *ipip_ops =1261 mlxsw_sp->router->ipip_ops_arr[ipipt];1262 1263 if (ipip_ops->inc_parsing_depth)1264 mlxsw_sp_parsing_depth_dec(mlxsw_sp);1265}1266 1267static int1268mlxsw_sp_fib_entry_decap_init(struct mlxsw_sp *mlxsw_sp,1269 struct mlxsw_sp_fib_entry *fib_entry,1270 struct mlxsw_sp_ipip_entry *ipip_entry)1271{1272 u32 tunnel_index;1273 int err;1274 1275 err = mlxsw_sp_kvdl_alloc(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ADJ,1276 1, &tunnel_index);1277 if (err)1278 return err;1279 1280 err = mlxsw_sp_ipip_decap_parsing_depth_inc(mlxsw_sp,1281 ipip_entry->ipipt);1282 if (err)1283 goto err_parsing_depth_inc;1284 1285 ipip_entry->decap_fib_entry = fib_entry;1286 fib_entry->decap.ipip_entry = ipip_entry;1287 fib_entry->decap.tunnel_index = tunnel_index;1288 1289 return 0;1290 1291err_parsing_depth_inc:1292 mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ADJ, 1,1293 fib_entry->decap.tunnel_index);1294 return err;1295}1296 1297static void mlxsw_sp_fib_entry_decap_fini(struct mlxsw_sp *mlxsw_sp,1298 struct mlxsw_sp_fib_entry *fib_entry)1299{1300 enum mlxsw_sp_ipip_type ipipt = fib_entry->decap.ipip_entry->ipipt;1301 1302 /* Unlink this node from the IPIP entry that it's the decap entry of. */1303 fib_entry->decap.ipip_entry->decap_fib_entry = NULL;1304 fib_entry->decap.ipip_entry = NULL;1305 mlxsw_sp_ipip_decap_parsing_depth_dec(mlxsw_sp, ipipt);1306 mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ADJ,1307 1, fib_entry->decap.tunnel_index);1308}1309 1310static struct mlxsw_sp_fib_node *1311mlxsw_sp_fib_node_lookup(struct mlxsw_sp_fib *fib, const void *addr,1312 size_t addr_len, unsigned char prefix_len);1313static int mlxsw_sp_fib_entry_update(struct mlxsw_sp *mlxsw_sp,1314 struct mlxsw_sp_fib_entry *fib_entry);1315 1316static void1317mlxsw_sp_ipip_entry_demote_decap(struct mlxsw_sp *mlxsw_sp,1318 struct mlxsw_sp_ipip_entry *ipip_entry)1319{1320 struct mlxsw_sp_fib_entry *fib_entry = ipip_entry->decap_fib_entry;1321 1322 mlxsw_sp_fib_entry_decap_fini(mlxsw_sp, fib_entry);1323 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;1324 1325 mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);1326}1327 1328static void1329mlxsw_sp_ipip_entry_promote_decap(struct mlxsw_sp *mlxsw_sp,1330 struct mlxsw_sp_ipip_entry *ipip_entry,1331 struct mlxsw_sp_fib_entry *decap_fib_entry)1332{1333 if (mlxsw_sp_fib_entry_decap_init(mlxsw_sp, decap_fib_entry,1334 ipip_entry))1335 return;1336 decap_fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP;1337 1338 if (mlxsw_sp_fib_entry_update(mlxsw_sp, decap_fib_entry))1339 mlxsw_sp_ipip_entry_demote_decap(mlxsw_sp, ipip_entry);1340}1341 1342static struct mlxsw_sp_fib_entry *1343mlxsw_sp_router_ip2me_fib_entry_find(struct mlxsw_sp *mlxsw_sp, u32 tb_id,1344 enum mlxsw_sp_l3proto proto,1345 const union mlxsw_sp_l3addr *addr,1346 enum mlxsw_sp_fib_entry_type type)1347{1348 struct mlxsw_sp_fib_node *fib_node;1349 unsigned char addr_prefix_len;1350 struct mlxsw_sp_fib *fib;1351 struct mlxsw_sp_vr *vr;1352 const void *addrp;1353 size_t addr_len;1354 u32 addr4;1355 1356 vr = mlxsw_sp_vr_find(mlxsw_sp, tb_id);1357 if (!vr)1358 return NULL;1359 fib = mlxsw_sp_vr_fib(vr, proto);1360 1361 switch (proto) {1362 case MLXSW_SP_L3_PROTO_IPV4:1363 addr4 = be32_to_cpu(addr->addr4);1364 addrp = &addr4;1365 addr_len = 4;1366 addr_prefix_len = 32;1367 break;1368 case MLXSW_SP_L3_PROTO_IPV6:1369 addrp = &addr->addr6;1370 addr_len = 16;1371 addr_prefix_len = 128;1372 break;1373 default:1374 WARN_ON(1);1375 return NULL;1376 }1377 1378 fib_node = mlxsw_sp_fib_node_lookup(fib, addrp, addr_len,1379 addr_prefix_len);1380 if (!fib_node || fib_node->fib_entry->type != type)1381 return NULL;1382 1383 return fib_node->fib_entry;1384}1385 1386/* Given an IPIP entry, find the corresponding decap route. */1387static struct mlxsw_sp_fib_entry *1388mlxsw_sp_ipip_entry_find_decap(struct mlxsw_sp *mlxsw_sp,1389 struct mlxsw_sp_ipip_entry *ipip_entry)1390{1391 static struct mlxsw_sp_fib_node *fib_node;1392 const struct mlxsw_sp_ipip_ops *ipip_ops;1393 unsigned char saddr_prefix_len;1394 union mlxsw_sp_l3addr saddr;1395 struct mlxsw_sp_fib *ul_fib;1396 struct mlxsw_sp_vr *ul_vr;1397 const void *saddrp;1398 size_t saddr_len;1399 u32 ul_tb_id;1400 u32 saddr4;1401 1402 ipip_ops = mlxsw_sp->router->ipip_ops_arr[ipip_entry->ipipt];1403 1404 ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(ipip_entry->ol_dev);1405 ul_vr = mlxsw_sp_vr_find(mlxsw_sp, ul_tb_id);1406 if (!ul_vr)1407 return NULL;1408 1409 ul_fib = mlxsw_sp_vr_fib(ul_vr, ipip_ops->ul_proto);1410 saddr = mlxsw_sp_ipip_netdev_saddr(ipip_ops->ul_proto,1411 ipip_entry->ol_dev);1412 1413 switch (ipip_ops->ul_proto) {1414 case MLXSW_SP_L3_PROTO_IPV4:1415 saddr4 = be32_to_cpu(saddr.addr4);1416 saddrp = &saddr4;1417 saddr_len = 4;1418 saddr_prefix_len = 32;1419 break;1420 case MLXSW_SP_L3_PROTO_IPV6:1421 saddrp = &saddr.addr6;1422 saddr_len = 16;1423 saddr_prefix_len = 128;1424 break;1425 default:1426 WARN_ON(1);1427 return NULL;1428 }1429 1430 fib_node = mlxsw_sp_fib_node_lookup(ul_fib, saddrp, saddr_len,1431 saddr_prefix_len);1432 if (!fib_node ||1433 fib_node->fib_entry->type != MLXSW_SP_FIB_ENTRY_TYPE_TRAP)1434 return NULL;1435 1436 return fib_node->fib_entry;1437}1438 1439static struct mlxsw_sp_ipip_entry *1440mlxsw_sp_ipip_entry_create(struct mlxsw_sp *mlxsw_sp,1441 enum mlxsw_sp_ipip_type ipipt,1442 struct net_device *ol_dev)1443{1444 struct mlxsw_sp_ipip_entry *ipip_entry;1445 1446 ipip_entry = mlxsw_sp_ipip_entry_alloc(mlxsw_sp, ipipt, ol_dev);1447 if (IS_ERR(ipip_entry))1448 return ipip_entry;1449 1450 list_add_tail(&ipip_entry->ipip_list_node,1451 &mlxsw_sp->router->ipip_list);1452 1453 return ipip_entry;1454}1455 1456static void1457mlxsw_sp_ipip_entry_destroy(struct mlxsw_sp *mlxsw_sp,1458 struct mlxsw_sp_ipip_entry *ipip_entry)1459{1460 list_del(&ipip_entry->ipip_list_node);1461 mlxsw_sp_ipip_entry_dealloc(mlxsw_sp, ipip_entry);1462}1463 1464static bool1465mlxsw_sp_ipip_entry_matches_decap(struct mlxsw_sp *mlxsw_sp,1466 const struct net_device *ul_dev,1467 enum mlxsw_sp_l3proto ul_proto,1468 union mlxsw_sp_l3addr ul_dip,1469 struct mlxsw_sp_ipip_entry *ipip_entry)1470{1471 u32 ul_tb_id = l3mdev_fib_table(ul_dev) ? : RT_TABLE_MAIN;1472 enum mlxsw_sp_ipip_type ipipt = ipip_entry->ipipt;1473 1474 if (mlxsw_sp->router->ipip_ops_arr[ipipt]->ul_proto != ul_proto)1475 return false;1476 1477 return mlxsw_sp_ipip_entry_saddr_matches(mlxsw_sp, ul_proto, ul_dip,1478 ul_tb_id, ipip_entry);1479}1480 1481/* Given decap parameters, find the corresponding IPIP entry. */1482static struct mlxsw_sp_ipip_entry *1483mlxsw_sp_ipip_entry_find_by_decap(struct mlxsw_sp *mlxsw_sp, int ul_dev_ifindex,1484 enum mlxsw_sp_l3proto ul_proto,1485 union mlxsw_sp_l3addr ul_dip)1486{1487 struct mlxsw_sp_ipip_entry *ipip_entry = NULL;1488 struct net_device *ul_dev;1489 1490 rcu_read_lock();1491 1492 ul_dev = dev_get_by_index_rcu(mlxsw_sp_net(mlxsw_sp), ul_dev_ifindex);1493 if (!ul_dev)1494 goto out_unlock;1495 1496 list_for_each_entry(ipip_entry, &mlxsw_sp->router->ipip_list,1497 ipip_list_node)1498 if (mlxsw_sp_ipip_entry_matches_decap(mlxsw_sp, ul_dev,1499 ul_proto, ul_dip,1500 ipip_entry))1501 goto out_unlock;1502 1503 rcu_read_unlock();1504 1505 return NULL;1506 1507out_unlock:1508 rcu_read_unlock();1509 return ipip_entry;1510}1511 1512static bool mlxsw_sp_netdev_ipip_type(const struct mlxsw_sp *mlxsw_sp,1513 const struct net_device *dev,1514 enum mlxsw_sp_ipip_type *p_type)1515{1516 struct mlxsw_sp_router *router = mlxsw_sp->router;1517 const struct mlxsw_sp_ipip_ops *ipip_ops;1518 enum mlxsw_sp_ipip_type ipipt;1519 1520 for (ipipt = 0; ipipt < MLXSW_SP_IPIP_TYPE_MAX; ++ipipt) {1521 ipip_ops = router->ipip_ops_arr[ipipt];1522 if (dev->type == ipip_ops->dev_type) {1523 if (p_type)1524 *p_type = ipipt;1525 return true;1526 }1527 }1528 return false;1529}1530 1531static bool mlxsw_sp_netdev_is_ipip_ol(const struct mlxsw_sp *mlxsw_sp,1532 const struct net_device *dev)1533{1534 return mlxsw_sp_netdev_ipip_type(mlxsw_sp, dev, NULL);1535}1536 1537static struct mlxsw_sp_ipip_entry *1538mlxsw_sp_ipip_entry_find_by_ol_dev(struct mlxsw_sp *mlxsw_sp,1539 const struct net_device *ol_dev)1540{1541 struct mlxsw_sp_ipip_entry *ipip_entry;1542 1543 list_for_each_entry(ipip_entry, &mlxsw_sp->router->ipip_list,1544 ipip_list_node)1545 if (ipip_entry->ol_dev == ol_dev)1546 return ipip_entry;1547 1548 return NULL;1549}1550 1551static struct mlxsw_sp_ipip_entry *1552mlxsw_sp_ipip_entry_find_by_ul_dev(const struct mlxsw_sp *mlxsw_sp,1553 const struct net_device *ul_dev,1554 struct mlxsw_sp_ipip_entry *start)1555{1556 struct mlxsw_sp_ipip_entry *ipip_entry;1557 1558 ipip_entry = list_prepare_entry(start, &mlxsw_sp->router->ipip_list,1559 ipip_list_node);1560 list_for_each_entry_continue(ipip_entry, &mlxsw_sp->router->ipip_list,1561 ipip_list_node) {1562 struct net_device *ol_dev = ipip_entry->ol_dev;1563 struct net_device *ipip_ul_dev;1564 1565 rcu_read_lock();1566 ipip_ul_dev = mlxsw_sp_ipip_netdev_ul_dev_get(ol_dev);1567 rcu_read_unlock();1568 1569 if (ipip_ul_dev == ul_dev)1570 return ipip_entry;1571 }1572 1573 return NULL;1574}1575 1576static bool mlxsw_sp_netdev_is_ipip_ul(struct mlxsw_sp *mlxsw_sp,1577 const struct net_device *dev)1578{1579 return mlxsw_sp_ipip_entry_find_by_ul_dev(mlxsw_sp, dev, NULL);1580}1581 1582static bool mlxsw_sp_netdevice_ipip_can_offload(struct mlxsw_sp *mlxsw_sp,1583 const struct net_device *ol_dev,1584 enum mlxsw_sp_ipip_type ipipt)1585{1586 const struct mlxsw_sp_ipip_ops *ops1587 = mlxsw_sp->router->ipip_ops_arr[ipipt];1588 1589 return ops->can_offload(mlxsw_sp, ol_dev);1590}1591 1592static int mlxsw_sp_netdevice_ipip_ol_reg_event(struct mlxsw_sp *mlxsw_sp,1593 struct net_device *ol_dev)1594{1595 enum mlxsw_sp_ipip_type ipipt = MLXSW_SP_IPIP_TYPE_MAX;1596 struct mlxsw_sp_ipip_entry *ipip_entry;1597 enum mlxsw_sp_l3proto ul_proto;1598 union mlxsw_sp_l3addr saddr;1599 u32 ul_tb_id;1600 1601 mlxsw_sp_netdev_ipip_type(mlxsw_sp, ol_dev, &ipipt);1602 if (mlxsw_sp_netdevice_ipip_can_offload(mlxsw_sp, ol_dev, ipipt)) {1603 ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(ol_dev);1604 ul_proto = mlxsw_sp->router->ipip_ops_arr[ipipt]->ul_proto;1605 saddr = mlxsw_sp_ipip_netdev_saddr(ul_proto, ol_dev);1606 if (!mlxsw_sp_ipip_demote_tunnel_by_saddr(mlxsw_sp, ul_proto,1607 saddr, ul_tb_id,1608 NULL)) {1609 ipip_entry = mlxsw_sp_ipip_entry_create(mlxsw_sp, ipipt,1610 ol_dev);1611 if (IS_ERR(ipip_entry))1612 return PTR_ERR(ipip_entry);1613 }1614 }1615 1616 return 0;1617}1618 1619static void mlxsw_sp_netdevice_ipip_ol_unreg_event(struct mlxsw_sp *mlxsw_sp,1620 struct net_device *ol_dev)1621{1622 struct mlxsw_sp_ipip_entry *ipip_entry;1623 1624 ipip_entry = mlxsw_sp_ipip_entry_find_by_ol_dev(mlxsw_sp, ol_dev);1625 if (ipip_entry)1626 mlxsw_sp_ipip_entry_destroy(mlxsw_sp, ipip_entry);1627}1628 1629static void1630mlxsw_sp_ipip_entry_ol_up_event(struct mlxsw_sp *mlxsw_sp,1631 struct mlxsw_sp_ipip_entry *ipip_entry)1632{1633 struct mlxsw_sp_fib_entry *decap_fib_entry;1634 1635 decap_fib_entry = mlxsw_sp_ipip_entry_find_decap(mlxsw_sp, ipip_entry);1636 if (decap_fib_entry)1637 mlxsw_sp_ipip_entry_promote_decap(mlxsw_sp, ipip_entry,1638 decap_fib_entry);1639}1640 1641static int1642mlxsw_sp_rif_ipip_lb_op(struct mlxsw_sp_rif_ipip_lb *lb_rif, u16 ul_vr_id,1643 u16 ul_rif_id, bool enable)1644{1645 struct mlxsw_sp_rif_ipip_lb_config lb_cf = lb_rif->lb_config;1646 struct net_device *dev = mlxsw_sp_rif_dev(&lb_rif->common);1647 enum mlxsw_reg_ritr_loopback_ipip_options ipip_options;1648 struct mlxsw_sp_rif *rif = &lb_rif->common;1649 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;1650 char ritr_pl[MLXSW_REG_RITR_LEN];1651 struct in6_addr *saddr6;1652 u32 saddr4;1653 1654 ipip_options = MLXSW_REG_RITR_LOOPBACK_IPIP_OPTIONS_GRE_KEY_PRESET;1655 switch (lb_cf.ul_protocol) {1656 case MLXSW_SP_L3_PROTO_IPV4:1657 saddr4 = be32_to_cpu(lb_cf.saddr.addr4);1658 mlxsw_reg_ritr_pack(ritr_pl, enable, MLXSW_REG_RITR_LOOPBACK_IF,1659 rif->rif_index, rif->vr_id, dev->mtu);1660 mlxsw_reg_ritr_loopback_ipip4_pack(ritr_pl, lb_cf.lb_ipipt,1661 ipip_options, ul_vr_id,1662 ul_rif_id, saddr4,1663 lb_cf.okey);1664 break;1665 1666 case MLXSW_SP_L3_PROTO_IPV6:1667 saddr6 = &lb_cf.saddr.addr6;1668 mlxsw_reg_ritr_pack(ritr_pl, enable, MLXSW_REG_RITR_LOOPBACK_IF,1669 rif->rif_index, rif->vr_id, dev->mtu);1670 mlxsw_reg_ritr_loopback_ipip6_pack(ritr_pl, lb_cf.lb_ipipt,1671 ipip_options, ul_vr_id,1672 ul_rif_id, saddr6,1673 lb_cf.okey);1674 break;1675 }1676 1677 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);1678}1679 1680static int mlxsw_sp_netdevice_ipip_ol_update_mtu(struct mlxsw_sp *mlxsw_sp,1681 struct net_device *ol_dev)1682{1683 struct mlxsw_sp_ipip_entry *ipip_entry;1684 struct mlxsw_sp_rif_ipip_lb *lb_rif;1685 int err = 0;1686 1687 ipip_entry = mlxsw_sp_ipip_entry_find_by_ol_dev(mlxsw_sp, ol_dev);1688 if (ipip_entry) {1689 lb_rif = ipip_entry->ol_lb;1690 err = mlxsw_sp_rif_ipip_lb_op(lb_rif, lb_rif->ul_vr_id,1691 lb_rif->ul_rif_id, true);1692 if (err)1693 goto out;1694 lb_rif->common.mtu = ol_dev->mtu;1695 }1696 1697out:1698 return err;1699}1700 1701static void mlxsw_sp_netdevice_ipip_ol_up_event(struct mlxsw_sp *mlxsw_sp,1702 struct net_device *ol_dev)1703{1704 struct mlxsw_sp_ipip_entry *ipip_entry;1705 1706 ipip_entry = mlxsw_sp_ipip_entry_find_by_ol_dev(mlxsw_sp, ol_dev);1707 if (ipip_entry)1708 mlxsw_sp_ipip_entry_ol_up_event(mlxsw_sp, ipip_entry);1709}1710 1711static void1712mlxsw_sp_ipip_entry_ol_down_event(struct mlxsw_sp *mlxsw_sp,1713 struct mlxsw_sp_ipip_entry *ipip_entry)1714{1715 if (ipip_entry->decap_fib_entry)1716 mlxsw_sp_ipip_entry_demote_decap(mlxsw_sp, ipip_entry);1717}1718 1719static void mlxsw_sp_netdevice_ipip_ol_down_event(struct mlxsw_sp *mlxsw_sp,1720 struct net_device *ol_dev)1721{1722 struct mlxsw_sp_ipip_entry *ipip_entry;1723 1724 ipip_entry = mlxsw_sp_ipip_entry_find_by_ol_dev(mlxsw_sp, ol_dev);1725 if (ipip_entry)1726 mlxsw_sp_ipip_entry_ol_down_event(mlxsw_sp, ipip_entry);1727}1728 1729static void mlxsw_sp_nexthop_rif_update(struct mlxsw_sp *mlxsw_sp,1730 struct mlxsw_sp_rif *rif);1731 1732static void mlxsw_sp_rif_migrate_destroy(struct mlxsw_sp *mlxsw_sp,1733 struct mlxsw_sp_rif *old_rif,1734 struct mlxsw_sp_rif *new_rif,1735 bool migrate_nhs)1736{1737 struct mlxsw_sp_crif *crif = old_rif->crif;1738 struct mlxsw_sp_crif mock_crif = {};1739 1740 if (migrate_nhs)1741 mlxsw_sp_nexthop_rif_update(mlxsw_sp, new_rif);1742 1743 /* Plant a mock CRIF so that destroying the old RIF doesn't unoffload1744 * our nexthops and IPIP tunnels, and doesn't sever the crif->rif link.1745 */1746 mlxsw_sp_crif_init(&mock_crif, crif->key.dev);1747 old_rif->crif = &mock_crif;1748 mock_crif.rif = old_rif;1749 mlxsw_sp_rif_destroy(old_rif);1750}1751 1752static int1753mlxsw_sp_ipip_entry_ol_lb_update(struct mlxsw_sp *mlxsw_sp,1754 struct mlxsw_sp_ipip_entry *ipip_entry,1755 bool keep_encap,1756 struct netlink_ext_ack *extack)1757{1758 struct mlxsw_sp_rif_ipip_lb *old_lb_rif = ipip_entry->ol_lb;1759 struct mlxsw_sp_rif_ipip_lb *new_lb_rif;1760 1761 new_lb_rif = mlxsw_sp_ipip_ol_ipip_lb_create(mlxsw_sp,1762 ipip_entry->ipipt,1763 ipip_entry->ol_dev,1764 extack);1765 if (IS_ERR(new_lb_rif))1766 return PTR_ERR(new_lb_rif);1767 ipip_entry->ol_lb = new_lb_rif;1768 1769 mlxsw_sp_rif_migrate_destroy(mlxsw_sp, &old_lb_rif->common,1770 &new_lb_rif->common, keep_encap);1771 return 0;1772}1773 1774/**1775 * __mlxsw_sp_ipip_entry_update_tunnel - Update offload related to IPIP entry.1776 * @mlxsw_sp: mlxsw_sp.1777 * @ipip_entry: IPIP entry.1778 * @recreate_loopback: Recreates the associated loopback RIF.1779 * @keep_encap: Updates next hops that use the tunnel netdevice. This is only1780 * relevant when recreate_loopback is true.1781 * @update_nexthops: Updates next hops, keeping the current loopback RIF. This1782 * is only relevant when recreate_loopback is false.1783 * @extack: extack.1784 *1785 * Return: Non-zero value on failure.1786 */1787int __mlxsw_sp_ipip_entry_update_tunnel(struct mlxsw_sp *mlxsw_sp,1788 struct mlxsw_sp_ipip_entry *ipip_entry,1789 bool recreate_loopback,1790 bool keep_encap,1791 bool update_nexthops,1792 struct netlink_ext_ack *extack)1793{1794 int err;1795 1796 /* RIFs can't be edited, so to update loopback, we need to destroy and1797 * recreate it. That creates a window of opportunity where RALUE and1798 * RATR registers end up referencing a RIF that's already gone. RATRs1799 * are handled in mlxsw_sp_ipip_entry_ol_lb_update(), and to take care1800 * of RALUE, demote the decap route back.1801 */1802 if (ipip_entry->decap_fib_entry)1803 mlxsw_sp_ipip_entry_demote_decap(mlxsw_sp, ipip_entry);1804 1805 if (recreate_loopback) {1806 err = mlxsw_sp_ipip_entry_ol_lb_update(mlxsw_sp, ipip_entry,1807 keep_encap, extack);1808 if (err)1809 return err;1810 } else if (update_nexthops) {1811 mlxsw_sp_nexthop_rif_update(mlxsw_sp,1812 &ipip_entry->ol_lb->common);1813 }1814 1815 if (ipip_entry->ol_dev->flags & IFF_UP)1816 mlxsw_sp_ipip_entry_ol_up_event(mlxsw_sp, ipip_entry);1817 1818 return 0;1819}1820 1821static int mlxsw_sp_netdevice_ipip_ol_vrf_event(struct mlxsw_sp *mlxsw_sp,1822 struct net_device *ol_dev,1823 struct netlink_ext_ack *extack)1824{1825 struct mlxsw_sp_ipip_entry *ipip_entry =1826 mlxsw_sp_ipip_entry_find_by_ol_dev(mlxsw_sp, ol_dev);1827 1828 if (!ipip_entry)1829 return 0;1830 1831 return __mlxsw_sp_ipip_entry_update_tunnel(mlxsw_sp, ipip_entry,1832 true, false, false, extack);1833}1834 1835static int1836mlxsw_sp_netdevice_ipip_ul_vrf_event(struct mlxsw_sp *mlxsw_sp,1837 struct mlxsw_sp_ipip_entry *ipip_entry,1838 struct net_device *ul_dev,1839 bool *demote_this,1840 struct netlink_ext_ack *extack)1841{1842 u32 ul_tb_id = l3mdev_fib_table(ul_dev) ? : RT_TABLE_MAIN;1843 enum mlxsw_sp_l3proto ul_proto;1844 union mlxsw_sp_l3addr saddr;1845 1846 /* Moving underlay to a different VRF might cause local address1847 * conflict, and the conflicting tunnels need to be demoted.1848 */1849 ul_proto = mlxsw_sp->router->ipip_ops_arr[ipip_entry->ipipt]->ul_proto;1850 saddr = mlxsw_sp_ipip_netdev_saddr(ul_proto, ipip_entry->ol_dev);1851 if (mlxsw_sp_ipip_demote_tunnel_by_saddr(mlxsw_sp, ul_proto,1852 saddr, ul_tb_id,1853 ipip_entry)) {1854 *demote_this = true;1855 return 0;1856 }1857 1858 return __mlxsw_sp_ipip_entry_update_tunnel(mlxsw_sp, ipip_entry,1859 true, true, false, extack);1860}1861 1862static int1863mlxsw_sp_netdevice_ipip_ul_up_event(struct mlxsw_sp *mlxsw_sp,1864 struct mlxsw_sp_ipip_entry *ipip_entry,1865 struct net_device *ul_dev)1866{1867 return __mlxsw_sp_ipip_entry_update_tunnel(mlxsw_sp, ipip_entry,1868 false, false, true, NULL);1869}1870 1871static int1872mlxsw_sp_netdevice_ipip_ul_down_event(struct mlxsw_sp *mlxsw_sp,1873 struct mlxsw_sp_ipip_entry *ipip_entry,1874 struct net_device *ul_dev)1875{1876 /* A down underlay device causes encapsulated packets to not be1877 * forwarded, but decap still works. So refresh next hops without1878 * touching anything else.1879 */1880 return __mlxsw_sp_ipip_entry_update_tunnel(mlxsw_sp, ipip_entry,1881 false, false, true, NULL);1882}1883 1884static int1885mlxsw_sp_netdevice_ipip_ol_change_event(struct mlxsw_sp *mlxsw_sp,1886 struct net_device *ol_dev,1887 struct netlink_ext_ack *extack)1888{1889 const struct mlxsw_sp_ipip_ops *ipip_ops;1890 struct mlxsw_sp_ipip_entry *ipip_entry;1891 int err;1892 1893 ipip_entry = mlxsw_sp_ipip_entry_find_by_ol_dev(mlxsw_sp, ol_dev);1894 if (!ipip_entry)1895 /* A change might make a tunnel eligible for offloading, but1896 * that is currently not implemented. What falls to slow path1897 * stays there.1898 */1899 return 0;1900 1901 /* A change might make a tunnel not eligible for offloading. */1902 if (!mlxsw_sp_netdevice_ipip_can_offload(mlxsw_sp, ol_dev,1903 ipip_entry->ipipt)) {1904 mlxsw_sp_ipip_entry_demote_tunnel(mlxsw_sp, ipip_entry);1905 return 0;1906 }1907 1908 ipip_ops = mlxsw_sp->router->ipip_ops_arr[ipip_entry->ipipt];1909 err = ipip_ops->ol_netdev_change(mlxsw_sp, ipip_entry, extack);1910 return err;1911}1912 1913void mlxsw_sp_ipip_entry_demote_tunnel(struct mlxsw_sp *mlxsw_sp,1914 struct mlxsw_sp_ipip_entry *ipip_entry)1915{1916 struct net_device *ol_dev = ipip_entry->ol_dev;1917 1918 if (ol_dev->flags & IFF_UP)1919 mlxsw_sp_ipip_entry_ol_down_event(mlxsw_sp, ipip_entry);1920 mlxsw_sp_ipip_entry_destroy(mlxsw_sp, ipip_entry);1921}1922 1923/* The configuration where several tunnels have the same local address in the1924 * same underlay table needs special treatment in the HW. That is currently not1925 * implemented in the driver. This function finds and demotes the first tunnel1926 * with a given source address, except the one passed in the argument1927 * `except'.1928 */1929bool1930mlxsw_sp_ipip_demote_tunnel_by_saddr(struct mlxsw_sp *mlxsw_sp,1931 enum mlxsw_sp_l3proto ul_proto,1932 union mlxsw_sp_l3addr saddr,1933 u32 ul_tb_id,1934 const struct mlxsw_sp_ipip_entry *except)1935{1936 struct mlxsw_sp_ipip_entry *ipip_entry, *tmp;1937 1938 list_for_each_entry_safe(ipip_entry, tmp, &mlxsw_sp->router->ipip_list,1939 ipip_list_node) {1940 if (ipip_entry != except &&1941 mlxsw_sp_ipip_entry_saddr_matches(mlxsw_sp, ul_proto, saddr,1942 ul_tb_id, ipip_entry)) {1943 mlxsw_sp_ipip_entry_demote_tunnel(mlxsw_sp, ipip_entry);1944 return true;1945 }1946 }1947 1948 return false;1949}1950 1951static void mlxsw_sp_ipip_demote_tunnel_by_ul_netdev(struct mlxsw_sp *mlxsw_sp,1952 struct net_device *ul_dev)1953{1954 struct mlxsw_sp_ipip_entry *ipip_entry, *tmp;1955 1956 list_for_each_entry_safe(ipip_entry, tmp, &mlxsw_sp->router->ipip_list,1957 ipip_list_node) {1958 struct net_device *ol_dev = ipip_entry->ol_dev;1959 struct net_device *ipip_ul_dev;1960 1961 rcu_read_lock();1962 ipip_ul_dev = mlxsw_sp_ipip_netdev_ul_dev_get(ol_dev);1963 rcu_read_unlock();1964 if (ipip_ul_dev == ul_dev)1965 mlxsw_sp_ipip_entry_demote_tunnel(mlxsw_sp, ipip_entry);1966 }1967}1968 1969static int mlxsw_sp_netdevice_ipip_ol_event(struct mlxsw_sp *mlxsw_sp,1970 struct net_device *ol_dev,1971 unsigned long event,1972 struct netdev_notifier_info *info)1973{1974 struct netdev_notifier_changeupper_info *chup;1975 struct netlink_ext_ack *extack;1976 int err = 0;1977 1978 switch (event) {1979 case NETDEV_REGISTER:1980 err = mlxsw_sp_netdevice_ipip_ol_reg_event(mlxsw_sp, ol_dev);1981 break;1982 case NETDEV_UNREGISTER:1983 mlxsw_sp_netdevice_ipip_ol_unreg_event(mlxsw_sp, ol_dev);1984 break;1985 case NETDEV_UP:1986 mlxsw_sp_netdevice_ipip_ol_up_event(mlxsw_sp, ol_dev);1987 break;1988 case NETDEV_DOWN:1989 mlxsw_sp_netdevice_ipip_ol_down_event(mlxsw_sp, ol_dev);1990 break;1991 case NETDEV_CHANGEUPPER:1992 chup = container_of(info, typeof(*chup), info);1993 extack = info->extack;1994 if (netif_is_l3_master(chup->upper_dev))1995 err = mlxsw_sp_netdevice_ipip_ol_vrf_event(mlxsw_sp,1996 ol_dev,1997 extack);1998 break;1999 case NETDEV_CHANGE:2000 extack = info->extack;2001 err = mlxsw_sp_netdevice_ipip_ol_change_event(mlxsw_sp,2002 ol_dev, extack);2003 break;2004 case NETDEV_CHANGEMTU:2005 err = mlxsw_sp_netdevice_ipip_ol_update_mtu(mlxsw_sp, ol_dev);2006 break;2007 }2008 return err;2009}2010 2011static int2012__mlxsw_sp_netdevice_ipip_ul_event(struct mlxsw_sp *mlxsw_sp,2013 struct mlxsw_sp_ipip_entry *ipip_entry,2014 struct net_device *ul_dev,2015 bool *demote_this,2016 unsigned long event,2017 struct netdev_notifier_info *info)2018{2019 struct netdev_notifier_changeupper_info *chup;2020 struct netlink_ext_ack *extack;2021 2022 switch (event) {2023 case NETDEV_CHANGEUPPER:2024 chup = container_of(info, typeof(*chup), info);2025 extack = info->extack;2026 if (netif_is_l3_master(chup->upper_dev))2027 return mlxsw_sp_netdevice_ipip_ul_vrf_event(mlxsw_sp,2028 ipip_entry,2029 ul_dev,2030 demote_this,2031 extack);2032 break;2033 2034 case NETDEV_UP:2035 return mlxsw_sp_netdevice_ipip_ul_up_event(mlxsw_sp, ipip_entry,2036 ul_dev);2037 case NETDEV_DOWN:2038 return mlxsw_sp_netdevice_ipip_ul_down_event(mlxsw_sp,2039 ipip_entry,2040 ul_dev);2041 }2042 return 0;2043}2044 2045static int2046mlxsw_sp_netdevice_ipip_ul_event(struct mlxsw_sp *mlxsw_sp,2047 struct net_device *ul_dev,2048 unsigned long event,2049 struct netdev_notifier_info *info)2050{2051 struct mlxsw_sp_ipip_entry *ipip_entry = NULL;2052 int err;2053 2054 while ((ipip_entry = mlxsw_sp_ipip_entry_find_by_ul_dev(mlxsw_sp,2055 ul_dev,2056 ipip_entry))) {2057 struct mlxsw_sp_ipip_entry *prev;2058 bool demote_this = false;2059 2060 err = __mlxsw_sp_netdevice_ipip_ul_event(mlxsw_sp, ipip_entry,2061 ul_dev, &demote_this,2062 event, info);2063 if (err) {2064 mlxsw_sp_ipip_demote_tunnel_by_ul_netdev(mlxsw_sp,2065 ul_dev);2066 return err;2067 }2068 2069 if (demote_this) {2070 if (list_is_first(&ipip_entry->ipip_list_node,2071 &mlxsw_sp->router->ipip_list))2072 prev = NULL;2073 else2074 /* This can't be cached from previous iteration,2075 * because that entry could be gone now.2076 */2077 prev = list_prev_entry(ipip_entry,2078 ipip_list_node);2079 mlxsw_sp_ipip_entry_demote_tunnel(mlxsw_sp, ipip_entry);2080 ipip_entry = prev;2081 }2082 }2083 2084 return 0;2085}2086 2087int mlxsw_sp_router_nve_promote_decap(struct mlxsw_sp *mlxsw_sp, u32 ul_tb_id,2088 enum mlxsw_sp_l3proto ul_proto,2089 const union mlxsw_sp_l3addr *ul_sip,2090 u32 tunnel_index)2091{2092 enum mlxsw_sp_fib_entry_type type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;2093 struct mlxsw_sp_router *router = mlxsw_sp->router;2094 struct mlxsw_sp_fib_entry *fib_entry;2095 int err = 0;2096 2097 mutex_lock(&mlxsw_sp->router->lock);2098 2099 if (WARN_ON_ONCE(router->nve_decap_config.valid)) {2100 err = -EINVAL;2101 goto out;2102 }2103 2104 router->nve_decap_config.ul_tb_id = ul_tb_id;2105 router->nve_decap_config.tunnel_index = tunnel_index;2106 router->nve_decap_config.ul_proto = ul_proto;2107 router->nve_decap_config.ul_sip = *ul_sip;2108 router->nve_decap_config.valid = true;2109 2110 /* It is valid to create a tunnel with a local IP and only later2111 * assign this IP address to a local interface2112 */2113 fib_entry = mlxsw_sp_router_ip2me_fib_entry_find(mlxsw_sp, ul_tb_id,2114 ul_proto, ul_sip,2115 type);2116 if (!fib_entry)2117 goto out;2118 2119 fib_entry->decap.tunnel_index = tunnel_index;2120 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_NVE_DECAP;2121 2122 err = mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);2123 if (err)2124 goto err_fib_entry_update;2125 2126 goto out;2127 2128err_fib_entry_update:2129 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;2130 mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);2131out:2132 mutex_unlock(&mlxsw_sp->router->lock);2133 return err;2134}2135 2136void mlxsw_sp_router_nve_demote_decap(struct mlxsw_sp *mlxsw_sp, u32 ul_tb_id,2137 enum mlxsw_sp_l3proto ul_proto,2138 const union mlxsw_sp_l3addr *ul_sip)2139{2140 enum mlxsw_sp_fib_entry_type type = MLXSW_SP_FIB_ENTRY_TYPE_NVE_DECAP;2141 struct mlxsw_sp_router *router = mlxsw_sp->router;2142 struct mlxsw_sp_fib_entry *fib_entry;2143 2144 mutex_lock(&mlxsw_sp->router->lock);2145 2146 if (WARN_ON_ONCE(!router->nve_decap_config.valid))2147 goto out;2148 2149 router->nve_decap_config.valid = false;2150 2151 fib_entry = mlxsw_sp_router_ip2me_fib_entry_find(mlxsw_sp, ul_tb_id,2152 ul_proto, ul_sip,2153 type);2154 if (!fib_entry)2155 goto out;2156 2157 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;2158 mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);2159out:2160 mutex_unlock(&mlxsw_sp->router->lock);2161}2162 2163static bool mlxsw_sp_router_nve_is_decap(struct mlxsw_sp *mlxsw_sp,2164 u32 ul_tb_id,2165 enum mlxsw_sp_l3proto ul_proto,2166 const union mlxsw_sp_l3addr *ul_sip)2167{2168 struct mlxsw_sp_router *router = mlxsw_sp->router;2169 2170 return router->nve_decap_config.valid &&2171 router->nve_decap_config.ul_tb_id == ul_tb_id &&2172 router->nve_decap_config.ul_proto == ul_proto &&2173 !memcmp(&router->nve_decap_config.ul_sip, ul_sip,2174 sizeof(*ul_sip));2175}2176 2177struct mlxsw_sp_neigh_key {2178 struct neighbour *n;2179};2180 2181struct mlxsw_sp_neigh_entry {2182 struct list_head rif_list_node;2183 struct rhash_head ht_node;2184 struct mlxsw_sp_neigh_key key;2185 u16 rif;2186 bool connected;2187 unsigned char ha[ETH_ALEN];2188 struct list_head nexthop_list; /* list of nexthops using2189 * this neigh entry2190 */2191 struct list_head nexthop_neighs_list_node;2192 unsigned int counter_index;2193 bool counter_valid;2194};2195 2196static const struct rhashtable_params mlxsw_sp_neigh_ht_params = {2197 .key_offset = offsetof(struct mlxsw_sp_neigh_entry, key),2198 .head_offset = offsetof(struct mlxsw_sp_neigh_entry, ht_node),2199 .key_len = sizeof(struct mlxsw_sp_neigh_key),2200};2201 2202struct mlxsw_sp_neigh_entry *2203mlxsw_sp_rif_neigh_next(struct mlxsw_sp_rif *rif,2204 struct mlxsw_sp_neigh_entry *neigh_entry)2205{2206 if (!neigh_entry) {2207 if (list_empty(&rif->neigh_list))2208 return NULL;2209 else2210 return list_first_entry(&rif->neigh_list,2211 typeof(*neigh_entry),2212 rif_list_node);2213 }2214 if (list_is_last(&neigh_entry->rif_list_node, &rif->neigh_list))2215 return NULL;2216 return list_next_entry(neigh_entry, rif_list_node);2217}2218 2219int mlxsw_sp_neigh_entry_type(struct mlxsw_sp_neigh_entry *neigh_entry)2220{2221 return neigh_entry->key.n->tbl->family;2222}2223 2224unsigned char *2225mlxsw_sp_neigh_entry_ha(struct mlxsw_sp_neigh_entry *neigh_entry)2226{2227 return neigh_entry->ha;2228}2229 2230u32 mlxsw_sp_neigh4_entry_dip(struct mlxsw_sp_neigh_entry *neigh_entry)2231{2232 struct neighbour *n;2233 2234 n = neigh_entry->key.n;2235 return ntohl(*((__be32 *) n->primary_key));2236}2237 2238struct in6_addr *2239mlxsw_sp_neigh6_entry_dip(struct mlxsw_sp_neigh_entry *neigh_entry)2240{2241 struct neighbour *n;2242 2243 n = neigh_entry->key.n;2244 return (struct in6_addr *) &n->primary_key;2245}2246 2247int mlxsw_sp_neigh_counter_get(struct mlxsw_sp *mlxsw_sp,2248 struct mlxsw_sp_neigh_entry *neigh_entry,2249 u64 *p_counter)2250{2251 if (!neigh_entry->counter_valid)2252 return -EINVAL;2253 2254 return mlxsw_sp_flow_counter_get(mlxsw_sp, neigh_entry->counter_index,2255 false, p_counter, NULL);2256}2257 2258static struct mlxsw_sp_neigh_entry *2259mlxsw_sp_neigh_entry_alloc(struct mlxsw_sp *mlxsw_sp, struct neighbour *n,2260 u16 rif)2261{2262 struct mlxsw_sp_neigh_entry *neigh_entry;2263 2264 neigh_entry = kzalloc(sizeof(*neigh_entry), GFP_KERNEL);2265 if (!neigh_entry)2266 return NULL;2267 2268 neigh_entry->key.n = n;2269 neigh_entry->rif = rif;2270 INIT_LIST_HEAD(&neigh_entry->nexthop_list);2271 2272 return neigh_entry;2273}2274 2275static void mlxsw_sp_neigh_entry_free(struct mlxsw_sp_neigh_entry *neigh_entry)2276{2277 kfree(neigh_entry);2278}2279 2280static int2281mlxsw_sp_neigh_entry_insert(struct mlxsw_sp *mlxsw_sp,2282 struct mlxsw_sp_neigh_entry *neigh_entry)2283{2284 return rhashtable_insert_fast(&mlxsw_sp->router->neigh_ht,2285 &neigh_entry->ht_node,2286 mlxsw_sp_neigh_ht_params);2287}2288 2289static void2290mlxsw_sp_neigh_entry_remove(struct mlxsw_sp *mlxsw_sp,2291 struct mlxsw_sp_neigh_entry *neigh_entry)2292{2293 rhashtable_remove_fast(&mlxsw_sp->router->neigh_ht,2294 &neigh_entry->ht_node,2295 mlxsw_sp_neigh_ht_params);2296}2297 2298static bool2299mlxsw_sp_neigh_counter_should_alloc(struct mlxsw_sp *mlxsw_sp,2300 struct mlxsw_sp_neigh_entry *neigh_entry)2301{2302 struct devlink *devlink;2303 const char *table_name;2304 2305 switch (mlxsw_sp_neigh_entry_type(neigh_entry)) {2306 case AF_INET:2307 table_name = MLXSW_SP_DPIPE_TABLE_NAME_HOST4;2308 break;2309 case AF_INET6:2310 table_name = MLXSW_SP_DPIPE_TABLE_NAME_HOST6;2311 break;2312 default:2313 WARN_ON(1);2314 return false;2315 }2316 2317 devlink = priv_to_devlink(mlxsw_sp->core);2318 return devlink_dpipe_table_counter_enabled(devlink, table_name);2319}2320 2321static void2322mlxsw_sp_neigh_counter_alloc(struct mlxsw_sp *mlxsw_sp,2323 struct mlxsw_sp_neigh_entry *neigh_entry)2324{2325 if (!mlxsw_sp_neigh_counter_should_alloc(mlxsw_sp, neigh_entry))2326 return;2327 2328 if (mlxsw_sp_flow_counter_alloc(mlxsw_sp, &neigh_entry->counter_index))2329 return;2330 2331 neigh_entry->counter_valid = true;2332}2333 2334static void2335mlxsw_sp_neigh_counter_free(struct mlxsw_sp *mlxsw_sp,2336 struct mlxsw_sp_neigh_entry *neigh_entry)2337{2338 if (!neigh_entry->counter_valid)2339 return;2340 mlxsw_sp_flow_counter_free(mlxsw_sp,2341 neigh_entry->counter_index);2342 neigh_entry->counter_valid = false;2343}2344 2345static struct mlxsw_sp_neigh_entry *2346mlxsw_sp_neigh_entry_create(struct mlxsw_sp *mlxsw_sp, struct neighbour *n)2347{2348 struct mlxsw_sp_neigh_entry *neigh_entry;2349 struct mlxsw_sp_rif *rif;2350 int err;2351 2352 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, n->dev);2353 if (!rif)2354 return ERR_PTR(-EINVAL);2355 2356 neigh_entry = mlxsw_sp_neigh_entry_alloc(mlxsw_sp, n, rif->rif_index);2357 if (!neigh_entry)2358 return ERR_PTR(-ENOMEM);2359 2360 err = mlxsw_sp_neigh_entry_insert(mlxsw_sp, neigh_entry);2361 if (err)2362 goto err_neigh_entry_insert;2363 2364 mlxsw_sp_neigh_counter_alloc(mlxsw_sp, neigh_entry);2365 atomic_inc(&mlxsw_sp->router->neighs_update.neigh_count);2366 list_add(&neigh_entry->rif_list_node, &rif->neigh_list);2367 2368 return neigh_entry;2369 2370err_neigh_entry_insert:2371 mlxsw_sp_neigh_entry_free(neigh_entry);2372 return ERR_PTR(err);2373}2374 2375static void2376mlxsw_sp_neigh_entry_destroy(struct mlxsw_sp *mlxsw_sp,2377 struct mlxsw_sp_neigh_entry *neigh_entry)2378{2379 list_del(&neigh_entry->rif_list_node);2380 atomic_dec(&mlxsw_sp->router->neighs_update.neigh_count);2381 mlxsw_sp_neigh_counter_free(mlxsw_sp, neigh_entry);2382 mlxsw_sp_neigh_entry_remove(mlxsw_sp, neigh_entry);2383 mlxsw_sp_neigh_entry_free(neigh_entry);2384}2385 2386static struct mlxsw_sp_neigh_entry *2387mlxsw_sp_neigh_entry_lookup(struct mlxsw_sp *mlxsw_sp, struct neighbour *n)2388{2389 struct mlxsw_sp_neigh_key key;2390 2391 key.n = n;2392 return rhashtable_lookup_fast(&mlxsw_sp->router->neigh_ht,2393 &key, mlxsw_sp_neigh_ht_params);2394}2395 2396static void2397mlxsw_sp_router_neighs_update_interval_init(struct mlxsw_sp *mlxsw_sp)2398{2399 unsigned long interval;2400 2401#if IS_ENABLED(CONFIG_IPV6)2402 interval = min_t(unsigned long,2403 NEIGH_VAR(&arp_tbl.parms, DELAY_PROBE_TIME),2404 NEIGH_VAR(&nd_tbl.parms, DELAY_PROBE_TIME));2405#else2406 interval = NEIGH_VAR(&arp_tbl.parms, DELAY_PROBE_TIME);2407#endif2408 mlxsw_sp->router->neighs_update.interval = jiffies_to_msecs(interval);2409}2410 2411static void mlxsw_sp_router_neigh_ent_ipv4_process(struct mlxsw_sp *mlxsw_sp,2412 char *rauhtd_pl,2413 int ent_index)2414{2415 u64 max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);2416 struct net_device *dev;2417 struct neighbour *n;2418 __be32 dipn;2419 u32 dip;2420 u16 rif;2421 2422 mlxsw_reg_rauhtd_ent_ipv4_unpack(rauhtd_pl, ent_index, &rif, &dip);2423 2424 if (WARN_ON_ONCE(rif >= max_rifs))2425 return;2426 if (!mlxsw_sp->router->rifs[rif]) {2427 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect RIF in neighbour entry\n");2428 return;2429 }2430 2431 dipn = htonl(dip);2432 dev = mlxsw_sp_rif_dev(mlxsw_sp->router->rifs[rif]);2433 n = neigh_lookup(&arp_tbl, &dipn, dev);2434 if (!n)2435 return;2436 2437 netdev_dbg(dev, "Updating neighbour with IP=%pI4h\n", &dip);2438 neigh_event_send(n, NULL);2439 neigh_release(n);2440}2441 2442#if IS_ENABLED(CONFIG_IPV6)2443static void mlxsw_sp_router_neigh_ent_ipv6_process(struct mlxsw_sp *mlxsw_sp,2444 char *rauhtd_pl,2445 int rec_index)2446{2447 struct net_device *dev;2448 struct neighbour *n;2449 struct in6_addr dip;2450 u16 rif;2451 2452 mlxsw_reg_rauhtd_ent_ipv6_unpack(rauhtd_pl, rec_index, &rif,2453 (char *) &dip);2454 2455 if (!mlxsw_sp->router->rifs[rif]) {2456 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect RIF in neighbour entry\n");2457 return;2458 }2459 2460 dev = mlxsw_sp_rif_dev(mlxsw_sp->router->rifs[rif]);2461 n = neigh_lookup(&nd_tbl, &dip, dev);2462 if (!n)2463 return;2464 2465 netdev_dbg(dev, "Updating neighbour with IP=%pI6c\n", &dip);2466 neigh_event_send(n, NULL);2467 neigh_release(n);2468}2469#else2470static void mlxsw_sp_router_neigh_ent_ipv6_process(struct mlxsw_sp *mlxsw_sp,2471 char *rauhtd_pl,2472 int rec_index)2473{2474}2475#endif2476 2477static void mlxsw_sp_router_neigh_rec_ipv4_process(struct mlxsw_sp *mlxsw_sp,2478 char *rauhtd_pl,2479 int rec_index)2480{2481 u8 num_entries;2482 int i;2483 2484 num_entries = mlxsw_reg_rauhtd_ipv4_rec_num_entries_get(rauhtd_pl,2485 rec_index);2486 /* Hardware starts counting at 0, so add 1. */2487 num_entries++;2488 2489 /* Each record consists of several neighbour entries. */2490 for (i = 0; i < num_entries; i++) {2491 int ent_index;2492 2493 ent_index = rec_index * MLXSW_REG_RAUHTD_IPV4_ENT_PER_REC + i;2494 mlxsw_sp_router_neigh_ent_ipv4_process(mlxsw_sp, rauhtd_pl,2495 ent_index);2496 }2497 2498}2499 2500static void mlxsw_sp_router_neigh_rec_ipv6_process(struct mlxsw_sp *mlxsw_sp,2501 char *rauhtd_pl,2502 int rec_index)2503{2504 /* One record contains one entry. */2505 mlxsw_sp_router_neigh_ent_ipv6_process(mlxsw_sp, rauhtd_pl,2506 rec_index);2507}2508 2509static void mlxsw_sp_router_neigh_rec_process(struct mlxsw_sp *mlxsw_sp,2510 char *rauhtd_pl, int rec_index)2511{2512 switch (mlxsw_reg_rauhtd_rec_type_get(rauhtd_pl, rec_index)) {2513 case MLXSW_REG_RAUHTD_TYPE_IPV4:2514 mlxsw_sp_router_neigh_rec_ipv4_process(mlxsw_sp, rauhtd_pl,2515 rec_index);2516 break;2517 case MLXSW_REG_RAUHTD_TYPE_IPV6:2518 mlxsw_sp_router_neigh_rec_ipv6_process(mlxsw_sp, rauhtd_pl,2519 rec_index);2520 break;2521 }2522}2523 2524static bool mlxsw_sp_router_rauhtd_is_full(char *rauhtd_pl)2525{2526 u8 num_rec, last_rec_index, num_entries;2527 2528 num_rec = mlxsw_reg_rauhtd_num_rec_get(rauhtd_pl);2529 last_rec_index = num_rec - 1;2530 2531 if (num_rec < MLXSW_REG_RAUHTD_REC_MAX_NUM)2532 return false;2533 if (mlxsw_reg_rauhtd_rec_type_get(rauhtd_pl, last_rec_index) ==2534 MLXSW_REG_RAUHTD_TYPE_IPV6)2535 return true;2536 2537 num_entries = mlxsw_reg_rauhtd_ipv4_rec_num_entries_get(rauhtd_pl,2538 last_rec_index);2539 if (++num_entries == MLXSW_REG_RAUHTD_IPV4_ENT_PER_REC)2540 return true;2541 return false;2542}2543 2544static int2545__mlxsw_sp_router_neighs_update_rauhtd(struct mlxsw_sp *mlxsw_sp,2546 char *rauhtd_pl,2547 enum mlxsw_reg_rauhtd_type type)2548{2549 int i, num_rec;2550 int err;2551 2552 /* Ensure the RIF we read from the device does not change mid-dump. */2553 mutex_lock(&mlxsw_sp->router->lock);2554 do {2555 mlxsw_reg_rauhtd_pack(rauhtd_pl, type);2556 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(rauhtd),2557 rauhtd_pl);2558 if (err) {2559 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to dump neighbour table\n");2560 break;2561 }2562 num_rec = mlxsw_reg_rauhtd_num_rec_get(rauhtd_pl);2563 for (i = 0; i < num_rec; i++)2564 mlxsw_sp_router_neigh_rec_process(mlxsw_sp, rauhtd_pl,2565 i);2566 } while (mlxsw_sp_router_rauhtd_is_full(rauhtd_pl));2567 mutex_unlock(&mlxsw_sp->router->lock);2568 2569 return err;2570}2571 2572static int mlxsw_sp_router_neighs_update_rauhtd(struct mlxsw_sp *mlxsw_sp)2573{2574 enum mlxsw_reg_rauhtd_type type;2575 char *rauhtd_pl;2576 int err;2577 2578 if (!atomic_read(&mlxsw_sp->router->neighs_update.neigh_count))2579 return 0;2580 2581 rauhtd_pl = kmalloc(MLXSW_REG_RAUHTD_LEN, GFP_KERNEL);2582 if (!rauhtd_pl)2583 return -ENOMEM;2584 2585 type = MLXSW_REG_RAUHTD_TYPE_IPV4;2586 err = __mlxsw_sp_router_neighs_update_rauhtd(mlxsw_sp, rauhtd_pl, type);2587 if (err)2588 goto out;2589 2590 type = MLXSW_REG_RAUHTD_TYPE_IPV6;2591 err = __mlxsw_sp_router_neighs_update_rauhtd(mlxsw_sp, rauhtd_pl, type);2592out:2593 kfree(rauhtd_pl);2594 return err;2595}2596 2597static void mlxsw_sp_router_neighs_update_nh(struct mlxsw_sp *mlxsw_sp)2598{2599 struct mlxsw_sp_neigh_entry *neigh_entry;2600 2601 mutex_lock(&mlxsw_sp->router->lock);2602 list_for_each_entry(neigh_entry, &mlxsw_sp->router->nexthop_neighs_list,2603 nexthop_neighs_list_node)2604 /* If this neigh have nexthops, make the kernel think this neigh2605 * is active regardless of the traffic.2606 */2607 neigh_event_send(neigh_entry->key.n, NULL);2608 mutex_unlock(&mlxsw_sp->router->lock);2609}2610 2611static void2612mlxsw_sp_router_neighs_update_work_schedule(struct mlxsw_sp *mlxsw_sp)2613{2614 unsigned long interval = mlxsw_sp->router->neighs_update.interval;2615 2616 mlxsw_core_schedule_dw(&mlxsw_sp->router->neighs_update.dw,2617 msecs_to_jiffies(interval));2618}2619 2620static void mlxsw_sp_router_neighs_update_work(struct work_struct *work)2621{2622 struct mlxsw_sp_router *router;2623 int err;2624 2625 router = container_of(work, struct mlxsw_sp_router,2626 neighs_update.dw.work);2627 err = mlxsw_sp_router_neighs_update_rauhtd(router->mlxsw_sp);2628 if (err)2629 dev_err(router->mlxsw_sp->bus_info->dev, "Could not update kernel for neigh activity");2630 2631 mlxsw_sp_router_neighs_update_nh(router->mlxsw_sp);2632 2633 mlxsw_sp_router_neighs_update_work_schedule(router->mlxsw_sp);2634}2635 2636static void mlxsw_sp_router_probe_unresolved_nexthops(struct work_struct *work)2637{2638 struct mlxsw_sp_neigh_entry *neigh_entry;2639 struct mlxsw_sp_router *router;2640 2641 router = container_of(work, struct mlxsw_sp_router,2642 nexthop_probe_dw.work);2643 /* Iterate over nexthop neighbours, find those who are unresolved and2644 * send arp on them. This solves the chicken-egg problem when2645 * the nexthop wouldn't get offloaded until the neighbor is resolved2646 * but it wouldn't get resolved ever in case traffic is flowing in HW2647 * using different nexthop.2648 */2649 mutex_lock(&router->lock);2650 list_for_each_entry(neigh_entry, &router->nexthop_neighs_list,2651 nexthop_neighs_list_node)2652 if (!neigh_entry->connected)2653 neigh_event_send(neigh_entry->key.n, NULL);2654 mutex_unlock(&router->lock);2655 2656 mlxsw_core_schedule_dw(&router->nexthop_probe_dw,2657 MLXSW_SP_UNRESOLVED_NH_PROBE_INTERVAL);2658}2659 2660static void2661mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp,2662 struct mlxsw_sp_neigh_entry *neigh_entry,2663 bool removing, bool dead);2664 2665static enum mlxsw_reg_rauht_op mlxsw_sp_rauht_op(bool adding)2666{2667 return adding ? MLXSW_REG_RAUHT_OP_WRITE_ADD :2668 MLXSW_REG_RAUHT_OP_WRITE_DELETE;2669}2670 2671static int2672mlxsw_sp_router_neigh_entry_op4(struct mlxsw_sp *mlxsw_sp,2673 struct mlxsw_sp_neigh_entry *neigh_entry,2674 enum mlxsw_reg_rauht_op op)2675{2676 struct neighbour *n = neigh_entry->key.n;2677 u32 dip = ntohl(*((__be32 *) n->primary_key));2678 char rauht_pl[MLXSW_REG_RAUHT_LEN];2679 2680 mlxsw_reg_rauht_pack4(rauht_pl, op, neigh_entry->rif, neigh_entry->ha,2681 dip);2682 if (neigh_entry->counter_valid)2683 mlxsw_reg_rauht_pack_counter(rauht_pl,2684 neigh_entry->counter_index);2685 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht), rauht_pl);2686}2687 2688static int2689mlxsw_sp_router_neigh_entry_op6(struct mlxsw_sp *mlxsw_sp,2690 struct mlxsw_sp_neigh_entry *neigh_entry,2691 enum mlxsw_reg_rauht_op op)2692{2693 struct neighbour *n = neigh_entry->key.n;2694 char rauht_pl[MLXSW_REG_RAUHT_LEN];2695 const char *dip = n->primary_key;2696 2697 mlxsw_reg_rauht_pack6(rauht_pl, op, neigh_entry->rif, neigh_entry->ha,2698 dip);2699 if (neigh_entry->counter_valid)2700 mlxsw_reg_rauht_pack_counter(rauht_pl,2701 neigh_entry->counter_index);2702 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht), rauht_pl);2703}2704 2705bool mlxsw_sp_neigh_ipv6_ignore(struct mlxsw_sp_neigh_entry *neigh_entry)2706{2707 struct neighbour *n = neigh_entry->key.n;2708 2709 /* Packets with a link-local destination address are trapped2710 * after LPM lookup and never reach the neighbour table, so2711 * there is no need to program such neighbours to the device.2712 */2713 if (ipv6_addr_type((struct in6_addr *) &n->primary_key) &2714 IPV6_ADDR_LINKLOCAL)2715 return true;2716 return false;2717}2718 2719static void2720mlxsw_sp_neigh_entry_update(struct mlxsw_sp *mlxsw_sp,2721 struct mlxsw_sp_neigh_entry *neigh_entry,2722 bool adding)2723{2724 enum mlxsw_reg_rauht_op op = mlxsw_sp_rauht_op(adding);2725 int err;2726 2727 if (!adding && !neigh_entry->connected)2728 return;2729 neigh_entry->connected = adding;2730 if (neigh_entry->key.n->tbl->family == AF_INET) {2731 err = mlxsw_sp_router_neigh_entry_op4(mlxsw_sp, neigh_entry,2732 op);2733 if (err)2734 return;2735 } else if (neigh_entry->key.n->tbl->family == AF_INET6) {2736 if (mlxsw_sp_neigh_ipv6_ignore(neigh_entry))2737 return;2738 err = mlxsw_sp_router_neigh_entry_op6(mlxsw_sp, neigh_entry,2739 op);2740 if (err)2741 return;2742 } else {2743 WARN_ON_ONCE(1);2744 return;2745 }2746 2747 if (adding)2748 neigh_entry->key.n->flags |= NTF_OFFLOADED;2749 else2750 neigh_entry->key.n->flags &= ~NTF_OFFLOADED;2751}2752 2753void2754mlxsw_sp_neigh_entry_counter_update(struct mlxsw_sp *mlxsw_sp,2755 struct mlxsw_sp_neigh_entry *neigh_entry,2756 bool adding)2757{2758 if (adding)2759 mlxsw_sp_neigh_counter_alloc(mlxsw_sp, neigh_entry);2760 else2761 mlxsw_sp_neigh_counter_free(mlxsw_sp, neigh_entry);2762 mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, true);2763}2764 2765struct mlxsw_sp_netevent_work {2766 struct work_struct work;2767 struct mlxsw_sp *mlxsw_sp;2768 struct neighbour *n;2769};2770 2771static void mlxsw_sp_router_neigh_event_work(struct work_struct *work)2772{2773 struct mlxsw_sp_netevent_work *net_work =2774 container_of(work, struct mlxsw_sp_netevent_work, work);2775 struct mlxsw_sp *mlxsw_sp = net_work->mlxsw_sp;2776 struct mlxsw_sp_neigh_entry *neigh_entry;2777 struct neighbour *n = net_work->n;2778 unsigned char ha[ETH_ALEN];2779 bool entry_connected;2780 u8 nud_state, dead;2781 2782 /* If these parameters are changed after we release the lock,2783 * then we are guaranteed to receive another event letting us2784 * know about it.2785 */2786 read_lock_bh(&n->lock);2787 memcpy(ha, n->ha, ETH_ALEN);2788 nud_state = n->nud_state;2789 dead = n->dead;2790 read_unlock_bh(&n->lock);2791 2792 mutex_lock(&mlxsw_sp->router->lock);2793 mlxsw_sp_span_respin(mlxsw_sp);2794 2795 entry_connected = nud_state & NUD_VALID && !dead;2796 neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, n);2797 if (!entry_connected && !neigh_entry)2798 goto out;2799 if (!neigh_entry) {2800 neigh_entry = mlxsw_sp_neigh_entry_create(mlxsw_sp, n);2801 if (IS_ERR(neigh_entry))2802 goto out;2803 }2804 2805 if (neigh_entry->connected && entry_connected &&2806 !memcmp(neigh_entry->ha, ha, ETH_ALEN))2807 goto out;2808 2809 memcpy(neigh_entry->ha, ha, ETH_ALEN);2810 mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, entry_connected);2811 mlxsw_sp_nexthop_neigh_update(mlxsw_sp, neigh_entry, !entry_connected,2812 dead);2813 2814 if (!neigh_entry->connected && list_empty(&neigh_entry->nexthop_list))2815 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);2816 2817out:2818 mutex_unlock(&mlxsw_sp->router->lock);2819 neigh_release(n);2820 kfree(net_work);2821}2822 2823static int mlxsw_sp_mp_hash_init(struct mlxsw_sp *mlxsw_sp);2824 2825static void mlxsw_sp_router_mp_hash_event_work(struct work_struct *work)2826{2827 struct mlxsw_sp_netevent_work *net_work =2828 container_of(work, struct mlxsw_sp_netevent_work, work);2829 struct mlxsw_sp *mlxsw_sp = net_work->mlxsw_sp;2830 2831 mlxsw_sp_mp_hash_init(mlxsw_sp);2832 kfree(net_work);2833}2834 2835static int __mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp);2836 2837static void mlxsw_sp_router_update_priority_work(struct work_struct *work)2838{2839 struct mlxsw_sp_netevent_work *net_work =2840 container_of(work, struct mlxsw_sp_netevent_work, work);2841 struct mlxsw_sp *mlxsw_sp = net_work->mlxsw_sp;2842 2843 __mlxsw_sp_router_init(mlxsw_sp);2844 kfree(net_work);2845}2846 2847static int mlxsw_sp_router_schedule_work(struct net *net,2848 struct mlxsw_sp_router *router,2849 struct neighbour *n,2850 void (*cb)(struct work_struct *))2851{2852 struct mlxsw_sp_netevent_work *net_work;2853 2854 if (!net_eq(net, mlxsw_sp_net(router->mlxsw_sp)))2855 return NOTIFY_DONE;2856 2857 net_work = kzalloc(sizeof(*net_work), GFP_ATOMIC);2858 if (!net_work)2859 return NOTIFY_BAD;2860 2861 INIT_WORK(&net_work->work, cb);2862 net_work->mlxsw_sp = router->mlxsw_sp;2863 net_work->n = n;2864 mlxsw_core_schedule_work(&net_work->work);2865 return NOTIFY_DONE;2866}2867 2868static bool mlxsw_sp_dev_lower_is_port(struct net_device *dev)2869{2870 struct mlxsw_sp_port *mlxsw_sp_port;2871 2872 rcu_read_lock();2873 mlxsw_sp_port = mlxsw_sp_port_dev_lower_find_rcu(dev);2874 rcu_read_unlock();2875 return !!mlxsw_sp_port;2876}2877 2878static int mlxsw_sp_router_schedule_neigh_work(struct mlxsw_sp_router *router,2879 struct neighbour *n)2880{2881 struct net *net;2882 2883 net = neigh_parms_net(n->parms);2884 2885 /* Take a reference to ensure the neighbour won't be destructed until we2886 * drop the reference in delayed work.2887 */2888 neigh_clone(n);2889 return mlxsw_sp_router_schedule_work(net, router, n,2890 mlxsw_sp_router_neigh_event_work);2891}2892 2893static int mlxsw_sp_router_netevent_event(struct notifier_block *nb,2894 unsigned long event, void *ptr)2895{2896 struct mlxsw_sp_router *router;2897 unsigned long interval;2898 struct neigh_parms *p;2899 struct neighbour *n;2900 2901 router = container_of(nb, struct mlxsw_sp_router, netevent_nb);2902 2903 switch (event) {2904 case NETEVENT_DELAY_PROBE_TIME_UPDATE:2905 p = ptr;2906 2907 /* We don't care about changes in the default table. */2908 if (!p->dev || (p->tbl->family != AF_INET &&2909 p->tbl->family != AF_INET6))2910 return NOTIFY_DONE;2911 2912 /* We are in atomic context and can't take RTNL mutex,2913 * so use RCU variant to walk the device chain.2914 */2915 if (!mlxsw_sp_dev_lower_is_port(p->dev))2916 return NOTIFY_DONE;2917 2918 interval = jiffies_to_msecs(NEIGH_VAR(p, DELAY_PROBE_TIME));2919 router->neighs_update.interval = interval;2920 break;2921 case NETEVENT_NEIGH_UPDATE:2922 n = ptr;2923 2924 if (n->tbl->family != AF_INET && n->tbl->family != AF_INET6)2925 return NOTIFY_DONE;2926 2927 if (!mlxsw_sp_dev_lower_is_port(n->dev))2928 return NOTIFY_DONE;2929 2930 return mlxsw_sp_router_schedule_neigh_work(router, n);2931 2932 case NETEVENT_IPV4_MPATH_HASH_UPDATE:2933 case NETEVENT_IPV6_MPATH_HASH_UPDATE:2934 return mlxsw_sp_router_schedule_work(ptr, router, NULL,2935 mlxsw_sp_router_mp_hash_event_work);2936 2937 case NETEVENT_IPV4_FWD_UPDATE_PRIORITY_UPDATE:2938 return mlxsw_sp_router_schedule_work(ptr, router, NULL,2939 mlxsw_sp_router_update_priority_work);2940 }2941 2942 return NOTIFY_DONE;2943}2944 2945static int mlxsw_sp_neigh_init(struct mlxsw_sp *mlxsw_sp)2946{2947 int err;2948 2949 err = rhashtable_init(&mlxsw_sp->router->neigh_ht,2950 &mlxsw_sp_neigh_ht_params);2951 if (err)2952 return err;2953 2954 /* Initialize the polling interval according to the default2955 * table.2956 */2957 mlxsw_sp_router_neighs_update_interval_init(mlxsw_sp);2958 2959 /* Create the delayed works for the activity_update */2960 INIT_DELAYED_WORK(&mlxsw_sp->router->neighs_update.dw,2961 mlxsw_sp_router_neighs_update_work);2962 INIT_DELAYED_WORK(&mlxsw_sp->router->nexthop_probe_dw,2963 mlxsw_sp_router_probe_unresolved_nexthops);2964 atomic_set(&mlxsw_sp->router->neighs_update.neigh_count, 0);2965 mlxsw_core_schedule_dw(&mlxsw_sp->router->neighs_update.dw, 0);2966 mlxsw_core_schedule_dw(&mlxsw_sp->router->nexthop_probe_dw, 0);2967 return 0;2968}2969 2970static void mlxsw_sp_neigh_fini(struct mlxsw_sp *mlxsw_sp)2971{2972 cancel_delayed_work_sync(&mlxsw_sp->router->neighs_update.dw);2973 cancel_delayed_work_sync(&mlxsw_sp->router->nexthop_probe_dw);2974 rhashtable_destroy(&mlxsw_sp->router->neigh_ht);2975}2976 2977static void mlxsw_sp_neigh_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,2978 struct mlxsw_sp_rif *rif)2979{2980 struct mlxsw_sp_neigh_entry *neigh_entry, *tmp;2981 2982 list_for_each_entry_safe(neigh_entry, tmp, &rif->neigh_list,2983 rif_list_node) {2984 mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, false);2985 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);2986 }2987}2988 2989struct mlxsw_sp_neigh_rif_made_sync {2990 struct mlxsw_sp *mlxsw_sp;2991 struct mlxsw_sp_rif *rif;2992 int err;2993};2994 2995static void mlxsw_sp_neigh_rif_made_sync_each(struct neighbour *n, void *data)2996{2997 struct mlxsw_sp_neigh_rif_made_sync *rms = data;2998 int rc;2999 3000 if (rms->err)3001 return;3002 if (n->dev != mlxsw_sp_rif_dev(rms->rif))3003 return;3004 rc = mlxsw_sp_router_schedule_neigh_work(rms->mlxsw_sp->router, n);3005 if (rc != NOTIFY_DONE)3006 rms->err = -ENOMEM;3007}3008 3009static int mlxsw_sp_neigh_rif_made_sync(struct mlxsw_sp *mlxsw_sp,3010 struct mlxsw_sp_rif *rif)3011{3012 struct mlxsw_sp_neigh_rif_made_sync rms = {3013 .mlxsw_sp = mlxsw_sp,3014 .rif = rif,3015 };3016 3017 neigh_for_each(&arp_tbl, mlxsw_sp_neigh_rif_made_sync_each, &rms);3018 if (rms.err)3019 goto err_arp;3020 3021#if IS_ENABLED(CONFIG_IPV6)3022 neigh_for_each(&nd_tbl, mlxsw_sp_neigh_rif_made_sync_each, &rms);3023#endif3024 if (rms.err)3025 goto err_nd;3026 3027 return 0;3028 3029err_nd:3030err_arp:3031 mlxsw_sp_neigh_rif_gone_sync(mlxsw_sp, rif);3032 return rms.err;3033}3034 3035enum mlxsw_sp_nexthop_type {3036 MLXSW_SP_NEXTHOP_TYPE_ETH,3037 MLXSW_SP_NEXTHOP_TYPE_IPIP,3038};3039 3040enum mlxsw_sp_nexthop_action {3041 /* Nexthop forwards packets to an egress RIF */3042 MLXSW_SP_NEXTHOP_ACTION_FORWARD,3043 /* Nexthop discards packets */3044 MLXSW_SP_NEXTHOP_ACTION_DISCARD,3045 /* Nexthop traps packets */3046 MLXSW_SP_NEXTHOP_ACTION_TRAP,3047};3048 3049struct mlxsw_sp_nexthop_key {3050 struct fib_nh *fib_nh;3051};3052 3053struct mlxsw_sp_nexthop_counter;3054 3055struct mlxsw_sp_nexthop {3056 struct list_head neigh_list_node; /* member of neigh entry list */3057 struct list_head crif_list_node;3058 struct list_head router_list_node;3059 struct mlxsw_sp_nexthop_group_info *nhgi; /* pointer back to the group3060 * this nexthop belongs to3061 */3062 struct rhash_head ht_node;3063 struct neigh_table *neigh_tbl;3064 struct mlxsw_sp_nexthop_key key;3065 unsigned char gw_addr[sizeof(struct in6_addr)];3066 int ifindex;3067 int nh_weight;3068 int norm_nh_weight;3069 int num_adj_entries;3070 struct mlxsw_sp_crif *crif;3071 u8 should_offload:1, /* set indicates this nexthop should be written3072 * to the adjacency table.3073 */3074 offloaded:1, /* set indicates this nexthop was written to the3075 * adjacency table.3076 */3077 update:1; /* set indicates this nexthop should be updated in the3078 * adjacency table (f.e., its MAC changed).3079 */3080 enum mlxsw_sp_nexthop_action action;3081 enum mlxsw_sp_nexthop_type type;3082 union {3083 struct mlxsw_sp_neigh_entry *neigh_entry;3084 struct mlxsw_sp_ipip_entry *ipip_entry;3085 };3086 struct mlxsw_sp_nexthop_counter *counter;3087 u32 id; /* NH ID for members of a NH object group. */3088};3089 3090static struct net_device *3091mlxsw_sp_nexthop_dev(const struct mlxsw_sp_nexthop *nh)3092{3093 if (!nh->crif)3094 return NULL;3095 return nh->crif->key.dev;3096}3097 3098enum mlxsw_sp_nexthop_group_type {3099 MLXSW_SP_NEXTHOP_GROUP_TYPE_IPV4,3100 MLXSW_SP_NEXTHOP_GROUP_TYPE_IPV6,3101 MLXSW_SP_NEXTHOP_GROUP_TYPE_OBJ,3102};3103 3104struct mlxsw_sp_nexthop_group_info {3105 struct mlxsw_sp_nexthop_group *nh_grp;3106 u32 adj_index;3107 u16 ecmp_size;3108 u16 count;3109 int sum_norm_weight;3110 u8 adj_index_valid:1,3111 gateway:1, /* routes using the group use a gateway */3112 is_resilient:1,3113 hw_stats:1;3114 struct list_head list; /* member in nh_res_grp_list */3115 struct xarray nexthop_counters;3116 struct mlxsw_sp_nexthop nexthops[] __counted_by(count);3117};3118 3119static struct mlxsw_sp_rif *3120mlxsw_sp_nhgi_rif(const struct mlxsw_sp_nexthop_group_info *nhgi)3121{3122 struct mlxsw_sp_crif *crif = nhgi->nexthops[0].crif;3123 3124 if (!crif)3125 return NULL;3126 return crif->rif;3127}3128 3129struct mlxsw_sp_nexthop_group_vr_key {3130 u16 vr_id;3131 enum mlxsw_sp_l3proto proto;3132};3133 3134struct mlxsw_sp_nexthop_group_vr_entry {3135 struct list_head list; /* member in vr_list */3136 struct rhash_head ht_node; /* member in vr_ht */3137 refcount_t ref_count;3138 struct mlxsw_sp_nexthop_group_vr_key key;3139};3140 3141struct mlxsw_sp_nexthop_group {3142 struct rhash_head ht_node;3143 struct list_head fib_list; /* list of fib entries that use this group */3144 union {3145 struct {3146 struct fib_info *fi;3147 } ipv4;3148 struct {3149 u32 id;3150 } obj;3151 };3152 struct mlxsw_sp_nexthop_group_info *nhgi;3153 struct list_head vr_list;3154 struct rhashtable vr_ht;3155 enum mlxsw_sp_nexthop_group_type type;3156 bool can_destroy;3157};3158 3159struct mlxsw_sp_nexthop_counter {3160 unsigned int counter_index;3161 refcount_t ref_count;3162};3163 3164static struct mlxsw_sp_nexthop_counter *3165mlxsw_sp_nexthop_counter_alloc(struct mlxsw_sp *mlxsw_sp)3166{3167 struct mlxsw_sp_nexthop_counter *nhct;3168 int err;3169 3170 nhct = kzalloc(sizeof(*nhct), GFP_KERNEL);3171 if (!nhct)3172 return ERR_PTR(-ENOMEM);3173 3174 err = mlxsw_sp_flow_counter_alloc(mlxsw_sp, &nhct->counter_index);3175 if (err)3176 goto err_counter_alloc;3177 3178 refcount_set(&nhct->ref_count, 1);3179 return nhct;3180 3181err_counter_alloc:3182 kfree(nhct);3183 return ERR_PTR(err);3184}3185 3186static void3187mlxsw_sp_nexthop_counter_free(struct mlxsw_sp *mlxsw_sp,3188 struct mlxsw_sp_nexthop_counter *nhct)3189{3190 mlxsw_sp_flow_counter_free(mlxsw_sp, nhct->counter_index);3191 kfree(nhct);3192}3193 3194static struct mlxsw_sp_nexthop_counter *3195mlxsw_sp_nexthop_sh_counter_get(struct mlxsw_sp *mlxsw_sp,3196 struct mlxsw_sp_nexthop *nh)3197{3198 struct mlxsw_sp_nexthop_group *nh_grp = nh->nhgi->nh_grp;3199 struct mlxsw_sp_nexthop_counter *nhct;3200 int err;3201 3202 nhct = xa_load(&nh_grp->nhgi->nexthop_counters, nh->id);3203 if (nhct) {3204 refcount_inc(&nhct->ref_count);3205 return nhct;3206 }3207 3208 nhct = mlxsw_sp_nexthop_counter_alloc(mlxsw_sp);3209 if (IS_ERR(nhct))3210 return nhct;3211 3212 err = xa_err(xa_store(&nh_grp->nhgi->nexthop_counters, nh->id, nhct,3213 GFP_KERNEL));3214 if (err)3215 goto err_store;3216 3217 return nhct;3218 3219err_store:3220 mlxsw_sp_nexthop_counter_free(mlxsw_sp, nhct);3221 return ERR_PTR(err);3222}3223 3224static void mlxsw_sp_nexthop_sh_counter_put(struct mlxsw_sp *mlxsw_sp,3225 struct mlxsw_sp_nexthop *nh)3226{3227 struct mlxsw_sp_nexthop_group *nh_grp = nh->nhgi->nh_grp;3228 struct mlxsw_sp_nexthop_counter *nhct;3229 3230 nhct = xa_load(&nh_grp->nhgi->nexthop_counters, nh->id);3231 if (WARN_ON(!nhct))3232 return;3233 3234 if (!refcount_dec_and_test(&nhct->ref_count))3235 return;3236 3237 xa_erase(&nh_grp->nhgi->nexthop_counters, nh->id);3238 mlxsw_sp_nexthop_counter_free(mlxsw_sp, nhct);3239}3240 3241int mlxsw_sp_nexthop_counter_enable(struct mlxsw_sp *mlxsw_sp,3242 struct mlxsw_sp_nexthop *nh)3243{3244 const char *table_adj = MLXSW_SP_DPIPE_TABLE_NAME_ADJ;3245 struct mlxsw_sp_nexthop_counter *nhct;3246 struct devlink *devlink;3247 bool dpipe_stats;3248 3249 if (nh->counter)3250 return 0;3251 3252 devlink = priv_to_devlink(mlxsw_sp->core);3253 dpipe_stats = devlink_dpipe_table_counter_enabled(devlink, table_adj);3254 if (!(nh->nhgi->hw_stats || dpipe_stats))3255 return 0;3256 3257 if (nh->id)3258 nhct = mlxsw_sp_nexthop_sh_counter_get(mlxsw_sp, nh);3259 else3260 nhct = mlxsw_sp_nexthop_counter_alloc(mlxsw_sp);3261 if (IS_ERR(nhct))3262 return PTR_ERR(nhct);3263 3264 nh->counter = nhct;3265 return 0;3266}3267 3268void mlxsw_sp_nexthop_counter_disable(struct mlxsw_sp *mlxsw_sp,3269 struct mlxsw_sp_nexthop *nh)3270{3271 if (!nh->counter)3272 return;3273 3274 if (nh->id)3275 mlxsw_sp_nexthop_sh_counter_put(mlxsw_sp, nh);3276 else3277 mlxsw_sp_nexthop_counter_free(mlxsw_sp, nh->counter);3278 nh->counter = NULL;3279}3280 3281static int mlxsw_sp_nexthop_counter_update(struct mlxsw_sp *mlxsw_sp,3282 struct mlxsw_sp_nexthop *nh)3283{3284 if (nh->nhgi->hw_stats)3285 return mlxsw_sp_nexthop_counter_enable(mlxsw_sp, nh);3286 mlxsw_sp_nexthop_counter_disable(mlxsw_sp, nh);3287 return 0;3288}3289 3290int mlxsw_sp_nexthop_counter_get(struct mlxsw_sp *mlxsw_sp,3291 struct mlxsw_sp_nexthop *nh, u64 *p_counter)3292{3293 if (!nh->counter)3294 return -EINVAL;3295 3296 return mlxsw_sp_flow_counter_get(mlxsw_sp, nh->counter->counter_index,3297 true, p_counter, NULL);3298}3299 3300struct mlxsw_sp_nexthop *mlxsw_sp_nexthop_next(struct mlxsw_sp_router *router,3301 struct mlxsw_sp_nexthop *nh)3302{3303 if (!nh) {3304 if (list_empty(&router->nexthop_list))3305 return NULL;3306 else3307 return list_first_entry(&router->nexthop_list,3308 typeof(*nh), router_list_node);3309 }3310 if (list_is_last(&nh->router_list_node, &router->nexthop_list))3311 return NULL;3312 return list_next_entry(nh, router_list_node);3313}3314 3315bool mlxsw_sp_nexthop_is_forward(const struct mlxsw_sp_nexthop *nh)3316{3317 return nh->offloaded && nh->action == MLXSW_SP_NEXTHOP_ACTION_FORWARD;3318}3319 3320unsigned char *mlxsw_sp_nexthop_ha(struct mlxsw_sp_nexthop *nh)3321{3322 if (nh->type != MLXSW_SP_NEXTHOP_TYPE_ETH ||3323 !mlxsw_sp_nexthop_is_forward(nh))3324 return NULL;3325 return nh->neigh_entry->ha;3326}3327 3328int mlxsw_sp_nexthop_indexes(struct mlxsw_sp_nexthop *nh, u32 *p_adj_index,3329 u32 *p_adj_size, u32 *p_adj_hash_index)3330{3331 struct mlxsw_sp_nexthop_group_info *nhgi = nh->nhgi;3332 u32 adj_hash_index = 0;3333 int i;3334 3335 if (!nh->offloaded || !nhgi->adj_index_valid)3336 return -EINVAL;3337 3338 *p_adj_index = nhgi->adj_index;3339 *p_adj_size = nhgi->ecmp_size;3340 3341 for (i = 0; i < nhgi->count; i++) {3342 struct mlxsw_sp_nexthop *nh_iter = &nhgi->nexthops[i];3343 3344 if (nh_iter == nh)3345 break;3346 if (nh_iter->offloaded)3347 adj_hash_index += nh_iter->num_adj_entries;3348 }3349 3350 *p_adj_hash_index = adj_hash_index;3351 return 0;3352}3353 3354struct mlxsw_sp_rif *mlxsw_sp_nexthop_rif(struct mlxsw_sp_nexthop *nh)3355{3356 if (WARN_ON(!nh->crif))3357 return NULL;3358 return nh->crif->rif;3359}3360 3361bool mlxsw_sp_nexthop_group_has_ipip(struct mlxsw_sp_nexthop *nh)3362{3363 struct mlxsw_sp_nexthop_group_info *nhgi = nh->nhgi;3364 int i;3365 3366 for (i = 0; i < nhgi->count; i++) {3367 struct mlxsw_sp_nexthop *nh_iter = &nhgi->nexthops[i];3368 3369 if (nh_iter->type == MLXSW_SP_NEXTHOP_TYPE_IPIP)3370 return true;3371 }3372 return false;3373}3374 3375static const struct rhashtable_params mlxsw_sp_nexthop_group_vr_ht_params = {3376 .key_offset = offsetof(struct mlxsw_sp_nexthop_group_vr_entry, key),3377 .head_offset = offsetof(struct mlxsw_sp_nexthop_group_vr_entry, ht_node),3378 .key_len = sizeof(struct mlxsw_sp_nexthop_group_vr_key),3379 .automatic_shrinking = true,3380};3381 3382static struct mlxsw_sp_nexthop_group_vr_entry *3383mlxsw_sp_nexthop_group_vr_entry_lookup(struct mlxsw_sp_nexthop_group *nh_grp,3384 const struct mlxsw_sp_fib *fib)3385{3386 struct mlxsw_sp_nexthop_group_vr_key key;3387 3388 memset(&key, 0, sizeof(key));3389 key.vr_id = fib->vr->id;3390 key.proto = fib->proto;3391 return rhashtable_lookup_fast(&nh_grp->vr_ht, &key,3392 mlxsw_sp_nexthop_group_vr_ht_params);3393}3394 3395static int3396mlxsw_sp_nexthop_group_vr_entry_create(struct mlxsw_sp_nexthop_group *nh_grp,3397 const struct mlxsw_sp_fib *fib)3398{3399 struct mlxsw_sp_nexthop_group_vr_entry *vr_entry;3400 int err;3401 3402 vr_entry = kzalloc(sizeof(*vr_entry), GFP_KERNEL);3403 if (!vr_entry)3404 return -ENOMEM;3405 3406 vr_entry->key.vr_id = fib->vr->id;3407 vr_entry->key.proto = fib->proto;3408 refcount_set(&vr_entry->ref_count, 1);3409 3410 err = rhashtable_insert_fast(&nh_grp->vr_ht, &vr_entry->ht_node,3411 mlxsw_sp_nexthop_group_vr_ht_params);3412 if (err)3413 goto err_hashtable_insert;3414 3415 list_add(&vr_entry->list, &nh_grp->vr_list);3416 3417 return 0;3418 3419err_hashtable_insert:3420 kfree(vr_entry);3421 return err;3422}3423 3424static void3425mlxsw_sp_nexthop_group_vr_entry_destroy(struct mlxsw_sp_nexthop_group *nh_grp,3426 struct mlxsw_sp_nexthop_group_vr_entry *vr_entry)3427{3428 list_del(&vr_entry->list);3429 rhashtable_remove_fast(&nh_grp->vr_ht, &vr_entry->ht_node,3430 mlxsw_sp_nexthop_group_vr_ht_params);3431 kfree(vr_entry);3432}3433 3434static int3435mlxsw_sp_nexthop_group_vr_link(struct mlxsw_sp_nexthop_group *nh_grp,3436 const struct mlxsw_sp_fib *fib)3437{3438 struct mlxsw_sp_nexthop_group_vr_entry *vr_entry;3439 3440 vr_entry = mlxsw_sp_nexthop_group_vr_entry_lookup(nh_grp, fib);3441 if (vr_entry) {3442 refcount_inc(&vr_entry->ref_count);3443 return 0;3444 }3445 3446 return mlxsw_sp_nexthop_group_vr_entry_create(nh_grp, fib);3447}3448 3449static void3450mlxsw_sp_nexthop_group_vr_unlink(struct mlxsw_sp_nexthop_group *nh_grp,3451 const struct mlxsw_sp_fib *fib)3452{3453 struct mlxsw_sp_nexthop_group_vr_entry *vr_entry;3454 3455 vr_entry = mlxsw_sp_nexthop_group_vr_entry_lookup(nh_grp, fib);3456 if (WARN_ON_ONCE(!vr_entry))3457 return;3458 3459 if (!refcount_dec_and_test(&vr_entry->ref_count))3460 return;3461 3462 mlxsw_sp_nexthop_group_vr_entry_destroy(nh_grp, vr_entry);3463}3464 3465struct mlxsw_sp_nexthop_group_cmp_arg {3466 enum mlxsw_sp_nexthop_group_type type;3467 union {3468 struct fib_info *fi;3469 struct mlxsw_sp_fib6_entry *fib6_entry;3470 u32 id;3471 };3472};3473 3474static bool3475mlxsw_sp_nexthop6_group_has_nexthop(const struct mlxsw_sp_nexthop_group *nh_grp,3476 const struct in6_addr *gw, int ifindex,3477 int weight)3478{3479 int i;3480 3481 for (i = 0; i < nh_grp->nhgi->count; i++) {3482 const struct mlxsw_sp_nexthop *nh;3483 3484 nh = &nh_grp->nhgi->nexthops[i];3485 if (nh->ifindex == ifindex && nh->nh_weight == weight &&3486 ipv6_addr_equal(gw, (struct in6_addr *) nh->gw_addr))3487 return true;3488 }3489 3490 return false;3491}3492 3493static bool3494mlxsw_sp_nexthop6_group_cmp(const struct mlxsw_sp_nexthop_group *nh_grp,3495 const struct mlxsw_sp_fib6_entry *fib6_entry)3496{3497 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;3498 3499 if (nh_grp->nhgi->count != fib6_entry->nrt6)3500 return false;3501 3502 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {3503 struct fib6_nh *fib6_nh = mlxsw_sp_rt6->rt->fib6_nh;3504 struct in6_addr *gw;3505 int ifindex, weight;3506 3507 ifindex = fib6_nh->fib_nh_dev->ifindex;3508 weight = fib6_nh->fib_nh_weight;3509 gw = &fib6_nh->fib_nh_gw6;3510 if (!mlxsw_sp_nexthop6_group_has_nexthop(nh_grp, gw, ifindex,3511 weight))3512 return false;3513 }3514 3515 return true;3516}3517 3518static int3519mlxsw_sp_nexthop_group_cmp(struct rhashtable_compare_arg *arg, const void *ptr)3520{3521 const struct mlxsw_sp_nexthop_group_cmp_arg *cmp_arg = arg->key;3522 const struct mlxsw_sp_nexthop_group *nh_grp = ptr;3523 3524 if (nh_grp->type != cmp_arg->type)3525 return 1;3526 3527 switch (cmp_arg->type) {3528 case MLXSW_SP_NEXTHOP_GROUP_TYPE_IPV4:3529 return cmp_arg->fi != nh_grp->ipv4.fi;3530 case MLXSW_SP_NEXTHOP_GROUP_TYPE_IPV6:3531 return !mlxsw_sp_nexthop6_group_cmp(nh_grp,3532 cmp_arg->fib6_entry);3533 case MLXSW_SP_NEXTHOP_GROUP_TYPE_OBJ:3534 return cmp_arg->id != nh_grp->obj.id;3535 default:3536 WARN_ON(1);3537 return 1;3538 }3539}3540 3541static u32 mlxsw_sp_nexthop_group_hash_obj(const void *data, u32 len, u32 seed)3542{3543 const struct mlxsw_sp_nexthop_group *nh_grp = data;3544 const struct mlxsw_sp_nexthop *nh;3545 struct fib_info *fi;3546 unsigned int val;3547 int i;3548 3549 switch (nh_grp->type) {3550 case MLXSW_SP_NEXTHOP_GROUP_TYPE_IPV4:3551 fi = nh_grp->ipv4.fi;3552 return jhash(&fi, sizeof(fi), seed);3553 case MLXSW_SP_NEXTHOP_GROUP_TYPE_IPV6:3554 val = nh_grp->nhgi->count;3555 for (i = 0; i < nh_grp->nhgi->count; i++) {3556 nh = &nh_grp->nhgi->nexthops[i];3557 val ^= jhash(&nh->ifindex, sizeof(nh->ifindex), seed);3558 val ^= jhash(&nh->gw_addr, sizeof(nh->gw_addr), seed);3559 }3560 return jhash(&val, sizeof(val), seed);3561 case MLXSW_SP_NEXTHOP_GROUP_TYPE_OBJ:3562 return jhash(&nh_grp->obj.id, sizeof(nh_grp->obj.id), seed);3563 default:3564 WARN_ON(1);3565 return 0;3566 }3567}3568 3569static u323570mlxsw_sp_nexthop6_group_hash(struct mlxsw_sp_fib6_entry *fib6_entry, u32 seed)3571{3572 unsigned int val = fib6_entry->nrt6;3573 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;3574 3575 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {3576 struct fib6_nh *fib6_nh = mlxsw_sp_rt6->rt->fib6_nh;3577 struct net_device *dev = fib6_nh->fib_nh_dev;3578 struct in6_addr *gw = &fib6_nh->fib_nh_gw6;3579 3580 val ^= jhash(&dev->ifindex, sizeof(dev->ifindex), seed);3581 val ^= jhash(gw, sizeof(*gw), seed);3582 }3583 3584 return jhash(&val, sizeof(val), seed);3585}3586 3587static u323588mlxsw_sp_nexthop_group_hash(const void *data, u32 len, u32 seed)3589{3590 const struct mlxsw_sp_nexthop_group_cmp_arg *cmp_arg = data;3591 3592 switch (cmp_arg->type) {3593 case MLXSW_SP_NEXTHOP_GROUP_TYPE_IPV4:3594 return jhash(&cmp_arg->fi, sizeof(cmp_arg->fi), seed);3595 case MLXSW_SP_NEXTHOP_GROUP_TYPE_IPV6:3596 return mlxsw_sp_nexthop6_group_hash(cmp_arg->fib6_entry, seed);3597 case MLXSW_SP_NEXTHOP_GROUP_TYPE_OBJ:3598 return jhash(&cmp_arg->id, sizeof(cmp_arg->id), seed);3599 default:3600 WARN_ON(1);3601 return 0;3602 }3603}3604 3605static const struct rhashtable_params mlxsw_sp_nexthop_group_ht_params = {3606 .head_offset = offsetof(struct mlxsw_sp_nexthop_group, ht_node),3607 .hashfn = mlxsw_sp_nexthop_group_hash,3608 .obj_hashfn = mlxsw_sp_nexthop_group_hash_obj,3609 .obj_cmpfn = mlxsw_sp_nexthop_group_cmp,3610};3611 3612static int mlxsw_sp_nexthop_group_insert(struct mlxsw_sp *mlxsw_sp,3613 struct mlxsw_sp_nexthop_group *nh_grp)3614{3615 if (nh_grp->type == MLXSW_SP_NEXTHOP_GROUP_TYPE_IPV6 &&3616 !nh_grp->nhgi->gateway)3617 return 0;3618 3619 return rhashtable_insert_fast(&mlxsw_sp->router->nexthop_group_ht,3620 &nh_grp->ht_node,3621 mlxsw_sp_nexthop_group_ht_params);3622}3623 3624static void mlxsw_sp_nexthop_group_remove(struct mlxsw_sp *mlxsw_sp,3625 struct mlxsw_sp_nexthop_group *nh_grp)3626{3627 if (nh_grp->type == MLXSW_SP_NEXTHOP_GROUP_TYPE_IPV6 &&3628 !nh_grp->nhgi->gateway)3629 return;3630 3631 rhashtable_remove_fast(&mlxsw_sp->router->nexthop_group_ht,3632 &nh_grp->ht_node,3633 mlxsw_sp_nexthop_group_ht_params);3634}3635 3636static struct mlxsw_sp_nexthop_group *3637mlxsw_sp_nexthop4_group_lookup(struct mlxsw_sp *mlxsw_sp,3638 struct fib_info *fi)3639{3640 struct mlxsw_sp_nexthop_group_cmp_arg cmp_arg;3641 3642 cmp_arg.type = MLXSW_SP_NEXTHOP_GROUP_TYPE_IPV4;3643 cmp_arg.fi = fi;3644 return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_group_ht,3645 &cmp_arg,3646 mlxsw_sp_nexthop_group_ht_params);3647}3648 3649static struct mlxsw_sp_nexthop_group *3650mlxsw_sp_nexthop6_group_lookup(struct mlxsw_sp *mlxsw_sp,3651 struct mlxsw_sp_fib6_entry *fib6_entry)3652{3653 struct mlxsw_sp_nexthop_group_cmp_arg cmp_arg;3654 3655 cmp_arg.type = MLXSW_SP_NEXTHOP_GROUP_TYPE_IPV6;3656 cmp_arg.fib6_entry = fib6_entry;3657 return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_group_ht,3658 &cmp_arg,3659 mlxsw_sp_nexthop_group_ht_params);3660}3661 3662static const struct rhashtable_params mlxsw_sp_nexthop_ht_params = {3663 .key_offset = offsetof(struct mlxsw_sp_nexthop, key),3664 .head_offset = offsetof(struct mlxsw_sp_nexthop, ht_node),3665 .key_len = sizeof(struct mlxsw_sp_nexthop_key),3666};3667 3668static int mlxsw_sp_nexthop_insert(struct mlxsw_sp *mlxsw_sp,3669 struct mlxsw_sp_nexthop *nh)3670{3671 return rhashtable_insert_fast(&mlxsw_sp->router->nexthop_ht,3672 &nh->ht_node, mlxsw_sp_nexthop_ht_params);3673}3674 3675static void mlxsw_sp_nexthop_remove(struct mlxsw_sp *mlxsw_sp,3676 struct mlxsw_sp_nexthop *nh)3677{3678 rhashtable_remove_fast(&mlxsw_sp->router->nexthop_ht, &nh->ht_node,3679 mlxsw_sp_nexthop_ht_params);3680}3681 3682static struct mlxsw_sp_nexthop *3683mlxsw_sp_nexthop_lookup(struct mlxsw_sp *mlxsw_sp,3684 struct mlxsw_sp_nexthop_key key)3685{3686 return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_ht, &key,3687 mlxsw_sp_nexthop_ht_params);3688}3689 3690static int mlxsw_sp_adj_index_mass_update_vr(struct mlxsw_sp *mlxsw_sp,3691 enum mlxsw_sp_l3proto proto,3692 u16 vr_id,3693 u32 adj_index, u16 ecmp_size,3694 u32 new_adj_index,3695 u16 new_ecmp_size)3696{3697 char raleu_pl[MLXSW_REG_RALEU_LEN];3698 3699 mlxsw_reg_raleu_pack(raleu_pl,3700 (enum mlxsw_reg_ralxx_protocol) proto, vr_id,3701 adj_index, ecmp_size, new_adj_index,3702 new_ecmp_size);3703 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raleu), raleu_pl);3704}3705 3706static int mlxsw_sp_adj_index_mass_update(struct mlxsw_sp *mlxsw_sp,3707 struct mlxsw_sp_nexthop_group *nh_grp,3708 u32 old_adj_index, u16 old_ecmp_size)3709{3710 struct mlxsw_sp_nexthop_group_info *nhgi = nh_grp->nhgi;3711 struct mlxsw_sp_nexthop_group_vr_entry *vr_entry;3712 int err;3713 3714 list_for_each_entry(vr_entry, &nh_grp->vr_list, list) {3715 err = mlxsw_sp_adj_index_mass_update_vr(mlxsw_sp,3716 vr_entry->key.proto,3717 vr_entry->key.vr_id,3718 old_adj_index,3719 old_ecmp_size,3720 nhgi->adj_index,3721 nhgi->ecmp_size);3722 if (err)3723 goto err_mass_update_vr;3724 }3725 return 0;3726 3727err_mass_update_vr:3728 list_for_each_entry_continue_reverse(vr_entry, &nh_grp->vr_list, list)3729 mlxsw_sp_adj_index_mass_update_vr(mlxsw_sp, vr_entry->key.proto,3730 vr_entry->key.vr_id,3731 nhgi->adj_index,3732 nhgi->ecmp_size,3733 old_adj_index, old_ecmp_size);3734 return err;3735}3736 3737static int __mlxsw_sp_nexthop_eth_update(struct mlxsw_sp *mlxsw_sp,3738 u32 adj_index,3739 struct mlxsw_sp_nexthop *nh,3740 bool force, char *ratr_pl)3741{3742 struct mlxsw_sp_neigh_entry *neigh_entry = nh->neigh_entry;3743 struct mlxsw_sp_rif *rif = mlxsw_sp_nexthop_rif(nh);3744 enum mlxsw_reg_ratr_op op;3745 u16 rif_index;3746 3747 rif_index = rif ? rif->rif_index :3748 mlxsw_sp->router->lb_crif->rif->rif_index;3749 op = force ? MLXSW_REG_RATR_OP_WRITE_WRITE_ENTRY :3750 MLXSW_REG_RATR_OP_WRITE_WRITE_ENTRY_ON_ACTIVITY;3751 mlxsw_reg_ratr_pack(ratr_pl, op, true, MLXSW_REG_RATR_TYPE_ETHERNET,3752 adj_index, rif_index);3753 switch (nh->action) {3754 case MLXSW_SP_NEXTHOP_ACTION_FORWARD:3755 mlxsw_reg_ratr_eth_entry_pack(ratr_pl, neigh_entry->ha);3756 break;3757 case MLXSW_SP_NEXTHOP_ACTION_DISCARD:3758 mlxsw_reg_ratr_trap_action_set(ratr_pl,3759 MLXSW_REG_RATR_TRAP_ACTION_DISCARD_ERRORS);3760 break;3761 case MLXSW_SP_NEXTHOP_ACTION_TRAP:3762 mlxsw_reg_ratr_trap_action_set(ratr_pl,3763 MLXSW_REG_RATR_TRAP_ACTION_TRAP);3764 mlxsw_reg_ratr_trap_id_set(ratr_pl, MLXSW_TRAP_ID_RTR_EGRESS0);3765 break;3766 default:3767 WARN_ON_ONCE(1);3768 return -EINVAL;3769 }3770 if (nh->counter)3771 mlxsw_reg_ratr_counter_pack(ratr_pl, nh->counter->counter_index,3772 true);3773 else3774 mlxsw_reg_ratr_counter_pack(ratr_pl, 0, false);3775 3776 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ratr), ratr_pl);3777}3778 3779int mlxsw_sp_nexthop_eth_update(struct mlxsw_sp *mlxsw_sp, u32 adj_index,3780 struct mlxsw_sp_nexthop *nh, bool force,3781 char *ratr_pl)3782{3783 int i;3784 3785 for (i = 0; i < nh->num_adj_entries; i++) {3786 int err;3787 3788 err = __mlxsw_sp_nexthop_eth_update(mlxsw_sp, adj_index + i,3789 nh, force, ratr_pl);3790 if (err)3791 return err;3792 }3793 3794 return 0;3795}3796 3797static int __mlxsw_sp_nexthop_ipip_update(struct mlxsw_sp *mlxsw_sp,3798 u32 adj_index,3799 struct mlxsw_sp_nexthop *nh,3800 bool force, char *ratr_pl)3801{3802 const struct mlxsw_sp_ipip_ops *ipip_ops;3803 3804 ipip_ops = mlxsw_sp->router->ipip_ops_arr[nh->ipip_entry->ipipt];3805 return ipip_ops->nexthop_update(mlxsw_sp, adj_index, nh->ipip_entry,3806 force, ratr_pl);3807}3808 3809static int mlxsw_sp_nexthop_ipip_update(struct mlxsw_sp *mlxsw_sp,3810 u32 adj_index,3811 struct mlxsw_sp_nexthop *nh, bool force,3812 char *ratr_pl)3813{3814 int i;3815 3816 for (i = 0; i < nh->num_adj_entries; i++) {3817 int err;3818 3819 err = __mlxsw_sp_nexthop_ipip_update(mlxsw_sp, adj_index + i,3820 nh, force, ratr_pl);3821 if (err)3822 return err;3823 }3824 3825 return 0;3826}3827 3828static int mlxsw_sp_nexthop_update(struct mlxsw_sp *mlxsw_sp, u32 adj_index,3829 struct mlxsw_sp_nexthop *nh, bool force,3830 char *ratr_pl)3831{3832 /* When action is discard or trap, the nexthop must be3833 * programmed as an Ethernet nexthop.3834 */3835 if (nh->type == MLXSW_SP_NEXTHOP_TYPE_ETH ||3836 nh->action == MLXSW_SP_NEXTHOP_ACTION_DISCARD ||3837 nh->action == MLXSW_SP_NEXTHOP_ACTION_TRAP)3838 return mlxsw_sp_nexthop_eth_update(mlxsw_sp, adj_index, nh,3839 force, ratr_pl);3840 else3841 return mlxsw_sp_nexthop_ipip_update(mlxsw_sp, adj_index, nh,3842 force, ratr_pl);3843}3844 3845static int3846mlxsw_sp_nexthop_group_update(struct mlxsw_sp *mlxsw_sp,3847 struct mlxsw_sp_nexthop_group_info *nhgi,3848 bool reallocate)3849{3850 char ratr_pl[MLXSW_REG_RATR_LEN];3851 u32 adj_index = nhgi->adj_index; /* base */3852 struct mlxsw_sp_nexthop *nh;3853 int i;3854 3855 for (i = 0; i < nhgi->count; i++) {3856 nh = &nhgi->nexthops[i];3857 3858 if (!nh->should_offload) {3859 mlxsw_sp_nexthop_counter_disable(mlxsw_sp, nh);3860 nh->offloaded = 0;3861 continue;3862 }3863 3864 if (nh->update || reallocate) {3865 int err = 0;3866 3867 err = mlxsw_sp_nexthop_counter_update(mlxsw_sp, nh);3868 if (err)3869 return err;3870 3871 err = mlxsw_sp_nexthop_update(mlxsw_sp, adj_index, nh,3872 true, ratr_pl);3873 if (err)3874 return err;3875 nh->update = 0;3876 nh->offloaded = 1;3877 }3878 adj_index += nh->num_adj_entries;3879 }3880 return 0;3881}3882 3883static int3884mlxsw_sp_nexthop_fib_entries_update(struct mlxsw_sp *mlxsw_sp,3885 struct mlxsw_sp_nexthop_group *nh_grp)3886{3887 struct mlxsw_sp_fib_entry *fib_entry;3888 int err;3889 3890 list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {3891 err = mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);3892 if (err)3893 return err;3894 }3895 return 0;3896}3897 3898struct mlxsw_sp_adj_grp_size_range {3899 u16 start; /* Inclusive */3900 u16 end; /* Inclusive */3901};3902 3903/* Ordered by range start value */3904static const struct mlxsw_sp_adj_grp_size_range3905mlxsw_sp1_adj_grp_size_ranges[] = {3906 { .start = 1, .end = 64 },3907 { .start = 512, .end = 512 },3908 { .start = 1024, .end = 1024 },3909 { .start = 2048, .end = 2048 },3910 { .start = 4096, .end = 4096 },3911};3912 3913/* Ordered by range start value */3914static const struct mlxsw_sp_adj_grp_size_range3915mlxsw_sp2_adj_grp_size_ranges[] = {3916 { .start = 1, .end = 128 },3917 { .start = 256, .end = 256 },3918 { .start = 512, .end = 512 },3919 { .start = 1024, .end = 1024 },3920 { .start = 2048, .end = 2048 },3921 { .start = 4096, .end = 4096 },3922};3923 3924static void mlxsw_sp_adj_grp_size_round_up(const struct mlxsw_sp *mlxsw_sp,3925 u16 *p_adj_grp_size)3926{3927 int i;3928 3929 for (i = 0; i < mlxsw_sp->router->adj_grp_size_ranges_count; i++) {3930 const struct mlxsw_sp_adj_grp_size_range *size_range;3931 3932 size_range = &mlxsw_sp->router->adj_grp_size_ranges[i];3933 3934 if (*p_adj_grp_size >= size_range->start &&3935 *p_adj_grp_size <= size_range->end)3936 return;3937 3938 if (*p_adj_grp_size <= size_range->end) {3939 *p_adj_grp_size = size_range->end;3940 return;3941 }3942 }3943}3944 3945static void mlxsw_sp_adj_grp_size_round_down(const struct mlxsw_sp *mlxsw_sp,3946 u16 *p_adj_grp_size,3947 unsigned int alloc_size)3948{3949 int i;3950 3951 for (i = mlxsw_sp->router->adj_grp_size_ranges_count - 1; i >= 0; i--) {3952 const struct mlxsw_sp_adj_grp_size_range *size_range;3953 3954 size_range = &mlxsw_sp->router->adj_grp_size_ranges[i];3955 3956 if (alloc_size >= size_range->end) {3957 *p_adj_grp_size = size_range->end;3958 return;3959 }3960 }3961}3962 3963static int mlxsw_sp_fix_adj_grp_size(struct mlxsw_sp *mlxsw_sp,3964 u16 *p_adj_grp_size)3965{3966 unsigned int alloc_size;3967 int err;3968 3969 /* Round up the requested group size to the next size supported3970 * by the device and make sure the request can be satisfied.3971 */3972 mlxsw_sp_adj_grp_size_round_up(mlxsw_sp, p_adj_grp_size);3973 err = mlxsw_sp_kvdl_alloc_count_query(mlxsw_sp,3974 MLXSW_SP_KVDL_ENTRY_TYPE_ADJ,3975 *p_adj_grp_size, &alloc_size);3976 if (err)3977 return err;3978 /* It is possible the allocation results in more allocated3979 * entries than requested. Try to use as much of them as3980 * possible.3981 */3982 mlxsw_sp_adj_grp_size_round_down(mlxsw_sp, p_adj_grp_size, alloc_size);3983 3984 return 0;3985}3986 3987static void3988mlxsw_sp_nexthop_group_normalize(struct mlxsw_sp_nexthop_group_info *nhgi)3989{3990 int i, g = 0, sum_norm_weight = 0;3991 struct mlxsw_sp_nexthop *nh;3992 3993 for (i = 0; i < nhgi->count; i++) {3994 nh = &nhgi->nexthops[i];3995 3996 if (!nh->should_offload)3997 continue;3998 if (g > 0)3999 g = gcd(nh->nh_weight, g);4000 else4001 g = nh->nh_weight;4002 }4003 4004 for (i = 0; i < nhgi->count; i++) {4005 nh = &nhgi->nexthops[i];4006 4007 if (!nh->should_offload)4008 continue;4009 nh->norm_nh_weight = nh->nh_weight / g;4010 sum_norm_weight += nh->norm_nh_weight;4011 }4012 4013 nhgi->sum_norm_weight = sum_norm_weight;4014}4015 4016static void4017mlxsw_sp_nexthop_group_rebalance(struct mlxsw_sp_nexthop_group_info *nhgi)4018{4019 int i, weight = 0, lower_bound = 0;4020 int total = nhgi->sum_norm_weight;4021 u16 ecmp_size = nhgi->ecmp_size;4022 4023 for (i = 0; i < nhgi->count; i++) {4024 struct mlxsw_sp_nexthop *nh = &nhgi->nexthops[i];4025 int upper_bound;4026 4027 if (!nh->should_offload)4028 continue;4029 weight += nh->norm_nh_weight;4030 upper_bound = DIV_ROUND_CLOSEST(ecmp_size * weight, total);4031 nh->num_adj_entries = upper_bound - lower_bound;4032 lower_bound = upper_bound;4033 }4034}4035 4036static struct mlxsw_sp_nexthop *4037mlxsw_sp_rt6_nexthop(struct mlxsw_sp_nexthop_group *nh_grp,4038 const struct mlxsw_sp_rt6 *mlxsw_sp_rt6);4039 4040static void4041mlxsw_sp_nexthop4_group_offload_refresh(struct mlxsw_sp *mlxsw_sp,4042 struct mlxsw_sp_nexthop_group *nh_grp)4043{4044 int i;4045 4046 for (i = 0; i < nh_grp->nhgi->count; i++) {4047 struct mlxsw_sp_nexthop *nh = &nh_grp->nhgi->nexthops[i];4048 4049 if (nh->offloaded)4050 nh->key.fib_nh->fib_nh_flags |= RTNH_F_OFFLOAD;4051 else4052 nh->key.fib_nh->fib_nh_flags &= ~RTNH_F_OFFLOAD;4053 }4054}4055 4056static void4057__mlxsw_sp_nexthop6_group_offload_refresh(struct mlxsw_sp_nexthop_group *nh_grp,4058 struct mlxsw_sp_fib6_entry *fib6_entry)4059{4060 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;4061 4062 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {4063 struct fib6_nh *fib6_nh = mlxsw_sp_rt6->rt->fib6_nh;4064 struct mlxsw_sp_nexthop *nh;4065 4066 nh = mlxsw_sp_rt6_nexthop(nh_grp, mlxsw_sp_rt6);4067 if (nh && nh->offloaded)4068 fib6_nh->fib_nh_flags |= RTNH_F_OFFLOAD;4069 else4070 fib6_nh->fib_nh_flags &= ~RTNH_F_OFFLOAD;4071 }4072}4073 4074static void4075mlxsw_sp_nexthop6_group_offload_refresh(struct mlxsw_sp *mlxsw_sp,4076 struct mlxsw_sp_nexthop_group *nh_grp)4077{4078 struct mlxsw_sp_fib6_entry *fib6_entry;4079 4080 /* Unfortunately, in IPv6 the route and the nexthop are described by4081 * the same struct, so we need to iterate over all the routes using the4082 * nexthop group and set / clear the offload indication for them.4083 */4084 list_for_each_entry(fib6_entry, &nh_grp->fib_list,4085 common.nexthop_group_node)4086 __mlxsw_sp_nexthop6_group_offload_refresh(nh_grp, fib6_entry);4087}4088 4089static void4090mlxsw_sp_nexthop_bucket_offload_refresh(struct mlxsw_sp *mlxsw_sp,4091 const struct mlxsw_sp_nexthop *nh,4092 u16 bucket_index)4093{4094 struct mlxsw_sp_nexthop_group *nh_grp = nh->nhgi->nh_grp;4095 bool offload = false, trap = false;4096 4097 if (nh->offloaded) {4098 if (nh->action == MLXSW_SP_NEXTHOP_ACTION_TRAP)4099 trap = true;4100 else4101 offload = true;4102 }4103 nexthop_bucket_set_hw_flags(mlxsw_sp_net(mlxsw_sp), nh_grp->obj.id,4104 bucket_index, offload, trap);4105}4106 4107static void4108mlxsw_sp_nexthop_obj_group_offload_refresh(struct mlxsw_sp *mlxsw_sp,4109 struct mlxsw_sp_nexthop_group *nh_grp)4110{4111 int i;4112 4113 /* Do not update the flags if the nexthop group is being destroyed4114 * since:4115 * 1. The nexthop objects is being deleted, in which case the flags are4116 * irrelevant.4117 * 2. The nexthop group was replaced by a newer group, in which case4118 * the flags of the nexthop object were already updated based on the4119 * new group.4120 */4121 if (nh_grp->can_destroy)4122 return;4123 4124 nexthop_set_hw_flags(mlxsw_sp_net(mlxsw_sp), nh_grp->obj.id,4125 nh_grp->nhgi->adj_index_valid, false);4126 4127 /* Update flags of individual nexthop buckets in case of a resilient4128 * nexthop group.4129 */4130 if (!nh_grp->nhgi->is_resilient)4131 return;4132 4133 for (i = 0; i < nh_grp->nhgi->count; i++) {4134 struct mlxsw_sp_nexthop *nh = &nh_grp->nhgi->nexthops[i];4135 4136 mlxsw_sp_nexthop_bucket_offload_refresh(mlxsw_sp, nh, i);4137 }4138}4139 4140static void4141mlxsw_sp_nexthop_group_offload_refresh(struct mlxsw_sp *mlxsw_sp,4142 struct mlxsw_sp_nexthop_group *nh_grp)4143{4144 switch (nh_grp->type) {4145 case MLXSW_SP_NEXTHOP_GROUP_TYPE_IPV4:4146 mlxsw_sp_nexthop4_group_offload_refresh(mlxsw_sp, nh_grp);4147 break;4148 case MLXSW_SP_NEXTHOP_GROUP_TYPE_IPV6:4149 mlxsw_sp_nexthop6_group_offload_refresh(mlxsw_sp, nh_grp);4150 break;4151 case MLXSW_SP_NEXTHOP_GROUP_TYPE_OBJ:4152 mlxsw_sp_nexthop_obj_group_offload_refresh(mlxsw_sp, nh_grp);4153 break;4154 }4155}4156 4157static int4158mlxsw_sp_nexthop_group_refresh(struct mlxsw_sp *mlxsw_sp,4159 struct mlxsw_sp_nexthop_group *nh_grp)4160{4161 struct mlxsw_sp_nexthop_group_info *nhgi = nh_grp->nhgi;4162 u16 ecmp_size, old_ecmp_size;4163 struct mlxsw_sp_nexthop *nh;4164 bool offload_change = false;4165 u32 adj_index;4166 bool old_adj_index_valid;4167 u32 old_adj_index;4168 int i, err2, err;4169 4170 if (!nhgi->gateway)4171 return mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);4172 4173 for (i = 0; i < nhgi->count; i++) {4174 nh = &nhgi->nexthops[i];4175 4176 if (nh->should_offload != nh->offloaded) {4177 offload_change = true;4178 if (nh->should_offload)4179 nh->update = 1;4180 }4181 }4182 if (!offload_change) {4183 /* Nothing was added or removed, so no need to reallocate. Just4184 * update MAC on existing adjacency indexes.4185 */4186 err = mlxsw_sp_nexthop_group_update(mlxsw_sp, nhgi, false);4187 if (err) {4188 dev_warn(mlxsw_sp->bus_info->dev, "Failed to update neigh MAC in adjacency table.\n");4189 goto set_trap;4190 }4191 /* Flags of individual nexthop buckets might need to be4192 * updated.4193 */4194 mlxsw_sp_nexthop_group_offload_refresh(mlxsw_sp, nh_grp);4195 return 0;4196 }4197 mlxsw_sp_nexthop_group_normalize(nhgi);4198 if (!nhgi->sum_norm_weight) {4199 /* No neigh of this group is connected so we just set4200 * the trap and let everthing flow through kernel.4201 */4202 err = 0;4203 goto set_trap;4204 }4205 4206 ecmp_size = nhgi->sum_norm_weight;4207 err = mlxsw_sp_fix_adj_grp_size(mlxsw_sp, &ecmp_size);4208 if (err)4209 /* No valid allocation size available. */4210 goto set_trap;4211 4212 err = mlxsw_sp_kvdl_alloc(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ADJ,4213 ecmp_size, &adj_index);4214 if (err) {4215 /* We ran out of KVD linear space, just set the4216 * trap and let everything flow through kernel.4217 */4218 dev_warn(mlxsw_sp->bus_info->dev, "Failed to allocate KVD linear area for nexthop group.\n");4219 goto set_trap;4220 }4221 old_adj_index_valid = nhgi->adj_index_valid;4222 old_adj_index = nhgi->adj_index;4223 old_ecmp_size = nhgi->ecmp_size;4224 nhgi->adj_index_valid = 1;4225 nhgi->adj_index = adj_index;4226 nhgi->ecmp_size = ecmp_size;4227 mlxsw_sp_nexthop_group_rebalance(nhgi);4228 err = mlxsw_sp_nexthop_group_update(mlxsw_sp, nhgi, true);4229 if (err) {4230 dev_warn(mlxsw_sp->bus_info->dev, "Failed to update neigh MAC in adjacency table.\n");4231 goto set_trap;4232 }4233 4234 mlxsw_sp_nexthop_group_offload_refresh(mlxsw_sp, nh_grp);4235 4236 if (!old_adj_index_valid) {4237 /* The trap was set for fib entries, so we have to call4238 * fib entry update to unset it and use adjacency index.4239 */4240 err = mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);4241 if (err) {4242 dev_warn(mlxsw_sp->bus_info->dev, "Failed to add adjacency index to fib entries.\n");4243 goto set_trap;4244 }4245 return 0;4246 }4247 4248 err = mlxsw_sp_adj_index_mass_update(mlxsw_sp, nh_grp,4249 old_adj_index, old_ecmp_size);4250 mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ADJ,4251 old_ecmp_size, old_adj_index);4252 if (err) {4253 dev_warn(mlxsw_sp->bus_info->dev, "Failed to mass-update adjacency index for nexthop group.\n");4254 goto set_trap;4255 }4256 4257 return 0;4258 4259set_trap:4260 old_adj_index_valid = nhgi->adj_index_valid;4261 nhgi->adj_index_valid = 0;4262 for (i = 0; i < nhgi->count; i++) {4263 nh = &nhgi->nexthops[i];4264 nh->offloaded = 0;4265 }4266 err2 = mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);4267 if (err2)4268 dev_warn(mlxsw_sp->bus_info->dev, "Failed to set traps for fib entries.\n");4269 mlxsw_sp_nexthop_group_offload_refresh(mlxsw_sp, nh_grp);4270 if (old_adj_index_valid)4271 mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ADJ,4272 nhgi->ecmp_size, nhgi->adj_index);4273 return err;4274}4275 4276static void __mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp_nexthop *nh,4277 bool removing)4278{4279 if (!removing) {4280 nh->action = MLXSW_SP_NEXTHOP_ACTION_FORWARD;4281 nh->should_offload = 1;4282 } else if (nh->nhgi->is_resilient) {4283 nh->action = MLXSW_SP_NEXTHOP_ACTION_TRAP;4284 nh->should_offload = 1;4285 } else {4286 nh->should_offload = 0;4287 }4288 nh->update = 1;4289}4290 4291static int4292mlxsw_sp_nexthop_dead_neigh_replace(struct mlxsw_sp *mlxsw_sp,4293 struct mlxsw_sp_neigh_entry *neigh_entry)4294{4295 struct neighbour *n, *old_n = neigh_entry->key.n;4296 struct mlxsw_sp_nexthop *nh;4297 struct net_device *dev;4298 bool entry_connected;4299 u8 nud_state, dead;4300 int err;4301 4302 nh = list_first_entry(&neigh_entry->nexthop_list,4303 struct mlxsw_sp_nexthop, neigh_list_node);4304 dev = mlxsw_sp_nexthop_dev(nh);4305 4306 n = neigh_lookup(nh->neigh_tbl, &nh->gw_addr, dev);4307 if (!n) {4308 n = neigh_create(nh->neigh_tbl, &nh->gw_addr, dev);4309 if (IS_ERR(n))4310 return PTR_ERR(n);4311 neigh_event_send(n, NULL);4312 }4313 4314 mlxsw_sp_neigh_entry_remove(mlxsw_sp, neigh_entry);4315 neigh_entry->key.n = n;4316 err = mlxsw_sp_neigh_entry_insert(mlxsw_sp, neigh_entry);4317 if (err)4318 goto err_neigh_entry_insert;4319 4320 read_lock_bh(&n->lock);4321 nud_state = n->nud_state;4322 dead = n->dead;4323 read_unlock_bh(&n->lock);4324 entry_connected = nud_state & NUD_VALID && !dead;4325 4326 list_for_each_entry(nh, &neigh_entry->nexthop_list,4327 neigh_list_node) {4328 neigh_release(old_n);4329 neigh_clone(n);4330 __mlxsw_sp_nexthop_neigh_update(nh, !entry_connected);4331 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nhgi->nh_grp);4332 }4333 4334 neigh_release(n);4335 4336 return 0;4337 4338err_neigh_entry_insert:4339 neigh_entry->key.n = old_n;4340 mlxsw_sp_neigh_entry_insert(mlxsw_sp, neigh_entry);4341 neigh_release(n);4342 return err;4343}4344 4345static void4346mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp,4347 struct mlxsw_sp_neigh_entry *neigh_entry,4348 bool removing, bool dead)4349{4350 struct mlxsw_sp_nexthop *nh;4351 4352 if (list_empty(&neigh_entry->nexthop_list))4353 return;4354 4355 if (dead) {4356 int err;4357 4358 err = mlxsw_sp_nexthop_dead_neigh_replace(mlxsw_sp,4359 neigh_entry);4360 if (err)4361 dev_err(mlxsw_sp->bus_info->dev, "Failed to replace dead neigh\n");4362 return;4363 }4364 4365 list_for_each_entry(nh, &neigh_entry->nexthop_list,4366 neigh_list_node) {4367 __mlxsw_sp_nexthop_neigh_update(nh, removing);4368 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nhgi->nh_grp);4369 }4370}4371 4372static void mlxsw_sp_nexthop_crif_init(struct mlxsw_sp_nexthop *nh,4373 struct mlxsw_sp_crif *crif)4374{4375 if (nh->crif)4376 return;4377 4378 nh->crif = crif;4379 list_add(&nh->crif_list_node, &crif->nexthop_list);4380}4381 4382static void mlxsw_sp_nexthop_crif_fini(struct mlxsw_sp_nexthop *nh)4383{4384 if (!nh->crif)4385 return;4386 4387 list_del(&nh->crif_list_node);4388 nh->crif = NULL;4389}4390 4391static int mlxsw_sp_nexthop_neigh_init(struct mlxsw_sp *mlxsw_sp,4392 struct mlxsw_sp_nexthop *nh)4393{4394 struct mlxsw_sp_neigh_entry *neigh_entry;4395 struct net_device *dev;4396 struct neighbour *n;4397 u8 nud_state, dead;4398 int err;4399 4400 if (WARN_ON(!nh->crif->rif))4401 return 0;4402 4403 if (!nh->nhgi->gateway || nh->neigh_entry)4404 return 0;4405 dev = mlxsw_sp_nexthop_dev(nh);4406 4407 /* Take a reference of neigh here ensuring that neigh would4408 * not be destructed before the nexthop entry is finished.4409 * The reference is taken either in neigh_lookup() or4410 * in neigh_create() in case n is not found.4411 */4412 n = neigh_lookup(nh->neigh_tbl, &nh->gw_addr, dev);4413 if (!n) {4414 n = neigh_create(nh->neigh_tbl, &nh->gw_addr, dev);4415 if (IS_ERR(n))4416 return PTR_ERR(n);4417 neigh_event_send(n, NULL);4418 }4419 neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, n);4420 if (!neigh_entry) {4421 neigh_entry = mlxsw_sp_neigh_entry_create(mlxsw_sp, n);4422 if (IS_ERR(neigh_entry)) {4423 err = -EINVAL;4424 goto err_neigh_entry_create;4425 }4426 }4427 4428 /* If that is the first nexthop connected to that neigh, add to4429 * nexthop_neighs_list4430 */4431 if (list_empty(&neigh_entry->nexthop_list))4432 list_add_tail(&neigh_entry->nexthop_neighs_list_node,4433 &mlxsw_sp->router->nexthop_neighs_list);4434 4435 nh->neigh_entry = neigh_entry;4436 list_add_tail(&nh->neigh_list_node, &neigh_entry->nexthop_list);4437 read_lock_bh(&n->lock);4438 nud_state = n->nud_state;4439 dead = n->dead;4440 read_unlock_bh(&n->lock);4441 __mlxsw_sp_nexthop_neigh_update(nh, !(nud_state & NUD_VALID && !dead));4442 4443 return 0;4444 4445err_neigh_entry_create:4446 neigh_release(n);4447 return err;4448}4449 4450static void mlxsw_sp_nexthop_neigh_fini(struct mlxsw_sp *mlxsw_sp,4451 struct mlxsw_sp_nexthop *nh)4452{4453 struct mlxsw_sp_neigh_entry *neigh_entry = nh->neigh_entry;4454 struct neighbour *n;4455 4456 if (!neigh_entry)4457 return;4458 n = neigh_entry->key.n;4459 4460 __mlxsw_sp_nexthop_neigh_update(nh, true);4461 list_del(&nh->neigh_list_node);4462 nh->neigh_entry = NULL;4463 4464 /* If that is the last nexthop connected to that neigh, remove from4465 * nexthop_neighs_list4466 */4467 if (list_empty(&neigh_entry->nexthop_list))4468 list_del(&neigh_entry->nexthop_neighs_list_node);4469 4470 if (!neigh_entry->connected && list_empty(&neigh_entry->nexthop_list))4471 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);4472 4473 neigh_release(n);4474}4475 4476static bool mlxsw_sp_ipip_netdev_ul_up(struct net_device *ol_dev)4477{4478 struct net_device *ul_dev;4479 bool is_up;4480 4481 rcu_read_lock();4482 ul_dev = mlxsw_sp_ipip_netdev_ul_dev_get(ol_dev);4483 is_up = ul_dev ? (ul_dev->flags & IFF_UP) : true;4484 rcu_read_unlock();4485 4486 return is_up;4487}4488 4489static void mlxsw_sp_nexthop_ipip_init(struct mlxsw_sp *mlxsw_sp,4490 struct mlxsw_sp_nexthop *nh,4491 struct mlxsw_sp_ipip_entry *ipip_entry)4492{4493 struct mlxsw_sp_crif *crif;4494 bool removing;4495 4496 if (!nh->nhgi->gateway || nh->ipip_entry)4497 return;4498 4499 crif = mlxsw_sp_crif_lookup(mlxsw_sp->router, ipip_entry->ol_dev);4500 if (WARN_ON(!crif))4501 return;4502 4503 nh->ipip_entry = ipip_entry;4504 removing = !mlxsw_sp_ipip_netdev_ul_up(ipip_entry->ol_dev);4505 __mlxsw_sp_nexthop_neigh_update(nh, removing);4506 mlxsw_sp_nexthop_crif_init(nh, crif);4507}4508 4509static void mlxsw_sp_nexthop_ipip_fini(struct mlxsw_sp *mlxsw_sp,4510 struct mlxsw_sp_nexthop *nh)4511{4512 struct mlxsw_sp_ipip_entry *ipip_entry = nh->ipip_entry;4513 4514 if (!ipip_entry)4515 return;4516 4517 __mlxsw_sp_nexthop_neigh_update(nh, true);4518 nh->ipip_entry = NULL;4519}4520 4521static bool mlxsw_sp_nexthop4_ipip_type(const struct mlxsw_sp *mlxsw_sp,4522 const struct fib_nh *fib_nh,4523 enum mlxsw_sp_ipip_type *p_ipipt)4524{4525 struct net_device *dev = fib_nh->fib_nh_dev;4526 4527 return dev &&4528 fib_nh->nh_parent->fib_type == RTN_UNICAST &&4529 mlxsw_sp_netdev_ipip_type(mlxsw_sp, dev, p_ipipt);4530}4531 4532static int mlxsw_sp_nexthop_type_init(struct mlxsw_sp *mlxsw_sp,4533 struct mlxsw_sp_nexthop *nh,4534 const struct net_device *dev)4535{4536 const struct mlxsw_sp_ipip_ops *ipip_ops;4537 struct mlxsw_sp_ipip_entry *ipip_entry;4538 struct mlxsw_sp_crif *crif;4539 int err;4540 4541 ipip_entry = mlxsw_sp_ipip_entry_find_by_ol_dev(mlxsw_sp, dev);4542 if (ipip_entry) {4543 ipip_ops = mlxsw_sp->router->ipip_ops_arr[ipip_entry->ipipt];4544 if (ipip_ops->can_offload(mlxsw_sp, dev)) {4545 nh->type = MLXSW_SP_NEXTHOP_TYPE_IPIP;4546 mlxsw_sp_nexthop_ipip_init(mlxsw_sp, nh, ipip_entry);4547 return 0;4548 }4549 }4550 4551 nh->type = MLXSW_SP_NEXTHOP_TYPE_ETH;4552 crif = mlxsw_sp_crif_lookup(mlxsw_sp->router, dev);4553 if (!crif)4554 return 0;4555 4556 mlxsw_sp_nexthop_crif_init(nh, crif);4557 4558 if (!crif->rif)4559 return 0;4560 4561 err = mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh);4562 if (err)4563 goto err_neigh_init;4564 4565 return 0;4566 4567err_neigh_init:4568 mlxsw_sp_nexthop_crif_fini(nh);4569 return err;4570}4571 4572static int mlxsw_sp_nexthop_type_rif_made(struct mlxsw_sp *mlxsw_sp,4573 struct mlxsw_sp_nexthop *nh)4574{4575 switch (nh->type) {4576 case MLXSW_SP_NEXTHOP_TYPE_ETH:4577 return mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh);4578 case MLXSW_SP_NEXTHOP_TYPE_IPIP:4579 break;4580 }4581 4582 return 0;4583}4584 4585static void mlxsw_sp_nexthop_type_rif_gone(struct mlxsw_sp *mlxsw_sp,4586 struct mlxsw_sp_nexthop *nh)4587{4588 switch (nh->type) {4589 case MLXSW_SP_NEXTHOP_TYPE_ETH:4590 mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);4591 break;4592 case MLXSW_SP_NEXTHOP_TYPE_IPIP:4593 mlxsw_sp_nexthop_ipip_fini(mlxsw_sp, nh);4594 break;4595 }4596}4597 4598static void mlxsw_sp_nexthop_type_fini(struct mlxsw_sp *mlxsw_sp,4599 struct mlxsw_sp_nexthop *nh)4600{4601 mlxsw_sp_nexthop_type_rif_gone(mlxsw_sp, nh);4602 mlxsw_sp_nexthop_crif_fini(nh);4603}4604 4605static int mlxsw_sp_nexthop4_init(struct mlxsw_sp *mlxsw_sp,4606 struct mlxsw_sp_nexthop_group *nh_grp,4607 struct mlxsw_sp_nexthop *nh,4608 struct fib_nh *fib_nh)4609{4610 struct net_device *dev = fib_nh->fib_nh_dev;4611 struct in_device *in_dev;4612 int err;4613 4614 nh->nhgi = nh_grp->nhgi;4615 nh->key.fib_nh = fib_nh;4616#ifdef CONFIG_IP_ROUTE_MULTIPATH4617 nh->nh_weight = fib_nh->fib_nh_weight;4618#else4619 nh->nh_weight = 1;4620#endif4621 memcpy(&nh->gw_addr, &fib_nh->fib_nh_gw4, sizeof(fib_nh->fib_nh_gw4));4622 nh->neigh_tbl = &arp_tbl;4623 err = mlxsw_sp_nexthop_insert(mlxsw_sp, nh);4624 if (err)4625 return err;4626 4627 err = mlxsw_sp_nexthop_counter_enable(mlxsw_sp, nh);4628 if (err)4629 goto err_counter_enable;4630 4631 list_add_tail(&nh->router_list_node, &mlxsw_sp->router->nexthop_list);4632 4633 if (!dev)4634 return 0;4635 nh->ifindex = dev->ifindex;4636 4637 rcu_read_lock();4638 in_dev = __in_dev_get_rcu(dev);4639 if (in_dev && IN_DEV_IGNORE_ROUTES_WITH_LINKDOWN(in_dev) &&4640 fib_nh->fib_nh_flags & RTNH_F_LINKDOWN) {4641 rcu_read_unlock();4642 return 0;4643 }4644 rcu_read_unlock();4645 4646 err = mlxsw_sp_nexthop_type_init(mlxsw_sp, nh, dev);4647 if (err)4648 goto err_nexthop_neigh_init;4649 4650 return 0;4651 4652err_nexthop_neigh_init:4653 list_del(&nh->router_list_node);4654 mlxsw_sp_nexthop_counter_disable(mlxsw_sp, nh);4655err_counter_enable:4656 mlxsw_sp_nexthop_remove(mlxsw_sp, nh);4657 return err;4658}4659 4660static void mlxsw_sp_nexthop4_fini(struct mlxsw_sp *mlxsw_sp,4661 struct mlxsw_sp_nexthop *nh)4662{4663 mlxsw_sp_nexthop_type_fini(mlxsw_sp, nh);4664 list_del(&nh->router_list_node);4665 mlxsw_sp_nexthop_counter_disable(mlxsw_sp, nh);4666 mlxsw_sp_nexthop_remove(mlxsw_sp, nh);4667}4668 4669static void mlxsw_sp_nexthop4_event(struct mlxsw_sp *mlxsw_sp,4670 unsigned long event, struct fib_nh *fib_nh)4671{4672 struct mlxsw_sp_nexthop_key key;4673 struct mlxsw_sp_nexthop *nh;4674 4675 key.fib_nh = fib_nh;4676 nh = mlxsw_sp_nexthop_lookup(mlxsw_sp, key);4677 if (!nh)4678 return;4679 4680 switch (event) {4681 case FIB_EVENT_NH_ADD:4682 mlxsw_sp_nexthop_type_init(mlxsw_sp, nh, fib_nh->fib_nh_dev);4683 break;4684 case FIB_EVENT_NH_DEL:4685 mlxsw_sp_nexthop_type_fini(mlxsw_sp, nh);4686 break;4687 }4688 4689 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nhgi->nh_grp);4690}4691 4692static void mlxsw_sp_nexthop_rif_update(struct mlxsw_sp *mlxsw_sp,4693 struct mlxsw_sp_rif *rif)4694{4695 struct net_device *dev = mlxsw_sp_rif_dev(rif);4696 struct mlxsw_sp_nexthop *nh;4697 bool removing;4698 4699 list_for_each_entry(nh, &rif->crif->nexthop_list, crif_list_node) {4700 switch (nh->type) {4701 case MLXSW_SP_NEXTHOP_TYPE_ETH:4702 removing = false;4703 break;4704 case MLXSW_SP_NEXTHOP_TYPE_IPIP:4705 removing = !mlxsw_sp_ipip_netdev_ul_up(dev);4706 break;4707 default:4708 WARN_ON(1);4709 continue;4710 }4711 4712 __mlxsw_sp_nexthop_neigh_update(nh, removing);4713 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nhgi->nh_grp);4714 }4715}4716 4717static int mlxsw_sp_nexthop_rif_made_sync(struct mlxsw_sp *mlxsw_sp,4718 struct mlxsw_sp_rif *rif)4719{4720 struct mlxsw_sp_nexthop *nh, *tmp;4721 unsigned int n = 0;4722 int err;4723 4724 list_for_each_entry_safe(nh, tmp, &rif->crif->nexthop_list,4725 crif_list_node) {4726 err = mlxsw_sp_nexthop_type_rif_made(mlxsw_sp, nh);4727 if (err)4728 goto err_nexthop_type_rif;4729 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nhgi->nh_grp);4730 n++;4731 }4732 4733 return 0;4734 4735err_nexthop_type_rif:4736 list_for_each_entry_safe(nh, tmp, &rif->crif->nexthop_list,4737 crif_list_node) {4738 if (!n--)4739 break;4740 mlxsw_sp_nexthop_type_rif_gone(mlxsw_sp, nh);4741 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nhgi->nh_grp);4742 }4743 return err;4744}4745 4746static void mlxsw_sp_nexthop_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,4747 struct mlxsw_sp_rif *rif)4748{4749 struct mlxsw_sp_nexthop *nh, *tmp;4750 4751 list_for_each_entry_safe(nh, tmp, &rif->crif->nexthop_list,4752 crif_list_node) {4753 mlxsw_sp_nexthop_type_rif_gone(mlxsw_sp, nh);4754 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nhgi->nh_grp);4755 }4756}4757 4758static int mlxsw_sp_adj_trap_entry_init(struct mlxsw_sp *mlxsw_sp)4759{4760 enum mlxsw_reg_ratr_trap_action trap_action;4761 char ratr_pl[MLXSW_REG_RATR_LEN];4762 int err;4763 4764 err = mlxsw_sp_kvdl_alloc(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ADJ, 1,4765 &mlxsw_sp->router->adj_trap_index);4766 if (err)4767 return err;4768 4769 trap_action = MLXSW_REG_RATR_TRAP_ACTION_TRAP;4770 mlxsw_reg_ratr_pack(ratr_pl, MLXSW_REG_RATR_OP_WRITE_WRITE_ENTRY, true,4771 MLXSW_REG_RATR_TYPE_ETHERNET,4772 mlxsw_sp->router->adj_trap_index,4773 mlxsw_sp->router->lb_crif->rif->rif_index);4774 mlxsw_reg_ratr_trap_action_set(ratr_pl, trap_action);4775 mlxsw_reg_ratr_trap_id_set(ratr_pl, MLXSW_TRAP_ID_RTR_EGRESS0);4776 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ratr), ratr_pl);4777 if (err)4778 goto err_ratr_write;4779 4780 return 0;4781 4782err_ratr_write:4783 mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ADJ, 1,4784 mlxsw_sp->router->adj_trap_index);4785 return err;4786}4787 4788static void mlxsw_sp_adj_trap_entry_fini(struct mlxsw_sp *mlxsw_sp)4789{4790 mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ADJ, 1,4791 mlxsw_sp->router->adj_trap_index);4792}4793 4794static int mlxsw_sp_nexthop_group_inc(struct mlxsw_sp *mlxsw_sp)4795{4796 int err;4797 4798 if (refcount_inc_not_zero(&mlxsw_sp->router->num_groups))4799 return 0;4800 4801 err = mlxsw_sp_adj_trap_entry_init(mlxsw_sp);4802 if (err)4803 return err;4804 4805 refcount_set(&mlxsw_sp->router->num_groups, 1);4806 4807 return 0;4808}4809 4810static void mlxsw_sp_nexthop_group_dec(struct mlxsw_sp *mlxsw_sp)4811{4812 if (!refcount_dec_and_test(&mlxsw_sp->router->num_groups))4813 return;4814 4815 mlxsw_sp_adj_trap_entry_fini(mlxsw_sp);4816}4817 4818static void4819mlxsw_sp_nh_grp_activity_get(struct mlxsw_sp *mlxsw_sp,4820 const struct mlxsw_sp_nexthop_group *nh_grp,4821 unsigned long *activity)4822{4823 char *ratrad_pl;4824 int i, err;4825 4826 ratrad_pl = kmalloc(MLXSW_REG_RATRAD_LEN, GFP_KERNEL);4827 if (!ratrad_pl)4828 return;4829 4830 mlxsw_reg_ratrad_pack(ratrad_pl, nh_grp->nhgi->adj_index,4831 nh_grp->nhgi->count);4832 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ratrad), ratrad_pl);4833 if (err)4834 goto out;4835 4836 for (i = 0; i < nh_grp->nhgi->count; i++) {4837 if (!mlxsw_reg_ratrad_activity_vector_get(ratrad_pl, i))4838 continue;4839 bitmap_set(activity, i, 1);4840 }4841 4842out:4843 kfree(ratrad_pl);4844}4845 4846#define MLXSW_SP_NH_GRP_ACTIVITY_UPDATE_INTERVAL 1000 /* ms */4847 4848static void4849mlxsw_sp_nh_grp_activity_update(struct mlxsw_sp *mlxsw_sp,4850 const struct mlxsw_sp_nexthop_group *nh_grp)4851{4852 unsigned long *activity;4853 4854 activity = bitmap_zalloc(nh_grp->nhgi->count, GFP_KERNEL);4855 if (!activity)4856 return;4857 4858 mlxsw_sp_nh_grp_activity_get(mlxsw_sp, nh_grp, activity);4859 nexthop_res_grp_activity_update(mlxsw_sp_net(mlxsw_sp), nh_grp->obj.id,4860 nh_grp->nhgi->count, activity);4861 4862 bitmap_free(activity);4863}4864 4865static void4866mlxsw_sp_nh_grp_activity_work_schedule(struct mlxsw_sp *mlxsw_sp)4867{4868 unsigned int interval = MLXSW_SP_NH_GRP_ACTIVITY_UPDATE_INTERVAL;4869 4870 mlxsw_core_schedule_dw(&mlxsw_sp->router->nh_grp_activity_dw,4871 msecs_to_jiffies(interval));4872}4873 4874static void mlxsw_sp_nh_grp_activity_work(struct work_struct *work)4875{4876 struct mlxsw_sp_nexthop_group_info *nhgi;4877 struct mlxsw_sp_router *router;4878 bool reschedule = false;4879 4880 router = container_of(work, struct mlxsw_sp_router,4881 nh_grp_activity_dw.work);4882 4883 mutex_lock(&router->lock);4884 4885 list_for_each_entry(nhgi, &router->nh_res_grp_list, list) {4886 mlxsw_sp_nh_grp_activity_update(router->mlxsw_sp, nhgi->nh_grp);4887 reschedule = true;4888 }4889 4890 mutex_unlock(&router->lock);4891 4892 if (!reschedule)4893 return;4894 mlxsw_sp_nh_grp_activity_work_schedule(router->mlxsw_sp);4895}4896 4897static int4898mlxsw_sp_nexthop_obj_single_validate(struct mlxsw_sp *mlxsw_sp,4899 const struct nh_notifier_single_info *nh,4900 struct netlink_ext_ack *extack)4901{4902 int err = -EINVAL;4903 4904 if (nh->is_fdb)4905 NL_SET_ERR_MSG_MOD(extack, "FDB nexthops are not supported");4906 else if (nh->has_encap)4907 NL_SET_ERR_MSG_MOD(extack, "Encapsulating nexthops are not supported");4908 else4909 err = 0;4910 4911 return err;4912}4913 4914static int4915mlxsw_sp_nexthop_obj_group_entry_validate(struct mlxsw_sp *mlxsw_sp,4916 const struct nh_notifier_single_info *nh,4917 struct netlink_ext_ack *extack)4918{4919 int err;4920 4921 err = mlxsw_sp_nexthop_obj_single_validate(mlxsw_sp, nh, extack);4922 if (err)4923 return err;4924 4925 /* Device only nexthops with an IPIP device are programmed as4926 * encapsulating adjacency entries.4927 */4928 if (!nh->gw_family && !nh->is_reject &&4929 !mlxsw_sp_netdev_ipip_type(mlxsw_sp, nh->dev, NULL)) {4930 NL_SET_ERR_MSG_MOD(extack, "Nexthop group entry does not have a gateway");4931 return -EINVAL;4932 }4933 4934 return 0;4935}4936 4937static int4938mlxsw_sp_nexthop_obj_group_validate(struct mlxsw_sp *mlxsw_sp,4939 const struct nh_notifier_grp_info *nh_grp,4940 struct netlink_ext_ack *extack)4941{4942 int i;4943 4944 if (nh_grp->is_fdb) {4945 NL_SET_ERR_MSG_MOD(extack, "FDB nexthop groups are not supported");4946 return -EINVAL;4947 }4948 4949 for (i = 0; i < nh_grp->num_nh; i++) {4950 const struct nh_notifier_single_info *nh;4951 int err;4952 4953 nh = &nh_grp->nh_entries[i].nh;4954 err = mlxsw_sp_nexthop_obj_group_entry_validate(mlxsw_sp, nh,4955 extack);4956 if (err)4957 return err;4958 }4959 4960 return 0;4961}4962 4963static int4964mlxsw_sp_nexthop_obj_res_group_size_validate(struct mlxsw_sp *mlxsw_sp,4965 const struct nh_notifier_res_table_info *nh_res_table,4966 struct netlink_ext_ack *extack)4967{4968 unsigned int alloc_size;4969 bool valid_size = false;4970 int err, i;4971 4972 if (nh_res_table->num_nh_buckets < 32) {4973 NL_SET_ERR_MSG_MOD(extack, "Minimum number of buckets is 32");4974 return -EINVAL;4975 }4976 4977 for (i = 0; i < mlxsw_sp->router->adj_grp_size_ranges_count; i++) {4978 const struct mlxsw_sp_adj_grp_size_range *size_range;4979 4980 size_range = &mlxsw_sp->router->adj_grp_size_ranges[i];4981 4982 if (nh_res_table->num_nh_buckets >= size_range->start &&4983 nh_res_table->num_nh_buckets <= size_range->end) {4984 valid_size = true;4985 break;4986 }4987 }4988 4989 if (!valid_size) {4990 NL_SET_ERR_MSG_MOD(extack, "Invalid number of buckets");4991 return -EINVAL;4992 }4993 4994 err = mlxsw_sp_kvdl_alloc_count_query(mlxsw_sp,4995 MLXSW_SP_KVDL_ENTRY_TYPE_ADJ,4996 nh_res_table->num_nh_buckets,4997 &alloc_size);4998 if (err || nh_res_table->num_nh_buckets != alloc_size) {4999 NL_SET_ERR_MSG_MOD(extack, "Number of buckets does not fit allocation size of any KVDL partition");5000 return -EINVAL;5001 }5002 5003 return 0;5004}5005 5006static int5007mlxsw_sp_nexthop_obj_res_group_validate(struct mlxsw_sp *mlxsw_sp,5008 const struct nh_notifier_res_table_info *nh_res_table,5009 struct netlink_ext_ack *extack)5010{5011 int err;5012 u16 i;5013 5014 err = mlxsw_sp_nexthop_obj_res_group_size_validate(mlxsw_sp,5015 nh_res_table,5016 extack);5017 if (err)5018 return err;5019 5020 for (i = 0; i < nh_res_table->num_nh_buckets; i++) {5021 const struct nh_notifier_single_info *nh;5022 int err;5023 5024 nh = &nh_res_table->nhs[i];5025 err = mlxsw_sp_nexthop_obj_group_entry_validate(mlxsw_sp, nh,5026 extack);5027 if (err)5028 return err;5029 }5030 5031 return 0;5032}5033 5034static int mlxsw_sp_nexthop_obj_validate(struct mlxsw_sp *mlxsw_sp,5035 unsigned long event,5036 struct nh_notifier_info *info)5037{5038 struct nh_notifier_single_info *nh;5039 5040 if (event != NEXTHOP_EVENT_REPLACE &&5041 event != NEXTHOP_EVENT_RES_TABLE_PRE_REPLACE &&5042 event != NEXTHOP_EVENT_BUCKET_REPLACE)5043 return 0;5044 5045 switch (info->type) {5046 case NH_NOTIFIER_INFO_TYPE_SINGLE:5047 return mlxsw_sp_nexthop_obj_single_validate(mlxsw_sp, info->nh,5048 info->extack);5049 case NH_NOTIFIER_INFO_TYPE_GRP:5050 return mlxsw_sp_nexthop_obj_group_validate(mlxsw_sp,5051 info->nh_grp,5052 info->extack);5053 case NH_NOTIFIER_INFO_TYPE_RES_TABLE:5054 return mlxsw_sp_nexthop_obj_res_group_validate(mlxsw_sp,5055 info->nh_res_table,5056 info->extack);5057 case NH_NOTIFIER_INFO_TYPE_RES_BUCKET:5058 nh = &info->nh_res_bucket->new_nh;5059 return mlxsw_sp_nexthop_obj_group_entry_validate(mlxsw_sp, nh,5060 info->extack);5061 default:5062 NL_SET_ERR_MSG_MOD(info->extack, "Unsupported nexthop type");5063 return -EOPNOTSUPP;5064 }5065}5066 5067static bool mlxsw_sp_nexthop_obj_is_gateway(struct mlxsw_sp *mlxsw_sp,5068 const struct nh_notifier_info *info)5069{5070 const struct net_device *dev;5071 5072 switch (info->type) {5073 case NH_NOTIFIER_INFO_TYPE_SINGLE:5074 dev = info->nh->dev;5075 return info->nh->gw_family || info->nh->is_reject ||5076 mlxsw_sp_netdev_ipip_type(mlxsw_sp, dev, NULL);5077 case NH_NOTIFIER_INFO_TYPE_GRP:5078 case NH_NOTIFIER_INFO_TYPE_RES_TABLE:5079 /* Already validated earlier. */5080 return true;5081 default:5082 return false;5083 }5084}5085 5086static void mlxsw_sp_nexthop_obj_blackhole_init(struct mlxsw_sp *mlxsw_sp,5087 struct mlxsw_sp_nexthop *nh)5088{5089 nh->action = MLXSW_SP_NEXTHOP_ACTION_DISCARD;5090 nh->should_offload = 1;5091 /* While nexthops that discard packets do not forward packets5092 * via an egress RIF, they still need to be programmed using a5093 * valid RIF, so use the loopback RIF created during init.5094 */5095 nh->crif = mlxsw_sp->router->lb_crif;5096}5097 5098static void mlxsw_sp_nexthop_obj_blackhole_fini(struct mlxsw_sp *mlxsw_sp,5099 struct mlxsw_sp_nexthop *nh)5100{5101 nh->crif = NULL;5102 nh->should_offload = 0;5103}5104 5105static int5106mlxsw_sp_nexthop_obj_init(struct mlxsw_sp *mlxsw_sp,5107 struct mlxsw_sp_nexthop_group *nh_grp,5108 struct mlxsw_sp_nexthop *nh,5109 struct nh_notifier_single_info *nh_obj, int weight)5110{5111 struct net_device *dev = nh_obj->dev;5112 int err;5113 5114 nh->nhgi = nh_grp->nhgi;5115 nh->nh_weight = weight;5116 5117 switch (nh_obj->gw_family) {5118 case AF_INET:5119 memcpy(&nh->gw_addr, &nh_obj->ipv4, sizeof(nh_obj->ipv4));5120 nh->neigh_tbl = &arp_tbl;5121 break;5122 case AF_INET6:5123 memcpy(&nh->gw_addr, &nh_obj->ipv6, sizeof(nh_obj->ipv6));5124#if IS_ENABLED(CONFIG_IPV6)5125 nh->neigh_tbl = &nd_tbl;5126#endif5127 break;5128 }5129 5130 list_add_tail(&nh->router_list_node, &mlxsw_sp->router->nexthop_list);5131 nh->ifindex = dev->ifindex;5132 nh->id = nh_obj->id;5133 5134 err = mlxsw_sp_nexthop_type_init(mlxsw_sp, nh, dev);5135 if (err)5136 goto err_type_init;5137 5138 if (nh_obj->is_reject)5139 mlxsw_sp_nexthop_obj_blackhole_init(mlxsw_sp, nh);5140 5141 /* In a resilient nexthop group, all the nexthops must be written to5142 * the adjacency table. Even if they do not have a valid neighbour or5143 * RIF.5144 */5145 if (nh_grp->nhgi->is_resilient && !nh->should_offload) {5146 nh->action = MLXSW_SP_NEXTHOP_ACTION_TRAP;5147 nh->should_offload = 1;5148 }5149 5150 return 0;5151 5152err_type_init:5153 list_del(&nh->router_list_node);5154 return err;5155}5156 5157static void mlxsw_sp_nexthop_obj_fini(struct mlxsw_sp *mlxsw_sp,5158 struct mlxsw_sp_nexthop *nh)5159{5160 if (nh->action == MLXSW_SP_NEXTHOP_ACTION_DISCARD)5161 mlxsw_sp_nexthop_obj_blackhole_fini(mlxsw_sp, nh);5162 mlxsw_sp_nexthop_type_fini(mlxsw_sp, nh);5163 list_del(&nh->router_list_node);5164 mlxsw_sp_nexthop_counter_disable(mlxsw_sp, nh);5165 nh->should_offload = 0;5166}5167 5168static int5169mlxsw_sp_nexthop_obj_group_info_init(struct mlxsw_sp *mlxsw_sp,5170 struct mlxsw_sp_nexthop_group *nh_grp,5171 struct nh_notifier_info *info)5172{5173 struct mlxsw_sp_nexthop_group_info *nhgi;5174 struct mlxsw_sp_nexthop *nh;5175 bool is_resilient = false;5176 bool hw_stats = false;5177 unsigned int nhs;5178 int err, i;5179 5180 switch (info->type) {5181 case NH_NOTIFIER_INFO_TYPE_SINGLE:5182 nhs = 1;5183 break;5184 case NH_NOTIFIER_INFO_TYPE_GRP:5185 nhs = info->nh_grp->num_nh;5186 hw_stats = info->nh_grp->hw_stats;5187 break;5188 case NH_NOTIFIER_INFO_TYPE_RES_TABLE:5189 nhs = info->nh_res_table->num_nh_buckets;5190 hw_stats = info->nh_res_table->hw_stats;5191 is_resilient = true;5192 break;5193 default:5194 return -EINVAL;5195 }5196 5197 nhgi = kzalloc(struct_size(nhgi, nexthops, nhs), GFP_KERNEL);5198 if (!nhgi)5199 return -ENOMEM;5200 nh_grp->nhgi = nhgi;5201 nhgi->nh_grp = nh_grp;5202 nhgi->gateway = mlxsw_sp_nexthop_obj_is_gateway(mlxsw_sp, info);5203 nhgi->is_resilient = is_resilient;5204 nhgi->count = nhs;5205 nhgi->hw_stats = hw_stats;5206 5207 xa_init_flags(&nhgi->nexthop_counters, XA_FLAGS_ALLOC1);5208 5209 for (i = 0; i < nhgi->count; i++) {5210 struct nh_notifier_single_info *nh_obj;5211 int weight;5212 5213 nh = &nhgi->nexthops[i];5214 switch (info->type) {5215 case NH_NOTIFIER_INFO_TYPE_SINGLE:5216 nh_obj = info->nh;5217 weight = 1;5218 break;5219 case NH_NOTIFIER_INFO_TYPE_GRP:5220 nh_obj = &info->nh_grp->nh_entries[i].nh;5221 weight = info->nh_grp->nh_entries[i].weight;5222 break;5223 case NH_NOTIFIER_INFO_TYPE_RES_TABLE:5224 nh_obj = &info->nh_res_table->nhs[i];5225 weight = 1;5226 break;5227 default:5228 err = -EINVAL;5229 goto err_nexthop_obj_init;5230 }5231 err = mlxsw_sp_nexthop_obj_init(mlxsw_sp, nh_grp, nh, nh_obj,5232 weight);5233 if (err)5234 goto err_nexthop_obj_init;5235 }5236 err = mlxsw_sp_nexthop_group_inc(mlxsw_sp);5237 if (err)5238 goto err_group_inc;5239 err = mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);5240 if (err) {5241 NL_SET_ERR_MSG_MOD(info->extack, "Failed to write adjacency entries to the device");5242 goto err_group_refresh;5243 }5244 5245 /* Add resilient nexthop groups to a list so that the activity of their5246 * nexthop buckets will be periodically queried and cleared.5247 */5248 if (nhgi->is_resilient) {5249 if (list_empty(&mlxsw_sp->router->nh_res_grp_list))5250 mlxsw_sp_nh_grp_activity_work_schedule(mlxsw_sp);5251 list_add(&nhgi->list, &mlxsw_sp->router->nh_res_grp_list);5252 }5253 5254 return 0;5255 5256err_group_refresh:5257 mlxsw_sp_nexthop_group_dec(mlxsw_sp);5258err_group_inc:5259 i = nhgi->count;5260err_nexthop_obj_init:5261 for (i--; i >= 0; i--) {5262 nh = &nhgi->nexthops[i];5263 mlxsw_sp_nexthop_obj_fini(mlxsw_sp, nh);5264 }5265 kfree(nhgi);5266 return err;5267}5268 5269static void5270mlxsw_sp_nexthop_obj_group_info_fini(struct mlxsw_sp *mlxsw_sp,5271 struct mlxsw_sp_nexthop_group *nh_grp)5272{5273 struct mlxsw_sp_nexthop_group_info *nhgi = nh_grp->nhgi;5274 struct mlxsw_sp_router *router = mlxsw_sp->router;5275 int i;5276 5277 if (nhgi->is_resilient) {5278 list_del(&nhgi->list);5279 if (list_empty(&mlxsw_sp->router->nh_res_grp_list))5280 cancel_delayed_work(&router->nh_grp_activity_dw);5281 }5282 5283 mlxsw_sp_nexthop_group_dec(mlxsw_sp);5284 for (i = nhgi->count - 1; i >= 0; i--) {5285 struct mlxsw_sp_nexthop *nh = &nhgi->nexthops[i];5286 5287 mlxsw_sp_nexthop_obj_fini(mlxsw_sp, nh);5288 }5289 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);5290 WARN_ON_ONCE(nhgi->adj_index_valid);5291 WARN_ON(!xa_empty(&nhgi->nexthop_counters));5292 xa_destroy(&nhgi->nexthop_counters);5293 kfree(nhgi);5294}5295 5296static struct mlxsw_sp_nexthop_group *5297mlxsw_sp_nexthop_obj_group_create(struct mlxsw_sp *mlxsw_sp,5298 struct nh_notifier_info *info)5299{5300 struct mlxsw_sp_nexthop_group *nh_grp;5301 int err;5302 5303 nh_grp = kzalloc(sizeof(*nh_grp), GFP_KERNEL);5304 if (!nh_grp)5305 return ERR_PTR(-ENOMEM);5306 INIT_LIST_HEAD(&nh_grp->vr_list);5307 err = rhashtable_init(&nh_grp->vr_ht,5308 &mlxsw_sp_nexthop_group_vr_ht_params);5309 if (err)5310 goto err_nexthop_group_vr_ht_init;5311 INIT_LIST_HEAD(&nh_grp->fib_list);5312 nh_grp->type = MLXSW_SP_NEXTHOP_GROUP_TYPE_OBJ;5313 nh_grp->obj.id = info->id;5314 5315 err = mlxsw_sp_nexthop_obj_group_info_init(mlxsw_sp, nh_grp, info);5316 if (err)5317 goto err_nexthop_group_info_init;5318 5319 nh_grp->can_destroy = false;5320 5321 return nh_grp;5322 5323err_nexthop_group_info_init:5324 rhashtable_destroy(&nh_grp->vr_ht);5325err_nexthop_group_vr_ht_init:5326 kfree(nh_grp);5327 return ERR_PTR(err);5328}5329 5330static void5331mlxsw_sp_nexthop_obj_group_destroy(struct mlxsw_sp *mlxsw_sp,5332 struct mlxsw_sp_nexthop_group *nh_grp)5333{5334 if (!nh_grp->can_destroy)5335 return;5336 mlxsw_sp_nexthop_obj_group_info_fini(mlxsw_sp, nh_grp);5337 WARN_ON_ONCE(!list_empty(&nh_grp->fib_list));5338 WARN_ON_ONCE(!list_empty(&nh_grp->vr_list));5339 rhashtable_destroy(&nh_grp->vr_ht);5340 kfree(nh_grp);5341}5342 5343static struct mlxsw_sp_nexthop_group *5344mlxsw_sp_nexthop_obj_group_lookup(struct mlxsw_sp *mlxsw_sp, u32 id)5345{5346 struct mlxsw_sp_nexthop_group_cmp_arg cmp_arg;5347 5348 cmp_arg.type = MLXSW_SP_NEXTHOP_GROUP_TYPE_OBJ;5349 cmp_arg.id = id;5350 return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_group_ht,5351 &cmp_arg,5352 mlxsw_sp_nexthop_group_ht_params);5353}5354 5355static int mlxsw_sp_nexthop_obj_group_add(struct mlxsw_sp *mlxsw_sp,5356 struct mlxsw_sp_nexthop_group *nh_grp)5357{5358 return mlxsw_sp_nexthop_group_insert(mlxsw_sp, nh_grp);5359}5360 5361static int5362mlxsw_sp_nexthop_obj_group_replace(struct mlxsw_sp *mlxsw_sp,5363 struct mlxsw_sp_nexthop_group *nh_grp,5364 struct mlxsw_sp_nexthop_group *old_nh_grp,5365 struct netlink_ext_ack *extack)5366{5367 struct mlxsw_sp_nexthop_group_info *old_nhgi = old_nh_grp->nhgi;5368 struct mlxsw_sp_nexthop_group_info *new_nhgi = nh_grp->nhgi;5369 int err;5370 5371 old_nh_grp->nhgi = new_nhgi;5372 new_nhgi->nh_grp = old_nh_grp;5373 nh_grp->nhgi = old_nhgi;5374 old_nhgi->nh_grp = nh_grp;5375 5376 if (old_nhgi->adj_index_valid && new_nhgi->adj_index_valid) {5377 /* Both the old adjacency index and the new one are valid.5378 * Routes are currently using the old one. Tell the device to5379 * replace the old adjacency index with the new one.5380 */5381 err = mlxsw_sp_adj_index_mass_update(mlxsw_sp, old_nh_grp,5382 old_nhgi->adj_index,5383 old_nhgi->ecmp_size);5384 if (err) {5385 NL_SET_ERR_MSG_MOD(extack, "Failed to replace old adjacency index with new one");5386 goto err_out;5387 }5388 } else if (old_nhgi->adj_index_valid && !new_nhgi->adj_index_valid) {5389 /* The old adjacency index is valid, while the new one is not.5390 * Iterate over all the routes using the group and change them5391 * to trap packets to the CPU.5392 */5393 err = mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, old_nh_grp);5394 if (err) {5395 NL_SET_ERR_MSG_MOD(extack, "Failed to update routes to trap packets");5396 goto err_out;5397 }5398 } else if (!old_nhgi->adj_index_valid && new_nhgi->adj_index_valid) {5399 /* The old adjacency index is invalid, while the new one is.5400 * Iterate over all the routes using the group and change them5401 * to forward packets using the new valid index.5402 */5403 err = mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, old_nh_grp);5404 if (err) {5405 NL_SET_ERR_MSG_MOD(extack, "Failed to update routes to forward packets");5406 goto err_out;5407 }5408 }5409 5410 /* Make sure the flags are set / cleared based on the new nexthop group5411 * information.5412 */5413 mlxsw_sp_nexthop_obj_group_offload_refresh(mlxsw_sp, old_nh_grp);5414 5415 /* At this point 'nh_grp' is just a shell that is not used by anyone5416 * and its nexthop group info is the old info that was just replaced5417 * with the new one. Remove it.5418 */5419 nh_grp->can_destroy = true;5420 mlxsw_sp_nexthop_obj_group_destroy(mlxsw_sp, nh_grp);5421 5422 return 0;5423 5424err_out:5425 old_nhgi->nh_grp = old_nh_grp;5426 nh_grp->nhgi = new_nhgi;5427 new_nhgi->nh_grp = nh_grp;5428 old_nh_grp->nhgi = old_nhgi;5429 return err;5430}5431 5432static int mlxsw_sp_nexthop_obj_res_group_pre(struct mlxsw_sp *mlxsw_sp,5433 struct nh_notifier_info *info)5434{5435 struct nh_notifier_grp_info *grp_info = info->nh_grp;5436 struct mlxsw_sp_nexthop_group_info *nhgi;5437 struct mlxsw_sp_nexthop_group *nh_grp;5438 int err;5439 int i;5440 5441 nh_grp = mlxsw_sp_nexthop_obj_group_lookup(mlxsw_sp, info->id);5442 if (!nh_grp)5443 return 0;5444 nhgi = nh_grp->nhgi;5445 5446 if (nhgi->hw_stats == grp_info->hw_stats)5447 return 0;5448 5449 nhgi->hw_stats = grp_info->hw_stats;5450 5451 for (i = 0; i < nhgi->count; i++) {5452 struct mlxsw_sp_nexthop *nh = &nhgi->nexthops[i];5453 5454 if (nh->offloaded)5455 nh->update = 1;5456 }5457 5458 err = mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);5459 if (err)5460 goto err_group_refresh;5461 5462 return 0;5463 5464err_group_refresh:5465 nhgi->hw_stats = !grp_info->hw_stats;5466 return err;5467}5468 5469static int mlxsw_sp_nexthop_obj_new(struct mlxsw_sp *mlxsw_sp,5470 struct nh_notifier_info *info)5471{5472 struct mlxsw_sp_nexthop_group *nh_grp, *old_nh_grp;5473 struct netlink_ext_ack *extack = info->extack;5474 int err;5475 5476 nh_grp = mlxsw_sp_nexthop_obj_group_create(mlxsw_sp, info);5477 if (IS_ERR(nh_grp))5478 return PTR_ERR(nh_grp);5479 5480 old_nh_grp = mlxsw_sp_nexthop_obj_group_lookup(mlxsw_sp, info->id);5481 if (!old_nh_grp)5482 err = mlxsw_sp_nexthop_obj_group_add(mlxsw_sp, nh_grp);5483 else5484 err = mlxsw_sp_nexthop_obj_group_replace(mlxsw_sp, nh_grp,5485 old_nh_grp, extack);5486 5487 if (err) {5488 nh_grp->can_destroy = true;5489 mlxsw_sp_nexthop_obj_group_destroy(mlxsw_sp, nh_grp);5490 }5491 5492 return err;5493}5494 5495static void mlxsw_sp_nexthop_obj_del(struct mlxsw_sp *mlxsw_sp,5496 struct nh_notifier_info *info)5497{5498 struct mlxsw_sp_nexthop_group *nh_grp;5499 5500 nh_grp = mlxsw_sp_nexthop_obj_group_lookup(mlxsw_sp, info->id);5501 if (!nh_grp)5502 return;5503 5504 nh_grp->can_destroy = true;5505 mlxsw_sp_nexthop_group_remove(mlxsw_sp, nh_grp);5506 5507 /* If the group still has routes using it, then defer the delete5508 * operation until the last route using it is deleted.5509 */5510 if (!list_empty(&nh_grp->fib_list))5511 return;5512 mlxsw_sp_nexthop_obj_group_destroy(mlxsw_sp, nh_grp);5513}5514 5515static int mlxsw_sp_nexthop_obj_bucket_query(struct mlxsw_sp *mlxsw_sp,5516 u32 adj_index, char *ratr_pl)5517{5518 MLXSW_REG_ZERO(ratr, ratr_pl);5519 mlxsw_reg_ratr_op_set(ratr_pl, MLXSW_REG_RATR_OP_QUERY_READ);5520 mlxsw_reg_ratr_adjacency_index_low_set(ratr_pl, adj_index);5521 mlxsw_reg_ratr_adjacency_index_high_set(ratr_pl, adj_index >> 16);5522 5523 return mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ratr), ratr_pl);5524}5525 5526static int mlxsw_sp_nexthop_obj_bucket_compare(char *ratr_pl, char *ratr_pl_new)5527{5528 /* Clear the opcode and activity on both the old and new payload as5529 * they are irrelevant for the comparison.5530 */5531 mlxsw_reg_ratr_op_set(ratr_pl, MLXSW_REG_RATR_OP_QUERY_READ);5532 mlxsw_reg_ratr_a_set(ratr_pl, 0);5533 mlxsw_reg_ratr_op_set(ratr_pl_new, MLXSW_REG_RATR_OP_QUERY_READ);5534 mlxsw_reg_ratr_a_set(ratr_pl_new, 0);5535 5536 /* If the contents of the adjacency entry are consistent with the5537 * replacement request, then replacement was successful.5538 */5539 if (!memcmp(ratr_pl, ratr_pl_new, MLXSW_REG_RATR_LEN))5540 return 0;5541 5542 return -EINVAL;5543}5544 5545static int5546mlxsw_sp_nexthop_obj_bucket_adj_update(struct mlxsw_sp *mlxsw_sp,5547 struct mlxsw_sp_nexthop *nh,5548 struct nh_notifier_info *info)5549{5550 u16 bucket_index = info->nh_res_bucket->bucket_index;5551 struct netlink_ext_ack *extack = info->extack;5552 bool force = info->nh_res_bucket->force;5553 char ratr_pl_new[MLXSW_REG_RATR_LEN];5554 char ratr_pl[MLXSW_REG_RATR_LEN];5555 u32 adj_index;5556 int err;5557 5558 /* No point in trying an atomic replacement if the idle timer interval5559 * is smaller than the interval in which we query and clear activity.5560 */5561 if (!force && info->nh_res_bucket->idle_timer_ms <5562 MLXSW_SP_NH_GRP_ACTIVITY_UPDATE_INTERVAL)5563 force = true;5564 5565 adj_index = nh->nhgi->adj_index + bucket_index;5566 err = mlxsw_sp_nexthop_update(mlxsw_sp, adj_index, nh, force, ratr_pl);5567 if (err) {5568 NL_SET_ERR_MSG_MOD(extack, "Failed to overwrite nexthop bucket");5569 return err;5570 }5571 5572 if (!force) {5573 err = mlxsw_sp_nexthop_obj_bucket_query(mlxsw_sp, adj_index,5574 ratr_pl_new);5575 if (err) {5576 NL_SET_ERR_MSG_MOD(extack, "Failed to query nexthop bucket state after replacement. State might be inconsistent");5577 return err;5578 }5579 5580 err = mlxsw_sp_nexthop_obj_bucket_compare(ratr_pl, ratr_pl_new);5581 if (err) {5582 NL_SET_ERR_MSG_MOD(extack, "Nexthop bucket was not replaced because it was active during replacement");5583 return err;5584 }5585 }5586 5587 nh->update = 0;5588 nh->offloaded = 1;5589 mlxsw_sp_nexthop_bucket_offload_refresh(mlxsw_sp, nh, bucket_index);5590 5591 return 0;5592}5593 5594static int mlxsw_sp_nexthop_obj_bucket_replace(struct mlxsw_sp *mlxsw_sp,5595 struct nh_notifier_info *info)5596{5597 u16 bucket_index = info->nh_res_bucket->bucket_index;5598 struct netlink_ext_ack *extack = info->extack;5599 struct mlxsw_sp_nexthop_group_info *nhgi;5600 struct nh_notifier_single_info *nh_obj;5601 struct mlxsw_sp_nexthop_group *nh_grp;5602 struct mlxsw_sp_nexthop *nh;5603 int err;5604 5605 nh_grp = mlxsw_sp_nexthop_obj_group_lookup(mlxsw_sp, info->id);5606 if (!nh_grp) {5607 NL_SET_ERR_MSG_MOD(extack, "Nexthop group was not found");5608 return -EINVAL;5609 }5610 5611 nhgi = nh_grp->nhgi;5612 5613 if (bucket_index >= nhgi->count) {5614 NL_SET_ERR_MSG_MOD(extack, "Nexthop bucket index out of range");5615 return -EINVAL;5616 }5617 5618 nh = &nhgi->nexthops[bucket_index];5619 mlxsw_sp_nexthop_obj_fini(mlxsw_sp, nh);5620 5621 nh_obj = &info->nh_res_bucket->new_nh;5622 err = mlxsw_sp_nexthop_obj_init(mlxsw_sp, nh_grp, nh, nh_obj, 1);5623 if (err) {5624 NL_SET_ERR_MSG_MOD(extack, "Failed to initialize nexthop object for nexthop bucket replacement");5625 goto err_nexthop_obj_init;5626 }5627 5628 err = mlxsw_sp_nexthop_obj_bucket_adj_update(mlxsw_sp, nh, info);5629 if (err)5630 goto err_nexthop_obj_bucket_adj_update;5631 5632 return 0;5633 5634err_nexthop_obj_bucket_adj_update:5635 mlxsw_sp_nexthop_obj_fini(mlxsw_sp, nh);5636err_nexthop_obj_init:5637 nh_obj = &info->nh_res_bucket->old_nh;5638 mlxsw_sp_nexthop_obj_init(mlxsw_sp, nh_grp, nh, nh_obj, 1);5639 /* The old adjacency entry was not overwritten */5640 nh->update = 0;5641 nh->offloaded = 1;5642 return err;5643}5644 5645static void5646mlxsw_sp_nexthop_obj_mp_hw_stats_get(struct mlxsw_sp *mlxsw_sp,5647 struct mlxsw_sp_nexthop_group_info *nhgi,5648 struct nh_notifier_grp_hw_stats_info *info)5649{5650 int nhi;5651 5652 for (nhi = 0; nhi < info->num_nh; nhi++) {5653 struct mlxsw_sp_nexthop *nh = &nhgi->nexthops[nhi];5654 u64 packets;5655 int err;5656 5657 err = mlxsw_sp_nexthop_counter_get(mlxsw_sp, nh, &packets);5658 if (err)5659 continue;5660 5661 nh_grp_hw_stats_report_delta(info, nhi, packets);5662 }5663}5664 5665static void5666mlxsw_sp_nexthop_obj_res_hw_stats_get(struct mlxsw_sp *mlxsw_sp,5667 struct mlxsw_sp_nexthop_group_info *nhgi,5668 struct nh_notifier_grp_hw_stats_info *info)5669{5670 int nhi = -1;5671 int bucket;5672 5673 for (bucket = 0; bucket < nhgi->count; bucket++) {5674 struct mlxsw_sp_nexthop *nh = &nhgi->nexthops[bucket];5675 u64 packets;5676 int err;5677 5678 if (nhi == -1 || info->stats[nhi].id != nh->id) {5679 for (nhi = 0; nhi < info->num_nh; nhi++)5680 if (info->stats[nhi].id == nh->id)5681 break;5682 if (WARN_ON_ONCE(nhi == info->num_nh)) {5683 nhi = -1;5684 continue;5685 }5686 }5687 5688 err = mlxsw_sp_nexthop_counter_get(mlxsw_sp, nh, &packets);5689 if (err)5690 continue;5691 5692 nh_grp_hw_stats_report_delta(info, nhi, packets);5693 }5694}5695 5696static void mlxsw_sp_nexthop_obj_hw_stats_get(struct mlxsw_sp *mlxsw_sp,5697 struct nh_notifier_info *info)5698{5699 struct mlxsw_sp_nexthop_group_info *nhgi;5700 struct mlxsw_sp_nexthop_group *nh_grp;5701 5702 if (info->type != NH_NOTIFIER_INFO_TYPE_GRP_HW_STATS)5703 return;5704 5705 nh_grp = mlxsw_sp_nexthop_obj_group_lookup(mlxsw_sp, info->id);5706 if (!nh_grp)5707 return;5708 nhgi = nh_grp->nhgi;5709 5710 if (nhgi->is_resilient)5711 mlxsw_sp_nexthop_obj_res_hw_stats_get(mlxsw_sp, nhgi,5712 info->nh_grp_hw_stats);5713 else5714 mlxsw_sp_nexthop_obj_mp_hw_stats_get(mlxsw_sp, nhgi,5715 info->nh_grp_hw_stats);5716}5717 5718static int mlxsw_sp_nexthop_obj_event(struct notifier_block *nb,5719 unsigned long event, void *ptr)5720{5721 struct nh_notifier_info *info = ptr;5722 struct mlxsw_sp_router *router;5723 int err = 0;5724 5725 router = container_of(nb, struct mlxsw_sp_router, nexthop_nb);5726 err = mlxsw_sp_nexthop_obj_validate(router->mlxsw_sp, event, info);5727 if (err)5728 goto out;5729 5730 mutex_lock(&router->lock);5731 5732 switch (event) {5733 case NEXTHOP_EVENT_RES_TABLE_PRE_REPLACE:5734 err = mlxsw_sp_nexthop_obj_res_group_pre(router->mlxsw_sp,5735 info);5736 break;5737 case NEXTHOP_EVENT_REPLACE:5738 err = mlxsw_sp_nexthop_obj_new(router->mlxsw_sp, info);5739 break;5740 case NEXTHOP_EVENT_DEL:5741 mlxsw_sp_nexthop_obj_del(router->mlxsw_sp, info);5742 break;5743 case NEXTHOP_EVENT_BUCKET_REPLACE:5744 err = mlxsw_sp_nexthop_obj_bucket_replace(router->mlxsw_sp,5745 info);5746 break;5747 case NEXTHOP_EVENT_HW_STATS_REPORT_DELTA:5748 mlxsw_sp_nexthop_obj_hw_stats_get(router->mlxsw_sp, info);5749 break;5750 default:5751 break;5752 }5753 5754 mutex_unlock(&router->lock);5755 5756out:5757 return notifier_from_errno(err);5758}5759 5760static bool mlxsw_sp_fi_is_gateway(const struct mlxsw_sp *mlxsw_sp,5761 struct fib_info *fi)5762{5763 const struct fib_nh *nh = fib_info_nh(fi, 0);5764 5765 return nh->fib_nh_gw_family ||5766 mlxsw_sp_nexthop4_ipip_type(mlxsw_sp, nh, NULL);5767}5768 5769static int5770mlxsw_sp_nexthop4_group_info_init(struct mlxsw_sp *mlxsw_sp,5771 struct mlxsw_sp_nexthop_group *nh_grp)5772{5773 unsigned int nhs = fib_info_num_path(nh_grp->ipv4.fi);5774 struct mlxsw_sp_nexthop_group_info *nhgi;5775 struct mlxsw_sp_nexthop *nh;5776 int err, i;5777 5778 nhgi = kzalloc(struct_size(nhgi, nexthops, nhs), GFP_KERNEL);5779 if (!nhgi)5780 return -ENOMEM;5781 nh_grp->nhgi = nhgi;5782 nhgi->nh_grp = nh_grp;5783 nhgi->gateway = mlxsw_sp_fi_is_gateway(mlxsw_sp, nh_grp->ipv4.fi);5784 nhgi->count = nhs;5785 for (i = 0; i < nhgi->count; i++) {5786 struct fib_nh *fib_nh;5787 5788 nh = &nhgi->nexthops[i];5789 fib_nh = fib_info_nh(nh_grp->ipv4.fi, i);5790 err = mlxsw_sp_nexthop4_init(mlxsw_sp, nh_grp, nh, fib_nh);5791 if (err)5792 goto err_nexthop4_init;5793 }5794 err = mlxsw_sp_nexthop_group_inc(mlxsw_sp);5795 if (err)5796 goto err_group_inc;5797 err = mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);5798 if (err)5799 goto err_group_refresh;5800 5801 return 0;5802 5803err_group_refresh:5804 mlxsw_sp_nexthop_group_dec(mlxsw_sp);5805err_group_inc:5806 i = nhgi->count;5807err_nexthop4_init:5808 for (i--; i >= 0; i--) {5809 nh = &nhgi->nexthops[i];5810 mlxsw_sp_nexthop4_fini(mlxsw_sp, nh);5811 }5812 kfree(nhgi);5813 return err;5814}5815 5816static void5817mlxsw_sp_nexthop4_group_info_fini(struct mlxsw_sp *mlxsw_sp,5818 struct mlxsw_sp_nexthop_group *nh_grp)5819{5820 struct mlxsw_sp_nexthop_group_info *nhgi = nh_grp->nhgi;5821 int i;5822 5823 mlxsw_sp_nexthop_group_dec(mlxsw_sp);5824 for (i = nhgi->count - 1; i >= 0; i--) {5825 struct mlxsw_sp_nexthop *nh = &nhgi->nexthops[i];5826 5827 mlxsw_sp_nexthop4_fini(mlxsw_sp, nh);5828 }5829 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);5830 WARN_ON_ONCE(nhgi->adj_index_valid);5831 kfree(nhgi);5832}5833 5834static struct mlxsw_sp_nexthop_group *5835mlxsw_sp_nexthop4_group_create(struct mlxsw_sp *mlxsw_sp, struct fib_info *fi)5836{5837 struct mlxsw_sp_nexthop_group *nh_grp;5838 int err;5839 5840 nh_grp = kzalloc(sizeof(*nh_grp), GFP_KERNEL);5841 if (!nh_grp)5842 return ERR_PTR(-ENOMEM);5843 INIT_LIST_HEAD(&nh_grp->vr_list);5844 err = rhashtable_init(&nh_grp->vr_ht,5845 &mlxsw_sp_nexthop_group_vr_ht_params);5846 if (err)5847 goto err_nexthop_group_vr_ht_init;5848 INIT_LIST_HEAD(&nh_grp->fib_list);5849 nh_grp->type = MLXSW_SP_NEXTHOP_GROUP_TYPE_IPV4;5850 nh_grp->ipv4.fi = fi;5851 fib_info_hold(fi);5852 5853 err = mlxsw_sp_nexthop4_group_info_init(mlxsw_sp, nh_grp);5854 if (err)5855 goto err_nexthop_group_info_init;5856 5857 err = mlxsw_sp_nexthop_group_insert(mlxsw_sp, nh_grp);5858 if (err)5859 goto err_nexthop_group_insert;5860 5861 nh_grp->can_destroy = true;5862 5863 return nh_grp;5864 5865err_nexthop_group_insert:5866 mlxsw_sp_nexthop4_group_info_fini(mlxsw_sp, nh_grp);5867err_nexthop_group_info_init:5868 fib_info_put(fi);5869 rhashtable_destroy(&nh_grp->vr_ht);5870err_nexthop_group_vr_ht_init:5871 kfree(nh_grp);5872 return ERR_PTR(err);5873}5874 5875static void5876mlxsw_sp_nexthop4_group_destroy(struct mlxsw_sp *mlxsw_sp,5877 struct mlxsw_sp_nexthop_group *nh_grp)5878{5879 if (!nh_grp->can_destroy)5880 return;5881 mlxsw_sp_nexthop_group_remove(mlxsw_sp, nh_grp);5882 mlxsw_sp_nexthop4_group_info_fini(mlxsw_sp, nh_grp);5883 fib_info_put(nh_grp->ipv4.fi);5884 WARN_ON_ONCE(!list_empty(&nh_grp->vr_list));5885 rhashtable_destroy(&nh_grp->vr_ht);5886 kfree(nh_grp);5887}5888 5889static int mlxsw_sp_nexthop4_group_get(struct mlxsw_sp *mlxsw_sp,5890 struct mlxsw_sp_fib_entry *fib_entry,5891 struct fib_info *fi)5892{5893 struct mlxsw_sp_nexthop_group *nh_grp;5894 5895 if (fi->nh) {5896 nh_grp = mlxsw_sp_nexthop_obj_group_lookup(mlxsw_sp,5897 fi->nh->id);5898 if (WARN_ON_ONCE(!nh_grp))5899 return -EINVAL;5900 goto out;5901 }5902 5903 nh_grp = mlxsw_sp_nexthop4_group_lookup(mlxsw_sp, fi);5904 if (!nh_grp) {5905 nh_grp = mlxsw_sp_nexthop4_group_create(mlxsw_sp, fi);5906 if (IS_ERR(nh_grp))5907 return PTR_ERR(nh_grp);5908 }5909out:5910 list_add_tail(&fib_entry->nexthop_group_node, &nh_grp->fib_list);5911 fib_entry->nh_group = nh_grp;5912 return 0;5913}5914 5915static void mlxsw_sp_nexthop4_group_put(struct mlxsw_sp *mlxsw_sp,5916 struct mlxsw_sp_fib_entry *fib_entry)5917{5918 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;5919 5920 list_del(&fib_entry->nexthop_group_node);5921 if (!list_empty(&nh_grp->fib_list))5922 return;5923 5924 if (nh_grp->type == MLXSW_SP_NEXTHOP_GROUP_TYPE_OBJ) {5925 mlxsw_sp_nexthop_obj_group_destroy(mlxsw_sp, nh_grp);5926 return;5927 }5928 5929 mlxsw_sp_nexthop4_group_destroy(mlxsw_sp, nh_grp);5930}5931 5932static bool5933mlxsw_sp_fib4_entry_should_offload(const struct mlxsw_sp_fib_entry *fib_entry)5934{5935 struct mlxsw_sp_fib4_entry *fib4_entry;5936 5937 fib4_entry = container_of(fib_entry, struct mlxsw_sp_fib4_entry,5938 common);5939 return !fib4_entry->dscp;5940}5941 5942static bool5943mlxsw_sp_fib_entry_should_offload(const struct mlxsw_sp_fib_entry *fib_entry)5944{5945 struct mlxsw_sp_nexthop_group *nh_group = fib_entry->nh_group;5946 5947 switch (fib_entry->fib_node->fib->proto) {5948 case MLXSW_SP_L3_PROTO_IPV4:5949 if (!mlxsw_sp_fib4_entry_should_offload(fib_entry))5950 return false;5951 break;5952 case MLXSW_SP_L3_PROTO_IPV6:5953 break;5954 }5955 5956 switch (fib_entry->type) {5957 case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE:5958 return !!nh_group->nhgi->adj_index_valid;5959 case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL:5960 return !!mlxsw_sp_nhgi_rif(nh_group->nhgi);5961 case MLXSW_SP_FIB_ENTRY_TYPE_BLACKHOLE:5962 case MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP:5963 case MLXSW_SP_FIB_ENTRY_TYPE_NVE_DECAP:5964 return true;5965 default:5966 return false;5967 }5968}5969 5970static struct mlxsw_sp_nexthop *5971mlxsw_sp_rt6_nexthop(struct mlxsw_sp_nexthop_group *nh_grp,5972 const struct mlxsw_sp_rt6 *mlxsw_sp_rt6)5973{5974 int i;5975 5976 for (i = 0; i < nh_grp->nhgi->count; i++) {5977 struct mlxsw_sp_nexthop *nh = &nh_grp->nhgi->nexthops[i];5978 struct net_device *dev = mlxsw_sp_nexthop_dev(nh);5979 struct fib6_info *rt = mlxsw_sp_rt6->rt;5980 5981 if (dev && dev == rt->fib6_nh->fib_nh_dev &&5982 ipv6_addr_equal((const struct in6_addr *) &nh->gw_addr,5983 &rt->fib6_nh->fib_nh_gw6))5984 return nh;5985 }5986 5987 return NULL;5988}5989 5990static void5991mlxsw_sp_fib4_offload_failed_flag_set(struct mlxsw_sp *mlxsw_sp,5992 struct fib_entry_notifier_info *fen_info)5993{5994 u32 *p_dst = (u32 *) &fen_info->dst;5995 struct fib_rt_info fri;5996 5997 fri.fi = fen_info->fi;5998 fri.tb_id = fen_info->tb_id;5999 fri.dst = cpu_to_be32(*p_dst);6000 fri.dst_len = fen_info->dst_len;6001 fri.dscp = fen_info->dscp;6002 fri.type = fen_info->type;6003 fri.offload = false;6004 fri.trap = false;6005 fri.offload_failed = true;6006 fib_alias_hw_flags_set(mlxsw_sp_net(mlxsw_sp), &fri);6007}6008 6009static void6010mlxsw_sp_fib4_entry_hw_flags_set(struct mlxsw_sp *mlxsw_sp,6011 struct mlxsw_sp_fib_entry *fib_entry)6012{6013 u32 *p_dst = (u32 *) fib_entry->fib_node->key.addr;6014 int dst_len = fib_entry->fib_node->key.prefix_len;6015 struct mlxsw_sp_fib4_entry *fib4_entry;6016 struct fib_rt_info fri;6017 bool should_offload;6018 6019 should_offload = mlxsw_sp_fib_entry_should_offload(fib_entry);6020 fib4_entry = container_of(fib_entry, struct mlxsw_sp_fib4_entry,6021 common);6022 fri.fi = fib4_entry->fi;6023 fri.tb_id = fib4_entry->tb_id;6024 fri.dst = cpu_to_be32(*p_dst);6025 fri.dst_len = dst_len;6026 fri.dscp = fib4_entry->dscp;6027 fri.type = fib4_entry->type;6028 fri.offload = should_offload;6029 fri.trap = !should_offload;6030 fri.offload_failed = false;6031 fib_alias_hw_flags_set(mlxsw_sp_net(mlxsw_sp), &fri);6032}6033 6034static void6035mlxsw_sp_fib4_entry_hw_flags_clear(struct mlxsw_sp *mlxsw_sp,6036 struct mlxsw_sp_fib_entry *fib_entry)6037{6038 u32 *p_dst = (u32 *) fib_entry->fib_node->key.addr;6039 int dst_len = fib_entry->fib_node->key.prefix_len;6040 struct mlxsw_sp_fib4_entry *fib4_entry;6041 struct fib_rt_info fri;6042 6043 fib4_entry = container_of(fib_entry, struct mlxsw_sp_fib4_entry,6044 common);6045 fri.fi = fib4_entry->fi;6046 fri.tb_id = fib4_entry->tb_id;6047 fri.dst = cpu_to_be32(*p_dst);6048 fri.dst_len = dst_len;6049 fri.dscp = fib4_entry->dscp;6050 fri.type = fib4_entry->type;6051 fri.offload = false;6052 fri.trap = false;6053 fri.offload_failed = false;6054 fib_alias_hw_flags_set(mlxsw_sp_net(mlxsw_sp), &fri);6055}6056 6057#if IS_ENABLED(CONFIG_IPV6)6058static void6059mlxsw_sp_fib6_offload_failed_flag_set(struct mlxsw_sp *mlxsw_sp,6060 struct fib6_info **rt_arr,6061 unsigned int nrt6)6062{6063 int i;6064 6065 /* In IPv6 a multipath route is represented using multiple routes, so6066 * we need to set the flags on all of them.6067 */6068 for (i = 0; i < nrt6; i++)6069 fib6_info_hw_flags_set(mlxsw_sp_net(mlxsw_sp), rt_arr[i],6070 false, false, true);6071}6072#else6073static void6074mlxsw_sp_fib6_offload_failed_flag_set(struct mlxsw_sp *mlxsw_sp,6075 struct fib6_info **rt_arr,6076 unsigned int nrt6)6077{6078}6079#endif6080 6081#if IS_ENABLED(CONFIG_IPV6)6082static void6083mlxsw_sp_fib6_entry_hw_flags_set(struct mlxsw_sp *mlxsw_sp,6084 struct mlxsw_sp_fib_entry *fib_entry)6085{6086 struct mlxsw_sp_fib6_entry *fib6_entry;6087 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;6088 bool should_offload;6089 6090 should_offload = mlxsw_sp_fib_entry_should_offload(fib_entry);6091 6092 /* In IPv6 a multipath route is represented using multiple routes, so6093 * we need to set the flags on all of them.6094 */6095 fib6_entry = container_of(fib_entry, struct mlxsw_sp_fib6_entry,6096 common);6097 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list)6098 fib6_info_hw_flags_set(mlxsw_sp_net(mlxsw_sp), mlxsw_sp_rt6->rt,6099 should_offload, !should_offload, false);6100}6101#else6102static void6103mlxsw_sp_fib6_entry_hw_flags_set(struct mlxsw_sp *mlxsw_sp,6104 struct mlxsw_sp_fib_entry *fib_entry)6105{6106}6107#endif6108 6109#if IS_ENABLED(CONFIG_IPV6)6110static void6111mlxsw_sp_fib6_entry_hw_flags_clear(struct mlxsw_sp *mlxsw_sp,6112 struct mlxsw_sp_fib_entry *fib_entry)6113{6114 struct mlxsw_sp_fib6_entry *fib6_entry;6115 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;6116 6117 fib6_entry = container_of(fib_entry, struct mlxsw_sp_fib6_entry,6118 common);6119 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list)6120 fib6_info_hw_flags_set(mlxsw_sp_net(mlxsw_sp), mlxsw_sp_rt6->rt,6121 false, false, false);6122}6123#else6124static void6125mlxsw_sp_fib6_entry_hw_flags_clear(struct mlxsw_sp *mlxsw_sp,6126 struct mlxsw_sp_fib_entry *fib_entry)6127{6128}6129#endif6130 6131static void6132mlxsw_sp_fib_entry_hw_flags_set(struct mlxsw_sp *mlxsw_sp,6133 struct mlxsw_sp_fib_entry *fib_entry)6134{6135 switch (fib_entry->fib_node->fib->proto) {6136 case MLXSW_SP_L3_PROTO_IPV4:6137 mlxsw_sp_fib4_entry_hw_flags_set(mlxsw_sp, fib_entry);6138 break;6139 case MLXSW_SP_L3_PROTO_IPV6:6140 mlxsw_sp_fib6_entry_hw_flags_set(mlxsw_sp, fib_entry);6141 break;6142 }6143}6144 6145static void6146mlxsw_sp_fib_entry_hw_flags_clear(struct mlxsw_sp *mlxsw_sp,6147 struct mlxsw_sp_fib_entry *fib_entry)6148{6149 switch (fib_entry->fib_node->fib->proto) {6150 case MLXSW_SP_L3_PROTO_IPV4:6151 mlxsw_sp_fib4_entry_hw_flags_clear(mlxsw_sp, fib_entry);6152 break;6153 case MLXSW_SP_L3_PROTO_IPV6:6154 mlxsw_sp_fib6_entry_hw_flags_clear(mlxsw_sp, fib_entry);6155 break;6156 }6157}6158 6159static void6160mlxsw_sp_fib_entry_hw_flags_refresh(struct mlxsw_sp *mlxsw_sp,6161 struct mlxsw_sp_fib_entry *fib_entry,6162 enum mlxsw_reg_ralue_op op)6163{6164 switch (op) {6165 case MLXSW_REG_RALUE_OP_WRITE_WRITE:6166 mlxsw_sp_fib_entry_hw_flags_set(mlxsw_sp, fib_entry);6167 break;6168 case MLXSW_REG_RALUE_OP_WRITE_DELETE:6169 mlxsw_sp_fib_entry_hw_flags_clear(mlxsw_sp, fib_entry);6170 break;6171 default:6172 break;6173 }6174}6175 6176static void6177mlxsw_sp_fib_entry_ralue_pack(char *ralue_pl,6178 const struct mlxsw_sp_fib_entry *fib_entry,6179 enum mlxsw_reg_ralue_op op)6180{6181 struct mlxsw_sp_fib *fib = fib_entry->fib_node->fib;6182 enum mlxsw_reg_ralxx_protocol proto;6183 u32 *p_dip;6184 6185 proto = (enum mlxsw_reg_ralxx_protocol) fib->proto;6186 6187 switch (fib->proto) {6188 case MLXSW_SP_L3_PROTO_IPV4:6189 p_dip = (u32 *) fib_entry->fib_node->key.addr;6190 mlxsw_reg_ralue_pack4(ralue_pl, proto, op, fib->vr->id,6191 fib_entry->fib_node->key.prefix_len,6192 *p_dip);6193 break;6194 case MLXSW_SP_L3_PROTO_IPV6:6195 mlxsw_reg_ralue_pack6(ralue_pl, proto, op, fib->vr->id,6196 fib_entry->fib_node->key.prefix_len,6197 fib_entry->fib_node->key.addr);6198 break;6199 }6200}6201 6202static int mlxsw_sp_fib_entry_op_remote(struct mlxsw_sp *mlxsw_sp,6203 struct mlxsw_sp_fib_entry *fib_entry,6204 enum mlxsw_reg_ralue_op op)6205{6206 struct mlxsw_sp_nexthop_group *nh_group = fib_entry->nh_group;6207 struct mlxsw_sp_nexthop_group_info *nhgi = nh_group->nhgi;6208 char ralue_pl[MLXSW_REG_RALUE_LEN];6209 enum mlxsw_reg_ralue_trap_action trap_action;6210 u16 trap_id = 0;6211 u32 adjacency_index = 0;6212 u16 ecmp_size = 0;6213 6214 /* In case the nexthop group adjacency index is valid, use it6215 * with provided ECMP size. Otherwise, setup trap and pass6216 * traffic to kernel.6217 */6218 if (mlxsw_sp_fib_entry_should_offload(fib_entry)) {6219 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_NOP;6220 adjacency_index = nhgi->adj_index;6221 ecmp_size = nhgi->ecmp_size;6222 } else if (!nhgi->adj_index_valid && nhgi->count &&6223 mlxsw_sp_nhgi_rif(nhgi)) {6224 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_NOP;6225 adjacency_index = mlxsw_sp->router->adj_trap_index;6226 ecmp_size = 1;6227 } else {6228 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_TRAP;6229 trap_id = MLXSW_TRAP_ID_RTR_INGRESS0;6230 }6231 6232 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);6233 mlxsw_reg_ralue_act_remote_pack(ralue_pl, trap_action, trap_id,6234 adjacency_index, ecmp_size);6235 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);6236}6237 6238static int mlxsw_sp_fib_entry_op_local(struct mlxsw_sp *mlxsw_sp,6239 struct mlxsw_sp_fib_entry *fib_entry,6240 enum mlxsw_reg_ralue_op op)6241{6242 struct mlxsw_sp_rif *rif = mlxsw_sp_nhgi_rif(fib_entry->nh_group->nhgi);6243 enum mlxsw_reg_ralue_trap_action trap_action;6244 char ralue_pl[MLXSW_REG_RALUE_LEN];6245 u16 trap_id = 0;6246 u16 rif_index = 0;6247 6248 if (mlxsw_sp_fib_entry_should_offload(fib_entry)) {6249 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_NOP;6250 rif_index = rif->rif_index;6251 } else {6252 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_TRAP;6253 trap_id = MLXSW_TRAP_ID_RTR_INGRESS0;6254 }6255 6256 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);6257 mlxsw_reg_ralue_act_local_pack(ralue_pl, trap_action, trap_id,6258 rif_index);6259 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);6260}6261 6262static int mlxsw_sp_fib_entry_op_trap(struct mlxsw_sp *mlxsw_sp,6263 struct mlxsw_sp_fib_entry *fib_entry,6264 enum mlxsw_reg_ralue_op op)6265{6266 char ralue_pl[MLXSW_REG_RALUE_LEN];6267 6268 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);6269 mlxsw_reg_ralue_act_ip2me_pack(ralue_pl);6270 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);6271}6272 6273static int mlxsw_sp_fib_entry_op_blackhole(struct mlxsw_sp *mlxsw_sp,6274 struct mlxsw_sp_fib_entry *fib_entry,6275 enum mlxsw_reg_ralue_op op)6276{6277 enum mlxsw_reg_ralue_trap_action trap_action;6278 char ralue_pl[MLXSW_REG_RALUE_LEN];6279 6280 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_DISCARD_ERROR;6281 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);6282 mlxsw_reg_ralue_act_local_pack(ralue_pl, trap_action, 0, 0);6283 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);6284}6285 6286static int6287mlxsw_sp_fib_entry_op_unreachable(struct mlxsw_sp *mlxsw_sp,6288 struct mlxsw_sp_fib_entry *fib_entry,6289 enum mlxsw_reg_ralue_op op)6290{6291 enum mlxsw_reg_ralue_trap_action trap_action;6292 char ralue_pl[MLXSW_REG_RALUE_LEN];6293 u16 trap_id;6294 6295 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_TRAP;6296 trap_id = MLXSW_TRAP_ID_RTR_INGRESS1;6297 6298 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);6299 mlxsw_reg_ralue_act_local_pack(ralue_pl, trap_action, trap_id, 0);6300 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);6301}6302 6303static int6304mlxsw_sp_fib_entry_op_ipip_decap(struct mlxsw_sp *mlxsw_sp,6305 struct mlxsw_sp_fib_entry *fib_entry,6306 enum mlxsw_reg_ralue_op op)6307{6308 struct mlxsw_sp_ipip_entry *ipip_entry = fib_entry->decap.ipip_entry;6309 const struct mlxsw_sp_ipip_ops *ipip_ops;6310 char ralue_pl[MLXSW_REG_RALUE_LEN];6311 int err;6312 6313 if (WARN_ON(!ipip_entry))6314 return -EINVAL;6315 6316 ipip_ops = mlxsw_sp->router->ipip_ops_arr[ipip_entry->ipipt];6317 err = ipip_ops->decap_config(mlxsw_sp, ipip_entry,6318 fib_entry->decap.tunnel_index);6319 if (err)6320 return err;6321 6322 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);6323 mlxsw_reg_ralue_act_ip2me_tun_pack(ralue_pl,6324 fib_entry->decap.tunnel_index);6325 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);6326}6327 6328static int mlxsw_sp_fib_entry_op_nve_decap(struct mlxsw_sp *mlxsw_sp,6329 struct mlxsw_sp_fib_entry *fib_entry,6330 enum mlxsw_reg_ralue_op op)6331{6332 char ralue_pl[MLXSW_REG_RALUE_LEN];6333 6334 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);6335 mlxsw_reg_ralue_act_ip2me_tun_pack(ralue_pl,6336 fib_entry->decap.tunnel_index);6337 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);6338}6339 6340static int __mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp,6341 struct mlxsw_sp_fib_entry *fib_entry,6342 enum mlxsw_reg_ralue_op op)6343{6344 switch (fib_entry->type) {6345 case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE:6346 return mlxsw_sp_fib_entry_op_remote(mlxsw_sp, fib_entry, op);6347 case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL:6348 return mlxsw_sp_fib_entry_op_local(mlxsw_sp, fib_entry, op);6349 case MLXSW_SP_FIB_ENTRY_TYPE_TRAP:6350 return mlxsw_sp_fib_entry_op_trap(mlxsw_sp, fib_entry, op);6351 case MLXSW_SP_FIB_ENTRY_TYPE_BLACKHOLE:6352 return mlxsw_sp_fib_entry_op_blackhole(mlxsw_sp, fib_entry, op);6353 case MLXSW_SP_FIB_ENTRY_TYPE_UNREACHABLE:6354 return mlxsw_sp_fib_entry_op_unreachable(mlxsw_sp, fib_entry,6355 op);6356 case MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP:6357 return mlxsw_sp_fib_entry_op_ipip_decap(mlxsw_sp,6358 fib_entry, op);6359 case MLXSW_SP_FIB_ENTRY_TYPE_NVE_DECAP:6360 return mlxsw_sp_fib_entry_op_nve_decap(mlxsw_sp, fib_entry, op);6361 }6362 return -EINVAL;6363}6364 6365static int mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp,6366 struct mlxsw_sp_fib_entry *fib_entry,6367 enum mlxsw_reg_ralue_op op)6368{6369 int err = __mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry, op);6370 6371 if (err)6372 return err;6373 6374 mlxsw_sp_fib_entry_hw_flags_refresh(mlxsw_sp, fib_entry, op);6375 6376 return err;6377}6378 6379static int mlxsw_sp_fib_entry_update(struct mlxsw_sp *mlxsw_sp,6380 struct mlxsw_sp_fib_entry *fib_entry)6381{6382 return mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry,6383 MLXSW_REG_RALUE_OP_WRITE_WRITE);6384}6385 6386static int mlxsw_sp_fib_entry_del(struct mlxsw_sp *mlxsw_sp,6387 struct mlxsw_sp_fib_entry *fib_entry)6388{6389 return mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry,6390 MLXSW_REG_RALUE_OP_WRITE_DELETE);6391}6392 6393static int6394mlxsw_sp_fib4_entry_type_set(struct mlxsw_sp *mlxsw_sp,6395 const struct fib_entry_notifier_info *fen_info,6396 struct mlxsw_sp_fib_entry *fib_entry)6397{6398 struct mlxsw_sp_nexthop_group_info *nhgi = fib_entry->nh_group->nhgi;6399 union mlxsw_sp_l3addr dip = { .addr4 = htonl(fen_info->dst) };6400 struct mlxsw_sp_router *router = mlxsw_sp->router;6401 u32 tb_id = mlxsw_sp_fix_tb_id(fen_info->tb_id);6402 int ifindex = nhgi->nexthops[0].ifindex;6403 struct mlxsw_sp_ipip_entry *ipip_entry;6404 6405 switch (fen_info->type) {6406 case RTN_LOCAL:6407 ipip_entry = mlxsw_sp_ipip_entry_find_by_decap(mlxsw_sp, ifindex,6408 MLXSW_SP_L3_PROTO_IPV4, dip);6409 if (ipip_entry && ipip_entry->ol_dev->flags & IFF_UP) {6410 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP;6411 return mlxsw_sp_fib_entry_decap_init(mlxsw_sp,6412 fib_entry,6413 ipip_entry);6414 }6415 if (mlxsw_sp_router_nve_is_decap(mlxsw_sp, tb_id,6416 MLXSW_SP_L3_PROTO_IPV4,6417 &dip)) {6418 u32 tunnel_index;6419 6420 tunnel_index = router->nve_decap_config.tunnel_index;6421 fib_entry->decap.tunnel_index = tunnel_index;6422 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_NVE_DECAP;6423 return 0;6424 }6425 fallthrough;6426 case RTN_BROADCAST:6427 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;6428 return 0;6429 case RTN_BLACKHOLE:6430 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_BLACKHOLE;6431 return 0;6432 case RTN_UNREACHABLE:6433 case RTN_PROHIBIT:6434 /* Packets hitting these routes need to be trapped, but6435 * can do so with a lower priority than packets directed6436 * at the host, so use action type local instead of trap.6437 */6438 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_UNREACHABLE;6439 return 0;6440 case RTN_UNICAST:6441 if (nhgi->gateway)6442 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_REMOTE;6443 else6444 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;6445 return 0;6446 default:6447 return -EINVAL;6448 }6449}6450 6451static void6452mlxsw_sp_fib_entry_type_unset(struct mlxsw_sp *mlxsw_sp,6453 struct mlxsw_sp_fib_entry *fib_entry)6454{6455 switch (fib_entry->type) {6456 case MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP:6457 mlxsw_sp_fib_entry_decap_fini(mlxsw_sp, fib_entry);6458 break;6459 default:6460 break;6461 }6462}6463 6464static void6465mlxsw_sp_fib4_entry_type_unset(struct mlxsw_sp *mlxsw_sp,6466 struct mlxsw_sp_fib4_entry *fib4_entry)6467{6468 mlxsw_sp_fib_entry_type_unset(mlxsw_sp, &fib4_entry->common);6469}6470 6471static struct mlxsw_sp_fib4_entry *6472mlxsw_sp_fib4_entry_create(struct mlxsw_sp *mlxsw_sp,6473 struct mlxsw_sp_fib_node *fib_node,6474 const struct fib_entry_notifier_info *fen_info)6475{6476 struct mlxsw_sp_fib4_entry *fib4_entry;6477 struct mlxsw_sp_fib_entry *fib_entry;6478 int err;6479 6480 fib4_entry = kzalloc(sizeof(*fib4_entry), GFP_KERNEL);6481 if (!fib4_entry)6482 return ERR_PTR(-ENOMEM);6483 fib_entry = &fib4_entry->common;6484 6485 err = mlxsw_sp_nexthop4_group_get(mlxsw_sp, fib_entry, fen_info->fi);6486 if (err)6487 goto err_nexthop4_group_get;6488 6489 err = mlxsw_sp_nexthop_group_vr_link(fib_entry->nh_group,6490 fib_node->fib);6491 if (err)6492 goto err_nexthop_group_vr_link;6493 6494 err = mlxsw_sp_fib4_entry_type_set(mlxsw_sp, fen_info, fib_entry);6495 if (err)6496 goto err_fib4_entry_type_set;6497 6498 fib4_entry->fi = fen_info->fi;6499 fib_info_hold(fib4_entry->fi);6500 fib4_entry->tb_id = fen_info->tb_id;6501 fib4_entry->type = fen_info->type;6502 fib4_entry->dscp = fen_info->dscp;6503 6504 fib_entry->fib_node = fib_node;6505 6506 return fib4_entry;6507 6508err_fib4_entry_type_set:6509 mlxsw_sp_nexthop_group_vr_unlink(fib_entry->nh_group, fib_node->fib);6510err_nexthop_group_vr_link:6511 mlxsw_sp_nexthop4_group_put(mlxsw_sp, &fib4_entry->common);6512err_nexthop4_group_get:6513 kfree(fib4_entry);6514 return ERR_PTR(err);6515}6516 6517static void mlxsw_sp_fib4_entry_destroy(struct mlxsw_sp *mlxsw_sp,6518 struct mlxsw_sp_fib4_entry *fib4_entry)6519{6520 struct mlxsw_sp_fib_node *fib_node = fib4_entry->common.fib_node;6521 6522 fib_info_put(fib4_entry->fi);6523 mlxsw_sp_fib4_entry_type_unset(mlxsw_sp, fib4_entry);6524 mlxsw_sp_nexthop_group_vr_unlink(fib4_entry->common.nh_group,6525 fib_node->fib);6526 mlxsw_sp_nexthop4_group_put(mlxsw_sp, &fib4_entry->common);6527 kfree(fib4_entry);6528}6529 6530static struct mlxsw_sp_fib4_entry *6531mlxsw_sp_fib4_entry_lookup(struct mlxsw_sp *mlxsw_sp,6532 const struct fib_entry_notifier_info *fen_info)6533{6534 struct mlxsw_sp_fib4_entry *fib4_entry;6535 struct mlxsw_sp_fib_node *fib_node;6536 struct mlxsw_sp_fib *fib;6537 struct mlxsw_sp_vr *vr;6538 6539 vr = mlxsw_sp_vr_find(mlxsw_sp, fen_info->tb_id);6540 if (!vr)6541 return NULL;6542 fib = mlxsw_sp_vr_fib(vr, MLXSW_SP_L3_PROTO_IPV4);6543 6544 fib_node = mlxsw_sp_fib_node_lookup(fib, &fen_info->dst,6545 sizeof(fen_info->dst),6546 fen_info->dst_len);6547 if (!fib_node)6548 return NULL;6549 6550 fib4_entry = container_of(fib_node->fib_entry,6551 struct mlxsw_sp_fib4_entry, common);6552 if (fib4_entry->tb_id == fen_info->tb_id &&6553 fib4_entry->dscp == fen_info->dscp &&6554 fib4_entry->type == fen_info->type &&6555 fib4_entry->fi == fen_info->fi)6556 return fib4_entry;6557 6558 return NULL;6559}6560 6561static const struct rhashtable_params mlxsw_sp_fib_ht_params = {6562 .key_offset = offsetof(struct mlxsw_sp_fib_node, key),6563 .head_offset = offsetof(struct mlxsw_sp_fib_node, ht_node),6564 .key_len = sizeof(struct mlxsw_sp_fib_key),6565 .automatic_shrinking = true,6566};6567 6568static int mlxsw_sp_fib_node_insert(struct mlxsw_sp_fib *fib,6569 struct mlxsw_sp_fib_node *fib_node)6570{6571 return rhashtable_insert_fast(&fib->ht, &fib_node->ht_node,6572 mlxsw_sp_fib_ht_params);6573}6574 6575static void mlxsw_sp_fib_node_remove(struct mlxsw_sp_fib *fib,6576 struct mlxsw_sp_fib_node *fib_node)6577{6578 rhashtable_remove_fast(&fib->ht, &fib_node->ht_node,6579 mlxsw_sp_fib_ht_params);6580}6581 6582static struct mlxsw_sp_fib_node *6583mlxsw_sp_fib_node_lookup(struct mlxsw_sp_fib *fib, const void *addr,6584 size_t addr_len, unsigned char prefix_len)6585{6586 struct mlxsw_sp_fib_key key;6587 6588 memset(&key, 0, sizeof(key));6589 memcpy(key.addr, addr, addr_len);6590 key.prefix_len = prefix_len;6591 return rhashtable_lookup_fast(&fib->ht, &key, mlxsw_sp_fib_ht_params);6592}6593 6594static struct mlxsw_sp_fib_node *6595mlxsw_sp_fib_node_create(struct mlxsw_sp_fib *fib, const void *addr,6596 size_t addr_len, unsigned char prefix_len)6597{6598 struct mlxsw_sp_fib_node *fib_node;6599 6600 fib_node = kzalloc(sizeof(*fib_node), GFP_KERNEL);6601 if (!fib_node)6602 return NULL;6603 6604 list_add(&fib_node->list, &fib->node_list);6605 memcpy(fib_node->key.addr, addr, addr_len);6606 fib_node->key.prefix_len = prefix_len;6607 6608 return fib_node;6609}6610 6611static void mlxsw_sp_fib_node_destroy(struct mlxsw_sp_fib_node *fib_node)6612{6613 list_del(&fib_node->list);6614 kfree(fib_node);6615}6616 6617static int mlxsw_sp_fib_lpm_tree_link(struct mlxsw_sp *mlxsw_sp,6618 struct mlxsw_sp_fib_node *fib_node)6619{6620 struct mlxsw_sp_prefix_usage req_prefix_usage;6621 struct mlxsw_sp_fib *fib = fib_node->fib;6622 struct mlxsw_sp_lpm_tree *lpm_tree;6623 int err;6624 6625 lpm_tree = mlxsw_sp->router->lpm.proto_trees[fib->proto];6626 if (lpm_tree->prefix_ref_count[fib_node->key.prefix_len] != 0)6627 goto out;6628 6629 mlxsw_sp_prefix_usage_cpy(&req_prefix_usage, &lpm_tree->prefix_usage);6630 mlxsw_sp_prefix_usage_set(&req_prefix_usage, fib_node->key.prefix_len);6631 lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage,6632 fib->proto);6633 if (IS_ERR(lpm_tree))6634 return PTR_ERR(lpm_tree);6635 6636 err = mlxsw_sp_vrs_lpm_tree_replace(mlxsw_sp, fib, lpm_tree);6637 if (err)6638 goto err_lpm_tree_replace;6639 6640out:6641 lpm_tree->prefix_ref_count[fib_node->key.prefix_len]++;6642 return 0;6643 6644err_lpm_tree_replace:6645 mlxsw_sp_lpm_tree_put(mlxsw_sp, lpm_tree);6646 return err;6647}6648 6649static void mlxsw_sp_fib_lpm_tree_unlink(struct mlxsw_sp *mlxsw_sp,6650 struct mlxsw_sp_fib_node *fib_node)6651{6652 struct mlxsw_sp_lpm_tree *lpm_tree = fib_node->fib->lpm_tree;6653 struct mlxsw_sp_prefix_usage req_prefix_usage;6654 struct mlxsw_sp_fib *fib = fib_node->fib;6655 int err;6656 6657 if (--lpm_tree->prefix_ref_count[fib_node->key.prefix_len] != 0)6658 return;6659 /* Try to construct a new LPM tree from the current prefix usage6660 * minus the unused one. If we fail, continue using the old one.6661 */6662 mlxsw_sp_prefix_usage_cpy(&req_prefix_usage, &lpm_tree->prefix_usage);6663 mlxsw_sp_prefix_usage_clear(&req_prefix_usage,6664 fib_node->key.prefix_len);6665 lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage,6666 fib->proto);6667 if (IS_ERR(lpm_tree))6668 return;6669 6670 err = mlxsw_sp_vrs_lpm_tree_replace(mlxsw_sp, fib, lpm_tree);6671 if (err)6672 goto err_lpm_tree_replace;6673 6674 return;6675 6676err_lpm_tree_replace:6677 mlxsw_sp_lpm_tree_put(mlxsw_sp, lpm_tree);6678}6679 6680static int mlxsw_sp_fib_node_init(struct mlxsw_sp *mlxsw_sp,6681 struct mlxsw_sp_fib_node *fib_node,6682 struct mlxsw_sp_fib *fib)6683{6684 int err;6685 6686 err = mlxsw_sp_fib_node_insert(fib, fib_node);6687 if (err)6688 return err;6689 fib_node->fib = fib;6690 6691 err = mlxsw_sp_fib_lpm_tree_link(mlxsw_sp, fib_node);6692 if (err)6693 goto err_fib_lpm_tree_link;6694 6695 return 0;6696 6697err_fib_lpm_tree_link:6698 fib_node->fib = NULL;6699 mlxsw_sp_fib_node_remove(fib, fib_node);6700 return err;6701}6702 6703static void mlxsw_sp_fib_node_fini(struct mlxsw_sp *mlxsw_sp,6704 struct mlxsw_sp_fib_node *fib_node)6705{6706 struct mlxsw_sp_fib *fib = fib_node->fib;6707 6708 mlxsw_sp_fib_lpm_tree_unlink(mlxsw_sp, fib_node);6709 fib_node->fib = NULL;6710 mlxsw_sp_fib_node_remove(fib, fib_node);6711}6712 6713static struct mlxsw_sp_fib_node *6714mlxsw_sp_fib_node_get(struct mlxsw_sp *mlxsw_sp, u32 tb_id, const void *addr,6715 size_t addr_len, unsigned char prefix_len,6716 enum mlxsw_sp_l3proto proto)6717{6718 struct mlxsw_sp_fib_node *fib_node;6719 struct mlxsw_sp_fib *fib;6720 struct mlxsw_sp_vr *vr;6721 int err;6722 6723 vr = mlxsw_sp_vr_get(mlxsw_sp, tb_id, NULL);6724 if (IS_ERR(vr))6725 return ERR_CAST(vr);6726 fib = mlxsw_sp_vr_fib(vr, proto);6727 6728 fib_node = mlxsw_sp_fib_node_lookup(fib, addr, addr_len, prefix_len);6729 if (fib_node)6730 return fib_node;6731 6732 fib_node = mlxsw_sp_fib_node_create(fib, addr, addr_len, prefix_len);6733 if (!fib_node) {6734 err = -ENOMEM;6735 goto err_fib_node_create;6736 }6737 6738 err = mlxsw_sp_fib_node_init(mlxsw_sp, fib_node, fib);6739 if (err)6740 goto err_fib_node_init;6741 6742 return fib_node;6743 6744err_fib_node_init:6745 mlxsw_sp_fib_node_destroy(fib_node);6746err_fib_node_create:6747 mlxsw_sp_vr_put(mlxsw_sp, vr);6748 return ERR_PTR(err);6749}6750 6751static void mlxsw_sp_fib_node_put(struct mlxsw_sp *mlxsw_sp,6752 struct mlxsw_sp_fib_node *fib_node)6753{6754 struct mlxsw_sp_vr *vr = fib_node->fib->vr;6755 6756 if (fib_node->fib_entry)6757 return;6758 mlxsw_sp_fib_node_fini(mlxsw_sp, fib_node);6759 mlxsw_sp_fib_node_destroy(fib_node);6760 mlxsw_sp_vr_put(mlxsw_sp, vr);6761}6762 6763static int mlxsw_sp_fib_node_entry_link(struct mlxsw_sp *mlxsw_sp,6764 struct mlxsw_sp_fib_entry *fib_entry)6765{6766 struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node;6767 int err;6768 6769 fib_node->fib_entry = fib_entry;6770 6771 err = mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);6772 if (err)6773 goto err_fib_entry_update;6774 6775 return 0;6776 6777err_fib_entry_update:6778 fib_node->fib_entry = NULL;6779 return err;6780}6781 6782static void6783mlxsw_sp_fib_node_entry_unlink(struct mlxsw_sp *mlxsw_sp,6784 struct mlxsw_sp_fib_entry *fib_entry)6785{6786 struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node;6787 6788 mlxsw_sp_fib_entry_del(mlxsw_sp, fib_entry);6789 fib_node->fib_entry = NULL;6790}6791 6792static bool mlxsw_sp_fib4_allow_replace(struct mlxsw_sp_fib4_entry *fib4_entry)6793{6794 struct mlxsw_sp_fib_node *fib_node = fib4_entry->common.fib_node;6795 struct mlxsw_sp_fib4_entry *fib4_replaced;6796 6797 if (!fib_node->fib_entry)6798 return true;6799 6800 fib4_replaced = container_of(fib_node->fib_entry,6801 struct mlxsw_sp_fib4_entry, common);6802 if (fib4_entry->tb_id == RT_TABLE_MAIN &&6803 fib4_replaced->tb_id == RT_TABLE_LOCAL)6804 return false;6805 6806 return true;6807}6808 6809static int6810mlxsw_sp_router_fib4_replace(struct mlxsw_sp *mlxsw_sp,6811 const struct fib_entry_notifier_info *fen_info)6812{6813 struct mlxsw_sp_fib4_entry *fib4_entry, *fib4_replaced;6814 struct mlxsw_sp_fib_entry *replaced;6815 struct mlxsw_sp_fib_node *fib_node;6816 int err;6817 6818 if (fen_info->fi->nh &&6819 !mlxsw_sp_nexthop_obj_group_lookup(mlxsw_sp, fen_info->fi->nh->id))6820 return 0;6821 6822 fib_node = mlxsw_sp_fib_node_get(mlxsw_sp, fen_info->tb_id,6823 &fen_info->dst, sizeof(fen_info->dst),6824 fen_info->dst_len,6825 MLXSW_SP_L3_PROTO_IPV4);6826 if (IS_ERR(fib_node)) {6827 dev_warn(mlxsw_sp->bus_info->dev, "Failed to get FIB node\n");6828 return PTR_ERR(fib_node);6829 }6830 6831 fib4_entry = mlxsw_sp_fib4_entry_create(mlxsw_sp, fib_node, fen_info);6832 if (IS_ERR(fib4_entry)) {6833 dev_warn(mlxsw_sp->bus_info->dev, "Failed to create FIB entry\n");6834 err = PTR_ERR(fib4_entry);6835 goto err_fib4_entry_create;6836 }6837 6838 if (!mlxsw_sp_fib4_allow_replace(fib4_entry)) {6839 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);6840 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);6841 return 0;6842 }6843 6844 replaced = fib_node->fib_entry;6845 err = mlxsw_sp_fib_node_entry_link(mlxsw_sp, &fib4_entry->common);6846 if (err) {6847 dev_warn(mlxsw_sp->bus_info->dev, "Failed to link FIB entry to node\n");6848 goto err_fib_node_entry_link;6849 }6850 6851 /* Nothing to replace */6852 if (!replaced)6853 return 0;6854 6855 mlxsw_sp_fib_entry_hw_flags_clear(mlxsw_sp, replaced);6856 fib4_replaced = container_of(replaced, struct mlxsw_sp_fib4_entry,6857 common);6858 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_replaced);6859 6860 return 0;6861 6862err_fib_node_entry_link:6863 fib_node->fib_entry = replaced;6864 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);6865err_fib4_entry_create:6866 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);6867 return err;6868}6869 6870static void mlxsw_sp_router_fib4_del(struct mlxsw_sp *mlxsw_sp,6871 struct fib_entry_notifier_info *fen_info)6872{6873 struct mlxsw_sp_fib4_entry *fib4_entry;6874 struct mlxsw_sp_fib_node *fib_node;6875 6876 fib4_entry = mlxsw_sp_fib4_entry_lookup(mlxsw_sp, fen_info);6877 if (!fib4_entry)6878 return;6879 fib_node = fib4_entry->common.fib_node;6880 6881 mlxsw_sp_fib_node_entry_unlink(mlxsw_sp, &fib4_entry->common);6882 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);6883 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);6884}6885 6886static bool mlxsw_sp_fib6_rt_should_ignore(const struct fib6_info *rt)6887{6888 /* Multicast routes aren't supported, so ignore them. Neighbour6889 * Discovery packets are specifically trapped.6890 */6891 if (ipv6_addr_type(&rt->fib6_dst.addr) & IPV6_ADDR_MULTICAST)6892 return true;6893 6894 /* Cloned routes are irrelevant in the forwarding path. */6895 if (rt->fib6_flags & RTF_CACHE)6896 return true;6897 6898 return false;6899}6900 6901static struct mlxsw_sp_rt6 *mlxsw_sp_rt6_create(struct fib6_info *rt)6902{6903 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;6904 6905 mlxsw_sp_rt6 = kzalloc(sizeof(*mlxsw_sp_rt6), GFP_KERNEL);6906 if (!mlxsw_sp_rt6)6907 return ERR_PTR(-ENOMEM);6908 6909 /* In case of route replace, replaced route is deleted with6910 * no notification. Take reference to prevent accessing freed6911 * memory.6912 */6913 mlxsw_sp_rt6->rt = rt;6914 fib6_info_hold(rt);6915 6916 return mlxsw_sp_rt6;6917}6918 6919#if IS_ENABLED(CONFIG_IPV6)6920static void mlxsw_sp_rt6_release(struct fib6_info *rt)6921{6922 fib6_info_release(rt);6923}6924#else6925static void mlxsw_sp_rt6_release(struct fib6_info *rt)6926{6927}6928#endif6929 6930static void mlxsw_sp_rt6_destroy(struct mlxsw_sp_rt6 *mlxsw_sp_rt6)6931{6932 struct fib6_nh *fib6_nh = mlxsw_sp_rt6->rt->fib6_nh;6933 6934 if (!mlxsw_sp_rt6->rt->nh)6935 fib6_nh->fib_nh_flags &= ~RTNH_F_OFFLOAD;6936 mlxsw_sp_rt6_release(mlxsw_sp_rt6->rt);6937 kfree(mlxsw_sp_rt6);6938}6939 6940static struct fib6_info *6941mlxsw_sp_fib6_entry_rt(const struct mlxsw_sp_fib6_entry *fib6_entry)6942{6943 return list_first_entry(&fib6_entry->rt6_list, struct mlxsw_sp_rt6,6944 list)->rt;6945}6946 6947static struct mlxsw_sp_rt6 *6948mlxsw_sp_fib6_entry_rt_find(const struct mlxsw_sp_fib6_entry *fib6_entry,6949 const struct fib6_info *rt)6950{6951 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;6952 6953 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {6954 if (mlxsw_sp_rt6->rt == rt)6955 return mlxsw_sp_rt6;6956 }6957 6958 return NULL;6959}6960 6961static bool mlxsw_sp_nexthop6_ipip_type(const struct mlxsw_sp *mlxsw_sp,6962 const struct fib6_info *rt,6963 enum mlxsw_sp_ipip_type *ret)6964{6965 return rt->fib6_nh->fib_nh_dev &&6966 mlxsw_sp_netdev_ipip_type(mlxsw_sp, rt->fib6_nh->fib_nh_dev, ret);6967}6968 6969static int mlxsw_sp_nexthop6_init(struct mlxsw_sp *mlxsw_sp,6970 struct mlxsw_sp_nexthop_group *nh_grp,6971 struct mlxsw_sp_nexthop *nh,6972 const struct fib6_info *rt)6973{6974 struct net_device *dev = rt->fib6_nh->fib_nh_dev;6975 int err;6976 6977 nh->nhgi = nh_grp->nhgi;6978 nh->nh_weight = rt->fib6_nh->fib_nh_weight;6979 memcpy(&nh->gw_addr, &rt->fib6_nh->fib_nh_gw6, sizeof(nh->gw_addr));6980#if IS_ENABLED(CONFIG_IPV6)6981 nh->neigh_tbl = &nd_tbl;6982#endif6983 6984 err = mlxsw_sp_nexthop_counter_enable(mlxsw_sp, nh);6985 if (err)6986 return err;6987 6988 list_add_tail(&nh->router_list_node, &mlxsw_sp->router->nexthop_list);6989 6990 if (!dev)6991 return 0;6992 nh->ifindex = dev->ifindex;6993 6994 err = mlxsw_sp_nexthop_type_init(mlxsw_sp, nh, dev);6995 if (err)6996 goto err_nexthop_type_init;6997 6998 return 0;6999 7000err_nexthop_type_init:7001 list_del(&nh->router_list_node);7002 mlxsw_sp_nexthop_counter_disable(mlxsw_sp, nh);7003 return err;7004}7005 7006static void mlxsw_sp_nexthop6_fini(struct mlxsw_sp *mlxsw_sp,7007 struct mlxsw_sp_nexthop *nh)7008{7009 mlxsw_sp_nexthop_type_fini(mlxsw_sp, nh);7010 list_del(&nh->router_list_node);7011 mlxsw_sp_nexthop_counter_disable(mlxsw_sp, nh);7012}7013 7014static bool mlxsw_sp_rt6_is_gateway(const struct mlxsw_sp *mlxsw_sp,7015 const struct fib6_info *rt)7016{7017 return rt->fib6_nh->fib_nh_gw_family ||7018 mlxsw_sp_nexthop6_ipip_type(mlxsw_sp, rt, NULL);7019}7020 7021static int7022mlxsw_sp_nexthop6_group_info_init(struct mlxsw_sp *mlxsw_sp,7023 struct mlxsw_sp_nexthop_group *nh_grp,7024 struct mlxsw_sp_fib6_entry *fib6_entry)7025{7026 struct mlxsw_sp_nexthop_group_info *nhgi;7027 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;7028 struct mlxsw_sp_nexthop *nh;7029 int err, i;7030 7031 nhgi = kzalloc(struct_size(nhgi, nexthops, fib6_entry->nrt6),7032 GFP_KERNEL);7033 if (!nhgi)7034 return -ENOMEM;7035 nh_grp->nhgi = nhgi;7036 nhgi->nh_grp = nh_grp;7037 mlxsw_sp_rt6 = list_first_entry(&fib6_entry->rt6_list,7038 struct mlxsw_sp_rt6, list);7039 nhgi->gateway = mlxsw_sp_rt6_is_gateway(mlxsw_sp, mlxsw_sp_rt6->rt);7040 nhgi->count = fib6_entry->nrt6;7041 for (i = 0; i < nhgi->count; i++) {7042 struct fib6_info *rt = mlxsw_sp_rt6->rt;7043 7044 nh = &nhgi->nexthops[i];7045 err = mlxsw_sp_nexthop6_init(mlxsw_sp, nh_grp, nh, rt);7046 if (err)7047 goto err_nexthop6_init;7048 mlxsw_sp_rt6 = list_next_entry(mlxsw_sp_rt6, list);7049 }7050 nh_grp->nhgi = nhgi;7051 err = mlxsw_sp_nexthop_group_inc(mlxsw_sp);7052 if (err)7053 goto err_group_inc;7054 err = mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);7055 if (err)7056 goto err_group_refresh;7057 7058 return 0;7059 7060err_group_refresh:7061 mlxsw_sp_nexthop_group_dec(mlxsw_sp);7062err_group_inc:7063 i = nhgi->count;7064err_nexthop6_init:7065 for (i--; i >= 0; i--) {7066 nh = &nhgi->nexthops[i];7067 mlxsw_sp_nexthop6_fini(mlxsw_sp, nh);7068 }7069 kfree(nhgi);7070 return err;7071}7072 7073static void7074mlxsw_sp_nexthop6_group_info_fini(struct mlxsw_sp *mlxsw_sp,7075 struct mlxsw_sp_nexthop_group *nh_grp)7076{7077 struct mlxsw_sp_nexthop_group_info *nhgi = nh_grp->nhgi;7078 int i;7079 7080 mlxsw_sp_nexthop_group_dec(mlxsw_sp);7081 for (i = nhgi->count - 1; i >= 0; i--) {7082 struct mlxsw_sp_nexthop *nh = &nhgi->nexthops[i];7083 7084 mlxsw_sp_nexthop6_fini(mlxsw_sp, nh);7085 }7086 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);7087 WARN_ON_ONCE(nhgi->adj_index_valid);7088 kfree(nhgi);7089}7090 7091static struct mlxsw_sp_nexthop_group *7092mlxsw_sp_nexthop6_group_create(struct mlxsw_sp *mlxsw_sp,7093 struct mlxsw_sp_fib6_entry *fib6_entry)7094{7095 struct mlxsw_sp_nexthop_group *nh_grp;7096 int err;7097 7098 nh_grp = kzalloc(sizeof(*nh_grp), GFP_KERNEL);7099 if (!nh_grp)7100 return ERR_PTR(-ENOMEM);7101 INIT_LIST_HEAD(&nh_grp->vr_list);7102 err = rhashtable_init(&nh_grp->vr_ht,7103 &mlxsw_sp_nexthop_group_vr_ht_params);7104 if (err)7105 goto err_nexthop_group_vr_ht_init;7106 INIT_LIST_HEAD(&nh_grp->fib_list);7107 nh_grp->type = MLXSW_SP_NEXTHOP_GROUP_TYPE_IPV6;7108 7109 err = mlxsw_sp_nexthop6_group_info_init(mlxsw_sp, nh_grp, fib6_entry);7110 if (err)7111 goto err_nexthop_group_info_init;7112 7113 err = mlxsw_sp_nexthop_group_insert(mlxsw_sp, nh_grp);7114 if (err)7115 goto err_nexthop_group_insert;7116 7117 nh_grp->can_destroy = true;7118 7119 return nh_grp;7120 7121err_nexthop_group_insert:7122 mlxsw_sp_nexthop6_group_info_fini(mlxsw_sp, nh_grp);7123err_nexthop_group_info_init:7124 rhashtable_destroy(&nh_grp->vr_ht);7125err_nexthop_group_vr_ht_init:7126 kfree(nh_grp);7127 return ERR_PTR(err);7128}7129 7130static void7131mlxsw_sp_nexthop6_group_destroy(struct mlxsw_sp *mlxsw_sp,7132 struct mlxsw_sp_nexthop_group *nh_grp)7133{7134 if (!nh_grp->can_destroy)7135 return;7136 mlxsw_sp_nexthop_group_remove(mlxsw_sp, nh_grp);7137 mlxsw_sp_nexthop6_group_info_fini(mlxsw_sp, nh_grp);7138 WARN_ON_ONCE(!list_empty(&nh_grp->vr_list));7139 rhashtable_destroy(&nh_grp->vr_ht);7140 kfree(nh_grp);7141}7142 7143static int mlxsw_sp_nexthop6_group_get(struct mlxsw_sp *mlxsw_sp,7144 struct mlxsw_sp_fib6_entry *fib6_entry)7145{7146 struct fib6_info *rt = mlxsw_sp_fib6_entry_rt(fib6_entry);7147 struct mlxsw_sp_nexthop_group *nh_grp;7148 7149 if (rt->nh) {7150 nh_grp = mlxsw_sp_nexthop_obj_group_lookup(mlxsw_sp,7151 rt->nh->id);7152 if (WARN_ON_ONCE(!nh_grp))7153 return -EINVAL;7154 goto out;7155 }7156 7157 nh_grp = mlxsw_sp_nexthop6_group_lookup(mlxsw_sp, fib6_entry);7158 if (!nh_grp) {7159 nh_grp = mlxsw_sp_nexthop6_group_create(mlxsw_sp, fib6_entry);7160 if (IS_ERR(nh_grp))7161 return PTR_ERR(nh_grp);7162 }7163 7164 /* The route and the nexthop are described by the same struct, so we7165 * need to the update the nexthop offload indication for the new route.7166 */7167 __mlxsw_sp_nexthop6_group_offload_refresh(nh_grp, fib6_entry);7168 7169out:7170 list_add_tail(&fib6_entry->common.nexthop_group_node,7171 &nh_grp->fib_list);7172 fib6_entry->common.nh_group = nh_grp;7173 7174 return 0;7175}7176 7177static void mlxsw_sp_nexthop6_group_put(struct mlxsw_sp *mlxsw_sp,7178 struct mlxsw_sp_fib_entry *fib_entry)7179{7180 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;7181 7182 list_del(&fib_entry->nexthop_group_node);7183 if (!list_empty(&nh_grp->fib_list))7184 return;7185 7186 if (nh_grp->type == MLXSW_SP_NEXTHOP_GROUP_TYPE_OBJ) {7187 mlxsw_sp_nexthop_obj_group_destroy(mlxsw_sp, nh_grp);7188 return;7189 }7190 7191 mlxsw_sp_nexthop6_group_destroy(mlxsw_sp, nh_grp);7192}7193 7194static int7195mlxsw_sp_nexthop6_group_update(struct mlxsw_sp *mlxsw_sp,7196 struct mlxsw_sp_fib6_entry *fib6_entry)7197{7198 struct mlxsw_sp_nexthop_group *old_nh_grp = fib6_entry->common.nh_group;7199 struct mlxsw_sp_fib_node *fib_node = fib6_entry->common.fib_node;7200 int err;7201 7202 mlxsw_sp_nexthop_group_vr_unlink(old_nh_grp, fib_node->fib);7203 fib6_entry->common.nh_group = NULL;7204 list_del(&fib6_entry->common.nexthop_group_node);7205 7206 err = mlxsw_sp_nexthop6_group_get(mlxsw_sp, fib6_entry);7207 if (err)7208 goto err_nexthop6_group_get;7209 7210 err = mlxsw_sp_nexthop_group_vr_link(fib6_entry->common.nh_group,7211 fib_node->fib);7212 if (err)7213 goto err_nexthop_group_vr_link;7214 7215 /* In case this entry is offloaded, then the adjacency index7216 * currently associated with it in the device's table is that7217 * of the old group. Start using the new one instead.7218 */7219 err = mlxsw_sp_fib_entry_update(mlxsw_sp, &fib6_entry->common);7220 if (err)7221 goto err_fib_entry_update;7222 7223 if (list_empty(&old_nh_grp->fib_list))7224 mlxsw_sp_nexthop6_group_destroy(mlxsw_sp, old_nh_grp);7225 7226 return 0;7227 7228err_fib_entry_update:7229 mlxsw_sp_nexthop_group_vr_unlink(fib6_entry->common.nh_group,7230 fib_node->fib);7231err_nexthop_group_vr_link:7232 mlxsw_sp_nexthop6_group_put(mlxsw_sp, &fib6_entry->common);7233err_nexthop6_group_get:7234 list_add_tail(&fib6_entry->common.nexthop_group_node,7235 &old_nh_grp->fib_list);7236 fib6_entry->common.nh_group = old_nh_grp;7237 mlxsw_sp_nexthop_group_vr_link(old_nh_grp, fib_node->fib);7238 return err;7239}7240 7241static int7242mlxsw_sp_fib6_entry_nexthop_add(struct mlxsw_sp *mlxsw_sp,7243 struct mlxsw_sp_fib6_entry *fib6_entry,7244 struct fib6_info **rt_arr, unsigned int nrt6)7245{7246 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;7247 int err, i;7248 7249 for (i = 0; i < nrt6; i++) {7250 mlxsw_sp_rt6 = mlxsw_sp_rt6_create(rt_arr[i]);7251 if (IS_ERR(mlxsw_sp_rt6)) {7252 err = PTR_ERR(mlxsw_sp_rt6);7253 goto err_rt6_unwind;7254 }7255 7256 list_add_tail(&mlxsw_sp_rt6->list, &fib6_entry->rt6_list);7257 fib6_entry->nrt6++;7258 }7259 7260 err = mlxsw_sp_nexthop6_group_update(mlxsw_sp, fib6_entry);7261 if (err)7262 goto err_rt6_unwind;7263 7264 return 0;7265 7266err_rt6_unwind:7267 for (; i > 0; i--) {7268 fib6_entry->nrt6--;7269 mlxsw_sp_rt6 = list_last_entry(&fib6_entry->rt6_list,7270 struct mlxsw_sp_rt6, list);7271 list_del(&mlxsw_sp_rt6->list);7272 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);7273 }7274 return err;7275}7276 7277static void7278mlxsw_sp_fib6_entry_nexthop_del(struct mlxsw_sp *mlxsw_sp,7279 struct mlxsw_sp_fib6_entry *fib6_entry,7280 struct fib6_info **rt_arr, unsigned int nrt6)7281{7282 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;7283 int i;7284 7285 for (i = 0; i < nrt6; i++) {7286 mlxsw_sp_rt6 = mlxsw_sp_fib6_entry_rt_find(fib6_entry,7287 rt_arr[i]);7288 if (WARN_ON_ONCE(!mlxsw_sp_rt6))7289 continue;7290 7291 fib6_entry->nrt6--;7292 list_del(&mlxsw_sp_rt6->list);7293 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);7294 }7295 7296 mlxsw_sp_nexthop6_group_update(mlxsw_sp, fib6_entry);7297}7298 7299static int7300mlxsw_sp_fib6_entry_type_set_local(struct mlxsw_sp *mlxsw_sp,7301 struct mlxsw_sp_fib_entry *fib_entry,7302 const struct fib6_info *rt)7303{7304 struct mlxsw_sp_nexthop_group_info *nhgi = fib_entry->nh_group->nhgi;7305 union mlxsw_sp_l3addr dip = { .addr6 = rt->fib6_dst.addr };7306 u32 tb_id = mlxsw_sp_fix_tb_id(rt->fib6_table->tb6_id);7307 struct mlxsw_sp_router *router = mlxsw_sp->router;7308 int ifindex = nhgi->nexthops[0].ifindex;7309 struct mlxsw_sp_ipip_entry *ipip_entry;7310 7311 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;7312 ipip_entry = mlxsw_sp_ipip_entry_find_by_decap(mlxsw_sp, ifindex,7313 MLXSW_SP_L3_PROTO_IPV6,7314 dip);7315 7316 if (ipip_entry && ipip_entry->ol_dev->flags & IFF_UP) {7317 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP;7318 return mlxsw_sp_fib_entry_decap_init(mlxsw_sp, fib_entry,7319 ipip_entry);7320 }7321 if (mlxsw_sp_router_nve_is_decap(mlxsw_sp, tb_id,7322 MLXSW_SP_L3_PROTO_IPV6, &dip)) {7323 u32 tunnel_index;7324 7325 tunnel_index = router->nve_decap_config.tunnel_index;7326 fib_entry->decap.tunnel_index = tunnel_index;7327 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_NVE_DECAP;7328 }7329 7330 return 0;7331}7332 7333static int mlxsw_sp_fib6_entry_type_set(struct mlxsw_sp *mlxsw_sp,7334 struct mlxsw_sp_fib_entry *fib_entry,7335 const struct fib6_info *rt)7336{7337 if (rt->fib6_flags & RTF_LOCAL)7338 return mlxsw_sp_fib6_entry_type_set_local(mlxsw_sp, fib_entry,7339 rt);7340 if (rt->fib6_flags & RTF_ANYCAST)7341 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;7342 else if (rt->fib6_type == RTN_BLACKHOLE)7343 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_BLACKHOLE;7344 else if (rt->fib6_flags & RTF_REJECT)7345 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_UNREACHABLE;7346 else if (fib_entry->nh_group->nhgi->gateway)7347 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_REMOTE;7348 else7349 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;7350 7351 return 0;7352}7353 7354static void7355mlxsw_sp_fib6_entry_rt_destroy_all(struct mlxsw_sp_fib6_entry *fib6_entry)7356{7357 struct mlxsw_sp_rt6 *mlxsw_sp_rt6, *tmp;7358 7359 list_for_each_entry_safe(mlxsw_sp_rt6, tmp, &fib6_entry->rt6_list,7360 list) {7361 fib6_entry->nrt6--;7362 list_del(&mlxsw_sp_rt6->list);7363 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);7364 }7365}7366 7367static struct mlxsw_sp_fib6_entry *7368mlxsw_sp_fib6_entry_create(struct mlxsw_sp *mlxsw_sp,7369 struct mlxsw_sp_fib_node *fib_node,7370 struct fib6_info **rt_arr, unsigned int nrt6)7371{7372 struct mlxsw_sp_fib6_entry *fib6_entry;7373 struct mlxsw_sp_fib_entry *fib_entry;7374 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;7375 int err, i;7376 7377 fib6_entry = kzalloc(sizeof(*fib6_entry), GFP_KERNEL);7378 if (!fib6_entry)7379 return ERR_PTR(-ENOMEM);7380 fib_entry = &fib6_entry->common;7381 7382 INIT_LIST_HEAD(&fib6_entry->rt6_list);7383 7384 for (i = 0; i < nrt6; i++) {7385 mlxsw_sp_rt6 = mlxsw_sp_rt6_create(rt_arr[i]);7386 if (IS_ERR(mlxsw_sp_rt6)) {7387 err = PTR_ERR(mlxsw_sp_rt6);7388 goto err_rt6_unwind;7389 }7390 list_add_tail(&mlxsw_sp_rt6->list, &fib6_entry->rt6_list);7391 fib6_entry->nrt6++;7392 }7393 7394 err = mlxsw_sp_nexthop6_group_get(mlxsw_sp, fib6_entry);7395 if (err)7396 goto err_rt6_unwind;7397 7398 err = mlxsw_sp_nexthop_group_vr_link(fib_entry->nh_group,7399 fib_node->fib);7400 if (err)7401 goto err_nexthop_group_vr_link;7402 7403 err = mlxsw_sp_fib6_entry_type_set(mlxsw_sp, fib_entry, rt_arr[0]);7404 if (err)7405 goto err_fib6_entry_type_set;7406 7407 fib_entry->fib_node = fib_node;7408 7409 return fib6_entry;7410 7411err_fib6_entry_type_set:7412 mlxsw_sp_nexthop_group_vr_unlink(fib_entry->nh_group, fib_node->fib);7413err_nexthop_group_vr_link:7414 mlxsw_sp_nexthop6_group_put(mlxsw_sp, fib_entry);7415err_rt6_unwind:7416 for (; i > 0; i--) {7417 fib6_entry->nrt6--;7418 mlxsw_sp_rt6 = list_last_entry(&fib6_entry->rt6_list,7419 struct mlxsw_sp_rt6, list);7420 list_del(&mlxsw_sp_rt6->list);7421 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);7422 }7423 kfree(fib6_entry);7424 return ERR_PTR(err);7425}7426 7427static void7428mlxsw_sp_fib6_entry_type_unset(struct mlxsw_sp *mlxsw_sp,7429 struct mlxsw_sp_fib6_entry *fib6_entry)7430{7431 mlxsw_sp_fib_entry_type_unset(mlxsw_sp, &fib6_entry->common);7432}7433 7434static void mlxsw_sp_fib6_entry_destroy(struct mlxsw_sp *mlxsw_sp,7435 struct mlxsw_sp_fib6_entry *fib6_entry)7436{7437 struct mlxsw_sp_fib_node *fib_node = fib6_entry->common.fib_node;7438 7439 mlxsw_sp_fib6_entry_type_unset(mlxsw_sp, fib6_entry);7440 mlxsw_sp_nexthop_group_vr_unlink(fib6_entry->common.nh_group,7441 fib_node->fib);7442 mlxsw_sp_nexthop6_group_put(mlxsw_sp, &fib6_entry->common);7443 mlxsw_sp_fib6_entry_rt_destroy_all(fib6_entry);7444 WARN_ON(fib6_entry->nrt6);7445 kfree(fib6_entry);7446}7447 7448static struct mlxsw_sp_fib6_entry *7449mlxsw_sp_fib6_entry_lookup(struct mlxsw_sp *mlxsw_sp,7450 const struct fib6_info *rt)7451{7452 struct mlxsw_sp_fib6_entry *fib6_entry;7453 struct mlxsw_sp_fib_node *fib_node;7454 struct mlxsw_sp_fib *fib;7455 struct fib6_info *cmp_rt;7456 struct mlxsw_sp_vr *vr;7457 7458 vr = mlxsw_sp_vr_find(mlxsw_sp, rt->fib6_table->tb6_id);7459 if (!vr)7460 return NULL;7461 fib = mlxsw_sp_vr_fib(vr, MLXSW_SP_L3_PROTO_IPV6);7462 7463 fib_node = mlxsw_sp_fib_node_lookup(fib, &rt->fib6_dst.addr,7464 sizeof(rt->fib6_dst.addr),7465 rt->fib6_dst.plen);7466 if (!fib_node)7467 return NULL;7468 7469 fib6_entry = container_of(fib_node->fib_entry,7470 struct mlxsw_sp_fib6_entry, common);7471 cmp_rt = mlxsw_sp_fib6_entry_rt(fib6_entry);7472 if (rt->fib6_table->tb6_id == cmp_rt->fib6_table->tb6_id &&7473 rt->fib6_metric == cmp_rt->fib6_metric &&7474 mlxsw_sp_fib6_entry_rt_find(fib6_entry, rt))7475 return fib6_entry;7476 7477 return NULL;7478}7479 7480static bool mlxsw_sp_fib6_allow_replace(struct mlxsw_sp_fib6_entry *fib6_entry)7481{7482 struct mlxsw_sp_fib_node *fib_node = fib6_entry->common.fib_node;7483 struct mlxsw_sp_fib6_entry *fib6_replaced;7484 struct fib6_info *rt, *rt_replaced;7485 7486 if (!fib_node->fib_entry)7487 return true;7488 7489 fib6_replaced = container_of(fib_node->fib_entry,7490 struct mlxsw_sp_fib6_entry,7491 common);7492 rt = mlxsw_sp_fib6_entry_rt(fib6_entry);7493 rt_replaced = mlxsw_sp_fib6_entry_rt(fib6_replaced);7494 if (rt->fib6_table->tb6_id == RT_TABLE_MAIN &&7495 rt_replaced->fib6_table->tb6_id == RT_TABLE_LOCAL)7496 return false;7497 7498 return true;7499}7500 7501static int mlxsw_sp_router_fib6_replace(struct mlxsw_sp *mlxsw_sp,7502 struct fib6_info **rt_arr,7503 unsigned int nrt6)7504{7505 struct mlxsw_sp_fib6_entry *fib6_entry, *fib6_replaced;7506 struct mlxsw_sp_fib_entry *replaced;7507 struct mlxsw_sp_fib_node *fib_node;7508 struct fib6_info *rt = rt_arr[0];7509 int err;7510 7511 if (rt->fib6_src.plen)7512 return -EINVAL;7513 7514 if (mlxsw_sp_fib6_rt_should_ignore(rt))7515 return 0;7516 7517 if (rt->nh && !mlxsw_sp_nexthop_obj_group_lookup(mlxsw_sp, rt->nh->id))7518 return 0;7519 7520 fib_node = mlxsw_sp_fib_node_get(mlxsw_sp, rt->fib6_table->tb6_id,7521 &rt->fib6_dst.addr,7522 sizeof(rt->fib6_dst.addr),7523 rt->fib6_dst.plen,7524 MLXSW_SP_L3_PROTO_IPV6);7525 if (IS_ERR(fib_node))7526 return PTR_ERR(fib_node);7527 7528 fib6_entry = mlxsw_sp_fib6_entry_create(mlxsw_sp, fib_node, rt_arr,7529 nrt6);7530 if (IS_ERR(fib6_entry)) {7531 err = PTR_ERR(fib6_entry);7532 goto err_fib6_entry_create;7533 }7534 7535 if (!mlxsw_sp_fib6_allow_replace(fib6_entry)) {7536 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);7537 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);7538 return 0;7539 }7540 7541 replaced = fib_node->fib_entry;7542 err = mlxsw_sp_fib_node_entry_link(mlxsw_sp, &fib6_entry->common);7543 if (err)7544 goto err_fib_node_entry_link;7545 7546 /* Nothing to replace */7547 if (!replaced)7548 return 0;7549 7550 mlxsw_sp_fib_entry_hw_flags_clear(mlxsw_sp, replaced);7551 fib6_replaced = container_of(replaced, struct mlxsw_sp_fib6_entry,7552 common);7553 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_replaced);7554 7555 return 0;7556 7557err_fib_node_entry_link:7558 fib_node->fib_entry = replaced;7559 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);7560err_fib6_entry_create:7561 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);7562 return err;7563}7564 7565static int mlxsw_sp_router_fib6_append(struct mlxsw_sp *mlxsw_sp,7566 struct fib6_info **rt_arr,7567 unsigned int nrt6)7568{7569 struct mlxsw_sp_fib6_entry *fib6_entry;7570 struct mlxsw_sp_fib_node *fib_node;7571 struct fib6_info *rt = rt_arr[0];7572 int err;7573 7574 if (rt->fib6_src.plen)7575 return -EINVAL;7576 7577 if (mlxsw_sp_fib6_rt_should_ignore(rt))7578 return 0;7579 7580 fib_node = mlxsw_sp_fib_node_get(mlxsw_sp, rt->fib6_table->tb6_id,7581 &rt->fib6_dst.addr,7582 sizeof(rt->fib6_dst.addr),7583 rt->fib6_dst.plen,7584 MLXSW_SP_L3_PROTO_IPV6);7585 if (IS_ERR(fib_node))7586 return PTR_ERR(fib_node);7587 7588 if (WARN_ON_ONCE(!fib_node->fib_entry)) {7589 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);7590 return -EINVAL;7591 }7592 7593 fib6_entry = container_of(fib_node->fib_entry,7594 struct mlxsw_sp_fib6_entry, common);7595 err = mlxsw_sp_fib6_entry_nexthop_add(mlxsw_sp, fib6_entry, rt_arr,7596 nrt6);7597 if (err)7598 goto err_fib6_entry_nexthop_add;7599 7600 return 0;7601 7602err_fib6_entry_nexthop_add:7603 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);7604 return err;7605}7606 7607static void mlxsw_sp_router_fib6_del(struct mlxsw_sp *mlxsw_sp,7608 struct fib6_info **rt_arr,7609 unsigned int nrt6)7610{7611 struct mlxsw_sp_fib6_entry *fib6_entry;7612 struct mlxsw_sp_fib_node *fib_node;7613 struct fib6_info *rt = rt_arr[0];7614 7615 if (mlxsw_sp_fib6_rt_should_ignore(rt))7616 return;7617 7618 /* Multipath routes are first added to the FIB trie and only then7619 * notified. If we vetoed the addition, we will get a delete7620 * notification for a route we do not have. Therefore, do not warn if7621 * route was not found.7622 */7623 fib6_entry = mlxsw_sp_fib6_entry_lookup(mlxsw_sp, rt);7624 if (!fib6_entry)7625 return;7626 7627 /* If not all the nexthops are deleted, then only reduce the nexthop7628 * group.7629 */7630 if (nrt6 != fib6_entry->nrt6) {7631 mlxsw_sp_fib6_entry_nexthop_del(mlxsw_sp, fib6_entry, rt_arr,7632 nrt6);7633 return;7634 }7635 7636 fib_node = fib6_entry->common.fib_node;7637 7638 mlxsw_sp_fib_node_entry_unlink(mlxsw_sp, &fib6_entry->common);7639 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);7640 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);7641}7642 7643static struct mlxsw_sp_mr_table *7644mlxsw_sp_router_fibmr_family_to_table(struct mlxsw_sp_vr *vr, int family)7645{7646 if (family == RTNL_FAMILY_IPMR)7647 return vr->mr_table[MLXSW_SP_L3_PROTO_IPV4];7648 else7649 return vr->mr_table[MLXSW_SP_L3_PROTO_IPV6];7650}7651 7652static int mlxsw_sp_router_fibmr_add(struct mlxsw_sp *mlxsw_sp,7653 struct mfc_entry_notifier_info *men_info,7654 bool replace)7655{7656 struct mlxsw_sp_mr_table *mrt;7657 struct mlxsw_sp_vr *vr;7658 7659 vr = mlxsw_sp_vr_get(mlxsw_sp, men_info->tb_id, NULL);7660 if (IS_ERR(vr))7661 return PTR_ERR(vr);7662 7663 mrt = mlxsw_sp_router_fibmr_family_to_table(vr, men_info->info.family);7664 return mlxsw_sp_mr_route_add(mrt, men_info->mfc, replace);7665}7666 7667static void mlxsw_sp_router_fibmr_del(struct mlxsw_sp *mlxsw_sp,7668 struct mfc_entry_notifier_info *men_info)7669{7670 struct mlxsw_sp_mr_table *mrt;7671 struct mlxsw_sp_vr *vr;7672 7673 vr = mlxsw_sp_vr_find(mlxsw_sp, men_info->tb_id);7674 if (WARN_ON(!vr))7675 return;7676 7677 mrt = mlxsw_sp_router_fibmr_family_to_table(vr, men_info->info.family);7678 mlxsw_sp_mr_route_del(mrt, men_info->mfc);7679 mlxsw_sp_vr_put(mlxsw_sp, vr);7680}7681 7682static int7683mlxsw_sp_router_fibmr_vif_add(struct mlxsw_sp *mlxsw_sp,7684 struct vif_entry_notifier_info *ven_info)7685{7686 struct mlxsw_sp_mr_table *mrt;7687 struct mlxsw_sp_rif *rif;7688 struct mlxsw_sp_vr *vr;7689 7690 vr = mlxsw_sp_vr_get(mlxsw_sp, ven_info->tb_id, NULL);7691 if (IS_ERR(vr))7692 return PTR_ERR(vr);7693 7694 mrt = mlxsw_sp_router_fibmr_family_to_table(vr, ven_info->info.family);7695 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, ven_info->dev);7696 return mlxsw_sp_mr_vif_add(mrt, ven_info->dev,7697 ven_info->vif_index,7698 ven_info->vif_flags, rif);7699}7700 7701static void7702mlxsw_sp_router_fibmr_vif_del(struct mlxsw_sp *mlxsw_sp,7703 struct vif_entry_notifier_info *ven_info)7704{7705 struct mlxsw_sp_mr_table *mrt;7706 struct mlxsw_sp_vr *vr;7707 7708 vr = mlxsw_sp_vr_find(mlxsw_sp, ven_info->tb_id);7709 if (WARN_ON(!vr))7710 return;7711 7712 mrt = mlxsw_sp_router_fibmr_family_to_table(vr, ven_info->info.family);7713 mlxsw_sp_mr_vif_del(mrt, ven_info->vif_index);7714 mlxsw_sp_vr_put(mlxsw_sp, vr);7715}7716 7717static void mlxsw_sp_fib4_node_flush(struct mlxsw_sp *mlxsw_sp,7718 struct mlxsw_sp_fib_node *fib_node)7719{7720 struct mlxsw_sp_fib4_entry *fib4_entry;7721 7722 fib4_entry = container_of(fib_node->fib_entry,7723 struct mlxsw_sp_fib4_entry, common);7724 mlxsw_sp_fib_node_entry_unlink(mlxsw_sp, fib_node->fib_entry);7725 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);7726 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);7727}7728 7729static void mlxsw_sp_fib6_node_flush(struct mlxsw_sp *mlxsw_sp,7730 struct mlxsw_sp_fib_node *fib_node)7731{7732 struct mlxsw_sp_fib6_entry *fib6_entry;7733 7734 fib6_entry = container_of(fib_node->fib_entry,7735 struct mlxsw_sp_fib6_entry, common);7736 mlxsw_sp_fib_node_entry_unlink(mlxsw_sp, fib_node->fib_entry);7737 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);7738 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);7739}7740 7741static void mlxsw_sp_fib_node_flush(struct mlxsw_sp *mlxsw_sp,7742 struct mlxsw_sp_fib_node *fib_node)7743{7744 switch (fib_node->fib->proto) {7745 case MLXSW_SP_L3_PROTO_IPV4:7746 mlxsw_sp_fib4_node_flush(mlxsw_sp, fib_node);7747 break;7748 case MLXSW_SP_L3_PROTO_IPV6:7749 mlxsw_sp_fib6_node_flush(mlxsw_sp, fib_node);7750 break;7751 }7752}7753 7754static void mlxsw_sp_vr_fib_flush(struct mlxsw_sp *mlxsw_sp,7755 struct mlxsw_sp_vr *vr,7756 enum mlxsw_sp_l3proto proto)7757{7758 struct mlxsw_sp_fib *fib = mlxsw_sp_vr_fib(vr, proto);7759 struct mlxsw_sp_fib_node *fib_node, *tmp;7760 7761 list_for_each_entry_safe(fib_node, tmp, &fib->node_list, list) {7762 bool do_break = &tmp->list == &fib->node_list;7763 7764 mlxsw_sp_fib_node_flush(mlxsw_sp, fib_node);7765 if (do_break)7766 break;7767 }7768}7769 7770static void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp)7771{7772 int max_vrs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS);7773 int i, j;7774 7775 for (i = 0; i < max_vrs; i++) {7776 struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i];7777 7778 if (!mlxsw_sp_vr_is_used(vr))7779 continue;7780 7781 for (j = 0; j < MLXSW_SP_L3_PROTO_MAX; j++)7782 mlxsw_sp_mr_table_flush(vr->mr_table[j]);7783 mlxsw_sp_vr_fib_flush(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV4);7784 7785 /* If virtual router was only used for IPv4, then it's no7786 * longer used.7787 */7788 if (!mlxsw_sp_vr_is_used(vr))7789 continue;7790 mlxsw_sp_vr_fib_flush(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV6);7791 }7792}7793 7794struct mlxsw_sp_fib6_event_work {7795 struct fib6_info **rt_arr;7796 unsigned int nrt6;7797};7798 7799struct mlxsw_sp_fib_event_work {7800 struct work_struct work;7801 netdevice_tracker dev_tracker;7802 union {7803 struct mlxsw_sp_fib6_event_work fib6_work;7804 struct fib_entry_notifier_info fen_info;7805 struct fib_rule_notifier_info fr_info;7806 struct fib_nh_notifier_info fnh_info;7807 struct mfc_entry_notifier_info men_info;7808 struct vif_entry_notifier_info ven_info;7809 };7810 struct mlxsw_sp *mlxsw_sp;7811 unsigned long event;7812};7813 7814static int7815mlxsw_sp_router_fib6_work_init(struct mlxsw_sp_fib6_event_work *fib6_work,7816 struct fib6_entry_notifier_info *fen6_info)7817{7818 struct fib6_info *rt = fen6_info->rt;7819 struct fib6_info **rt_arr;7820 struct fib6_info *iter;7821 unsigned int nrt6;7822 int i = 0;7823 7824 nrt6 = fen6_info->nsiblings + 1;7825 7826 rt_arr = kcalloc(nrt6, sizeof(struct fib6_info *), GFP_ATOMIC);7827 if (!rt_arr)7828 return -ENOMEM;7829 7830 fib6_work->rt_arr = rt_arr;7831 fib6_work->nrt6 = nrt6;7832 7833 rt_arr[0] = rt;7834 fib6_info_hold(rt);7835 7836 if (!fen6_info->nsiblings)7837 return 0;7838 7839 list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) {7840 if (i == fen6_info->nsiblings)7841 break;7842 7843 rt_arr[i + 1] = iter;7844 fib6_info_hold(iter);7845 i++;7846 }7847 WARN_ON_ONCE(i != fen6_info->nsiblings);7848 7849 return 0;7850}7851 7852static void7853mlxsw_sp_router_fib6_work_fini(struct mlxsw_sp_fib6_event_work *fib6_work)7854{7855 int i;7856 7857 for (i = 0; i < fib6_work->nrt6; i++)7858 mlxsw_sp_rt6_release(fib6_work->rt_arr[i]);7859 kfree(fib6_work->rt_arr);7860}7861 7862static void mlxsw_sp_router_fib4_event_work(struct work_struct *work)7863{7864 struct mlxsw_sp_fib_event_work *fib_work =7865 container_of(work, struct mlxsw_sp_fib_event_work, work);7866 struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp;7867 int err;7868 7869 mutex_lock(&mlxsw_sp->router->lock);7870 mlxsw_sp_span_respin(mlxsw_sp);7871 7872 switch (fib_work->event) {7873 case FIB_EVENT_ENTRY_REPLACE:7874 err = mlxsw_sp_router_fib4_replace(mlxsw_sp,7875 &fib_work->fen_info);7876 if (err) {7877 dev_warn(mlxsw_sp->bus_info->dev, "FIB replace failed.\n");7878 mlxsw_sp_fib4_offload_failed_flag_set(mlxsw_sp,7879 &fib_work->fen_info);7880 }7881 fib_info_put(fib_work->fen_info.fi);7882 break;7883 case FIB_EVENT_ENTRY_DEL:7884 mlxsw_sp_router_fib4_del(mlxsw_sp, &fib_work->fen_info);7885 fib_info_put(fib_work->fen_info.fi);7886 break;7887 case FIB_EVENT_NH_ADD:7888 case FIB_EVENT_NH_DEL:7889 mlxsw_sp_nexthop4_event(mlxsw_sp, fib_work->event,7890 fib_work->fnh_info.fib_nh);7891 fib_info_put(fib_work->fnh_info.fib_nh->nh_parent);7892 break;7893 }7894 mutex_unlock(&mlxsw_sp->router->lock);7895 kfree(fib_work);7896}7897 7898static void mlxsw_sp_router_fib6_event_work(struct work_struct *work)7899{7900 struct mlxsw_sp_fib_event_work *fib_work =7901 container_of(work, struct mlxsw_sp_fib_event_work, work);7902 struct mlxsw_sp_fib6_event_work *fib6_work = &fib_work->fib6_work;7903 struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp;7904 int err;7905 7906 mutex_lock(&mlxsw_sp->router->lock);7907 mlxsw_sp_span_respin(mlxsw_sp);7908 7909 switch (fib_work->event) {7910 case FIB_EVENT_ENTRY_REPLACE:7911 err = mlxsw_sp_router_fib6_replace(mlxsw_sp,7912 fib6_work->rt_arr,7913 fib6_work->nrt6);7914 if (err) {7915 dev_warn(mlxsw_sp->bus_info->dev, "FIB replace failed.\n");7916 mlxsw_sp_fib6_offload_failed_flag_set(mlxsw_sp,7917 fib6_work->rt_arr,7918 fib6_work->nrt6);7919 }7920 mlxsw_sp_router_fib6_work_fini(fib6_work);7921 break;7922 case FIB_EVENT_ENTRY_APPEND:7923 err = mlxsw_sp_router_fib6_append(mlxsw_sp,7924 fib6_work->rt_arr,7925 fib6_work->nrt6);7926 if (err) {7927 dev_warn(mlxsw_sp->bus_info->dev, "FIB append failed.\n");7928 mlxsw_sp_fib6_offload_failed_flag_set(mlxsw_sp,7929 fib6_work->rt_arr,7930 fib6_work->nrt6);7931 }7932 mlxsw_sp_router_fib6_work_fini(fib6_work);7933 break;7934 case FIB_EVENT_ENTRY_DEL:7935 mlxsw_sp_router_fib6_del(mlxsw_sp,7936 fib6_work->rt_arr,7937 fib6_work->nrt6);7938 mlxsw_sp_router_fib6_work_fini(fib6_work);7939 break;7940 }7941 mutex_unlock(&mlxsw_sp->router->lock);7942 kfree(fib_work);7943}7944 7945static void mlxsw_sp_router_fibmr_event_work(struct work_struct *work)7946{7947 struct mlxsw_sp_fib_event_work *fib_work =7948 container_of(work, struct mlxsw_sp_fib_event_work, work);7949 struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp;7950 bool replace;7951 int err;7952 7953 rtnl_lock();7954 mutex_lock(&mlxsw_sp->router->lock);7955 switch (fib_work->event) {7956 case FIB_EVENT_ENTRY_REPLACE:7957 case FIB_EVENT_ENTRY_ADD:7958 replace = fib_work->event == FIB_EVENT_ENTRY_REPLACE;7959 7960 err = mlxsw_sp_router_fibmr_add(mlxsw_sp, &fib_work->men_info,7961 replace);7962 if (err)7963 dev_warn(mlxsw_sp->bus_info->dev, "MR entry add failed.\n");7964 mr_cache_put(fib_work->men_info.mfc);7965 break;7966 case FIB_EVENT_ENTRY_DEL:7967 mlxsw_sp_router_fibmr_del(mlxsw_sp, &fib_work->men_info);7968 mr_cache_put(fib_work->men_info.mfc);7969 break;7970 case FIB_EVENT_VIF_ADD:7971 err = mlxsw_sp_router_fibmr_vif_add(mlxsw_sp,7972 &fib_work->ven_info);7973 if (err)7974 dev_warn(mlxsw_sp->bus_info->dev, "MR VIF add failed.\n");7975 netdev_put(fib_work->ven_info.dev, &fib_work->dev_tracker);7976 break;7977 case FIB_EVENT_VIF_DEL:7978 mlxsw_sp_router_fibmr_vif_del(mlxsw_sp,7979 &fib_work->ven_info);7980 netdev_put(fib_work->ven_info.dev, &fib_work->dev_tracker);7981 break;7982 }7983 mutex_unlock(&mlxsw_sp->router->lock);7984 rtnl_unlock();7985 kfree(fib_work);7986}7987 7988static void mlxsw_sp_router_fib4_event(struct mlxsw_sp_fib_event_work *fib_work,7989 struct fib_notifier_info *info)7990{7991 struct fib_entry_notifier_info *fen_info;7992 struct fib_nh_notifier_info *fnh_info;7993 7994 switch (fib_work->event) {7995 case FIB_EVENT_ENTRY_REPLACE:7996 case FIB_EVENT_ENTRY_DEL:7997 fen_info = container_of(info, struct fib_entry_notifier_info,7998 info);7999 fib_work->fen_info = *fen_info;8000 /* Take reference on fib_info to prevent it from being8001 * freed while work is queued. Release it afterwards.8002 */8003 fib_info_hold(fib_work->fen_info.fi);8004 break;8005 case FIB_EVENT_NH_ADD:8006 case FIB_EVENT_NH_DEL:8007 fnh_info = container_of(info, struct fib_nh_notifier_info,8008 info);8009 fib_work->fnh_info = *fnh_info;8010 fib_info_hold(fib_work->fnh_info.fib_nh->nh_parent);8011 break;8012 }8013}8014 8015static int mlxsw_sp_router_fib6_event(struct mlxsw_sp_fib_event_work *fib_work,8016 struct fib_notifier_info *info)8017{8018 struct fib6_entry_notifier_info *fen6_info;8019 int err;8020 8021 switch (fib_work->event) {8022 case FIB_EVENT_ENTRY_REPLACE:8023 case FIB_EVENT_ENTRY_APPEND:8024 case FIB_EVENT_ENTRY_DEL:8025 fen6_info = container_of(info, struct fib6_entry_notifier_info,8026 info);8027 err = mlxsw_sp_router_fib6_work_init(&fib_work->fib6_work,8028 fen6_info);8029 if (err)8030 return err;8031 break;8032 }8033 8034 return 0;8035}8036 8037static void8038mlxsw_sp_router_fibmr_event(struct mlxsw_sp_fib_event_work *fib_work,8039 struct fib_notifier_info *info)8040{8041 switch (fib_work->event) {8042 case FIB_EVENT_ENTRY_REPLACE:8043 case FIB_EVENT_ENTRY_ADD:8044 case FIB_EVENT_ENTRY_DEL:8045 memcpy(&fib_work->men_info, info, sizeof(fib_work->men_info));8046 mr_cache_hold(fib_work->men_info.mfc);8047 break;8048 case FIB_EVENT_VIF_ADD:8049 case FIB_EVENT_VIF_DEL:8050 memcpy(&fib_work->ven_info, info, sizeof(fib_work->ven_info));8051 netdev_hold(fib_work->ven_info.dev, &fib_work->dev_tracker,8052 GFP_ATOMIC);8053 break;8054 }8055}8056 8057static int mlxsw_sp_router_fib_rule_event(unsigned long event,8058 struct fib_notifier_info *info,8059 struct mlxsw_sp *mlxsw_sp)8060{8061 struct netlink_ext_ack *extack = info->extack;8062 struct fib_rule_notifier_info *fr_info;8063 struct fib_rule *rule;8064 int err = 0;8065 8066 /* nothing to do at the moment */8067 if (event == FIB_EVENT_RULE_DEL)8068 return 0;8069 8070 fr_info = container_of(info, struct fib_rule_notifier_info, info);8071 rule = fr_info->rule;8072 8073 /* Rule only affects locally generated traffic */8074 if (rule->iifindex == mlxsw_sp_net(mlxsw_sp)->loopback_dev->ifindex)8075 return 0;8076 8077 switch (info->family) {8078 case AF_INET:8079 if (!fib4_rule_default(rule) && !rule->l3mdev)8080 err = -EOPNOTSUPP;8081 break;8082 case AF_INET6:8083 if (!fib6_rule_default(rule) && !rule->l3mdev)8084 err = -EOPNOTSUPP;8085 break;8086 case RTNL_FAMILY_IPMR:8087 if (!ipmr_rule_default(rule) && !rule->l3mdev)8088 err = -EOPNOTSUPP;8089 break;8090 case RTNL_FAMILY_IP6MR:8091 if (!ip6mr_rule_default(rule) && !rule->l3mdev)8092 err = -EOPNOTSUPP;8093 break;8094 }8095 8096 if (err < 0)8097 NL_SET_ERR_MSG_MOD(extack, "FIB rules not supported");8098 8099 return err;8100}8101 8102/* Called with rcu_read_lock() */8103static int mlxsw_sp_router_fib_event(struct notifier_block *nb,8104 unsigned long event, void *ptr)8105{8106 struct mlxsw_sp_fib_event_work *fib_work;8107 struct fib_notifier_info *info = ptr;8108 struct mlxsw_sp_router *router;8109 int err;8110 8111 if ((info->family != AF_INET && info->family != AF_INET6 &&8112 info->family != RTNL_FAMILY_IPMR &&8113 info->family != RTNL_FAMILY_IP6MR))8114 return NOTIFY_DONE;8115 8116 router = container_of(nb, struct mlxsw_sp_router, fib_nb);8117 8118 switch (event) {8119 case FIB_EVENT_RULE_ADD:8120 case FIB_EVENT_RULE_DEL:8121 err = mlxsw_sp_router_fib_rule_event(event, info,8122 router->mlxsw_sp);8123 return notifier_from_errno(err);8124 case FIB_EVENT_ENTRY_ADD:8125 case FIB_EVENT_ENTRY_REPLACE:8126 case FIB_EVENT_ENTRY_APPEND:8127 if (info->family == AF_INET) {8128 struct fib_entry_notifier_info *fen_info = ptr;8129 8130 if (fen_info->fi->fib_nh_is_v6) {8131 NL_SET_ERR_MSG_MOD(info->extack, "IPv6 gateway with IPv4 route is not supported");8132 return notifier_from_errno(-EINVAL);8133 }8134 }8135 break;8136 }8137 8138 fib_work = kzalloc(sizeof(*fib_work), GFP_ATOMIC);8139 if (!fib_work)8140 return NOTIFY_BAD;8141 8142 fib_work->mlxsw_sp = router->mlxsw_sp;8143 fib_work->event = event;8144 8145 switch (info->family) {8146 case AF_INET:8147 INIT_WORK(&fib_work->work, mlxsw_sp_router_fib4_event_work);8148 mlxsw_sp_router_fib4_event(fib_work, info);8149 break;8150 case AF_INET6:8151 INIT_WORK(&fib_work->work, mlxsw_sp_router_fib6_event_work);8152 err = mlxsw_sp_router_fib6_event(fib_work, info);8153 if (err)8154 goto err_fib_event;8155 break;8156 case RTNL_FAMILY_IP6MR:8157 case RTNL_FAMILY_IPMR:8158 INIT_WORK(&fib_work->work, mlxsw_sp_router_fibmr_event_work);8159 mlxsw_sp_router_fibmr_event(fib_work, info);8160 break;8161 }8162 8163 mlxsw_core_schedule_work(&fib_work->work);8164 8165 return NOTIFY_DONE;8166 8167err_fib_event:8168 kfree(fib_work);8169 return NOTIFY_BAD;8170}8171 8172static struct mlxsw_sp_rif *8173mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp,8174 const struct net_device *dev)8175{8176 int max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);8177 int i;8178 8179 for (i = 0; i < max_rifs; i++)8180 if (mlxsw_sp->router->rifs[i] &&8181 mlxsw_sp_rif_dev_is(mlxsw_sp->router->rifs[i], dev))8182 return mlxsw_sp->router->rifs[i];8183 8184 return NULL;8185}8186 8187bool mlxsw_sp_rif_exists(struct mlxsw_sp *mlxsw_sp,8188 const struct net_device *dev)8189{8190 struct mlxsw_sp_rif *rif;8191 8192 mutex_lock(&mlxsw_sp->router->lock);8193 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);8194 mutex_unlock(&mlxsw_sp->router->lock);8195 8196 return rif;8197}8198 8199u16 mlxsw_sp_rif_vid(struct mlxsw_sp *mlxsw_sp, const struct net_device *dev)8200{8201 struct mlxsw_sp_rif *rif;8202 u16 vid = 0;8203 8204 mutex_lock(&mlxsw_sp->router->lock);8205 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);8206 if (!rif)8207 goto out;8208 8209 /* We only return the VID for VLAN RIFs. Otherwise we return an8210 * invalid value (0).8211 */8212 if (rif->ops->type != MLXSW_SP_RIF_TYPE_VLAN)8213 goto out;8214 8215 vid = mlxsw_sp_fid_8021q_vid(rif->fid);8216 8217out:8218 mutex_unlock(&mlxsw_sp->router->lock);8219 return vid;8220}8221 8222static int mlxsw_sp_router_rif_disable(struct mlxsw_sp *mlxsw_sp, u16 rif)8223{8224 char ritr_pl[MLXSW_REG_RITR_LEN];8225 int err;8226 8227 mlxsw_reg_ritr_rif_pack(ritr_pl, rif);8228 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);8229 if (err)8230 return err;8231 8232 mlxsw_reg_ritr_enable_set(ritr_pl, false);8233 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);8234}8235 8236static int mlxsw_sp_router_rif_made_sync(struct mlxsw_sp *mlxsw_sp,8237 struct mlxsw_sp_rif *rif)8238{8239 int err;8240 8241 err = mlxsw_sp_neigh_rif_made_sync(mlxsw_sp, rif);8242 if (err)8243 return err;8244 8245 err = mlxsw_sp_nexthop_rif_made_sync(mlxsw_sp, rif);8246 if (err)8247 goto err_nexthop;8248 8249 return 0;8250 8251err_nexthop:8252 mlxsw_sp_neigh_rif_gone_sync(mlxsw_sp, rif);8253 return err;8254}8255 8256static void mlxsw_sp_router_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,8257 struct mlxsw_sp_rif *rif)8258{8259 /* Signal to nexthop cleanup that the RIF is going away. */8260 rif->crif->rif = NULL;8261 8262 mlxsw_sp_router_rif_disable(mlxsw_sp, rif->rif_index);8263 mlxsw_sp_nexthop_rif_gone_sync(mlxsw_sp, rif);8264 mlxsw_sp_neigh_rif_gone_sync(mlxsw_sp, rif);8265}8266 8267static bool __mlxsw_sp_dev_addr_list_empty(const struct net_device *dev)8268{8269 struct inet6_dev *inet6_dev;8270 struct in_device *idev;8271 8272 idev = __in_dev_get_rcu(dev);8273 if (idev && idev->ifa_list)8274 return false;8275 8276 inet6_dev = __in6_dev_get(dev);8277 if (inet6_dev && !list_empty(&inet6_dev->addr_list))8278 return false;8279 8280 return true;8281}8282 8283static bool mlxsw_sp_dev_addr_list_empty(const struct net_device *dev)8284{8285 bool addr_list_empty;8286 8287 rcu_read_lock();8288 addr_list_empty = __mlxsw_sp_dev_addr_list_empty(dev);8289 rcu_read_unlock();8290 8291 return addr_list_empty;8292}8293 8294static bool8295mlxsw_sp_rif_should_config(struct mlxsw_sp_rif *rif, struct net_device *dev,8296 unsigned long event)8297{8298 bool addr_list_empty;8299 8300 switch (event) {8301 case NETDEV_UP:8302 return rif == NULL;8303 case NETDEV_DOWN:8304 addr_list_empty = mlxsw_sp_dev_addr_list_empty(dev);8305 8306 /* macvlans do not have a RIF, but rather piggy back on the8307 * RIF of their lower device.8308 */8309 if (netif_is_macvlan(dev) && addr_list_empty)8310 return true;8311 8312 if (rif && addr_list_empty &&8313 !netif_is_l3_slave(mlxsw_sp_rif_dev(rif)))8314 return true;8315 /* It is possible we already removed the RIF ourselves8316 * if it was assigned to a netdev that is now a bridge8317 * or LAG slave.8318 */8319 return false;8320 }8321 8322 return false;8323}8324 8325static enum mlxsw_sp_rif_type8326mlxsw_sp_dev_rif_type(const struct mlxsw_sp *mlxsw_sp,8327 const struct net_device *dev)8328{8329 enum mlxsw_sp_fid_type type;8330 8331 if (mlxsw_sp_netdev_ipip_type(mlxsw_sp, dev, NULL))8332 return MLXSW_SP_RIF_TYPE_IPIP_LB;8333 8334 /* Otherwise RIF type is derived from the type of the underlying FID. */8335 if (is_vlan_dev(dev) && netif_is_bridge_master(vlan_dev_real_dev(dev)))8336 type = MLXSW_SP_FID_TYPE_8021Q;8337 else if (netif_is_bridge_master(dev) && br_vlan_enabled(dev))8338 type = MLXSW_SP_FID_TYPE_8021Q;8339 else if (netif_is_bridge_master(dev))8340 type = MLXSW_SP_FID_TYPE_8021D;8341 else8342 type = MLXSW_SP_FID_TYPE_RFID;8343 8344 return mlxsw_sp_fid_type_rif_type(mlxsw_sp, type);8345}8346 8347static int mlxsw_sp_rif_index_alloc(struct mlxsw_sp *mlxsw_sp, u16 *p_rif_index,8348 u8 rif_entries)8349{8350 *p_rif_index = gen_pool_alloc(mlxsw_sp->router->rifs_table,8351 rif_entries);8352 if (*p_rif_index == 0)8353 return -ENOBUFS;8354 *p_rif_index -= MLXSW_SP_ROUTER_GENALLOC_OFFSET;8355 8356 /* RIF indexes must be aligned to the allocation size. */8357 WARN_ON_ONCE(*p_rif_index % rif_entries);8358 8359 return 0;8360}8361 8362static void mlxsw_sp_rif_index_free(struct mlxsw_sp *mlxsw_sp, u16 rif_index,8363 u8 rif_entries)8364{8365 gen_pool_free(mlxsw_sp->router->rifs_table,8366 MLXSW_SP_ROUTER_GENALLOC_OFFSET + rif_index, rif_entries);8367}8368 8369static struct mlxsw_sp_rif *mlxsw_sp_rif_alloc(size_t rif_size, u16 rif_index,8370 u16 vr_id,8371 struct mlxsw_sp_crif *crif)8372{8373 struct net_device *l3_dev = crif ? crif->key.dev : NULL;8374 struct mlxsw_sp_rif *rif;8375 8376 rif = kzalloc(rif_size, GFP_KERNEL);8377 if (!rif)8378 return NULL;8379 8380 INIT_LIST_HEAD(&rif->neigh_list);8381 if (l3_dev) {8382 ether_addr_copy(rif->addr, l3_dev->dev_addr);8383 rif->mtu = l3_dev->mtu;8384 }8385 rif->vr_id = vr_id;8386 rif->rif_index = rif_index;8387 if (crif) {8388 rif->crif = crif;8389 crif->rif = rif;8390 }8391 8392 return rif;8393}8394 8395static void mlxsw_sp_rif_free(struct mlxsw_sp_rif *rif)8396{8397 WARN_ON(!list_empty(&rif->neigh_list));8398 8399 if (rif->crif)8400 rif->crif->rif = NULL;8401 kfree(rif);8402}8403 8404struct mlxsw_sp_rif *mlxsw_sp_rif_by_index(const struct mlxsw_sp *mlxsw_sp,8405 u16 rif_index)8406{8407 return mlxsw_sp->router->rifs[rif_index];8408}8409 8410u16 mlxsw_sp_rif_index(const struct mlxsw_sp_rif *rif)8411{8412 return rif->rif_index;8413}8414 8415u16 mlxsw_sp_ipip_lb_rif_index(const struct mlxsw_sp_rif_ipip_lb *lb_rif)8416{8417 return lb_rif->common.rif_index;8418}8419 8420u16 mlxsw_sp_ipip_lb_ul_vr_id(const struct mlxsw_sp_rif_ipip_lb *lb_rif)8421{8422 struct net_device *dev = mlxsw_sp_rif_dev(&lb_rif->common);8423 u32 ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(dev);8424 struct mlxsw_sp_vr *ul_vr;8425 8426 ul_vr = mlxsw_sp_vr_get(lb_rif->common.mlxsw_sp, ul_tb_id, NULL);8427 if (WARN_ON(IS_ERR(ul_vr)))8428 return 0;8429 8430 return ul_vr->id;8431}8432 8433u16 mlxsw_sp_ipip_lb_ul_rif_id(const struct mlxsw_sp_rif_ipip_lb *lb_rif)8434{8435 return lb_rif->ul_rif_id;8436}8437 8438static bool8439mlxsw_sp_router_port_l3_stats_enabled(struct mlxsw_sp_rif *rif)8440{8441 return mlxsw_sp_rif_counter_valid_get(rif,8442 MLXSW_SP_RIF_COUNTER_EGRESS) &&8443 mlxsw_sp_rif_counter_valid_get(rif,8444 MLXSW_SP_RIF_COUNTER_INGRESS);8445}8446 8447static int8448mlxsw_sp_router_port_l3_stats_enable(struct mlxsw_sp_rif *rif)8449{8450 int err;8451 8452 err = mlxsw_sp_rif_counter_alloc(rif, MLXSW_SP_RIF_COUNTER_INGRESS);8453 if (err)8454 return err;8455 8456 /* Clear stale data. */8457 err = mlxsw_sp_rif_counter_fetch_clear(rif,8458 MLXSW_SP_RIF_COUNTER_INGRESS,8459 NULL);8460 if (err)8461 goto err_clear_ingress;8462 8463 err = mlxsw_sp_rif_counter_alloc(rif, MLXSW_SP_RIF_COUNTER_EGRESS);8464 if (err)8465 goto err_alloc_egress;8466 8467 /* Clear stale data. */8468 err = mlxsw_sp_rif_counter_fetch_clear(rif,8469 MLXSW_SP_RIF_COUNTER_EGRESS,8470 NULL);8471 if (err)8472 goto err_clear_egress;8473 8474 return 0;8475 8476err_clear_egress:8477 mlxsw_sp_rif_counter_free(rif, MLXSW_SP_RIF_COUNTER_EGRESS);8478err_alloc_egress:8479err_clear_ingress:8480 mlxsw_sp_rif_counter_free(rif, MLXSW_SP_RIF_COUNTER_INGRESS);8481 return err;8482}8483 8484static void8485mlxsw_sp_router_port_l3_stats_disable(struct mlxsw_sp_rif *rif)8486{8487 mlxsw_sp_rif_counter_free(rif, MLXSW_SP_RIF_COUNTER_EGRESS);8488 mlxsw_sp_rif_counter_free(rif, MLXSW_SP_RIF_COUNTER_INGRESS);8489}8490 8491static void8492mlxsw_sp_router_port_l3_stats_report_used(struct mlxsw_sp_rif *rif,8493 struct netdev_notifier_offload_xstats_info *info)8494{8495 if (!mlxsw_sp_router_port_l3_stats_enabled(rif))8496 return;8497 netdev_offload_xstats_report_used(info->report_used);8498}8499 8500static int8501mlxsw_sp_router_port_l3_stats_fetch(struct mlxsw_sp_rif *rif,8502 struct rtnl_hw_stats64 *p_stats)8503{8504 struct mlxsw_sp_rif_counter_set_basic ingress;8505 struct mlxsw_sp_rif_counter_set_basic egress;8506 int err;8507 8508 err = mlxsw_sp_rif_counter_fetch_clear(rif,8509 MLXSW_SP_RIF_COUNTER_INGRESS,8510 &ingress);8511 if (err)8512 return err;8513 8514 err = mlxsw_sp_rif_counter_fetch_clear(rif,8515 MLXSW_SP_RIF_COUNTER_EGRESS,8516 &egress);8517 if (err)8518 return err;8519 8520#define MLXSW_SP_ROUTER_ALL_GOOD(SET, SFX) \8521 ((SET.good_unicast_ ## SFX) + \8522 (SET.good_multicast_ ## SFX) + \8523 (SET.good_broadcast_ ## SFX))8524 8525 p_stats->rx_packets = MLXSW_SP_ROUTER_ALL_GOOD(ingress, packets);8526 p_stats->tx_packets = MLXSW_SP_ROUTER_ALL_GOOD(egress, packets);8527 p_stats->rx_bytes = MLXSW_SP_ROUTER_ALL_GOOD(ingress, bytes);8528 p_stats->tx_bytes = MLXSW_SP_ROUTER_ALL_GOOD(egress, bytes);8529 p_stats->rx_errors = ingress.error_packets;8530 p_stats->tx_errors = egress.error_packets;8531 p_stats->rx_dropped = ingress.discard_packets;8532 p_stats->tx_dropped = egress.discard_packets;8533 p_stats->multicast = ingress.good_multicast_packets +8534 ingress.good_broadcast_packets;8535 8536#undef MLXSW_SP_ROUTER_ALL_GOOD8537 8538 return 0;8539}8540 8541static int8542mlxsw_sp_router_port_l3_stats_report_delta(struct mlxsw_sp_rif *rif,8543 struct netdev_notifier_offload_xstats_info *info)8544{8545 struct rtnl_hw_stats64 stats = {};8546 int err;8547 8548 if (!mlxsw_sp_router_port_l3_stats_enabled(rif))8549 return 0;8550 8551 err = mlxsw_sp_router_port_l3_stats_fetch(rif, &stats);8552 if (err)8553 return err;8554 8555 netdev_offload_xstats_report_delta(info->report_delta, &stats);8556 return 0;8557}8558 8559struct mlxsw_sp_router_hwstats_notify_work {8560 struct work_struct work;8561 struct net_device *dev;8562 netdevice_tracker dev_tracker;8563};8564 8565static void mlxsw_sp_router_hwstats_notify_work(struct work_struct *work)8566{8567 struct mlxsw_sp_router_hwstats_notify_work *hws_work =8568 container_of(work, struct mlxsw_sp_router_hwstats_notify_work,8569 work);8570 8571 rtnl_lock();8572 rtnl_offload_xstats_notify(hws_work->dev);8573 rtnl_unlock();8574 netdev_put(hws_work->dev, &hws_work->dev_tracker);8575 kfree(hws_work);8576}8577 8578static void8579mlxsw_sp_router_hwstats_notify_schedule(struct net_device *dev)8580{8581 struct mlxsw_sp_router_hwstats_notify_work *hws_work;8582 8583 /* To collect notification payload, the core ends up sending another8584 * notifier block message, which would deadlock on the attempt to8585 * acquire the router lock again. Just postpone the notification until8586 * later.8587 */8588 8589 hws_work = kzalloc(sizeof(*hws_work), GFP_KERNEL);8590 if (!hws_work)8591 return;8592 8593 INIT_WORK(&hws_work->work, mlxsw_sp_router_hwstats_notify_work);8594 netdev_hold(dev, &hws_work->dev_tracker, GFP_KERNEL);8595 hws_work->dev = dev;8596 mlxsw_core_schedule_work(&hws_work->work);8597}8598 8599int mlxsw_sp_rif_dev_ifindex(const struct mlxsw_sp_rif *rif)8600{8601 return mlxsw_sp_rif_dev(rif)->ifindex;8602}8603 8604bool mlxsw_sp_rif_has_dev(const struct mlxsw_sp_rif *rif)8605{8606 return !!mlxsw_sp_rif_dev(rif);8607}8608 8609bool mlxsw_sp_rif_dev_is(const struct mlxsw_sp_rif *rif,8610 const struct net_device *dev)8611{8612 return mlxsw_sp_rif_dev(rif) == dev;8613}8614 8615static void mlxsw_sp_rif_push_l3_stats(struct mlxsw_sp_rif *rif)8616{8617 struct rtnl_hw_stats64 stats = {};8618 8619 if (!mlxsw_sp_router_port_l3_stats_fetch(rif, &stats))8620 netdev_offload_xstats_push_delta(mlxsw_sp_rif_dev(rif),8621 NETDEV_OFFLOAD_XSTATS_TYPE_L3,8622 &stats);8623}8624 8625static struct mlxsw_sp_rif *8626mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp,8627 const struct mlxsw_sp_rif_params *params,8628 struct netlink_ext_ack *extack)8629{8630 u8 rif_entries = params->double_entry ? 2 : 1;8631 u32 tb_id = l3mdev_fib_table(params->dev);8632 const struct mlxsw_sp_rif_ops *ops;8633 struct mlxsw_sp_fid *fid = NULL;8634 enum mlxsw_sp_rif_type type;8635 struct mlxsw_sp_crif *crif;8636 struct mlxsw_sp_rif *rif;8637 struct mlxsw_sp_vr *vr;8638 u16 rif_index;8639 int i, err;8640 8641 type = mlxsw_sp_dev_rif_type(mlxsw_sp, params->dev);8642 ops = mlxsw_sp->router->rif_ops_arr[type];8643 8644 vr = mlxsw_sp_vr_get(mlxsw_sp, tb_id ? : RT_TABLE_MAIN, extack);8645 if (IS_ERR(vr))8646 return ERR_CAST(vr);8647 vr->rif_count++;8648 8649 err = mlxsw_sp_rif_index_alloc(mlxsw_sp, &rif_index, rif_entries);8650 if (err) {8651 NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported router interfaces");8652 goto err_rif_index_alloc;8653 }8654 8655 crif = mlxsw_sp_crif_lookup(mlxsw_sp->router, params->dev);8656 if (WARN_ON(!crif)) {8657 err = -ENOENT;8658 goto err_crif_lookup;8659 }8660 8661 rif = mlxsw_sp_rif_alloc(ops->rif_size, rif_index, vr->id, crif);8662 if (!rif) {8663 err = -ENOMEM;8664 goto err_rif_alloc;8665 }8666 netdev_hold(params->dev, &rif->dev_tracker, GFP_KERNEL);8667 mlxsw_sp->router->rifs[rif_index] = rif;8668 rif->mlxsw_sp = mlxsw_sp;8669 rif->ops = ops;8670 rif->rif_entries = rif_entries;8671 8672 if (ops->setup)8673 ops->setup(rif, params);8674 8675 if (ops->fid_get) {8676 fid = ops->fid_get(rif, params, extack);8677 if (IS_ERR(fid)) {8678 err = PTR_ERR(fid);8679 goto err_fid_get;8680 }8681 rif->fid = fid;8682 }8683 8684 err = ops->configure(rif, extack);8685 if (err)8686 goto err_configure;8687 8688 for (i = 0; i < MLXSW_SP_L3_PROTO_MAX; i++) {8689 err = mlxsw_sp_mr_rif_add(vr->mr_table[i], rif);8690 if (err)8691 goto err_mr_rif_add;8692 }8693 8694 err = mlxsw_sp_router_rif_made_sync(mlxsw_sp, rif);8695 if (err)8696 goto err_rif_made_sync;8697 8698 if (netdev_offload_xstats_enabled(params->dev,8699 NETDEV_OFFLOAD_XSTATS_TYPE_L3)) {8700 err = mlxsw_sp_router_port_l3_stats_enable(rif);8701 if (err)8702 goto err_stats_enable;8703 mlxsw_sp_router_hwstats_notify_schedule(params->dev);8704 } else {8705 mlxsw_sp_rif_counters_alloc(rif);8706 }8707 8708 atomic_add(rif_entries, &mlxsw_sp->router->rifs_count);8709 return rif;8710 8711err_stats_enable:8712 mlxsw_sp_router_rif_gone_sync(mlxsw_sp, rif);8713err_rif_made_sync:8714err_mr_rif_add:8715 for (i--; i >= 0; i--)8716 mlxsw_sp_mr_rif_del(vr->mr_table[i], rif);8717 ops->deconfigure(rif);8718err_configure:8719 if (fid)8720 mlxsw_sp_fid_put(fid);8721err_fid_get:8722 mlxsw_sp->router->rifs[rif_index] = NULL;8723 netdev_put(params->dev, &rif->dev_tracker);8724 mlxsw_sp_rif_free(rif);8725err_rif_alloc:8726err_crif_lookup:8727 mlxsw_sp_rif_index_free(mlxsw_sp, rif_index, rif_entries);8728err_rif_index_alloc:8729 vr->rif_count--;8730 mlxsw_sp_vr_put(mlxsw_sp, vr);8731 return ERR_PTR(err);8732}8733 8734static void mlxsw_sp_rif_destroy(struct mlxsw_sp_rif *rif)8735{8736 struct net_device *dev = mlxsw_sp_rif_dev(rif);8737 const struct mlxsw_sp_rif_ops *ops = rif->ops;8738 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;8739 struct mlxsw_sp_crif *crif = rif->crif;8740 struct mlxsw_sp_fid *fid = rif->fid;8741 u8 rif_entries = rif->rif_entries;8742 u16 rif_index = rif->rif_index;8743 struct mlxsw_sp_vr *vr;8744 int i;8745 8746 atomic_sub(rif_entries, &mlxsw_sp->router->rifs_count);8747 mlxsw_sp_router_rif_gone_sync(mlxsw_sp, rif);8748 vr = &mlxsw_sp->router->vrs[rif->vr_id];8749 8750 if (netdev_offload_xstats_enabled(dev, NETDEV_OFFLOAD_XSTATS_TYPE_L3)) {8751 mlxsw_sp_rif_push_l3_stats(rif);8752 mlxsw_sp_router_port_l3_stats_disable(rif);8753 mlxsw_sp_router_hwstats_notify_schedule(dev);8754 } else {8755 mlxsw_sp_rif_counters_free(rif);8756 }8757 8758 for (i = 0; i < MLXSW_SP_L3_PROTO_MAX; i++)8759 mlxsw_sp_mr_rif_del(vr->mr_table[i], rif);8760 ops->deconfigure(rif);8761 if (fid)8762 /* Loopback RIFs are not associated with a FID. */8763 mlxsw_sp_fid_put(fid);8764 mlxsw_sp->router->rifs[rif->rif_index] = NULL;8765 netdev_put(dev, &rif->dev_tracker);8766 mlxsw_sp_rif_free(rif);8767 mlxsw_sp_rif_index_free(mlxsw_sp, rif_index, rif_entries);8768 vr->rif_count--;8769 mlxsw_sp_vr_put(mlxsw_sp, vr);8770 8771 if (crif->can_destroy)8772 mlxsw_sp_crif_free(crif);8773}8774 8775void mlxsw_sp_rif_destroy_by_dev(struct mlxsw_sp *mlxsw_sp,8776 struct net_device *dev)8777{8778 struct mlxsw_sp_rif *rif;8779 8780 mutex_lock(&mlxsw_sp->router->lock);8781 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);8782 if (!rif)8783 goto out;8784 mlxsw_sp_rif_destroy(rif);8785out:8786 mutex_unlock(&mlxsw_sp->router->lock);8787}8788 8789static void mlxsw_sp_rif_destroy_vlan_upper(struct mlxsw_sp *mlxsw_sp,8790 struct net_device *br_dev,8791 u16 vid)8792{8793 struct net_device *upper_dev;8794 struct mlxsw_sp_crif *crif;8795 8796 rcu_read_lock();8797 upper_dev = __vlan_find_dev_deep_rcu(br_dev, htons(ETH_P_8021Q), vid);8798 rcu_read_unlock();8799 8800 if (!upper_dev)8801 return;8802 8803 crif = mlxsw_sp_crif_lookup(mlxsw_sp->router, upper_dev);8804 if (!crif || !crif->rif)8805 return;8806 8807 mlxsw_sp_rif_destroy(crif->rif);8808}8809 8810static int mlxsw_sp_inetaddr_bridge_event(struct mlxsw_sp *mlxsw_sp,8811 struct net_device *l3_dev,8812 int lower_pvid,8813 unsigned long event,8814 struct netlink_ext_ack *extack);8815 8816int mlxsw_sp_router_bridge_vlan_add(struct mlxsw_sp *mlxsw_sp,8817 struct net_device *br_dev,8818 u16 new_vid, bool is_pvid,8819 struct netlink_ext_ack *extack)8820{8821 struct mlxsw_sp_rif *old_rif;8822 struct mlxsw_sp_rif *new_rif;8823 struct net_device *upper_dev;8824 u16 old_pvid = 0;8825 u16 new_pvid;8826 int err = 0;8827 8828 mutex_lock(&mlxsw_sp->router->lock);8829 old_rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, br_dev);8830 if (old_rif) {8831 /* If the RIF on the bridge is not a VLAN RIF, we shouldn't have8832 * gotten a PVID notification.8833 */8834 if (WARN_ON(old_rif->ops->type != MLXSW_SP_RIF_TYPE_VLAN))8835 old_rif = NULL;8836 else8837 old_pvid = mlxsw_sp_fid_8021q_vid(old_rif->fid);8838 }8839 8840 if (is_pvid)8841 new_pvid = new_vid;8842 else if (old_pvid == new_vid)8843 new_pvid = 0;8844 else8845 goto out;8846 8847 if (old_pvid == new_pvid)8848 goto out;8849 8850 if (new_pvid) {8851 struct mlxsw_sp_rif_params params = {8852 .dev = br_dev,8853 .vid = new_pvid,8854 };8855 8856 /* If there is a VLAN upper with the same VID as the new PVID,8857 * kill its RIF, if there is one.8858 */8859 mlxsw_sp_rif_destroy_vlan_upper(mlxsw_sp, br_dev, new_pvid);8860 8861 if (mlxsw_sp_dev_addr_list_empty(br_dev))8862 goto out;8863 new_rif = mlxsw_sp_rif_create(mlxsw_sp, ¶ms, extack);8864 if (IS_ERR(new_rif)) {8865 err = PTR_ERR(new_rif);8866 goto out;8867 }8868 8869 if (old_pvid)8870 mlxsw_sp_rif_migrate_destroy(mlxsw_sp, old_rif, new_rif,8871 true);8872 } else {8873 mlxsw_sp_rif_destroy(old_rif);8874 }8875 8876 if (old_pvid) {8877 rcu_read_lock();8878 upper_dev = __vlan_find_dev_deep_rcu(br_dev, htons(ETH_P_8021Q),8879 old_pvid);8880 rcu_read_unlock();8881 if (upper_dev)8882 err = mlxsw_sp_inetaddr_bridge_event(mlxsw_sp,8883 upper_dev,8884 new_pvid,8885 NETDEV_UP, extack);8886 }8887 8888out:8889 mutex_unlock(&mlxsw_sp->router->lock);8890 return err;8891}8892 8893static void8894mlxsw_sp_rif_subport_params_init(struct mlxsw_sp_rif_params *params,8895 struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)8896{8897 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;8898 8899 params->vid = mlxsw_sp_port_vlan->vid;8900 params->lag = mlxsw_sp_port->lagged;8901 if (params->lag)8902 params->lag_id = mlxsw_sp_port->lag_id;8903 else8904 params->system_port = mlxsw_sp_port->local_port;8905}8906 8907static struct mlxsw_sp_rif_subport *8908mlxsw_sp_rif_subport_rif(const struct mlxsw_sp_rif *rif)8909{8910 return container_of(rif, struct mlxsw_sp_rif_subport, common);8911}8912 8913int mlxsw_sp_rif_subport_port(const struct mlxsw_sp_rif *rif,8914 u16 *port, bool *is_lag)8915{8916 struct mlxsw_sp_rif_subport *rif_subport;8917 8918 if (WARN_ON(rif->ops->type != MLXSW_SP_RIF_TYPE_SUBPORT))8919 return -EINVAL;8920 8921 rif_subport = mlxsw_sp_rif_subport_rif(rif);8922 *is_lag = rif_subport->lag;8923 *port = *is_lag ? rif_subport->lag_id : rif_subport->system_port;8924 return 0;8925}8926 8927static struct mlxsw_sp_rif *8928mlxsw_sp_rif_subport_get(struct mlxsw_sp *mlxsw_sp,8929 const struct mlxsw_sp_rif_params *params,8930 struct netlink_ext_ack *extack)8931{8932 struct mlxsw_sp_rif_subport *rif_subport;8933 struct mlxsw_sp_rif *rif;8934 8935 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, params->dev);8936 if (!rif)8937 return mlxsw_sp_rif_create(mlxsw_sp, params, extack);8938 8939 rif_subport = mlxsw_sp_rif_subport_rif(rif);8940 refcount_inc(&rif_subport->ref_count);8941 return rif;8942}8943 8944static void mlxsw_sp_rif_subport_put(struct mlxsw_sp_rif *rif)8945{8946 struct mlxsw_sp_rif_subport *rif_subport;8947 8948 rif_subport = mlxsw_sp_rif_subport_rif(rif);8949 if (!refcount_dec_and_test(&rif_subport->ref_count))8950 return;8951 8952 mlxsw_sp_rif_destroy(rif);8953}8954 8955static int mlxsw_sp_rif_mac_profile_index_alloc(struct mlxsw_sp *mlxsw_sp,8956 struct mlxsw_sp_rif_mac_profile *profile,8957 struct netlink_ext_ack *extack)8958{8959 u8 max_rif_mac_profiles = mlxsw_sp->router->max_rif_mac_profile;8960 struct mlxsw_sp_router *router = mlxsw_sp->router;8961 int id;8962 8963 id = idr_alloc(&router->rif_mac_profiles_idr, profile, 0,8964 max_rif_mac_profiles, GFP_KERNEL);8965 8966 if (id >= 0) {8967 profile->id = id;8968 return 0;8969 }8970 8971 if (id == -ENOSPC)8972 NL_SET_ERR_MSG_MOD(extack,8973 "Exceeded number of supported router interface MAC profiles");8974 8975 return id;8976}8977 8978static struct mlxsw_sp_rif_mac_profile *8979mlxsw_sp_rif_mac_profile_index_free(struct mlxsw_sp *mlxsw_sp, u8 mac_profile)8980{8981 struct mlxsw_sp_rif_mac_profile *profile;8982 8983 profile = idr_remove(&mlxsw_sp->router->rif_mac_profiles_idr,8984 mac_profile);8985 WARN_ON(!profile);8986 return profile;8987}8988 8989static struct mlxsw_sp_rif_mac_profile *8990mlxsw_sp_rif_mac_profile_alloc(const char *mac)8991{8992 struct mlxsw_sp_rif_mac_profile *profile;8993 8994 profile = kzalloc(sizeof(*profile), GFP_KERNEL);8995 if (!profile)8996 return NULL;8997 8998 ether_addr_copy(profile->mac_prefix, mac);8999 refcount_set(&profile->ref_count, 1);9000 return profile;9001}9002 9003static struct mlxsw_sp_rif_mac_profile *9004mlxsw_sp_rif_mac_profile_find(const struct mlxsw_sp *mlxsw_sp, const char *mac)9005{9006 struct mlxsw_sp_router *router = mlxsw_sp->router;9007 struct mlxsw_sp_rif_mac_profile *profile;9008 int id;9009 9010 idr_for_each_entry(&router->rif_mac_profiles_idr, profile, id) {9011 if (ether_addr_equal_masked(profile->mac_prefix, mac,9012 mlxsw_sp->mac_mask))9013 return profile;9014 }9015 9016 return NULL;9017}9018 9019static u64 mlxsw_sp_rif_mac_profiles_occ_get(void *priv)9020{9021 const struct mlxsw_sp *mlxsw_sp = priv;9022 9023 return atomic_read(&mlxsw_sp->router->rif_mac_profiles_count);9024}9025 9026static u64 mlxsw_sp_rifs_occ_get(void *priv)9027{9028 const struct mlxsw_sp *mlxsw_sp = priv;9029 9030 return atomic_read(&mlxsw_sp->router->rifs_count);9031}9032 9033static struct mlxsw_sp_rif_mac_profile *9034mlxsw_sp_rif_mac_profile_create(struct mlxsw_sp *mlxsw_sp, const char *mac,9035 struct netlink_ext_ack *extack)9036{9037 struct mlxsw_sp_rif_mac_profile *profile;9038 int err;9039 9040 profile = mlxsw_sp_rif_mac_profile_alloc(mac);9041 if (!profile)9042 return ERR_PTR(-ENOMEM);9043 9044 err = mlxsw_sp_rif_mac_profile_index_alloc(mlxsw_sp, profile, extack);9045 if (err)9046 goto profile_index_alloc_err;9047 9048 atomic_inc(&mlxsw_sp->router->rif_mac_profiles_count);9049 return profile;9050 9051profile_index_alloc_err:9052 kfree(profile);9053 return ERR_PTR(err);9054}9055 9056static void mlxsw_sp_rif_mac_profile_destroy(struct mlxsw_sp *mlxsw_sp,9057 u8 mac_profile)9058{9059 struct mlxsw_sp_rif_mac_profile *profile;9060 9061 atomic_dec(&mlxsw_sp->router->rif_mac_profiles_count);9062 profile = mlxsw_sp_rif_mac_profile_index_free(mlxsw_sp, mac_profile);9063 kfree(profile);9064}9065 9066static int mlxsw_sp_rif_mac_profile_get(struct mlxsw_sp *mlxsw_sp,9067 const char *mac, u8 *p_mac_profile,9068 struct netlink_ext_ack *extack)9069{9070 struct mlxsw_sp_rif_mac_profile *profile;9071 9072 profile = mlxsw_sp_rif_mac_profile_find(mlxsw_sp, mac);9073 if (profile) {9074 refcount_inc(&profile->ref_count);9075 goto out;9076 }9077 9078 profile = mlxsw_sp_rif_mac_profile_create(mlxsw_sp, mac, extack);9079 if (IS_ERR(profile))9080 return PTR_ERR(profile);9081 9082out:9083 *p_mac_profile = profile->id;9084 return 0;9085}9086 9087static void mlxsw_sp_rif_mac_profile_put(struct mlxsw_sp *mlxsw_sp,9088 u8 mac_profile)9089{9090 struct mlxsw_sp_rif_mac_profile *profile;9091 9092 profile = idr_find(&mlxsw_sp->router->rif_mac_profiles_idr,9093 mac_profile);9094 if (WARN_ON(!profile))9095 return;9096 9097 if (!refcount_dec_and_test(&profile->ref_count))9098 return;9099 9100 mlxsw_sp_rif_mac_profile_destroy(mlxsw_sp, mac_profile);9101}9102 9103static bool mlxsw_sp_rif_mac_profile_is_shared(const struct mlxsw_sp_rif *rif)9104{9105 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;9106 struct mlxsw_sp_rif_mac_profile *profile;9107 9108 profile = idr_find(&mlxsw_sp->router->rif_mac_profiles_idr,9109 rif->mac_profile_id);9110 if (WARN_ON(!profile))9111 return false;9112 9113 return refcount_read(&profile->ref_count) > 1;9114}9115 9116static int mlxsw_sp_rif_mac_profile_edit(struct mlxsw_sp_rif *rif,9117 const char *new_mac)9118{9119 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;9120 struct mlxsw_sp_rif_mac_profile *profile;9121 9122 profile = idr_find(&mlxsw_sp->router->rif_mac_profiles_idr,9123 rif->mac_profile_id);9124 if (WARN_ON(!profile))9125 return -EINVAL;9126 9127 ether_addr_copy(profile->mac_prefix, new_mac);9128 return 0;9129}9130 9131static int9132mlxsw_sp_rif_mac_profile_replace(struct mlxsw_sp *mlxsw_sp,9133 struct mlxsw_sp_rif *rif,9134 const char *new_mac,9135 struct netlink_ext_ack *extack)9136{9137 u8 mac_profile;9138 int err;9139 9140 if (!mlxsw_sp_rif_mac_profile_is_shared(rif) &&9141 !mlxsw_sp_rif_mac_profile_find(mlxsw_sp, new_mac))9142 return mlxsw_sp_rif_mac_profile_edit(rif, new_mac);9143 9144 err = mlxsw_sp_rif_mac_profile_get(mlxsw_sp, new_mac,9145 &mac_profile, extack);9146 if (err)9147 return err;9148 9149 mlxsw_sp_rif_mac_profile_put(mlxsw_sp, rif->mac_profile_id);9150 rif->mac_profile_id = mac_profile;9151 return 0;9152}9153 9154static int9155__mlxsw_sp_port_vlan_router_join(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan,9156 struct net_device *l3_dev,9157 struct netlink_ext_ack *extack)9158{9159 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;9160 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;9161 struct mlxsw_sp_rif_params params;9162 u16 vid = mlxsw_sp_port_vlan->vid;9163 struct mlxsw_sp_rif *rif;9164 struct mlxsw_sp_fid *fid;9165 int err;9166 9167 params = (struct mlxsw_sp_rif_params) {9168 .dev = l3_dev,9169 .vid = vid,9170 };9171 9172 mlxsw_sp_rif_subport_params_init(¶ms, mlxsw_sp_port_vlan);9173 rif = mlxsw_sp_rif_subport_get(mlxsw_sp, ¶ms, extack);9174 if (IS_ERR(rif))9175 return PTR_ERR(rif);9176 9177 /* FID was already created, just take a reference */9178 fid = rif->ops->fid_get(rif, ¶ms, extack);9179 err = mlxsw_sp_fid_port_vid_map(fid, mlxsw_sp_port, vid);9180 if (err)9181 goto err_fid_port_vid_map;9182 9183 err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, false);9184 if (err)9185 goto err_port_vid_learning_set;9186 9187 err = mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid,9188 BR_STATE_FORWARDING);9189 if (err)9190 goto err_port_vid_stp_set;9191 9192 mlxsw_sp_port_vlan->fid = fid;9193 9194 return 0;9195 9196err_port_vid_stp_set:9197 mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true);9198err_port_vid_learning_set:9199 mlxsw_sp_fid_port_vid_unmap(fid, mlxsw_sp_port, vid);9200err_fid_port_vid_map:9201 mlxsw_sp_fid_put(fid);9202 mlxsw_sp_rif_subport_put(rif);9203 return err;9204}9205 9206static void9207__mlxsw_sp_port_vlan_router_leave(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)9208{9209 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;9210 struct mlxsw_sp_fid *fid = mlxsw_sp_port_vlan->fid;9211 struct mlxsw_sp_rif *rif = mlxsw_sp_fid_rif(fid);9212 u16 vid = mlxsw_sp_port_vlan->vid;9213 9214 if (WARN_ON(mlxsw_sp_fid_type(fid) != MLXSW_SP_FID_TYPE_RFID))9215 return;9216 9217 mlxsw_sp_port_vlan->fid = NULL;9218 mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid, BR_STATE_BLOCKING);9219 mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true);9220 mlxsw_sp_fid_port_vid_unmap(fid, mlxsw_sp_port, vid);9221 mlxsw_sp_fid_put(fid);9222 mlxsw_sp_rif_subport_put(rif);9223}9224 9225static int9226mlxsw_sp_port_vlan_router_join_existing(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan,9227 struct net_device *l3_dev,9228 struct netlink_ext_ack *extack)9229{9230 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port_vlan->mlxsw_sp_port->mlxsw_sp;9231 9232 lockdep_assert_held(&mlxsw_sp->router->lock);9233 9234 if (!mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev))9235 return 0;9236 9237 return __mlxsw_sp_port_vlan_router_join(mlxsw_sp_port_vlan, l3_dev,9238 extack);9239}9240 9241void9242mlxsw_sp_port_vlan_router_leave(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)9243{9244 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port_vlan->mlxsw_sp_port->mlxsw_sp;9245 9246 mutex_lock(&mlxsw_sp->router->lock);9247 __mlxsw_sp_port_vlan_router_leave(mlxsw_sp_port_vlan);9248 mutex_unlock(&mlxsw_sp->router->lock);9249}9250 9251static int mlxsw_sp_inetaddr_port_vlan_event(struct net_device *l3_dev,9252 struct net_device *port_dev,9253 unsigned long event, u16 vid,9254 struct netlink_ext_ack *extack)9255{9256 struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(port_dev);9257 struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;9258 9259 mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid);9260 if (WARN_ON(!mlxsw_sp_port_vlan))9261 return -EINVAL;9262 9263 switch (event) {9264 case NETDEV_UP:9265 return __mlxsw_sp_port_vlan_router_join(mlxsw_sp_port_vlan,9266 l3_dev, extack);9267 case NETDEV_DOWN:9268 __mlxsw_sp_port_vlan_router_leave(mlxsw_sp_port_vlan);9269 break;9270 }9271 9272 return 0;9273}9274 9275static int mlxsw_sp_inetaddr_port_event(struct net_device *port_dev,9276 unsigned long event, bool nomaster,9277 struct netlink_ext_ack *extack)9278{9279 if (!nomaster && (netif_is_any_bridge_port(port_dev) ||9280 netif_is_lag_port(port_dev)))9281 return 0;9282 9283 return mlxsw_sp_inetaddr_port_vlan_event(port_dev, port_dev, event,9284 MLXSW_SP_DEFAULT_VID, extack);9285}9286 9287static int __mlxsw_sp_inetaddr_lag_event(struct net_device *l3_dev,9288 struct net_device *lag_dev,9289 unsigned long event, u16 vid,9290 struct netlink_ext_ack *extack)9291{9292 struct net_device *port_dev;9293 struct list_head *iter;9294 int err;9295 9296 netdev_for_each_lower_dev(lag_dev, port_dev, iter) {9297 if (mlxsw_sp_port_dev_check(port_dev)) {9298 err = mlxsw_sp_inetaddr_port_vlan_event(l3_dev,9299 port_dev,9300 event, vid,9301 extack);9302 if (err)9303 return err;9304 }9305 }9306 9307 return 0;9308}9309 9310static int mlxsw_sp_inetaddr_lag_event(struct net_device *lag_dev,9311 unsigned long event, bool nomaster,9312 struct netlink_ext_ack *extack)9313{9314 if (!nomaster && netif_is_bridge_port(lag_dev))9315 return 0;9316 9317 return __mlxsw_sp_inetaddr_lag_event(lag_dev, lag_dev, event,9318 MLXSW_SP_DEFAULT_VID, extack);9319}9320 9321static int mlxsw_sp_inetaddr_bridge_event(struct mlxsw_sp *mlxsw_sp,9322 struct net_device *l3_dev,9323 int lower_pvid,9324 unsigned long event,9325 struct netlink_ext_ack *extack)9326{9327 struct mlxsw_sp_rif_params params = {9328 .dev = l3_dev,9329 };9330 struct mlxsw_sp_rif *rif;9331 int err;9332 9333 switch (event) {9334 case NETDEV_UP:9335 if (netif_is_bridge_master(l3_dev) && br_vlan_enabled(l3_dev)) {9336 u16 proto;9337 9338 br_vlan_get_proto(l3_dev, &proto);9339 if (proto == ETH_P_8021AD) {9340 NL_SET_ERR_MSG_MOD(extack, "Adding an IP address to 802.1ad bridge is not supported");9341 return -EOPNOTSUPP;9342 }9343 err = br_vlan_get_pvid(l3_dev, ¶ms.vid);9344 if (err)9345 return err;9346 if (!params.vid)9347 return 0;9348 } else if (is_vlan_dev(l3_dev)) {9349 params.vid = vlan_dev_vlan_id(l3_dev);9350 9351 /* If the VID matches PVID of the bridge below, the9352 * bridge owns the RIF for this VLAN. Don't do anything.9353 */9354 if ((int)params.vid == lower_pvid)9355 return 0;9356 }9357 9358 rif = mlxsw_sp_rif_create(mlxsw_sp, ¶ms, extack);9359 if (IS_ERR(rif))9360 return PTR_ERR(rif);9361 break;9362 case NETDEV_DOWN:9363 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);9364 mlxsw_sp_rif_destroy(rif);9365 break;9366 }9367 9368 return 0;9369}9370 9371static int mlxsw_sp_inetaddr_vlan_event(struct mlxsw_sp *mlxsw_sp,9372 struct net_device *vlan_dev,9373 unsigned long event, bool nomaster,9374 struct netlink_ext_ack *extack)9375{9376 struct net_device *real_dev = vlan_dev_real_dev(vlan_dev);9377 u16 vid = vlan_dev_vlan_id(vlan_dev);9378 u16 lower_pvid;9379 int err;9380 9381 if (!nomaster && netif_is_bridge_port(vlan_dev))9382 return 0;9383 9384 if (mlxsw_sp_port_dev_check(real_dev)) {9385 return mlxsw_sp_inetaddr_port_vlan_event(vlan_dev, real_dev,9386 event, vid, extack);9387 } else if (netif_is_lag_master(real_dev)) {9388 return __mlxsw_sp_inetaddr_lag_event(vlan_dev, real_dev, event,9389 vid, extack);9390 } else if (netif_is_bridge_master(real_dev) &&9391 br_vlan_enabled(real_dev)) {9392 err = br_vlan_get_pvid(real_dev, &lower_pvid);9393 if (err)9394 return err;9395 return mlxsw_sp_inetaddr_bridge_event(mlxsw_sp, vlan_dev,9396 lower_pvid, event,9397 extack);9398 }9399 9400 return 0;9401}9402 9403static bool mlxsw_sp_rif_macvlan_is_vrrp4(const u8 *mac)9404{9405 u8 vrrp4[ETH_ALEN] = { 0x00, 0x00, 0x5e, 0x00, 0x01, 0x00 };9406 u8 mask[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 };9407 9408 return ether_addr_equal_masked(mac, vrrp4, mask);9409}9410 9411static bool mlxsw_sp_rif_macvlan_is_vrrp6(const u8 *mac)9412{9413 u8 vrrp6[ETH_ALEN] = { 0x00, 0x00, 0x5e, 0x00, 0x02, 0x00 };9414 u8 mask[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 };9415 9416 return ether_addr_equal_masked(mac, vrrp6, mask);9417}9418 9419static int mlxsw_sp_rif_vrrp_op(struct mlxsw_sp *mlxsw_sp, u16 rif_index,9420 const u8 *mac, bool adding)9421{9422 char ritr_pl[MLXSW_REG_RITR_LEN];9423 u8 vrrp_id = adding ? mac[5] : 0;9424 int err;9425 9426 if (!mlxsw_sp_rif_macvlan_is_vrrp4(mac) &&9427 !mlxsw_sp_rif_macvlan_is_vrrp6(mac))9428 return 0;9429 9430 mlxsw_reg_ritr_rif_pack(ritr_pl, rif_index);9431 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);9432 if (err)9433 return err;9434 9435 if (mlxsw_sp_rif_macvlan_is_vrrp4(mac))9436 mlxsw_reg_ritr_if_vrrp_id_ipv4_set(ritr_pl, vrrp_id);9437 else9438 mlxsw_reg_ritr_if_vrrp_id_ipv6_set(ritr_pl, vrrp_id);9439 9440 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);9441}9442 9443static int mlxsw_sp_rif_macvlan_add(struct mlxsw_sp *mlxsw_sp,9444 const struct net_device *macvlan_dev,9445 struct netlink_ext_ack *extack)9446{9447 struct macvlan_dev *vlan = netdev_priv(macvlan_dev);9448 struct mlxsw_sp_rif *rif;9449 int err;9450 9451 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, vlan->lowerdev);9452 if (!rif)9453 return 0;9454 9455 err = mlxsw_sp_rif_fdb_op(mlxsw_sp, macvlan_dev->dev_addr,9456 mlxsw_sp_fid_index(rif->fid), true);9457 if (err)9458 return err;9459 9460 err = mlxsw_sp_rif_vrrp_op(mlxsw_sp, rif->rif_index,9461 macvlan_dev->dev_addr, true);9462 if (err)9463 goto err_rif_vrrp_add;9464 9465 /* Make sure the bridge driver does not have this MAC pointing at9466 * some other port.9467 */9468 if (rif->ops->fdb_del)9469 rif->ops->fdb_del(rif, macvlan_dev->dev_addr);9470 9471 return 0;9472 9473err_rif_vrrp_add:9474 mlxsw_sp_rif_fdb_op(mlxsw_sp, macvlan_dev->dev_addr,9475 mlxsw_sp_fid_index(rif->fid), false);9476 return err;9477}9478 9479static void __mlxsw_sp_rif_macvlan_del(struct mlxsw_sp *mlxsw_sp,9480 const struct net_device *macvlan_dev)9481{9482 struct macvlan_dev *vlan = netdev_priv(macvlan_dev);9483 struct mlxsw_sp_rif *rif;9484 9485 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, vlan->lowerdev);9486 /* If we do not have a RIF, then we already took care of9487 * removing the macvlan's MAC during RIF deletion.9488 */9489 if (!rif)9490 return;9491 mlxsw_sp_rif_vrrp_op(mlxsw_sp, rif->rif_index, macvlan_dev->dev_addr,9492 false);9493 mlxsw_sp_rif_fdb_op(mlxsw_sp, macvlan_dev->dev_addr,9494 mlxsw_sp_fid_index(rif->fid), false);9495}9496 9497void mlxsw_sp_rif_macvlan_del(struct mlxsw_sp *mlxsw_sp,9498 const struct net_device *macvlan_dev)9499{9500 mutex_lock(&mlxsw_sp->router->lock);9501 __mlxsw_sp_rif_macvlan_del(mlxsw_sp, macvlan_dev);9502 mutex_unlock(&mlxsw_sp->router->lock);9503}9504 9505static int mlxsw_sp_inetaddr_macvlan_event(struct mlxsw_sp *mlxsw_sp,9506 struct net_device *macvlan_dev,9507 unsigned long event,9508 struct netlink_ext_ack *extack)9509{9510 switch (event) {9511 case NETDEV_UP:9512 return mlxsw_sp_rif_macvlan_add(mlxsw_sp, macvlan_dev, extack);9513 case NETDEV_DOWN:9514 __mlxsw_sp_rif_macvlan_del(mlxsw_sp, macvlan_dev);9515 break;9516 }9517 9518 return 0;9519}9520 9521static int __mlxsw_sp_inetaddr_event(struct mlxsw_sp *mlxsw_sp,9522 struct net_device *dev,9523 unsigned long event, bool nomaster,9524 struct netlink_ext_ack *extack)9525{9526 if (mlxsw_sp_port_dev_check(dev))9527 return mlxsw_sp_inetaddr_port_event(dev, event, nomaster,9528 extack);9529 else if (netif_is_lag_master(dev))9530 return mlxsw_sp_inetaddr_lag_event(dev, event, nomaster,9531 extack);9532 else if (netif_is_bridge_master(dev))9533 return mlxsw_sp_inetaddr_bridge_event(mlxsw_sp, dev, -1, event,9534 extack);9535 else if (is_vlan_dev(dev))9536 return mlxsw_sp_inetaddr_vlan_event(mlxsw_sp, dev, event,9537 nomaster, extack);9538 else if (netif_is_macvlan(dev))9539 return mlxsw_sp_inetaddr_macvlan_event(mlxsw_sp, dev, event,9540 extack);9541 else9542 return 0;9543}9544 9545static int mlxsw_sp_inetaddr_event(struct notifier_block *nb,9546 unsigned long event, void *ptr)9547{9548 struct in_ifaddr *ifa = (struct in_ifaddr *) ptr;9549 struct net_device *dev = ifa->ifa_dev->dev;9550 struct mlxsw_sp_router *router;9551 struct mlxsw_sp_rif *rif;9552 int err = 0;9553 9554 /* NETDEV_UP event is handled by mlxsw_sp_inetaddr_valid_event */9555 if (event == NETDEV_UP)9556 return NOTIFY_DONE;9557 9558 router = container_of(nb, struct mlxsw_sp_router, inetaddr_nb);9559 mutex_lock(&router->lock);9560 rif = mlxsw_sp_rif_find_by_dev(router->mlxsw_sp, dev);9561 if (!mlxsw_sp_rif_should_config(rif, dev, event))9562 goto out;9563 9564 err = __mlxsw_sp_inetaddr_event(router->mlxsw_sp, dev, event, false,9565 NULL);9566out:9567 mutex_unlock(&router->lock);9568 return notifier_from_errno(err);9569}9570 9571static int mlxsw_sp_inetaddr_valid_event(struct notifier_block *unused,9572 unsigned long event, void *ptr)9573{9574 struct in_validator_info *ivi = (struct in_validator_info *) ptr;9575 struct net_device *dev = ivi->ivi_dev->dev;9576 struct mlxsw_sp *mlxsw_sp;9577 struct mlxsw_sp_rif *rif;9578 int err = 0;9579 9580 mlxsw_sp = mlxsw_sp_lower_get(dev);9581 if (!mlxsw_sp)9582 return NOTIFY_DONE;9583 9584 mutex_lock(&mlxsw_sp->router->lock);9585 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);9586 if (!mlxsw_sp_rif_should_config(rif, dev, event))9587 goto out;9588 9589 err = __mlxsw_sp_inetaddr_event(mlxsw_sp, dev, event, false,9590 ivi->extack);9591out:9592 mutex_unlock(&mlxsw_sp->router->lock);9593 return notifier_from_errno(err);9594}9595 9596struct mlxsw_sp_inet6addr_event_work {9597 struct work_struct work;9598 struct mlxsw_sp *mlxsw_sp;9599 struct net_device *dev;9600 netdevice_tracker dev_tracker;9601 unsigned long event;9602};9603 9604static void mlxsw_sp_inet6addr_event_work(struct work_struct *work)9605{9606 struct mlxsw_sp_inet6addr_event_work *inet6addr_work =9607 container_of(work, struct mlxsw_sp_inet6addr_event_work, work);9608 struct mlxsw_sp *mlxsw_sp = inet6addr_work->mlxsw_sp;9609 struct net_device *dev = inet6addr_work->dev;9610 unsigned long event = inet6addr_work->event;9611 struct mlxsw_sp_rif *rif;9612 9613 rtnl_lock();9614 mutex_lock(&mlxsw_sp->router->lock);9615 9616 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);9617 if (!mlxsw_sp_rif_should_config(rif, dev, event))9618 goto out;9619 9620 __mlxsw_sp_inetaddr_event(mlxsw_sp, dev, event, false, NULL);9621out:9622 mutex_unlock(&mlxsw_sp->router->lock);9623 rtnl_unlock();9624 netdev_put(dev, &inet6addr_work->dev_tracker);9625 kfree(inet6addr_work);9626}9627 9628/* Called with rcu_read_lock() */9629static int mlxsw_sp_inet6addr_event(struct notifier_block *nb,9630 unsigned long event, void *ptr)9631{9632 struct inet6_ifaddr *if6 = (struct inet6_ifaddr *) ptr;9633 struct mlxsw_sp_inet6addr_event_work *inet6addr_work;9634 struct net_device *dev = if6->idev->dev;9635 struct mlxsw_sp_router *router;9636 9637 /* NETDEV_UP event is handled by mlxsw_sp_inet6addr_valid_event */9638 if (event == NETDEV_UP)9639 return NOTIFY_DONE;9640 9641 inet6addr_work = kzalloc(sizeof(*inet6addr_work), GFP_ATOMIC);9642 if (!inet6addr_work)9643 return NOTIFY_BAD;9644 9645 router = container_of(nb, struct mlxsw_sp_router, inet6addr_nb);9646 INIT_WORK(&inet6addr_work->work, mlxsw_sp_inet6addr_event_work);9647 inet6addr_work->mlxsw_sp = router->mlxsw_sp;9648 inet6addr_work->dev = dev;9649 inet6addr_work->event = event;9650 netdev_hold(dev, &inet6addr_work->dev_tracker, GFP_ATOMIC);9651 mlxsw_core_schedule_work(&inet6addr_work->work);9652 9653 return NOTIFY_DONE;9654}9655 9656static int mlxsw_sp_inet6addr_valid_event(struct notifier_block *unused,9657 unsigned long event, void *ptr)9658{9659 struct in6_validator_info *i6vi = (struct in6_validator_info *) ptr;9660 struct net_device *dev = i6vi->i6vi_dev->dev;9661 struct mlxsw_sp *mlxsw_sp;9662 struct mlxsw_sp_rif *rif;9663 int err = 0;9664 9665 mlxsw_sp = mlxsw_sp_lower_get(dev);9666 if (!mlxsw_sp)9667 return NOTIFY_DONE;9668 9669 mutex_lock(&mlxsw_sp->router->lock);9670 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);9671 if (!mlxsw_sp_rif_should_config(rif, dev, event))9672 goto out;9673 9674 err = __mlxsw_sp_inetaddr_event(mlxsw_sp, dev, event, false,9675 i6vi->extack);9676out:9677 mutex_unlock(&mlxsw_sp->router->lock);9678 return notifier_from_errno(err);9679}9680 9681static int mlxsw_sp_rif_edit(struct mlxsw_sp *mlxsw_sp, u16 rif_index,9682 const char *mac, int mtu, u8 mac_profile)9683{9684 char ritr_pl[MLXSW_REG_RITR_LEN];9685 int err;9686 9687 mlxsw_reg_ritr_rif_pack(ritr_pl, rif_index);9688 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);9689 if (err)9690 return err;9691 9692 mlxsw_reg_ritr_mtu_set(ritr_pl, mtu);9693 mlxsw_reg_ritr_if_mac_memcpy_to(ritr_pl, mac);9694 mlxsw_reg_ritr_if_mac_profile_id_set(ritr_pl, mac_profile);9695 mlxsw_reg_ritr_op_set(ritr_pl, MLXSW_REG_RITR_RIF_CREATE);9696 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);9697}9698 9699static int9700mlxsw_sp_router_port_change_event(struct mlxsw_sp *mlxsw_sp,9701 struct mlxsw_sp_rif *rif,9702 struct netlink_ext_ack *extack)9703{9704 struct net_device *dev = mlxsw_sp_rif_dev(rif);9705 u8 old_mac_profile;9706 u16 fid_index;9707 int err;9708 9709 fid_index = mlxsw_sp_fid_index(rif->fid);9710 9711 err = mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->addr, fid_index, false);9712 if (err)9713 return err;9714 9715 old_mac_profile = rif->mac_profile_id;9716 err = mlxsw_sp_rif_mac_profile_replace(mlxsw_sp, rif, dev->dev_addr,9717 extack);9718 if (err)9719 goto err_rif_mac_profile_replace;9720 9721 err = mlxsw_sp_rif_edit(mlxsw_sp, rif->rif_index, dev->dev_addr,9722 dev->mtu, rif->mac_profile_id);9723 if (err)9724 goto err_rif_edit;9725 9726 err = mlxsw_sp_rif_fdb_op(mlxsw_sp, dev->dev_addr, fid_index, true);9727 if (err)9728 goto err_rif_fdb_op;9729 9730 if (rif->mtu != dev->mtu) {9731 struct mlxsw_sp_vr *vr;9732 int i;9733 9734 /* The RIF is relevant only to its mr_table instance, as unlike9735 * unicast routing, in multicast routing a RIF cannot be shared9736 * between several multicast routing tables.9737 */9738 vr = &mlxsw_sp->router->vrs[rif->vr_id];9739 for (i = 0; i < MLXSW_SP_L3_PROTO_MAX; i++)9740 mlxsw_sp_mr_rif_mtu_update(vr->mr_table[i],9741 rif, dev->mtu);9742 }9743 9744 ether_addr_copy(rif->addr, dev->dev_addr);9745 rif->mtu = dev->mtu;9746 9747 netdev_dbg(dev, "Updated RIF=%d\n", rif->rif_index);9748 9749 return 0;9750 9751err_rif_fdb_op:9752 mlxsw_sp_rif_edit(mlxsw_sp, rif->rif_index, rif->addr, rif->mtu,9753 old_mac_profile);9754err_rif_edit:9755 mlxsw_sp_rif_mac_profile_replace(mlxsw_sp, rif, rif->addr, extack);9756err_rif_mac_profile_replace:9757 mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->addr, fid_index, true);9758 return err;9759}9760 9761static int mlxsw_sp_router_port_pre_changeaddr_event(struct mlxsw_sp_rif *rif,9762 struct netdev_notifier_pre_changeaddr_info *info)9763{9764 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;9765 struct mlxsw_sp_rif_mac_profile *profile;9766 struct netlink_ext_ack *extack;9767 u8 max_rif_mac_profiles;9768 u64 occ;9769 9770 extack = netdev_notifier_info_to_extack(&info->info);9771 9772 profile = mlxsw_sp_rif_mac_profile_find(mlxsw_sp, info->dev_addr);9773 if (profile)9774 return 0;9775 9776 max_rif_mac_profiles = mlxsw_sp->router->max_rif_mac_profile;9777 occ = mlxsw_sp_rif_mac_profiles_occ_get(mlxsw_sp);9778 if (occ < max_rif_mac_profiles)9779 return 0;9780 9781 if (!mlxsw_sp_rif_mac_profile_is_shared(rif))9782 return 0;9783 9784 NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported router interface MAC profiles");9785 return -ENOBUFS;9786}9787 9788static bool mlxsw_sp_router_netdevice_interesting(struct mlxsw_sp *mlxsw_sp,9789 struct net_device *dev)9790{9791 struct vlan_dev_priv *vlan;9792 9793 if (netif_is_lag_master(dev) ||9794 netif_is_bridge_master(dev) ||9795 mlxsw_sp_port_dev_check(dev) ||9796 mlxsw_sp_netdev_is_ipip_ol(mlxsw_sp, dev) ||9797 netif_is_l3_master(dev))9798 return true;9799 9800 if (!is_vlan_dev(dev))9801 return false;9802 9803 vlan = vlan_dev_priv(dev);9804 return netif_is_lag_master(vlan->real_dev) ||9805 netif_is_bridge_master(vlan->real_dev) ||9806 mlxsw_sp_port_dev_check(vlan->real_dev);9807}9808 9809static struct mlxsw_sp_crif *9810mlxsw_sp_crif_register(struct mlxsw_sp_router *router, struct net_device *dev)9811{9812 struct mlxsw_sp_crif *crif;9813 int err;9814 9815 if (WARN_ON(mlxsw_sp_crif_lookup(router, dev)))9816 return NULL;9817 9818 crif = mlxsw_sp_crif_alloc(dev);9819 if (!crif)9820 return ERR_PTR(-ENOMEM);9821 9822 err = mlxsw_sp_crif_insert(router, crif);9823 if (err)9824 goto err_netdev_insert;9825 9826 return crif;9827 9828err_netdev_insert:9829 mlxsw_sp_crif_free(crif);9830 return ERR_PTR(err);9831}9832 9833static void mlxsw_sp_crif_unregister(struct mlxsw_sp_router *router,9834 struct mlxsw_sp_crif *crif)9835{9836 struct mlxsw_sp_nexthop *nh, *tmp;9837 9838 mlxsw_sp_crif_remove(router, crif);9839 9840 list_for_each_entry_safe(nh, tmp, &crif->nexthop_list, crif_list_node)9841 mlxsw_sp_nexthop_type_fini(router->mlxsw_sp, nh);9842 9843 if (crif->rif)9844 crif->can_destroy = true;9845 else9846 mlxsw_sp_crif_free(crif);9847}9848 9849static int mlxsw_sp_netdevice_register(struct mlxsw_sp_router *router,9850 struct net_device *dev)9851{9852 struct mlxsw_sp_crif *crif;9853 9854 if (!mlxsw_sp_router_netdevice_interesting(router->mlxsw_sp, dev))9855 return 0;9856 9857 crif = mlxsw_sp_crif_register(router, dev);9858 return PTR_ERR_OR_ZERO(crif);9859}9860 9861static void mlxsw_sp_netdevice_unregister(struct mlxsw_sp_router *router,9862 struct net_device *dev)9863{9864 struct mlxsw_sp_crif *crif;9865 9866 if (!mlxsw_sp_router_netdevice_interesting(router->mlxsw_sp, dev))9867 return;9868 9869 /* netdev_run_todo(), by way of netdev_wait_allrefs_any(), rebroadcasts9870 * the NETDEV_UNREGISTER message, so we can get here twice. If that's9871 * what happened, the netdevice state is NETREG_UNREGISTERED. In that9872 * case, we expect to have collected the CRIF already, and warn if it9873 * still exists. Otherwise we expect the CRIF to exist.9874 */9875 crif = mlxsw_sp_crif_lookup(router, dev);9876 if (dev->reg_state == NETREG_UNREGISTERED) {9877 if (!WARN_ON(crif))9878 return;9879 }9880 if (WARN_ON(!crif))9881 return;9882 9883 mlxsw_sp_crif_unregister(router, crif);9884}9885 9886static bool mlxsw_sp_is_offload_xstats_event(unsigned long event)9887{9888 switch (event) {9889 case NETDEV_OFFLOAD_XSTATS_ENABLE:9890 case NETDEV_OFFLOAD_XSTATS_DISABLE:9891 case NETDEV_OFFLOAD_XSTATS_REPORT_USED:9892 case NETDEV_OFFLOAD_XSTATS_REPORT_DELTA:9893 return true;9894 }9895 9896 return false;9897}9898 9899static int9900mlxsw_sp_router_port_offload_xstats_cmd(struct mlxsw_sp_rif *rif,9901 unsigned long event,9902 struct netdev_notifier_offload_xstats_info *info)9903{9904 switch (info->type) {9905 case NETDEV_OFFLOAD_XSTATS_TYPE_L3:9906 break;9907 default:9908 return 0;9909 }9910 9911 switch (event) {9912 case NETDEV_OFFLOAD_XSTATS_ENABLE:9913 return mlxsw_sp_router_port_l3_stats_enable(rif);9914 case NETDEV_OFFLOAD_XSTATS_DISABLE:9915 mlxsw_sp_router_port_l3_stats_disable(rif);9916 return 0;9917 case NETDEV_OFFLOAD_XSTATS_REPORT_USED:9918 mlxsw_sp_router_port_l3_stats_report_used(rif, info);9919 return 0;9920 case NETDEV_OFFLOAD_XSTATS_REPORT_DELTA:9921 return mlxsw_sp_router_port_l3_stats_report_delta(rif, info);9922 }9923 9924 WARN_ON_ONCE(1);9925 return 0;9926}9927 9928static int9929mlxsw_sp_netdevice_offload_xstats_cmd(struct mlxsw_sp *mlxsw_sp,9930 struct net_device *dev,9931 unsigned long event,9932 struct netdev_notifier_offload_xstats_info *info)9933{9934 struct mlxsw_sp_rif *rif;9935 9936 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);9937 if (!rif)9938 return 0;9939 9940 return mlxsw_sp_router_port_offload_xstats_cmd(rif, event, info);9941}9942 9943static bool mlxsw_sp_is_router_event(unsigned long event)9944{9945 switch (event) {9946 case NETDEV_PRE_CHANGEADDR:9947 case NETDEV_CHANGEADDR:9948 case NETDEV_CHANGEMTU:9949 return true;9950 default:9951 return false;9952 }9953}9954 9955static int mlxsw_sp_netdevice_router_port_event(struct net_device *dev,9956 unsigned long event, void *ptr)9957{9958 struct netlink_ext_ack *extack = netdev_notifier_info_to_extack(ptr);9959 struct mlxsw_sp *mlxsw_sp;9960 struct mlxsw_sp_rif *rif;9961 9962 mlxsw_sp = mlxsw_sp_lower_get(dev);9963 if (!mlxsw_sp)9964 return 0;9965 9966 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);9967 if (!rif)9968 return 0;9969 9970 switch (event) {9971 case NETDEV_CHANGEMTU:9972 case NETDEV_CHANGEADDR:9973 return mlxsw_sp_router_port_change_event(mlxsw_sp, rif, extack);9974 case NETDEV_PRE_CHANGEADDR:9975 return mlxsw_sp_router_port_pre_changeaddr_event(rif, ptr);9976 default:9977 WARN_ON_ONCE(1);9978 break;9979 }9980 9981 return 0;9982}9983 9984static int mlxsw_sp_port_vrf_join(struct mlxsw_sp *mlxsw_sp,9985 struct net_device *l3_dev,9986 struct netlink_ext_ack *extack)9987{9988 struct mlxsw_sp_rif *rif;9989 9990 /* If netdev is already associated with a RIF, then we need to9991 * destroy it and create a new one with the new virtual router ID.9992 */9993 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);9994 if (rif)9995 __mlxsw_sp_inetaddr_event(mlxsw_sp, l3_dev, NETDEV_DOWN, false,9996 extack);9997 9998 return __mlxsw_sp_inetaddr_event(mlxsw_sp, l3_dev, NETDEV_UP, false,9999 extack);10000}10001 10002static void mlxsw_sp_port_vrf_leave(struct mlxsw_sp *mlxsw_sp,10003 struct net_device *l3_dev)10004{10005 struct mlxsw_sp_rif *rif;10006 10007 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);10008 if (!rif)10009 return;10010 __mlxsw_sp_inetaddr_event(mlxsw_sp, l3_dev, NETDEV_DOWN, false, NULL);10011}10012 10013static bool mlxsw_sp_is_vrf_event(unsigned long event, void *ptr)10014{10015 struct netdev_notifier_changeupper_info *info = ptr;10016 10017 if (event != NETDEV_PRECHANGEUPPER && event != NETDEV_CHANGEUPPER)10018 return false;10019 return netif_is_l3_master(info->upper_dev);10020}10021 10022static int10023mlxsw_sp_netdevice_vrf_event(struct net_device *l3_dev, unsigned long event,10024 struct netdev_notifier_changeupper_info *info)10025{10026 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(l3_dev);10027 int err = 0;10028 10029 /* We do not create a RIF for a macvlan, but only use it to10030 * direct more MAC addresses to the router.10031 */10032 if (!mlxsw_sp || netif_is_macvlan(l3_dev))10033 return 0;10034 10035 switch (event) {10036 case NETDEV_PRECHANGEUPPER:10037 break;10038 case NETDEV_CHANGEUPPER:10039 if (info->linking) {10040 struct netlink_ext_ack *extack;10041 10042 extack = netdev_notifier_info_to_extack(&info->info);10043 err = mlxsw_sp_port_vrf_join(mlxsw_sp, l3_dev, extack);10044 } else {10045 mlxsw_sp_port_vrf_leave(mlxsw_sp, l3_dev);10046 }10047 break;10048 }10049 10050 return err;10051}10052 10053struct mlxsw_sp_router_replay_inetaddr_up {10054 struct mlxsw_sp *mlxsw_sp;10055 struct netlink_ext_ack *extack;10056 unsigned int done;10057 bool deslavement;10058};10059 10060static int mlxsw_sp_router_replay_inetaddr_up(struct net_device *dev,10061 struct netdev_nested_priv *priv)10062{10063 struct mlxsw_sp_router_replay_inetaddr_up *ctx = priv->data;10064 bool nomaster = ctx->deslavement;10065 struct mlxsw_sp_crif *crif;10066 int err;10067 10068 if (mlxsw_sp_dev_addr_list_empty(dev))10069 return 0;10070 10071 crif = mlxsw_sp_crif_lookup(ctx->mlxsw_sp->router, dev);10072 if (!crif || crif->rif)10073 return 0;10074 10075 if (!mlxsw_sp_rif_should_config(crif->rif, dev, NETDEV_UP))10076 return 0;10077 10078 err = __mlxsw_sp_inetaddr_event(ctx->mlxsw_sp, dev, NETDEV_UP,10079 nomaster, ctx->extack);10080 if (err)10081 return err;10082 10083 ctx->done++;10084 return 0;10085}10086 10087static int mlxsw_sp_router_unreplay_inetaddr_up(struct net_device *dev,10088 struct netdev_nested_priv *priv)10089{10090 struct mlxsw_sp_router_replay_inetaddr_up *ctx = priv->data;10091 bool nomaster = ctx->deslavement;10092 struct mlxsw_sp_crif *crif;10093 10094 if (!ctx->done)10095 return 0;10096 10097 if (mlxsw_sp_dev_addr_list_empty(dev))10098 return 0;10099 10100 crif = mlxsw_sp_crif_lookup(ctx->mlxsw_sp->router, dev);10101 if (!crif || !crif->rif)10102 return 0;10103 10104 /* We are rolling back NETDEV_UP, so ask for that. */10105 if (!mlxsw_sp_rif_should_config(crif->rif, dev, NETDEV_UP))10106 return 0;10107 10108 __mlxsw_sp_inetaddr_event(ctx->mlxsw_sp, dev, NETDEV_DOWN, nomaster,10109 NULL);10110 10111 ctx->done--;10112 return 0;10113}10114 10115int mlxsw_sp_netdevice_enslavement_replay(struct mlxsw_sp *mlxsw_sp,10116 struct net_device *upper_dev,10117 struct netlink_ext_ack *extack)10118{10119 struct mlxsw_sp_router_replay_inetaddr_up ctx = {10120 .mlxsw_sp = mlxsw_sp,10121 .extack = extack,10122 .deslavement = false,10123 };10124 struct netdev_nested_priv priv = {10125 .data = &ctx,10126 };10127 int err;10128 10129 err = mlxsw_sp_router_replay_inetaddr_up(upper_dev, &priv);10130 if (err)10131 return err;10132 10133 err = netdev_walk_all_upper_dev_rcu(upper_dev,10134 mlxsw_sp_router_replay_inetaddr_up,10135 &priv);10136 if (err)10137 goto err_replay_up;10138 10139 return 0;10140 10141err_replay_up:10142 netdev_walk_all_upper_dev_rcu(upper_dev,10143 mlxsw_sp_router_unreplay_inetaddr_up,10144 &priv);10145 mlxsw_sp_router_unreplay_inetaddr_up(upper_dev, &priv);10146 return err;10147}10148 10149void mlxsw_sp_netdevice_deslavement_replay(struct mlxsw_sp *mlxsw_sp,10150 struct net_device *dev)10151{10152 struct mlxsw_sp_router_replay_inetaddr_up ctx = {10153 .mlxsw_sp = mlxsw_sp,10154 .deslavement = true,10155 };10156 struct netdev_nested_priv priv = {10157 .data = &ctx,10158 };10159 10160 mlxsw_sp_router_replay_inetaddr_up(dev, &priv);10161}10162 10163static int10164mlxsw_sp_port_vid_router_join_existing(struct mlxsw_sp_port *mlxsw_sp_port,10165 u16 vid, struct net_device *dev,10166 struct netlink_ext_ack *extack)10167{10168 struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;10169 10170 mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port,10171 vid);10172 if (WARN_ON(!mlxsw_sp_port_vlan))10173 return -EINVAL;10174 10175 return mlxsw_sp_port_vlan_router_join_existing(mlxsw_sp_port_vlan,10176 dev, extack);10177}10178 10179static void10180mlxsw_sp_port_vid_router_leave(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid,10181 struct net_device *dev)10182{10183 struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;10184 10185 mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port,10186 vid);10187 if (WARN_ON(!mlxsw_sp_port_vlan))10188 return;10189 10190 __mlxsw_sp_port_vlan_router_leave(mlxsw_sp_port_vlan);10191}10192 10193static int __mlxsw_sp_router_port_join_lag(struct mlxsw_sp_port *mlxsw_sp_port,10194 struct net_device *lag_dev,10195 struct netlink_ext_ack *extack)10196{10197 u16 default_vid = MLXSW_SP_DEFAULT_VID;10198 struct net_device *upper_dev;10199 struct list_head *iter;10200 int done = 0;10201 u16 vid;10202 int err;10203 10204 err = mlxsw_sp_port_vid_router_join_existing(mlxsw_sp_port, default_vid,10205 lag_dev, extack);10206 if (err)10207 return err;10208 10209 netdev_for_each_upper_dev_rcu(lag_dev, upper_dev, iter) {10210 if (!is_vlan_dev(upper_dev))10211 continue;10212 10213 vid = vlan_dev_vlan_id(upper_dev);10214 err = mlxsw_sp_port_vid_router_join_existing(mlxsw_sp_port, vid,10215 upper_dev, extack);10216 if (err)10217 goto err_router_join_dev;10218 10219 ++done;10220 }10221 10222 return 0;10223 10224err_router_join_dev:10225 netdev_for_each_upper_dev_rcu(lag_dev, upper_dev, iter) {10226 if (!is_vlan_dev(upper_dev))10227 continue;10228 if (!done--)10229 break;10230 10231 vid = vlan_dev_vlan_id(upper_dev);10232 mlxsw_sp_port_vid_router_leave(mlxsw_sp_port, vid, upper_dev);10233 }10234 10235 mlxsw_sp_port_vid_router_leave(mlxsw_sp_port, default_vid, lag_dev);10236 return err;10237}10238 10239static void10240__mlxsw_sp_router_port_leave_lag(struct mlxsw_sp_port *mlxsw_sp_port,10241 struct net_device *lag_dev)10242{10243 u16 default_vid = MLXSW_SP_DEFAULT_VID;10244 struct net_device *upper_dev;10245 struct list_head *iter;10246 u16 vid;10247 10248 netdev_for_each_upper_dev_rcu(lag_dev, upper_dev, iter) {10249 if (!is_vlan_dev(upper_dev))10250 continue;10251 10252 vid = vlan_dev_vlan_id(upper_dev);10253 mlxsw_sp_port_vid_router_leave(mlxsw_sp_port, vid, upper_dev);10254 }10255 10256 mlxsw_sp_port_vid_router_leave(mlxsw_sp_port, default_vid, lag_dev);10257}10258 10259int mlxsw_sp_router_port_join_lag(struct mlxsw_sp_port *mlxsw_sp_port,10260 struct net_device *lag_dev,10261 struct netlink_ext_ack *extack)10262{10263 int err;10264 10265 mutex_lock(&mlxsw_sp_port->mlxsw_sp->router->lock);10266 err = __mlxsw_sp_router_port_join_lag(mlxsw_sp_port, lag_dev, extack);10267 mutex_unlock(&mlxsw_sp_port->mlxsw_sp->router->lock);10268 10269 return err;10270}10271 10272void mlxsw_sp_router_port_leave_lag(struct mlxsw_sp_port *mlxsw_sp_port,10273 struct net_device *lag_dev)10274{10275 mutex_lock(&mlxsw_sp_port->mlxsw_sp->router->lock);10276 __mlxsw_sp_router_port_leave_lag(mlxsw_sp_port, lag_dev);10277 mutex_unlock(&mlxsw_sp_port->mlxsw_sp->router->lock);10278}10279 10280static int mlxsw_sp_router_netdevice_event(struct notifier_block *nb,10281 unsigned long event, void *ptr)10282{10283 struct net_device *dev = netdev_notifier_info_to_dev(ptr);10284 struct mlxsw_sp_router *router;10285 struct mlxsw_sp *mlxsw_sp;10286 int err = 0;10287 10288 router = container_of(nb, struct mlxsw_sp_router, netdevice_nb);10289 mlxsw_sp = router->mlxsw_sp;10290 10291 mutex_lock(&mlxsw_sp->router->lock);10292 10293 if (event == NETDEV_REGISTER) {10294 err = mlxsw_sp_netdevice_register(router, dev);10295 if (err)10296 /* No need to roll this back, UNREGISTER will collect it10297 * anyhow.10298 */10299 goto out;10300 }10301 10302 if (mlxsw_sp_is_offload_xstats_event(event))10303 err = mlxsw_sp_netdevice_offload_xstats_cmd(mlxsw_sp, dev,10304 event, ptr);10305 else if (mlxsw_sp_netdev_is_ipip_ol(mlxsw_sp, dev))10306 err = mlxsw_sp_netdevice_ipip_ol_event(mlxsw_sp, dev,10307 event, ptr);10308 else if (mlxsw_sp_netdev_is_ipip_ul(mlxsw_sp, dev))10309 err = mlxsw_sp_netdevice_ipip_ul_event(mlxsw_sp, dev,10310 event, ptr);10311 else if (mlxsw_sp_is_router_event(event))10312 err = mlxsw_sp_netdevice_router_port_event(dev, event, ptr);10313 else if (mlxsw_sp_is_vrf_event(event, ptr))10314 err = mlxsw_sp_netdevice_vrf_event(dev, event, ptr);10315 10316 if (event == NETDEV_UNREGISTER)10317 mlxsw_sp_netdevice_unregister(router, dev);10318 10319out:10320 mutex_unlock(&mlxsw_sp->router->lock);10321 10322 return notifier_from_errno(err);10323}10324 10325struct mlxsw_sp_macvlan_replay {10326 struct mlxsw_sp *mlxsw_sp;10327 struct netlink_ext_ack *extack;10328};10329 10330static int mlxsw_sp_macvlan_replay_upper(struct net_device *dev,10331 struct netdev_nested_priv *priv)10332{10333 const struct mlxsw_sp_macvlan_replay *rms = priv->data;10334 struct netlink_ext_ack *extack = rms->extack;10335 struct mlxsw_sp *mlxsw_sp = rms->mlxsw_sp;10336 10337 if (!netif_is_macvlan(dev))10338 return 0;10339 10340 return mlxsw_sp_rif_macvlan_add(mlxsw_sp, dev, extack);10341}10342 10343static int mlxsw_sp_macvlan_replay(struct mlxsw_sp_rif *rif,10344 struct netlink_ext_ack *extack)10345{10346 struct mlxsw_sp_macvlan_replay rms = {10347 .mlxsw_sp = rif->mlxsw_sp,10348 .extack = extack,10349 };10350 struct netdev_nested_priv priv = {10351 .data = &rms,10352 };10353 10354 return netdev_walk_all_upper_dev_rcu(mlxsw_sp_rif_dev(rif),10355 mlxsw_sp_macvlan_replay_upper,10356 &priv);10357}10358 10359static int __mlxsw_sp_rif_macvlan_flush(struct net_device *dev,10360 struct netdev_nested_priv *priv)10361{10362 struct mlxsw_sp_rif *rif = (struct mlxsw_sp_rif *)priv->data;10363 10364 if (!netif_is_macvlan(dev))10365 return 0;10366 10367 return mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, dev->dev_addr,10368 mlxsw_sp_fid_index(rif->fid), false);10369}10370 10371static int mlxsw_sp_rif_macvlan_flush(struct mlxsw_sp_rif *rif)10372{10373 struct net_device *dev = mlxsw_sp_rif_dev(rif);10374 struct netdev_nested_priv priv = {10375 .data = (void *)rif,10376 };10377 10378 if (!netif_is_macvlan_port(dev))10379 return 0;10380 10381 return netdev_walk_all_upper_dev_rcu(dev,10382 __mlxsw_sp_rif_macvlan_flush, &priv);10383}10384 10385static void mlxsw_sp_rif_subport_setup(struct mlxsw_sp_rif *rif,10386 const struct mlxsw_sp_rif_params *params)10387{10388 struct mlxsw_sp_rif_subport *rif_subport;10389 10390 rif_subport = mlxsw_sp_rif_subport_rif(rif);10391 refcount_set(&rif_subport->ref_count, 1);10392 rif_subport->vid = params->vid;10393 rif_subport->lag = params->lag;10394 if (params->lag)10395 rif_subport->lag_id = params->lag_id;10396 else10397 rif_subport->system_port = params->system_port;10398}10399 10400static int mlxsw_sp_rif_subport_op(struct mlxsw_sp_rif *rif, bool enable)10401{10402 struct net_device *dev = mlxsw_sp_rif_dev(rif);10403 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;10404 struct mlxsw_sp_rif_subport *rif_subport;10405 char ritr_pl[MLXSW_REG_RITR_LEN];10406 u16 efid;10407 10408 rif_subport = mlxsw_sp_rif_subport_rif(rif);10409 mlxsw_reg_ritr_pack(ritr_pl, enable, MLXSW_REG_RITR_SP_IF,10410 rif->rif_index, rif->vr_id, dev->mtu);10411 mlxsw_reg_ritr_mac_pack(ritr_pl, dev->dev_addr);10412 mlxsw_reg_ritr_if_mac_profile_id_set(ritr_pl, rif->mac_profile_id);10413 efid = mlxsw_sp_fid_index(rif->fid);10414 mlxsw_reg_ritr_sp_if_pack(ritr_pl, rif_subport->lag,10415 rif_subport->lag ? rif_subport->lag_id :10416 rif_subport->system_port,10417 efid, 0);10418 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);10419}10420 10421static int mlxsw_sp_rif_subport_configure(struct mlxsw_sp_rif *rif,10422 struct netlink_ext_ack *extack)10423{10424 struct net_device *dev = mlxsw_sp_rif_dev(rif);10425 u8 mac_profile;10426 int err;10427 10428 err = mlxsw_sp_rif_mac_profile_get(rif->mlxsw_sp, rif->addr,10429 &mac_profile, extack);10430 if (err)10431 return err;10432 rif->mac_profile_id = mac_profile;10433 10434 err = mlxsw_sp_rif_subport_op(rif, true);10435 if (err)10436 goto err_rif_subport_op;10437 10438 err = mlxsw_sp_macvlan_replay(rif, extack);10439 if (err)10440 goto err_macvlan_replay;10441 10442 err = mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, dev->dev_addr,10443 mlxsw_sp_fid_index(rif->fid), true);10444 if (err)10445 goto err_rif_fdb_op;10446 10447 err = mlxsw_sp_fid_rif_set(rif->fid, rif);10448 if (err)10449 goto err_fid_rif_set;10450 10451 return 0;10452 10453err_fid_rif_set:10454 mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, dev->dev_addr,10455 mlxsw_sp_fid_index(rif->fid), false);10456err_rif_fdb_op:10457 mlxsw_sp_rif_macvlan_flush(rif);10458err_macvlan_replay:10459 mlxsw_sp_rif_subport_op(rif, false);10460err_rif_subport_op:10461 mlxsw_sp_rif_mac_profile_put(rif->mlxsw_sp, mac_profile);10462 return err;10463}10464 10465static void mlxsw_sp_rif_subport_deconfigure(struct mlxsw_sp_rif *rif)10466{10467 struct net_device *dev = mlxsw_sp_rif_dev(rif);10468 struct mlxsw_sp_fid *fid = rif->fid;10469 10470 mlxsw_sp_fid_rif_unset(fid);10471 mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, dev->dev_addr,10472 mlxsw_sp_fid_index(fid), false);10473 mlxsw_sp_rif_macvlan_flush(rif);10474 mlxsw_sp_rif_subport_op(rif, false);10475 mlxsw_sp_rif_mac_profile_put(rif->mlxsw_sp, rif->mac_profile_id);10476}10477 10478static struct mlxsw_sp_fid *10479mlxsw_sp_rif_subport_fid_get(struct mlxsw_sp_rif *rif,10480 const struct mlxsw_sp_rif_params *params,10481 struct netlink_ext_ack *extack)10482{10483 return mlxsw_sp_fid_rfid_get(rif->mlxsw_sp, rif->rif_index);10484}10485 10486static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_subport_ops = {10487 .type = MLXSW_SP_RIF_TYPE_SUBPORT,10488 .rif_size = sizeof(struct mlxsw_sp_rif_subport),10489 .setup = mlxsw_sp_rif_subport_setup,10490 .configure = mlxsw_sp_rif_subport_configure,10491 .deconfigure = mlxsw_sp_rif_subport_deconfigure,10492 .fid_get = mlxsw_sp_rif_subport_fid_get,10493};10494 10495static int mlxsw_sp_rif_fid_op(struct mlxsw_sp_rif *rif, u16 fid, bool enable)10496{10497 enum mlxsw_reg_ritr_if_type type = MLXSW_REG_RITR_FID_IF;10498 struct net_device *dev = mlxsw_sp_rif_dev(rif);10499 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;10500 char ritr_pl[MLXSW_REG_RITR_LEN];10501 10502 mlxsw_reg_ritr_pack(ritr_pl, enable, type, rif->rif_index, rif->vr_id,10503 dev->mtu);10504 mlxsw_reg_ritr_mac_pack(ritr_pl, dev->dev_addr);10505 mlxsw_reg_ritr_if_mac_profile_id_set(ritr_pl, rif->mac_profile_id);10506 mlxsw_reg_ritr_fid_if_fid_set(ritr_pl, fid);10507 10508 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);10509}10510 10511u16 mlxsw_sp_router_port(const struct mlxsw_sp *mlxsw_sp)10512{10513 return mlxsw_core_max_ports(mlxsw_sp->core) + 1;10514}10515 10516static int mlxsw_sp_rif_fid_configure(struct mlxsw_sp_rif *rif,10517 struct netlink_ext_ack *extack)10518{10519 struct net_device *dev = mlxsw_sp_rif_dev(rif);10520 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;10521 u16 fid_index = mlxsw_sp_fid_index(rif->fid);10522 u8 mac_profile;10523 int err;10524 10525 err = mlxsw_sp_rif_mac_profile_get(mlxsw_sp, rif->addr,10526 &mac_profile, extack);10527 if (err)10528 return err;10529 rif->mac_profile_id = mac_profile;10530 10531 err = mlxsw_sp_rif_fid_op(rif, fid_index, true);10532 if (err)10533 goto err_rif_fid_op;10534 10535 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,10536 mlxsw_sp_router_port(mlxsw_sp), true);10537 if (err)10538 goto err_fid_mc_flood_set;10539 10540 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,10541 mlxsw_sp_router_port(mlxsw_sp), true);10542 if (err)10543 goto err_fid_bc_flood_set;10544 10545 err = mlxsw_sp_macvlan_replay(rif, extack);10546 if (err)10547 goto err_macvlan_replay;10548 10549 err = mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, dev->dev_addr,10550 mlxsw_sp_fid_index(rif->fid), true);10551 if (err)10552 goto err_rif_fdb_op;10553 10554 err = mlxsw_sp_fid_rif_set(rif->fid, rif);10555 if (err)10556 goto err_fid_rif_set;10557 10558 return 0;10559 10560err_fid_rif_set:10561 mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, dev->dev_addr,10562 mlxsw_sp_fid_index(rif->fid), false);10563err_rif_fdb_op:10564 mlxsw_sp_rif_macvlan_flush(rif);10565err_macvlan_replay:10566 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,10567 mlxsw_sp_router_port(mlxsw_sp), false);10568err_fid_bc_flood_set:10569 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,10570 mlxsw_sp_router_port(mlxsw_sp), false);10571err_fid_mc_flood_set:10572 mlxsw_sp_rif_fid_op(rif, fid_index, false);10573err_rif_fid_op:10574 mlxsw_sp_rif_mac_profile_put(mlxsw_sp, mac_profile);10575 return err;10576}10577 10578static void mlxsw_sp_rif_fid_deconfigure(struct mlxsw_sp_rif *rif)10579{10580 struct net_device *dev = mlxsw_sp_rif_dev(rif);10581 u16 fid_index = mlxsw_sp_fid_index(rif->fid);10582 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;10583 struct mlxsw_sp_fid *fid = rif->fid;10584 10585 mlxsw_sp_fid_rif_unset(fid);10586 mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, dev->dev_addr,10587 mlxsw_sp_fid_index(fid), false);10588 mlxsw_sp_rif_macvlan_flush(rif);10589 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,10590 mlxsw_sp_router_port(mlxsw_sp), false);10591 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,10592 mlxsw_sp_router_port(mlxsw_sp), false);10593 mlxsw_sp_rif_fid_op(rif, fid_index, false);10594 mlxsw_sp_rif_mac_profile_put(rif->mlxsw_sp, rif->mac_profile_id);10595}10596 10597static struct mlxsw_sp_fid *10598mlxsw_sp_rif_fid_fid_get(struct mlxsw_sp_rif *rif,10599 const struct mlxsw_sp_rif_params *params,10600 struct netlink_ext_ack *extack)10601{10602 int rif_ifindex = mlxsw_sp_rif_dev_ifindex(rif);10603 10604 return mlxsw_sp_fid_8021d_get(rif->mlxsw_sp, rif_ifindex);10605}10606 10607static void mlxsw_sp_rif_fid_fdb_del(struct mlxsw_sp_rif *rif, const char *mac)10608{10609 struct switchdev_notifier_fdb_info info = {};10610 struct net_device *dev;10611 10612 dev = br_fdb_find_port(mlxsw_sp_rif_dev(rif), mac, 0);10613 if (!dev)10614 return;10615 10616 info.addr = mac;10617 info.vid = 0;10618 call_switchdev_notifiers(SWITCHDEV_FDB_DEL_TO_BRIDGE, dev, &info.info,10619 NULL);10620}10621 10622static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_fid_ops = {10623 .type = MLXSW_SP_RIF_TYPE_FID,10624 .rif_size = sizeof(struct mlxsw_sp_rif),10625 .configure = mlxsw_sp_rif_fid_configure,10626 .deconfigure = mlxsw_sp_rif_fid_deconfigure,10627 .fid_get = mlxsw_sp_rif_fid_fid_get,10628 .fdb_del = mlxsw_sp_rif_fid_fdb_del,10629};10630 10631static struct mlxsw_sp_fid *10632mlxsw_sp_rif_vlan_fid_get(struct mlxsw_sp_rif *rif,10633 const struct mlxsw_sp_rif_params *params,10634 struct netlink_ext_ack *extack)10635{10636 struct net_device *dev = mlxsw_sp_rif_dev(rif);10637 struct net_device *br_dev;10638 10639 if (WARN_ON(!params->vid))10640 return ERR_PTR(-EINVAL);10641 10642 if (is_vlan_dev(dev)) {10643 br_dev = vlan_dev_real_dev(dev);10644 if (WARN_ON(!netif_is_bridge_master(br_dev)))10645 return ERR_PTR(-EINVAL);10646 }10647 10648 return mlxsw_sp_fid_8021q_get(rif->mlxsw_sp, params->vid);10649}10650 10651static void mlxsw_sp_rif_vlan_fdb_del(struct mlxsw_sp_rif *rif, const char *mac)10652{10653 struct net_device *rif_dev = mlxsw_sp_rif_dev(rif);10654 struct switchdev_notifier_fdb_info info = {};10655 u16 vid = mlxsw_sp_fid_8021q_vid(rif->fid);10656 struct net_device *br_dev;10657 struct net_device *dev;10658 10659 br_dev = is_vlan_dev(rif_dev) ? vlan_dev_real_dev(rif_dev) : rif_dev;10660 dev = br_fdb_find_port(br_dev, mac, vid);10661 if (!dev)10662 return;10663 10664 info.addr = mac;10665 info.vid = vid;10666 call_switchdev_notifiers(SWITCHDEV_FDB_DEL_TO_BRIDGE, dev, &info.info,10667 NULL);10668}10669 10670static int mlxsw_sp_rif_vlan_op(struct mlxsw_sp_rif *rif, u16 vid, u16 efid,10671 bool enable)10672{10673 struct net_device *dev = mlxsw_sp_rif_dev(rif);10674 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;10675 char ritr_pl[MLXSW_REG_RITR_LEN];10676 10677 mlxsw_reg_ritr_vlan_if_pack(ritr_pl, enable, rif->rif_index, rif->vr_id,10678 dev->mtu, dev->dev_addr,10679 rif->mac_profile_id, vid, efid);10680 10681 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);10682}10683 10684static int mlxsw_sp_rif_vlan_configure(struct mlxsw_sp_rif *rif, u16 efid,10685 struct netlink_ext_ack *extack)10686{10687 struct net_device *dev = mlxsw_sp_rif_dev(rif);10688 u16 vid = mlxsw_sp_fid_8021q_vid(rif->fid);10689 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;10690 u8 mac_profile;10691 int err;10692 10693 err = mlxsw_sp_rif_mac_profile_get(mlxsw_sp, rif->addr,10694 &mac_profile, extack);10695 if (err)10696 return err;10697 rif->mac_profile_id = mac_profile;10698 10699 err = mlxsw_sp_rif_vlan_op(rif, vid, efid, true);10700 if (err)10701 goto err_rif_vlan_fid_op;10702 10703 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,10704 mlxsw_sp_router_port(mlxsw_sp), true);10705 if (err)10706 goto err_fid_mc_flood_set;10707 10708 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,10709 mlxsw_sp_router_port(mlxsw_sp), true);10710 if (err)10711 goto err_fid_bc_flood_set;10712 10713 err = mlxsw_sp_macvlan_replay(rif, extack);10714 if (err)10715 goto err_macvlan_replay;10716 10717 err = mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, dev->dev_addr,10718 mlxsw_sp_fid_index(rif->fid), true);10719 if (err)10720 goto err_rif_fdb_op;10721 10722 err = mlxsw_sp_fid_rif_set(rif->fid, rif);10723 if (err)10724 goto err_fid_rif_set;10725 10726 return 0;10727 10728err_fid_rif_set:10729 mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, dev->dev_addr,10730 mlxsw_sp_fid_index(rif->fid), false);10731err_rif_fdb_op:10732 mlxsw_sp_rif_macvlan_flush(rif);10733err_macvlan_replay:10734 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,10735 mlxsw_sp_router_port(mlxsw_sp), false);10736err_fid_bc_flood_set:10737 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,10738 mlxsw_sp_router_port(mlxsw_sp), false);10739err_fid_mc_flood_set:10740 mlxsw_sp_rif_vlan_op(rif, vid, 0, false);10741err_rif_vlan_fid_op:10742 mlxsw_sp_rif_mac_profile_put(mlxsw_sp, mac_profile);10743 return err;10744}10745 10746static void mlxsw_sp_rif_vlan_deconfigure(struct mlxsw_sp_rif *rif)10747{10748 struct net_device *dev = mlxsw_sp_rif_dev(rif);10749 u16 vid = mlxsw_sp_fid_8021q_vid(rif->fid);10750 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;10751 10752 mlxsw_sp_fid_rif_unset(rif->fid);10753 mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, dev->dev_addr,10754 mlxsw_sp_fid_index(rif->fid), false);10755 mlxsw_sp_rif_macvlan_flush(rif);10756 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,10757 mlxsw_sp_router_port(mlxsw_sp), false);10758 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,10759 mlxsw_sp_router_port(mlxsw_sp), false);10760 mlxsw_sp_rif_vlan_op(rif, vid, 0, false);10761 mlxsw_sp_rif_mac_profile_put(rif->mlxsw_sp, rif->mac_profile_id);10762}10763 10764static int mlxsw_sp1_rif_vlan_configure(struct mlxsw_sp_rif *rif,10765 struct netlink_ext_ack *extack)10766{10767 return mlxsw_sp_rif_vlan_configure(rif, 0, extack);10768}10769 10770static const struct mlxsw_sp_rif_ops mlxsw_sp1_rif_vlan_ops = {10771 .type = MLXSW_SP_RIF_TYPE_VLAN,10772 .rif_size = sizeof(struct mlxsw_sp_rif),10773 .configure = mlxsw_sp1_rif_vlan_configure,10774 .deconfigure = mlxsw_sp_rif_vlan_deconfigure,10775 .fid_get = mlxsw_sp_rif_vlan_fid_get,10776 .fdb_del = mlxsw_sp_rif_vlan_fdb_del,10777};10778 10779static int mlxsw_sp2_rif_vlan_configure(struct mlxsw_sp_rif *rif,10780 struct netlink_ext_ack *extack)10781{10782 u16 efid = mlxsw_sp_fid_index(rif->fid);10783 10784 return mlxsw_sp_rif_vlan_configure(rif, efid, extack);10785}10786 10787static const struct mlxsw_sp_rif_ops mlxsw_sp2_rif_vlan_ops = {10788 .type = MLXSW_SP_RIF_TYPE_VLAN,10789 .rif_size = sizeof(struct mlxsw_sp_rif),10790 .configure = mlxsw_sp2_rif_vlan_configure,10791 .deconfigure = mlxsw_sp_rif_vlan_deconfigure,10792 .fid_get = mlxsw_sp_rif_vlan_fid_get,10793 .fdb_del = mlxsw_sp_rif_vlan_fdb_del,10794};10795 10796static struct mlxsw_sp_rif_ipip_lb *10797mlxsw_sp_rif_ipip_lb_rif(struct mlxsw_sp_rif *rif)10798{10799 return container_of(rif, struct mlxsw_sp_rif_ipip_lb, common);10800}10801 10802static void10803mlxsw_sp_rif_ipip_lb_setup(struct mlxsw_sp_rif *rif,10804 const struct mlxsw_sp_rif_params *params)10805{10806 struct mlxsw_sp_rif_params_ipip_lb *params_lb;10807 struct mlxsw_sp_rif_ipip_lb *rif_lb;10808 10809 params_lb = container_of(params, struct mlxsw_sp_rif_params_ipip_lb,10810 common);10811 rif_lb = mlxsw_sp_rif_ipip_lb_rif(rif);10812 rif_lb->lb_config = params_lb->lb_config;10813}10814 10815static int10816mlxsw_sp1_rif_ipip_lb_configure(struct mlxsw_sp_rif *rif,10817 struct netlink_ext_ack *extack)10818{10819 struct mlxsw_sp_rif_ipip_lb *lb_rif = mlxsw_sp_rif_ipip_lb_rif(rif);10820 struct net_device *dev = mlxsw_sp_rif_dev(rif);10821 u32 ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(dev);10822 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;10823 struct mlxsw_sp_vr *ul_vr;10824 int err;10825 10826 ul_vr = mlxsw_sp_vr_get(mlxsw_sp, ul_tb_id, extack);10827 if (IS_ERR(ul_vr))10828 return PTR_ERR(ul_vr);10829 10830 err = mlxsw_sp_rif_ipip_lb_op(lb_rif, ul_vr->id, 0, true);10831 if (err)10832 goto err_loopback_op;10833 10834 lb_rif->ul_vr_id = ul_vr->id;10835 lb_rif->ul_rif_id = 0;10836 ++ul_vr->rif_count;10837 return 0;10838 10839err_loopback_op:10840 mlxsw_sp_vr_put(mlxsw_sp, ul_vr);10841 return err;10842}10843 10844static void mlxsw_sp1_rif_ipip_lb_deconfigure(struct mlxsw_sp_rif *rif)10845{10846 struct mlxsw_sp_rif_ipip_lb *lb_rif = mlxsw_sp_rif_ipip_lb_rif(rif);10847 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;10848 struct mlxsw_sp_vr *ul_vr;10849 10850 ul_vr = &mlxsw_sp->router->vrs[lb_rif->ul_vr_id];10851 mlxsw_sp_rif_ipip_lb_op(lb_rif, ul_vr->id, 0, false);10852 10853 --ul_vr->rif_count;10854 mlxsw_sp_vr_put(mlxsw_sp, ul_vr);10855}10856 10857static const struct mlxsw_sp_rif_ops mlxsw_sp1_rif_ipip_lb_ops = {10858 .type = MLXSW_SP_RIF_TYPE_IPIP_LB,10859 .rif_size = sizeof(struct mlxsw_sp_rif_ipip_lb),10860 .setup = mlxsw_sp_rif_ipip_lb_setup,10861 .configure = mlxsw_sp1_rif_ipip_lb_configure,10862 .deconfigure = mlxsw_sp1_rif_ipip_lb_deconfigure,10863};10864 10865static const struct mlxsw_sp_rif_ops *mlxsw_sp1_rif_ops_arr[] = {10866 [MLXSW_SP_RIF_TYPE_SUBPORT] = &mlxsw_sp_rif_subport_ops,10867 [MLXSW_SP_RIF_TYPE_VLAN] = &mlxsw_sp1_rif_vlan_ops,10868 [MLXSW_SP_RIF_TYPE_FID] = &mlxsw_sp_rif_fid_ops,10869 [MLXSW_SP_RIF_TYPE_IPIP_LB] = &mlxsw_sp1_rif_ipip_lb_ops,10870};10871 10872static int10873mlxsw_sp_rif_ipip_lb_ul_rif_op(struct mlxsw_sp_rif *ul_rif, bool enable)10874{10875 struct mlxsw_sp *mlxsw_sp = ul_rif->mlxsw_sp;10876 char ritr_pl[MLXSW_REG_RITR_LEN];10877 10878 mlxsw_reg_ritr_pack(ritr_pl, enable, MLXSW_REG_RITR_LOOPBACK_IF,10879 ul_rif->rif_index, ul_rif->vr_id, IP_MAX_MTU);10880 mlxsw_reg_ritr_loopback_protocol_set(ritr_pl,10881 MLXSW_REG_RITR_LOOPBACK_GENERIC);10882 10883 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);10884}10885 10886static struct mlxsw_sp_rif *10887mlxsw_sp_ul_rif_create(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_vr *vr,10888 struct mlxsw_sp_crif *ul_crif,10889 struct netlink_ext_ack *extack)10890{10891 struct mlxsw_sp_rif *ul_rif;10892 u8 rif_entries = 1;10893 u16 rif_index;10894 int err;10895 10896 err = mlxsw_sp_rif_index_alloc(mlxsw_sp, &rif_index, rif_entries);10897 if (err) {10898 NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported router interfaces");10899 return ERR_PTR(err);10900 }10901 10902 ul_rif = mlxsw_sp_rif_alloc(sizeof(*ul_rif), rif_index, vr->id,10903 ul_crif);10904 if (!ul_rif) {10905 err = -ENOMEM;10906 goto err_rif_alloc;10907 }10908 10909 mlxsw_sp->router->rifs[rif_index] = ul_rif;10910 ul_rif->mlxsw_sp = mlxsw_sp;10911 ul_rif->rif_entries = rif_entries;10912 err = mlxsw_sp_rif_ipip_lb_ul_rif_op(ul_rif, true);10913 if (err)10914 goto ul_rif_op_err;10915 10916 atomic_add(rif_entries, &mlxsw_sp->router->rifs_count);10917 return ul_rif;10918 10919ul_rif_op_err:10920 mlxsw_sp->router->rifs[rif_index] = NULL;10921 mlxsw_sp_rif_free(ul_rif);10922err_rif_alloc:10923 mlxsw_sp_rif_index_free(mlxsw_sp, rif_index, rif_entries);10924 return ERR_PTR(err);10925}10926 10927static void mlxsw_sp_ul_rif_destroy(struct mlxsw_sp_rif *ul_rif)10928{10929 struct mlxsw_sp *mlxsw_sp = ul_rif->mlxsw_sp;10930 u8 rif_entries = ul_rif->rif_entries;10931 u16 rif_index = ul_rif->rif_index;10932 10933 atomic_sub(rif_entries, &mlxsw_sp->router->rifs_count);10934 mlxsw_sp_rif_ipip_lb_ul_rif_op(ul_rif, false);10935 mlxsw_sp->router->rifs[ul_rif->rif_index] = NULL;10936 mlxsw_sp_rif_free(ul_rif);10937 mlxsw_sp_rif_index_free(mlxsw_sp, rif_index, rif_entries);10938}10939 10940static struct mlxsw_sp_rif *10941mlxsw_sp_ul_rif_get(struct mlxsw_sp *mlxsw_sp, u32 tb_id,10942 struct mlxsw_sp_crif *ul_crif,10943 struct netlink_ext_ack *extack)10944{10945 struct mlxsw_sp_vr *vr;10946 int err;10947 10948 vr = mlxsw_sp_vr_get(mlxsw_sp, tb_id, extack);10949 if (IS_ERR(vr))10950 return ERR_CAST(vr);10951 10952 if (refcount_inc_not_zero(&vr->ul_rif_refcnt))10953 return vr->ul_rif;10954 10955 vr->ul_rif = mlxsw_sp_ul_rif_create(mlxsw_sp, vr, ul_crif, extack);10956 if (IS_ERR(vr->ul_rif)) {10957 err = PTR_ERR(vr->ul_rif);10958 goto err_ul_rif_create;10959 }10960 10961 vr->rif_count++;10962 refcount_set(&vr->ul_rif_refcnt, 1);10963 10964 return vr->ul_rif;10965 10966err_ul_rif_create:10967 mlxsw_sp_vr_put(mlxsw_sp, vr);10968 return ERR_PTR(err);10969}10970 10971static void mlxsw_sp_ul_rif_put(struct mlxsw_sp_rif *ul_rif)10972{10973 struct mlxsw_sp *mlxsw_sp = ul_rif->mlxsw_sp;10974 struct mlxsw_sp_vr *vr;10975 10976 vr = &mlxsw_sp->router->vrs[ul_rif->vr_id];10977 10978 if (!refcount_dec_and_test(&vr->ul_rif_refcnt))10979 return;10980 10981 vr->rif_count--;10982 mlxsw_sp_ul_rif_destroy(ul_rif);10983 mlxsw_sp_vr_put(mlxsw_sp, vr);10984}10985 10986int mlxsw_sp_router_ul_rif_get(struct mlxsw_sp *mlxsw_sp, u32 ul_tb_id,10987 u16 *ul_rif_index)10988{10989 struct mlxsw_sp_rif *ul_rif;10990 int err = 0;10991 10992 mutex_lock(&mlxsw_sp->router->lock);10993 ul_rif = mlxsw_sp_ul_rif_get(mlxsw_sp, ul_tb_id, NULL, NULL);10994 if (IS_ERR(ul_rif)) {10995 err = PTR_ERR(ul_rif);10996 goto out;10997 }10998 *ul_rif_index = ul_rif->rif_index;10999out:11000 mutex_unlock(&mlxsw_sp->router->lock);11001 return err;11002}11003 11004void mlxsw_sp_router_ul_rif_put(struct mlxsw_sp *mlxsw_sp, u16 ul_rif_index)11005{11006 struct mlxsw_sp_rif *ul_rif;11007 11008 mutex_lock(&mlxsw_sp->router->lock);11009 ul_rif = mlxsw_sp->router->rifs[ul_rif_index];11010 if (WARN_ON(!ul_rif))11011 goto out;11012 11013 mlxsw_sp_ul_rif_put(ul_rif);11014out:11015 mutex_unlock(&mlxsw_sp->router->lock);11016}11017 11018static int11019mlxsw_sp2_rif_ipip_lb_configure(struct mlxsw_sp_rif *rif,11020 struct netlink_ext_ack *extack)11021{11022 struct mlxsw_sp_rif_ipip_lb *lb_rif = mlxsw_sp_rif_ipip_lb_rif(rif);11023 struct net_device *dev = mlxsw_sp_rif_dev(rif);11024 u32 ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(dev);11025 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;11026 struct mlxsw_sp_rif *ul_rif;11027 int err;11028 11029 ul_rif = mlxsw_sp_ul_rif_get(mlxsw_sp, ul_tb_id, NULL, extack);11030 if (IS_ERR(ul_rif))11031 return PTR_ERR(ul_rif);11032 11033 err = mlxsw_sp_rif_ipip_lb_op(lb_rif, 0, ul_rif->rif_index, true);11034 if (err)11035 goto err_loopback_op;11036 11037 lb_rif->ul_vr_id = 0;11038 lb_rif->ul_rif_id = ul_rif->rif_index;11039 11040 return 0;11041 11042err_loopback_op:11043 mlxsw_sp_ul_rif_put(ul_rif);11044 return err;11045}11046 11047static void mlxsw_sp2_rif_ipip_lb_deconfigure(struct mlxsw_sp_rif *rif)11048{11049 struct mlxsw_sp_rif_ipip_lb *lb_rif = mlxsw_sp_rif_ipip_lb_rif(rif);11050 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;11051 struct mlxsw_sp_rif *ul_rif;11052 11053 ul_rif = mlxsw_sp_rif_by_index(mlxsw_sp, lb_rif->ul_rif_id);11054 mlxsw_sp_rif_ipip_lb_op(lb_rif, 0, lb_rif->ul_rif_id, false);11055 mlxsw_sp_ul_rif_put(ul_rif);11056}11057 11058static const struct mlxsw_sp_rif_ops mlxsw_sp2_rif_ipip_lb_ops = {11059 .type = MLXSW_SP_RIF_TYPE_IPIP_LB,11060 .rif_size = sizeof(struct mlxsw_sp_rif_ipip_lb),11061 .setup = mlxsw_sp_rif_ipip_lb_setup,11062 .configure = mlxsw_sp2_rif_ipip_lb_configure,11063 .deconfigure = mlxsw_sp2_rif_ipip_lb_deconfigure,11064};11065 11066static const struct mlxsw_sp_rif_ops *mlxsw_sp2_rif_ops_arr[] = {11067 [MLXSW_SP_RIF_TYPE_SUBPORT] = &mlxsw_sp_rif_subport_ops,11068 [MLXSW_SP_RIF_TYPE_VLAN] = &mlxsw_sp2_rif_vlan_ops,11069 [MLXSW_SP_RIF_TYPE_FID] = &mlxsw_sp_rif_fid_ops,11070 [MLXSW_SP_RIF_TYPE_IPIP_LB] = &mlxsw_sp2_rif_ipip_lb_ops,11071};11072 11073static int mlxsw_sp_rifs_table_init(struct mlxsw_sp *mlxsw_sp)11074{11075 struct gen_pool *rifs_table;11076 int err;11077 11078 rifs_table = gen_pool_create(0, -1);11079 if (!rifs_table)11080 return -ENOMEM;11081 11082 gen_pool_set_algo(rifs_table, gen_pool_first_fit_order_align,11083 NULL);11084 11085 err = gen_pool_add(rifs_table, MLXSW_SP_ROUTER_GENALLOC_OFFSET,11086 MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS), -1);11087 if (err)11088 goto err_gen_pool_add;11089 11090 mlxsw_sp->router->rifs_table = rifs_table;11091 11092 return 0;11093 11094err_gen_pool_add:11095 gen_pool_destroy(rifs_table);11096 return err;11097}11098 11099static void mlxsw_sp_rifs_table_fini(struct mlxsw_sp *mlxsw_sp)11100{11101 gen_pool_destroy(mlxsw_sp->router->rifs_table);11102}11103 11104static int mlxsw_sp_rifs_init(struct mlxsw_sp *mlxsw_sp)11105{11106 u64 max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);11107 struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);11108 struct mlxsw_core *core = mlxsw_sp->core;11109 int err;11110 11111 if (!MLXSW_CORE_RES_VALID(core, MAX_RIF_MAC_PROFILES))11112 return -EIO;11113 mlxsw_sp->router->max_rif_mac_profile =11114 MLXSW_CORE_RES_GET(core, MAX_RIF_MAC_PROFILES);11115 11116 mlxsw_sp->router->rifs = kcalloc(max_rifs,11117 sizeof(struct mlxsw_sp_rif *),11118 GFP_KERNEL);11119 if (!mlxsw_sp->router->rifs)11120 return -ENOMEM;11121 11122 err = mlxsw_sp_rifs_table_init(mlxsw_sp);11123 if (err)11124 goto err_rifs_table_init;11125 11126 idr_init(&mlxsw_sp->router->rif_mac_profiles_idr);11127 atomic_set(&mlxsw_sp->router->rif_mac_profiles_count, 0);11128 atomic_set(&mlxsw_sp->router->rifs_count, 0);11129 devl_resource_occ_get_register(devlink,11130 MLXSW_SP_RESOURCE_RIF_MAC_PROFILES,11131 mlxsw_sp_rif_mac_profiles_occ_get,11132 mlxsw_sp);11133 devl_resource_occ_get_register(devlink,11134 MLXSW_SP_RESOURCE_RIFS,11135 mlxsw_sp_rifs_occ_get,11136 mlxsw_sp);11137 11138 return 0;11139 11140err_rifs_table_init:11141 kfree(mlxsw_sp->router->rifs);11142 return err;11143}11144 11145static void mlxsw_sp_rifs_fini(struct mlxsw_sp *mlxsw_sp)11146{11147 int max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);11148 struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);11149 int i;11150 11151 WARN_ON_ONCE(atomic_read(&mlxsw_sp->router->rifs_count));11152 for (i = 0; i < max_rifs; i++)11153 WARN_ON_ONCE(mlxsw_sp->router->rifs[i]);11154 11155 devl_resource_occ_get_unregister(devlink, MLXSW_SP_RESOURCE_RIFS);11156 devl_resource_occ_get_unregister(devlink,11157 MLXSW_SP_RESOURCE_RIF_MAC_PROFILES);11158 WARN_ON(!idr_is_empty(&mlxsw_sp->router->rif_mac_profiles_idr));11159 idr_destroy(&mlxsw_sp->router->rif_mac_profiles_idr);11160 mlxsw_sp_rifs_table_fini(mlxsw_sp);11161 kfree(mlxsw_sp->router->rifs);11162}11163 11164static int11165mlxsw_sp_ipip_config_tigcr(struct mlxsw_sp *mlxsw_sp)11166{11167 char tigcr_pl[MLXSW_REG_TIGCR_LEN];11168 11169 mlxsw_reg_tigcr_pack(tigcr_pl, true, 0);11170 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(tigcr), tigcr_pl);11171}11172 11173static int mlxsw_sp_ipips_init(struct mlxsw_sp *mlxsw_sp)11174{11175 int err;11176 11177 INIT_LIST_HEAD(&mlxsw_sp->router->ipip_list);11178 11179 err = mlxsw_sp_ipip_ecn_encap_init(mlxsw_sp);11180 if (err)11181 return err;11182 err = mlxsw_sp_ipip_ecn_decap_init(mlxsw_sp);11183 if (err)11184 return err;11185 11186 return mlxsw_sp_ipip_config_tigcr(mlxsw_sp);11187}11188 11189static int mlxsw_sp1_ipips_init(struct mlxsw_sp *mlxsw_sp)11190{11191 mlxsw_sp->router->ipip_ops_arr = mlxsw_sp1_ipip_ops_arr;11192 return mlxsw_sp_ipips_init(mlxsw_sp);11193}11194 11195static int mlxsw_sp2_ipips_init(struct mlxsw_sp *mlxsw_sp)11196{11197 mlxsw_sp->router->ipip_ops_arr = mlxsw_sp2_ipip_ops_arr;11198 return mlxsw_sp_ipips_init(mlxsw_sp);11199}11200 11201static void mlxsw_sp_ipips_fini(struct mlxsw_sp *mlxsw_sp)11202{11203 WARN_ON(!list_empty(&mlxsw_sp->router->ipip_list));11204}11205 11206static void mlxsw_sp_router_fib_dump_flush(struct notifier_block *nb)11207{11208 struct mlxsw_sp_router *router;11209 11210 /* Flush pending FIB notifications and then flush the device's11211 * table before requesting another dump. The FIB notification11212 * block is unregistered, so no need to take RTNL.11213 */11214 mlxsw_core_flush_owq();11215 router = container_of(nb, struct mlxsw_sp_router, fib_nb);11216 mlxsw_sp_router_fib_flush(router->mlxsw_sp);11217}11218 11219#ifdef CONFIG_IP_ROUTE_MULTIPATH11220struct mlxsw_sp_mp_hash_config {11221 DECLARE_BITMAP(headers, __MLXSW_REG_RECR2_HEADER_CNT);11222 DECLARE_BITMAP(fields, __MLXSW_REG_RECR2_FIELD_CNT);11223 DECLARE_BITMAP(inner_headers, __MLXSW_REG_RECR2_HEADER_CNT);11224 DECLARE_BITMAP(inner_fields, __MLXSW_REG_RECR2_INNER_FIELD_CNT);11225 bool inc_parsing_depth;11226};11227 11228#define MLXSW_SP_MP_HASH_HEADER_SET(_headers, _header) \11229 bitmap_set(_headers, MLXSW_REG_RECR2_##_header, 1)11230 11231#define MLXSW_SP_MP_HASH_FIELD_SET(_fields, _field) \11232 bitmap_set(_fields, MLXSW_REG_RECR2_##_field, 1)11233 11234#define MLXSW_SP_MP_HASH_FIELD_RANGE_SET(_fields, _field, _nr) \11235 bitmap_set(_fields, MLXSW_REG_RECR2_##_field, _nr)11236 11237static void mlxsw_sp_mp_hash_inner_l3(struct mlxsw_sp_mp_hash_config *config)11238{11239 unsigned long *inner_headers = config->inner_headers;11240 unsigned long *inner_fields = config->inner_fields;11241 11242 /* IPv4 inner */11243 MLXSW_SP_MP_HASH_HEADER_SET(inner_headers, IPV4_EN_NOT_TCP_NOT_UDP);11244 MLXSW_SP_MP_HASH_HEADER_SET(inner_headers, IPV4_EN_TCP_UDP);11245 MLXSW_SP_MP_HASH_FIELD_RANGE_SET(inner_fields, INNER_IPV4_SIP0, 4);11246 MLXSW_SP_MP_HASH_FIELD_RANGE_SET(inner_fields, INNER_IPV4_DIP0, 4);11247 /* IPv6 inner */11248 MLXSW_SP_MP_HASH_HEADER_SET(inner_headers, IPV6_EN_NOT_TCP_NOT_UDP);11249 MLXSW_SP_MP_HASH_HEADER_SET(inner_headers, IPV6_EN_TCP_UDP);11250 MLXSW_SP_MP_HASH_FIELD_SET(inner_fields, INNER_IPV6_SIP0_7);11251 MLXSW_SP_MP_HASH_FIELD_RANGE_SET(inner_fields, INNER_IPV6_SIP8, 8);11252 MLXSW_SP_MP_HASH_FIELD_SET(inner_fields, INNER_IPV6_DIP0_7);11253 MLXSW_SP_MP_HASH_FIELD_RANGE_SET(inner_fields, INNER_IPV6_DIP8, 8);11254 MLXSW_SP_MP_HASH_FIELD_SET(inner_fields, INNER_IPV6_NEXT_HEADER);11255 MLXSW_SP_MP_HASH_FIELD_SET(inner_fields, INNER_IPV6_FLOW_LABEL);11256}11257 11258static void mlxsw_sp_mp4_hash_outer_addr(struct mlxsw_sp_mp_hash_config *config)11259{11260 unsigned long *headers = config->headers;11261 unsigned long *fields = config->fields;11262 11263 MLXSW_SP_MP_HASH_HEADER_SET(headers, IPV4_EN_NOT_TCP_NOT_UDP);11264 MLXSW_SP_MP_HASH_HEADER_SET(headers, IPV4_EN_TCP_UDP);11265 MLXSW_SP_MP_HASH_FIELD_RANGE_SET(fields, IPV4_SIP0, 4);11266 MLXSW_SP_MP_HASH_FIELD_RANGE_SET(fields, IPV4_DIP0, 4);11267}11268 11269static void11270mlxsw_sp_mp_hash_inner_custom(struct mlxsw_sp_mp_hash_config *config,11271 u32 hash_fields)11272{11273 unsigned long *inner_headers = config->inner_headers;11274 unsigned long *inner_fields = config->inner_fields;11275 11276 /* IPv4 Inner */11277 MLXSW_SP_MP_HASH_HEADER_SET(inner_headers, IPV4_EN_NOT_TCP_NOT_UDP);11278 MLXSW_SP_MP_HASH_HEADER_SET(inner_headers, IPV4_EN_TCP_UDP);11279 if (hash_fields & FIB_MULTIPATH_HASH_FIELD_INNER_SRC_IP)11280 MLXSW_SP_MP_HASH_FIELD_RANGE_SET(inner_fields, INNER_IPV4_SIP0, 4);11281 if (hash_fields & FIB_MULTIPATH_HASH_FIELD_INNER_DST_IP)11282 MLXSW_SP_MP_HASH_FIELD_RANGE_SET(inner_fields, INNER_IPV4_DIP0, 4);11283 if (hash_fields & FIB_MULTIPATH_HASH_FIELD_INNER_IP_PROTO)11284 MLXSW_SP_MP_HASH_FIELD_SET(inner_fields, INNER_IPV4_PROTOCOL);11285 /* IPv6 inner */11286 MLXSW_SP_MP_HASH_HEADER_SET(inner_headers, IPV6_EN_NOT_TCP_NOT_UDP);11287 MLXSW_SP_MP_HASH_HEADER_SET(inner_headers, IPV6_EN_TCP_UDP);11288 if (hash_fields & FIB_MULTIPATH_HASH_FIELD_INNER_SRC_IP) {11289 MLXSW_SP_MP_HASH_FIELD_SET(inner_fields, INNER_IPV6_SIP0_7);11290 MLXSW_SP_MP_HASH_FIELD_RANGE_SET(inner_fields, INNER_IPV6_SIP8, 8);11291 }11292 if (hash_fields & FIB_MULTIPATH_HASH_FIELD_INNER_DST_IP) {11293 MLXSW_SP_MP_HASH_FIELD_SET(inner_fields, INNER_IPV6_DIP0_7);11294 MLXSW_SP_MP_HASH_FIELD_RANGE_SET(inner_fields, INNER_IPV6_DIP8, 8);11295 }11296 if (hash_fields & FIB_MULTIPATH_HASH_FIELD_INNER_IP_PROTO)11297 MLXSW_SP_MP_HASH_FIELD_SET(inner_fields, INNER_IPV6_NEXT_HEADER);11298 if (hash_fields & FIB_MULTIPATH_HASH_FIELD_INNER_FLOWLABEL)11299 MLXSW_SP_MP_HASH_FIELD_SET(inner_fields, INNER_IPV6_FLOW_LABEL);11300 /* L4 inner */11301 MLXSW_SP_MP_HASH_HEADER_SET(inner_headers, TCP_UDP_EN_IPV4);11302 MLXSW_SP_MP_HASH_HEADER_SET(inner_headers, TCP_UDP_EN_IPV6);11303 if (hash_fields & FIB_MULTIPATH_HASH_FIELD_INNER_SRC_PORT)11304 MLXSW_SP_MP_HASH_FIELD_SET(inner_fields, INNER_TCP_UDP_SPORT);11305 if (hash_fields & FIB_MULTIPATH_HASH_FIELD_INNER_DST_PORT)11306 MLXSW_SP_MP_HASH_FIELD_SET(inner_fields, INNER_TCP_UDP_DPORT);11307}11308 11309static void mlxsw_sp_mp4_hash_init(struct mlxsw_sp *mlxsw_sp,11310 struct mlxsw_sp_mp_hash_config *config)11311{11312 struct net *net = mlxsw_sp_net(mlxsw_sp);11313 unsigned long *headers = config->headers;11314 unsigned long *fields = config->fields;11315 u32 hash_fields;11316 11317 switch (READ_ONCE(net->ipv4.sysctl_fib_multipath_hash_policy)) {11318 case 0:11319 mlxsw_sp_mp4_hash_outer_addr(config);11320 break;11321 case 1:11322 mlxsw_sp_mp4_hash_outer_addr(config);11323 MLXSW_SP_MP_HASH_HEADER_SET(headers, TCP_UDP_EN_IPV4);11324 MLXSW_SP_MP_HASH_FIELD_SET(fields, IPV4_PROTOCOL);11325 MLXSW_SP_MP_HASH_FIELD_SET(fields, TCP_UDP_SPORT);11326 MLXSW_SP_MP_HASH_FIELD_SET(fields, TCP_UDP_DPORT);11327 break;11328 case 2:11329 /* Outer */11330 mlxsw_sp_mp4_hash_outer_addr(config);11331 /* Inner */11332 mlxsw_sp_mp_hash_inner_l3(config);11333 break;11334 case 3:11335 hash_fields = READ_ONCE(net->ipv4.sysctl_fib_multipath_hash_fields);11336 /* Outer */11337 MLXSW_SP_MP_HASH_HEADER_SET(headers, IPV4_EN_NOT_TCP_NOT_UDP);11338 MLXSW_SP_MP_HASH_HEADER_SET(headers, IPV4_EN_TCP_UDP);11339 MLXSW_SP_MP_HASH_HEADER_SET(headers, TCP_UDP_EN_IPV4);11340 if (hash_fields & FIB_MULTIPATH_HASH_FIELD_SRC_IP)11341 MLXSW_SP_MP_HASH_FIELD_RANGE_SET(fields, IPV4_SIP0, 4);11342 if (hash_fields & FIB_MULTIPATH_HASH_FIELD_DST_IP)11343 MLXSW_SP_MP_HASH_FIELD_RANGE_SET(fields, IPV4_DIP0, 4);11344 if (hash_fields & FIB_MULTIPATH_HASH_FIELD_IP_PROTO)11345 MLXSW_SP_MP_HASH_FIELD_SET(fields, IPV4_PROTOCOL);11346 if (hash_fields & FIB_MULTIPATH_HASH_FIELD_SRC_PORT)11347 MLXSW_SP_MP_HASH_FIELD_SET(fields, TCP_UDP_SPORT);11348 if (hash_fields & FIB_MULTIPATH_HASH_FIELD_DST_PORT)11349 MLXSW_SP_MP_HASH_FIELD_SET(fields, TCP_UDP_DPORT);11350 /* Inner */11351 mlxsw_sp_mp_hash_inner_custom(config, hash_fields);11352 break;11353 }11354}11355 11356static void mlxsw_sp_mp6_hash_outer_addr(struct mlxsw_sp_mp_hash_config *config)11357{11358 unsigned long *headers = config->headers;11359 unsigned long *fields = config->fields;11360 11361 MLXSW_SP_MP_HASH_HEADER_SET(headers, IPV6_EN_NOT_TCP_NOT_UDP);11362 MLXSW_SP_MP_HASH_HEADER_SET(headers, IPV6_EN_TCP_UDP);11363 MLXSW_SP_MP_HASH_FIELD_SET(fields, IPV6_SIP0_7);11364 MLXSW_SP_MP_HASH_FIELD_RANGE_SET(fields, IPV6_SIP8, 8);11365 MLXSW_SP_MP_HASH_FIELD_SET(fields, IPV6_DIP0_7);11366 MLXSW_SP_MP_HASH_FIELD_RANGE_SET(fields, IPV6_DIP8, 8);11367}11368 11369static void mlxsw_sp_mp6_hash_init(struct mlxsw_sp *mlxsw_sp,11370 struct mlxsw_sp_mp_hash_config *config)11371{11372 u32 hash_fields = ip6_multipath_hash_fields(mlxsw_sp_net(mlxsw_sp));11373 unsigned long *headers = config->headers;11374 unsigned long *fields = config->fields;11375 11376 switch (ip6_multipath_hash_policy(mlxsw_sp_net(mlxsw_sp))) {11377 case 0:11378 mlxsw_sp_mp6_hash_outer_addr(config);11379 MLXSW_SP_MP_HASH_FIELD_SET(fields, IPV6_NEXT_HEADER);11380 MLXSW_SP_MP_HASH_FIELD_SET(fields, IPV6_FLOW_LABEL);11381 break;11382 case 1:11383 mlxsw_sp_mp6_hash_outer_addr(config);11384 MLXSW_SP_MP_HASH_HEADER_SET(headers, TCP_UDP_EN_IPV6);11385 MLXSW_SP_MP_HASH_FIELD_SET(fields, IPV6_NEXT_HEADER);11386 MLXSW_SP_MP_HASH_FIELD_SET(fields, TCP_UDP_SPORT);11387 MLXSW_SP_MP_HASH_FIELD_SET(fields, TCP_UDP_DPORT);11388 break;11389 case 2:11390 /* Outer */11391 mlxsw_sp_mp6_hash_outer_addr(config);11392 MLXSW_SP_MP_HASH_FIELD_SET(fields, IPV6_NEXT_HEADER);11393 MLXSW_SP_MP_HASH_FIELD_SET(fields, IPV6_FLOW_LABEL);11394 /* Inner */11395 mlxsw_sp_mp_hash_inner_l3(config);11396 config->inc_parsing_depth = true;11397 break;11398 case 3:11399 /* Outer */11400 MLXSW_SP_MP_HASH_HEADER_SET(headers, IPV6_EN_NOT_TCP_NOT_UDP);11401 MLXSW_SP_MP_HASH_HEADER_SET(headers, IPV6_EN_TCP_UDP);11402 MLXSW_SP_MP_HASH_HEADER_SET(headers, TCP_UDP_EN_IPV6);11403 if (hash_fields & FIB_MULTIPATH_HASH_FIELD_SRC_IP) {11404 MLXSW_SP_MP_HASH_FIELD_SET(fields, IPV6_SIP0_7);11405 MLXSW_SP_MP_HASH_FIELD_RANGE_SET(fields, IPV6_SIP8, 8);11406 }11407 if (hash_fields & FIB_MULTIPATH_HASH_FIELD_DST_IP) {11408 MLXSW_SP_MP_HASH_FIELD_SET(fields, IPV6_DIP0_7);11409 MLXSW_SP_MP_HASH_FIELD_RANGE_SET(fields, IPV6_DIP8, 8);11410 }11411 if (hash_fields & FIB_MULTIPATH_HASH_FIELD_IP_PROTO)11412 MLXSW_SP_MP_HASH_FIELD_SET(fields, IPV6_NEXT_HEADER);11413 if (hash_fields & FIB_MULTIPATH_HASH_FIELD_FLOWLABEL)11414 MLXSW_SP_MP_HASH_FIELD_SET(fields, IPV6_FLOW_LABEL);11415 if (hash_fields & FIB_MULTIPATH_HASH_FIELD_SRC_PORT)11416 MLXSW_SP_MP_HASH_FIELD_SET(fields, TCP_UDP_SPORT);11417 if (hash_fields & FIB_MULTIPATH_HASH_FIELD_DST_PORT)11418 MLXSW_SP_MP_HASH_FIELD_SET(fields, TCP_UDP_DPORT);11419 /* Inner */11420 mlxsw_sp_mp_hash_inner_custom(config, hash_fields);11421 if (hash_fields & FIB_MULTIPATH_HASH_FIELD_INNER_MASK)11422 config->inc_parsing_depth = true;11423 break;11424 }11425}11426 11427static int mlxsw_sp_mp_hash_parsing_depth_adjust(struct mlxsw_sp *mlxsw_sp,11428 bool old_inc_parsing_depth,11429 bool new_inc_parsing_depth)11430{11431 int err;11432 11433 if (!old_inc_parsing_depth && new_inc_parsing_depth) {11434 err = mlxsw_sp_parsing_depth_inc(mlxsw_sp);11435 if (err)11436 return err;11437 mlxsw_sp->router->inc_parsing_depth = true;11438 } else if (old_inc_parsing_depth && !new_inc_parsing_depth) {11439 mlxsw_sp_parsing_depth_dec(mlxsw_sp);11440 mlxsw_sp->router->inc_parsing_depth = false;11441 }11442 11443 return 0;11444}11445 11446static int mlxsw_sp_mp_hash_init(struct mlxsw_sp *mlxsw_sp)11447{11448 bool old_inc_parsing_depth, new_inc_parsing_depth;11449 struct mlxsw_sp_mp_hash_config config = {};11450 struct net *net = mlxsw_sp_net(mlxsw_sp);11451 char recr2_pl[MLXSW_REG_RECR2_LEN];11452 unsigned long bit;11453 u32 seed;11454 int err;11455 11456 seed = READ_ONCE(net->ipv4.sysctl_fib_multipath_hash_seed).user_seed;11457 if (!seed)11458 seed = jhash(mlxsw_sp->base_mac, sizeof(mlxsw_sp->base_mac), 0);11459 11460 mlxsw_reg_recr2_pack(recr2_pl, seed);11461 mlxsw_sp_mp4_hash_init(mlxsw_sp, &config);11462 mlxsw_sp_mp6_hash_init(mlxsw_sp, &config);11463 11464 old_inc_parsing_depth = mlxsw_sp->router->inc_parsing_depth;11465 new_inc_parsing_depth = config.inc_parsing_depth;11466 err = mlxsw_sp_mp_hash_parsing_depth_adjust(mlxsw_sp,11467 old_inc_parsing_depth,11468 new_inc_parsing_depth);11469 if (err)11470 return err;11471 11472 for_each_set_bit(bit, config.headers, __MLXSW_REG_RECR2_HEADER_CNT)11473 mlxsw_reg_recr2_outer_header_enables_set(recr2_pl, bit, 1);11474 for_each_set_bit(bit, config.fields, __MLXSW_REG_RECR2_FIELD_CNT)11475 mlxsw_reg_recr2_outer_header_fields_enable_set(recr2_pl, bit, 1);11476 for_each_set_bit(bit, config.inner_headers, __MLXSW_REG_RECR2_HEADER_CNT)11477 mlxsw_reg_recr2_inner_header_enables_set(recr2_pl, bit, 1);11478 for_each_set_bit(bit, config.inner_fields, __MLXSW_REG_RECR2_INNER_FIELD_CNT)11479 mlxsw_reg_recr2_inner_header_fields_enable_set(recr2_pl, bit, 1);11480 11481 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(recr2), recr2_pl);11482 if (err)11483 goto err_reg_write;11484 11485 return 0;11486 11487err_reg_write:11488 mlxsw_sp_mp_hash_parsing_depth_adjust(mlxsw_sp, new_inc_parsing_depth,11489 old_inc_parsing_depth);11490 return err;11491}11492 11493static void mlxsw_sp_mp_hash_fini(struct mlxsw_sp *mlxsw_sp)11494{11495 bool old_inc_parsing_depth = mlxsw_sp->router->inc_parsing_depth;11496 11497 mlxsw_sp_mp_hash_parsing_depth_adjust(mlxsw_sp, old_inc_parsing_depth,11498 false);11499}11500#else11501static int mlxsw_sp_mp_hash_init(struct mlxsw_sp *mlxsw_sp)11502{11503 return 0;11504}11505 11506static void mlxsw_sp_mp_hash_fini(struct mlxsw_sp *mlxsw_sp)11507{11508}11509#endif11510 11511static int mlxsw_sp_dscp_init(struct mlxsw_sp *mlxsw_sp)11512{11513 char rdpm_pl[MLXSW_REG_RDPM_LEN];11514 unsigned int i;11515 11516 MLXSW_REG_ZERO(rdpm, rdpm_pl);11517 11518 /* HW is determining switch priority based on DSCP-bits, but the11519 * kernel is still doing that based on the ToS. Since there's a11520 * mismatch in bits we need to make sure to translate the right11521 * value ToS would observe, skipping the 2 least-significant ECN bits.11522 */11523 for (i = 0; i < MLXSW_REG_RDPM_DSCP_ENTRY_REC_MAX_COUNT; i++)11524 mlxsw_reg_rdpm_pack(rdpm_pl, i, rt_tos2priority(i << 2));11525 11526 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rdpm), rdpm_pl);11527}11528 11529static int __mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)11530{11531 struct net *net = mlxsw_sp_net(mlxsw_sp);11532 char rgcr_pl[MLXSW_REG_RGCR_LEN];11533 u64 max_rifs;11534 bool usp;11535 11536 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_RIFS))11537 return -EIO;11538 max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);11539 usp = READ_ONCE(net->ipv4.sysctl_ip_fwd_update_priority);11540 11541 mlxsw_reg_rgcr_pack(rgcr_pl, true, true);11542 mlxsw_reg_rgcr_max_router_interfaces_set(rgcr_pl, max_rifs);11543 mlxsw_reg_rgcr_usp_set(rgcr_pl, usp);11544 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);11545}11546 11547static void __mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)11548{11549 char rgcr_pl[MLXSW_REG_RGCR_LEN];11550 11551 mlxsw_reg_rgcr_pack(rgcr_pl, false, false);11552 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);11553}11554 11555static int mlxsw_sp_lb_rif_init(struct mlxsw_sp *mlxsw_sp,11556 struct netlink_ext_ack *extack)11557{11558 struct mlxsw_sp_router *router = mlxsw_sp->router;11559 struct mlxsw_sp_rif *lb_rif;11560 int err;11561 11562 router->lb_crif = mlxsw_sp_crif_alloc(NULL);11563 if (!router->lb_crif)11564 return -ENOMEM;11565 11566 /* Create a generic loopback RIF associated with the main table11567 * (default VRF). Any table can be used, but the main table exists11568 * anyway, so we do not waste resources. Loopback RIFs are usually11569 * created with a NULL CRIF, but this RIF is used as a fallback RIF11570 * for blackhole nexthops, and nexthops expect to have a valid CRIF.11571 */11572 lb_rif = mlxsw_sp_ul_rif_get(mlxsw_sp, RT_TABLE_MAIN, router->lb_crif,11573 extack);11574 if (IS_ERR(lb_rif)) {11575 err = PTR_ERR(lb_rif);11576 goto err_ul_rif_get;11577 }11578 11579 return 0;11580 11581err_ul_rif_get:11582 mlxsw_sp_crif_free(router->lb_crif);11583 return err;11584}11585 11586static void mlxsw_sp_lb_rif_fini(struct mlxsw_sp *mlxsw_sp)11587{11588 mlxsw_sp_ul_rif_put(mlxsw_sp->router->lb_crif->rif);11589 mlxsw_sp_crif_free(mlxsw_sp->router->lb_crif);11590}11591 11592static int mlxsw_sp1_router_init(struct mlxsw_sp *mlxsw_sp)11593{11594 size_t size_ranges_count = ARRAY_SIZE(mlxsw_sp1_adj_grp_size_ranges);11595 11596 mlxsw_sp->router->rif_ops_arr = mlxsw_sp1_rif_ops_arr;11597 mlxsw_sp->router->adj_grp_size_ranges = mlxsw_sp1_adj_grp_size_ranges;11598 mlxsw_sp->router->adj_grp_size_ranges_count = size_ranges_count;11599 11600 return 0;11601}11602 11603const struct mlxsw_sp_router_ops mlxsw_sp1_router_ops = {11604 .init = mlxsw_sp1_router_init,11605 .ipips_init = mlxsw_sp1_ipips_init,11606};11607 11608static int mlxsw_sp2_router_init(struct mlxsw_sp *mlxsw_sp)11609{11610 size_t size_ranges_count = ARRAY_SIZE(mlxsw_sp2_adj_grp_size_ranges);11611 11612 mlxsw_sp->router->rif_ops_arr = mlxsw_sp2_rif_ops_arr;11613 mlxsw_sp->router->adj_grp_size_ranges = mlxsw_sp2_adj_grp_size_ranges;11614 mlxsw_sp->router->adj_grp_size_ranges_count = size_ranges_count;11615 11616 return 0;11617}11618 11619const struct mlxsw_sp_router_ops mlxsw_sp2_router_ops = {11620 .init = mlxsw_sp2_router_init,11621 .ipips_init = mlxsw_sp2_ipips_init,11622};11623 11624int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp,11625 struct netlink_ext_ack *extack)11626{11627 struct mlxsw_sp_router *router;11628 struct notifier_block *nb;11629 int err;11630 11631 router = kzalloc(sizeof(*mlxsw_sp->router), GFP_KERNEL);11632 if (!router)11633 return -ENOMEM;11634 mutex_init(&router->lock);11635 mlxsw_sp->router = router;11636 router->mlxsw_sp = mlxsw_sp;11637 11638 err = mlxsw_sp->router_ops->init(mlxsw_sp);11639 if (err)11640 goto err_router_ops_init;11641 11642 INIT_LIST_HEAD(&mlxsw_sp->router->nh_res_grp_list);11643 INIT_DELAYED_WORK(&mlxsw_sp->router->nh_grp_activity_dw,11644 mlxsw_sp_nh_grp_activity_work);11645 INIT_LIST_HEAD(&mlxsw_sp->router->nexthop_neighs_list);11646 err = __mlxsw_sp_router_init(mlxsw_sp);11647 if (err)11648 goto err_router_init;11649 11650 err = mlxsw_sp->router_ops->ipips_init(mlxsw_sp);11651 if (err)11652 goto err_ipips_init;11653 11654 err = rhashtable_init(&mlxsw_sp->router->crif_ht,11655 &mlxsw_sp_crif_ht_params);11656 if (err)11657 goto err_crif_ht_init;11658 11659 err = mlxsw_sp_rifs_init(mlxsw_sp);11660 if (err)11661 goto err_rifs_init;11662 11663 err = rhashtable_init(&mlxsw_sp->router->nexthop_ht,11664 &mlxsw_sp_nexthop_ht_params);11665 if (err)11666 goto err_nexthop_ht_init;11667 11668 err = rhashtable_init(&mlxsw_sp->router->nexthop_group_ht,11669 &mlxsw_sp_nexthop_group_ht_params);11670 if (err)11671 goto err_nexthop_group_ht_init;11672 11673 INIT_LIST_HEAD(&mlxsw_sp->router->nexthop_list);11674 err = mlxsw_sp_lpm_init(mlxsw_sp);11675 if (err)11676 goto err_lpm_init;11677 11678 err = mlxsw_sp_mr_init(mlxsw_sp, &mlxsw_sp_mr_tcam_ops);11679 if (err)11680 goto err_mr_init;11681 11682 err = mlxsw_sp_vrs_init(mlxsw_sp);11683 if (err)11684 goto err_vrs_init;11685 11686 err = mlxsw_sp_lb_rif_init(mlxsw_sp, extack);11687 if (err)11688 goto err_lb_rif_init;11689 11690 err = mlxsw_sp_neigh_init(mlxsw_sp);11691 if (err)11692 goto err_neigh_init;11693 11694 err = mlxsw_sp_mp_hash_init(mlxsw_sp);11695 if (err)11696 goto err_mp_hash_init;11697 11698 err = mlxsw_sp_dscp_init(mlxsw_sp);11699 if (err)11700 goto err_dscp_init;11701 11702 router->inetaddr_nb.notifier_call = mlxsw_sp_inetaddr_event;11703 err = register_inetaddr_notifier(&router->inetaddr_nb);11704 if (err)11705 goto err_register_inetaddr_notifier;11706 11707 router->inet6addr_nb.notifier_call = mlxsw_sp_inet6addr_event;11708 err = register_inet6addr_notifier(&router->inet6addr_nb);11709 if (err)11710 goto err_register_inet6addr_notifier;11711 11712 router->inetaddr_valid_nb.notifier_call = mlxsw_sp_inetaddr_valid_event;11713 err = register_inetaddr_validator_notifier(&router->inetaddr_valid_nb);11714 if (err)11715 goto err_register_inetaddr_valid_notifier;11716 11717 nb = &router->inet6addr_valid_nb;11718 nb->notifier_call = mlxsw_sp_inet6addr_valid_event;11719 err = register_inet6addr_validator_notifier(nb);11720 if (err)11721 goto err_register_inet6addr_valid_notifier;11722 11723 mlxsw_sp->router->netevent_nb.notifier_call =11724 mlxsw_sp_router_netevent_event;11725 err = register_netevent_notifier(&mlxsw_sp->router->netevent_nb);11726 if (err)11727 goto err_register_netevent_notifier;11728 11729 mlxsw_sp->router->netdevice_nb.notifier_call =11730 mlxsw_sp_router_netdevice_event;11731 err = register_netdevice_notifier_net(mlxsw_sp_net(mlxsw_sp),11732 &mlxsw_sp->router->netdevice_nb);11733 if (err)11734 goto err_register_netdev_notifier;11735 11736 mlxsw_sp->router->nexthop_nb.notifier_call =11737 mlxsw_sp_nexthop_obj_event;11738 err = register_nexthop_notifier(mlxsw_sp_net(mlxsw_sp),11739 &mlxsw_sp->router->nexthop_nb,11740 extack);11741 if (err)11742 goto err_register_nexthop_notifier;11743 11744 mlxsw_sp->router->fib_nb.notifier_call = mlxsw_sp_router_fib_event;11745 err = register_fib_notifier(mlxsw_sp_net(mlxsw_sp),11746 &mlxsw_sp->router->fib_nb,11747 mlxsw_sp_router_fib_dump_flush, extack);11748 if (err)11749 goto err_register_fib_notifier;11750 11751 return 0;11752 11753err_register_fib_notifier:11754 unregister_nexthop_notifier(mlxsw_sp_net(mlxsw_sp),11755 &mlxsw_sp->router->nexthop_nb);11756err_register_nexthop_notifier:11757 unregister_netdevice_notifier_net(mlxsw_sp_net(mlxsw_sp),11758 &router->netdevice_nb);11759err_register_netdev_notifier:11760 unregister_netevent_notifier(&mlxsw_sp->router->netevent_nb);11761err_register_netevent_notifier:11762 unregister_inet6addr_validator_notifier(&router->inet6addr_valid_nb);11763err_register_inet6addr_valid_notifier:11764 unregister_inetaddr_validator_notifier(&router->inetaddr_valid_nb);11765err_register_inetaddr_valid_notifier:11766 unregister_inet6addr_notifier(&router->inet6addr_nb);11767err_register_inet6addr_notifier:11768 unregister_inetaddr_notifier(&router->inetaddr_nb);11769err_register_inetaddr_notifier:11770 mlxsw_core_flush_owq();11771err_dscp_init:11772 mlxsw_sp_mp_hash_fini(mlxsw_sp);11773err_mp_hash_init:11774 mlxsw_sp_neigh_fini(mlxsw_sp);11775err_neigh_init:11776 mlxsw_sp_lb_rif_fini(mlxsw_sp);11777err_lb_rif_init:11778 mlxsw_sp_vrs_fini(mlxsw_sp);11779err_vrs_init:11780 mlxsw_sp_mr_fini(mlxsw_sp);11781err_mr_init:11782 mlxsw_sp_lpm_fini(mlxsw_sp);11783err_lpm_init:11784 rhashtable_destroy(&mlxsw_sp->router->nexthop_group_ht);11785err_nexthop_group_ht_init:11786 rhashtable_destroy(&mlxsw_sp->router->nexthop_ht);11787err_nexthop_ht_init:11788 mlxsw_sp_rifs_fini(mlxsw_sp);11789err_rifs_init:11790 rhashtable_destroy(&mlxsw_sp->router->crif_ht);11791err_crif_ht_init:11792 mlxsw_sp_ipips_fini(mlxsw_sp);11793err_ipips_init:11794 __mlxsw_sp_router_fini(mlxsw_sp);11795err_router_init:11796 cancel_delayed_work_sync(&mlxsw_sp->router->nh_grp_activity_dw);11797err_router_ops_init:11798 mutex_destroy(&mlxsw_sp->router->lock);11799 kfree(mlxsw_sp->router);11800 return err;11801}11802 11803void mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)11804{11805 struct mlxsw_sp_router *router = mlxsw_sp->router;11806 11807 unregister_fib_notifier(mlxsw_sp_net(mlxsw_sp), &router->fib_nb);11808 unregister_nexthop_notifier(mlxsw_sp_net(mlxsw_sp),11809 &router->nexthop_nb);11810 unregister_netdevice_notifier_net(mlxsw_sp_net(mlxsw_sp),11811 &router->netdevice_nb);11812 unregister_netevent_notifier(&router->netevent_nb);11813 unregister_inet6addr_validator_notifier(&router->inet6addr_valid_nb);11814 unregister_inetaddr_validator_notifier(&router->inetaddr_valid_nb);11815 unregister_inet6addr_notifier(&router->inet6addr_nb);11816 unregister_inetaddr_notifier(&router->inetaddr_nb);11817 mlxsw_core_flush_owq();11818 mlxsw_sp_mp_hash_fini(mlxsw_sp);11819 mlxsw_sp_neigh_fini(mlxsw_sp);11820 mlxsw_sp_lb_rif_fini(mlxsw_sp);11821 mlxsw_sp_vrs_fini(mlxsw_sp);11822 mlxsw_sp_mr_fini(mlxsw_sp);11823 mlxsw_sp_lpm_fini(mlxsw_sp);11824 rhashtable_destroy(&router->nexthop_group_ht);11825 rhashtable_destroy(&router->nexthop_ht);11826 mlxsw_sp_rifs_fini(mlxsw_sp);11827 rhashtable_destroy(&mlxsw_sp->router->crif_ht);11828 mlxsw_sp_ipips_fini(mlxsw_sp);11829 __mlxsw_sp_router_fini(mlxsw_sp);11830 cancel_delayed_work_sync(&router->nh_grp_activity_dw);11831 mutex_destroy(&router->lock);11832 kfree(router);11833}11834