[{"id":1774328,"web_url":"http://patchwork.ozlabs.org/comment/1774328/","msgid":"<fb5d1fcc-ad70-d656-f860-7b05cdd2834a@huawei.com>","list_archive_url":null,"date":"2017-09-25T01:48:38","subject":"Re: [patch net-next v2 07/12] mlxsw: spectrum: Add the multicast\n\trouting offloading logic","submitter":{"id":71804,"url":"http://patchwork.ozlabs.org/api/people/71804/","name":"Yunsheng Lin","email":"linyunsheng@huawei.com"},"content":"Hi, Jiri\n\nOn 2017/9/25 1:22, Jiri Pirko wrote:\n> From: Yotam Gigi <yotamg@mellanox.com>\n> \n> Add the multicast router offloading logic, which is in charge of handling\n> the VIF and MFC notifications and translating it to the hardware logic API.\n> \n> The offloading logic has to overcome several obstacles in order to safely\n> comply with the kernel multicast router user API:\n>  - It must keep track of the mapping between VIFs to netdevices. The user\n>    can add an MFC cache entry pointing to a VIF, delete the VIF and add\n>    re-add it with a different netdevice. The offloading logic has to handle\n>    this in order to be compatible with the kernel logic.\n>  - It must keep track of the mapping between netdevices to spectrum RIFs,\n>    as the current hardware implementation assume having a RIF for every\n>    port in a multicast router.\n>  - It must handle routes pointing to pimreg device to be trapped to the\n>    kernel, as the packet should be delivered to userspace.\n>  - It must handle routes pointing tunnel VIFs. The current implementation\n>    does not support multicast forwarding to tunnels, thus routes that point\n>    to a tunnel should be trapped to the kernel.\n>  - It must be aware of proxy multicast routes, which include both (*,*)\n>    routes and duplicate routes. Currently proxy routes are not offloaded\n>    and trigger the abort mechanism: removal of all routes from hardware and\n>    triggering the traffic to go through the kernel.\n> \n> The multicast routing offloading logic also updates the counters of the\n> offloaded MFC routes in a periodic work.\n> \n> Signed-off-by: Yotam Gigi <yotamg@mellanox.com>\n> Reviewed-by: Ido Schimmel <idosch@mellanox.com>\n> Signed-off-by: Jiri Pirko <jiri@mellanox.com>\n> ---\n> v1->v2:\n>  - Update the lastuse MFC entry field too, in addition to packets an bytes.\n> ---\n>  drivers/net/ethernet/mellanox/mlxsw/Makefile      |    3 +-\n>  drivers/net/ethernet/mellanox/mlxsw/spectrum.h    |    1 +\n>  drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.c | 1014 +++++++++++++++++++++\n>  drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.h |  133 +++\n>  4 files changed, 1150 insertions(+), 1 deletion(-)\n>  create mode 100644 drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.c\n>  create mode 100644 drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.h\n> \n> diff --git a/drivers/net/ethernet/mellanox/mlxsw/Makefile b/drivers/net/ethernet/mellanox/mlxsw/Makefile\n> index 4b88158..9b29764 100644\n> --- a/drivers/net/ethernet/mellanox/mlxsw/Makefile\n> +++ b/drivers/net/ethernet/mellanox/mlxsw/Makefile\n> @@ -17,7 +17,8 @@ mlxsw_spectrum-objs\t\t:= spectrum.o spectrum_buffers.o \\\n>  \t\t\t\t   spectrum_kvdl.o spectrum_acl_tcam.o \\\n>  \t\t\t\t   spectrum_acl.o spectrum_flower.o \\\n>  \t\t\t\t   spectrum_cnt.o spectrum_fid.o \\\n> -\t\t\t\t   spectrum_ipip.o spectrum_acl_flex_actions.o\n> +\t\t\t\t   spectrum_ipip.o spectrum_acl_flex_actions.o \\\n> +\t\t\t\t   spectrum_mr.o\n>  mlxsw_spectrum-$(CONFIG_MLXSW_SPECTRUM_DCB)\t+= spectrum_dcb.o\n>  mlxsw_spectrum-$(CONFIG_NET_DEVLINK) += spectrum_dpipe.o\n>  obj-$(CONFIG_MLXSW_MINIMAL)\t+= mlxsw_minimal.o\n> diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h\n> index e907ec4..51d8b9f 100644\n> --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h\n> +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h\n> @@ -153,6 +153,7 @@ struct mlxsw_sp {\n>  \tstruct mlxsw_sp_sb *sb;\n>  \tstruct mlxsw_sp_bridge *bridge;\n>  \tstruct mlxsw_sp_router *router;\n> +\tstruct mlxsw_sp_mr *mr;\n>  \tstruct mlxsw_afa *afa;\n>  \tstruct mlxsw_sp_acl *acl;\n>  \tstruct mlxsw_sp_fid_core *fid_core;\n> diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.c\n> new file mode 100644\n> index 0000000..89b2e60\n> --- /dev/null\n> +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.c\n> @@ -0,0 +1,1014 @@\n> +/*\n> + * drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.c\n> + * Copyright (c) 2017 Mellanox Technologies. All rights reserved.\n> + * Copyright (c) 2017 Yotam Gigi <yotamg@mellanox.com>\n> + *\n> + * Redistribution and use in source and binary forms, with or without\n> + * modification, are permitted provided that the following conditions are met:\n> + *\n> + * 1. Redistributions of source code must retain the above copyright\n> + *    notice, this list of conditions and the following disclaimer.\n> + * 2. Redistributions in binary form must reproduce the above copyright\n> + *    notice, this list of conditions and the following disclaimer in the\n> + *    documentation and/or other materials provided with the distribution.\n> + * 3. Neither the names of the copyright holders nor the names of its\n> + *    contributors may be used to endorse or promote products derived from\n> + *    this software without specific prior written permission.\n> + *\n> + * Alternatively, this software may be distributed under the terms of the\n> + * GNU General Public License (\"GPL\") version 2 as published by the Free\n> + * Software Foundation.\n> + *\n> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n> + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n> + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n> + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n> + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n> + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n> + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n> + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n> + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n> + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n> + * POSSIBILITY OF SUCH DAMAGE.\n> + */\n> +\n> +#include <linux/rhashtable.h>\n> +\n> +#include \"spectrum_mr.h\"\n> +#include \"spectrum_router.h\"\n> +\n> +struct mlxsw_sp_mr {\n> +\tconst struct mlxsw_sp_mr_ops *mr_ops;\n> +\tvoid *catchall_route_priv;\n> +\tstruct delayed_work stats_update_dw;\n> +\tstruct list_head table_list;\n> +#define MLXSW_SP_MR_ROUTES_COUNTER_UPDATE_INTERVAL 5000 /* ms */\n> +\tunsigned long priv[0];\n> +\t/* priv has to be always the last item */\n> +};\n> +\n> +struct mlxsw_sp_mr_vif {\n> +\tstruct net_device *dev;\n> +\tconst struct mlxsw_sp_rif *rif;\n> +\tunsigned long vif_flags;\n> +\n> +\t/* A list of route_vif_entry structs that point to routes that the VIF\n> +\t * instance is used as one of the egress VIFs\n> +\t */\n> +\tstruct list_head route_evif_list;\n> +\n> +\t/* A list of route_vif_entry structs that point to routes that the VIF\n> +\t * instance is used as an ingress VIF\n> +\t */\n> +\tstruct list_head route_ivif_list;\n> +};\n> +\n> +struct mlxsw_sp_mr_route_vif_entry {\n> +\tstruct list_head vif_node;\n> +\tstruct list_head route_node;\n> +\tstruct mlxsw_sp_mr_vif *mr_vif;\n> +\tstruct mlxsw_sp_mr_route *mr_route;\n> +};\n> +\n> +struct mlxsw_sp_mr_table {\n> +\tstruct list_head node;\n> +\tenum mlxsw_sp_l3proto proto;\n> +\tstruct mlxsw_sp *mlxsw_sp;\n> +\tu32 vr_id;\n> +\tstruct mlxsw_sp_mr_vif vifs[MAXVIFS];\n> +\tstruct list_head route_list;\n> +\tstruct rhashtable route_ht;\n> +\tchar catchall_route_priv[0];\n> +\t/* catchall_route_priv has to be always the last item */\n> +};\n> +\n> +struct mlxsw_sp_mr_route {\n> +\tstruct list_head node;\n> +\tstruct rhash_head ht_node;\n> +\tstruct mlxsw_sp_mr_route_key key;\n> +\tenum mlxsw_sp_mr_route_action route_action;\n> +\tu16 min_mtu;\n> +\tstruct mfc_cache *mfc4;\n> +\tvoid *route_priv;\n> +\tconst struct mlxsw_sp_mr_table *mr_table;\n> +\t/* A list of route_vif_entry structs that point to the egress VIFs */\n> +\tstruct list_head evif_list;\n> +\t/* A route_vif_entry struct that point to the ingress VIF */\n> +\tstruct mlxsw_sp_mr_route_vif_entry ivif;\n> +};\n> +\n> +static const struct rhashtable_params mlxsw_sp_mr_route_ht_params = {\n> +\t.key_len = sizeof(struct mlxsw_sp_mr_route_key),\n> +\t.key_offset = offsetof(struct mlxsw_sp_mr_route, key),\n> +\t.head_offset = offsetof(struct mlxsw_sp_mr_route, ht_node),\n> +\t.automatic_shrinking = true,\n> +};\n> +\n> +static bool mlxsw_sp_mr_vif_regular(const struct mlxsw_sp_mr_vif *vif)\n> +{\n> +\treturn !(vif->vif_flags & (VIFF_TUNNEL | VIFF_REGISTER));\n> +}\n> +\n> +static bool mlxsw_sp_mr_vif_valid(const struct mlxsw_sp_mr_vif *vif)\n> +{\n> +\treturn mlxsw_sp_mr_vif_regular(vif) && vif->dev && vif->rif;\n> +}\n> +\n> +static bool mlxsw_sp_mr_vif_rif_invalid(const struct mlxsw_sp_mr_vif *vif)\n> +{\n> +\treturn mlxsw_sp_mr_vif_regular(vif) && vif->dev && !vif->rif;\n> +}\n> +\n> +static bool\n> +mlxsw_sp_mr_route_ivif_in_evifs(const struct mlxsw_sp_mr_route *mr_route)\n> +{\n> +\tvifi_t ivif;\n> +\n> +\tswitch (mr_route->mr_table->proto) {\n> +\tcase MLXSW_SP_L3_PROTO_IPV4:\n> +\t\tivif = mr_route->mfc4->mfc_parent;\n> +\t\treturn mr_route->mfc4->mfc_un.res.ttls[ivif] != 255;\n> +\tcase MLXSW_SP_L3_PROTO_IPV6:\n> +\t\t/* fall through */\n> +\tdefault:\n> +\t\tWARN_ON_ONCE(1);\n> +\t}\n> +\treturn false;\n> +}\n> +\n> +static int\n> +mlxsw_sp_mr_route_valid_evifs_num(const struct mlxsw_sp_mr_route *mr_route)\n> +{\n> +\tstruct mlxsw_sp_mr_route_vif_entry *rve;\n> +\tint valid_evifs = 0;\n> +\n> +\tvalid_evifs = 0;\n\nyou are doing valid_evifs = 0 twice.\n\n> +\tlist_for_each_entry(rve, &mr_route->evif_list, route_node)\n> +\t\tif (mlxsw_sp_mr_vif_valid(rve->mr_vif))\n> +\t\t\tvalid_evifs++;\n> +\treturn valid_evifs;\n> +}\n> +\n> +static bool mlxsw_sp_mr_route_starg(const struct mlxsw_sp_mr_route *mr_route)\n> +{\n> +\tswitch (mr_route->mr_table->proto) {\n> +\tcase MLXSW_SP_L3_PROTO_IPV4:\n> +\t\treturn mr_route->key.source_mask.addr4 == INADDR_ANY;\n> +\tcase MLXSW_SP_L3_PROTO_IPV6:\n> +\t\t/* fall through */\n> +\tdefault:\n> +\t\tWARN_ON_ONCE(1);\n> +\t}\n> +\treturn false;\n> +}\n> +\n> +static enum mlxsw_sp_mr_route_action\n> +mlxsw_sp_mr_route_action(const struct mlxsw_sp_mr_route *mr_route)\n> +{\n> +\tstruct mlxsw_sp_mr_route_vif_entry *rve;\n> +\n> +\t/* If the ingress port is not regular and resolved, trap the route */\n> +\tif (!mlxsw_sp_mr_vif_valid(mr_route->ivif.mr_vif))\n> +\t\treturn MLXSW_SP_MR_ROUTE_ACTION_TRAP;\n> +\n> +\t/* The kernel does not match a (*,G) route that the ingress interface is\n> +\t * not one of the egress interfaces, so trap these kind of routes.\n> +\t */\n> +\tif (mlxsw_sp_mr_route_starg(mr_route) &&\n> +\t    !mlxsw_sp_mr_route_ivif_in_evifs(mr_route))\n> +\t\treturn MLXSW_SP_MR_ROUTE_ACTION_TRAP;\n> +\n> +\t/* If the route has no valid eVIFs, trap it. */\n> +\tif (!mlxsw_sp_mr_route_valid_evifs_num(mr_route))\n> +\t\treturn MLXSW_SP_MR_ROUTE_ACTION_TRAP;\n> +\n> +\t/* If either one of the eVIFs is not regular (VIF of type pimreg or\n> +\t * tunnel) or one of the VIFs has no matching RIF, trap the packet.\n> +\t */\n> +\tlist_for_each_entry(rve, &mr_route->evif_list, route_node) {\n> +\t\tif (!mlxsw_sp_mr_vif_regular(rve->mr_vif) ||\n> +\t\t    mlxsw_sp_mr_vif_rif_invalid(rve->mr_vif))\n> +\t\t\treturn MLXSW_SP_MR_ROUTE_ACTION_TRAP;\n> +\t}\n> +\treturn MLXSW_SP_MR_ROUTE_ACTION_FORWARD;\n> +}\n> +\n> +static enum mlxsw_sp_mr_route_prio\n> +mlxsw_sp_mr_route_prio(const struct mlxsw_sp_mr_route *mr_route)\n> +{\n> +\treturn mlxsw_sp_mr_route_starg(mr_route) ?\n> +\t\tMLXSW_SP_MR_ROUTE_PRIO_STARG : MLXSW_SP_MR_ROUTE_PRIO_SG;\n> +}\n> +\n> +static void mlxsw_sp_mr_route4_key(struct mlxsw_sp_mr_table *mr_table,\n> +\t\t\t\t   struct mlxsw_sp_mr_route_key *key,\n> +\t\t\t\t   const struct mfc_cache *mfc)\n> +{\n> +\tbool starg = (mfc->mfc_origin == INADDR_ANY);\n> +\n> +\tmemset(key, 0, sizeof(*key));\n> +\tkey->vrid = mr_table->vr_id;\n> +\tkey->proto = mr_table->proto;\n> +\tkey->group.addr4 = mfc->mfc_mcastgrp;\n> +\tkey->group_mask.addr4 = 0xffffffff;\n> +\tkey->source.addr4 = mfc->mfc_origin;\n> +\tkey->source_mask.addr4 = starg ? 0 : 0xffffffff;\n> +}\n> +\n> +static int mlxsw_sp_mr_route_evif_link(struct mlxsw_sp_mr_route *mr_route,\n> +\t\t\t\t       struct mlxsw_sp_mr_vif *mr_vif)\n> +{\n> +\tstruct mlxsw_sp_mr_route_vif_entry *rve;\n> +\n> +\trve = kzalloc(sizeof(*rve), GFP_KERNEL);\n> +\tif (!rve)\n> +\t\treturn -ENOMEM;\n> +\trve->mr_route = mr_route;\n> +\trve->mr_vif = mr_vif;\n> +\tlist_add_tail(&rve->route_node, &mr_route->evif_list);\n> +\tlist_add_tail(&rve->vif_node, &mr_vif->route_evif_list);\n> +\treturn 0;\n> +}\n> +\n> +static void\n> +mlxsw_sp_mr_route_evif_unlink(struct mlxsw_sp_mr_route_vif_entry *rve)\n> +{\n> +\tlist_del(&rve->route_node);\n> +\tlist_del(&rve->vif_node);\n> +\tkfree(rve);\n> +}\n> +\n> +static void mlxsw_sp_mr_route_ivif_link(struct mlxsw_sp_mr_route *mr_route,\n> +\t\t\t\t\tstruct mlxsw_sp_mr_vif *mr_vif)\n> +{\n> +\tmr_route->ivif.mr_route = mr_route;\n> +\tmr_route->ivif.mr_vif = mr_vif;\n> +\tlist_add_tail(&mr_route->ivif.vif_node, &mr_vif->route_ivif_list);\n> +}\n> +\n> +static void mlxsw_sp_mr_route_ivif_unlink(struct mlxsw_sp_mr_route *mr_route)\n> +{\n> +\tlist_del(&mr_route->ivif.vif_node);\n> +}\n> +\n> +static int\n> +mlxsw_sp_mr_route_info_create(struct mlxsw_sp_mr_table *mr_table,\n> +\t\t\t      struct mlxsw_sp_mr_route *mr_route,\n> +\t\t\t      struct mlxsw_sp_mr_route_info *route_info)\n> +{\n> +\tstruct mlxsw_sp_mr_route_vif_entry *rve;\n> +\tu16 *erif_indices;\n> +\tu16 irif_index;\n> +\tu16 erif = 0;\n> +\n> +\terif_indices = kmalloc_array(MAXVIFS, sizeof(*erif_indices),\n> +\t\t\t\t     GFP_KERNEL);\n> +\tif (!erif_indices)\n> +\t\treturn -ENOMEM;\n> +\n> +\tlist_for_each_entry(rve, &mr_route->evif_list, route_node) {\n> +\t\tif (mlxsw_sp_mr_vif_valid(rve->mr_vif)) {\n> +\t\t\tu16 rifi = mlxsw_sp_rif_index(rve->mr_vif->rif);\n> +\n> +\t\t\terif_indices[erif++] = rifi;\n> +\t\t}\n> +\t}\n> +\n> +\tif (mlxsw_sp_mr_vif_valid(mr_route->ivif.mr_vif))\n> +\t\tirif_index = mlxsw_sp_rif_index(mr_route->ivif.mr_vif->rif);\n> +\telse\n> +\t\tirif_index = 0;\n> +\n> +\troute_info->irif_index = irif_index;\n> +\troute_info->erif_indices = erif_indices;\n> +\troute_info->min_mtu = mr_route->min_mtu;\n> +\troute_info->route_action = mr_route->route_action;\n> +\troute_info->erif_num = erif;\n> +\treturn 0;\n> +}\n> +\n> +static void\n> +mlxsw_sp_mr_route_info_destroy(struct mlxsw_sp_mr_route_info *route_info)\n> +{\n> +\tkfree(route_info->erif_indices);\n> +}\n> +\n> +static int mlxsw_sp_mr_route_write(struct mlxsw_sp_mr_table *mr_table,\n> +\t\t\t\t   struct mlxsw_sp_mr_route *mr_route,\n> +\t\t\t\t   bool replace)\n> +{\n> +\tstruct mlxsw_sp *mlxsw_sp = mr_table->mlxsw_sp;\n> +\tstruct mlxsw_sp_mr_route_info route_info;\n> +\tstruct mlxsw_sp_mr *mr = mlxsw_sp->mr;\n> +\tint err;\n> +\n> +\terr = mlxsw_sp_mr_route_info_create(mr_table, mr_route, &route_info);\n> +\tif (err)\n> +\t\treturn err;\n> +\n> +\tif (!replace) {\n> +\t\tstruct mlxsw_sp_mr_route_params route_params;\n> +\n> +\t\tmr_route->route_priv = kzalloc(mr->mr_ops->route_priv_size,\n> +\t\t\t\t\t       GFP_KERNEL);\n> +\t\tif (!mr_route->route_priv) {\n> +\t\t\terr = -ENOMEM;\n> +\t\t\tgoto out;\n> +\t\t}\n> +\n> +\t\troute_params.key = mr_route->key;\n> +\t\troute_params.value = route_info;\n> +\t\troute_params.prio = mlxsw_sp_mr_route_prio(mr_route);\n> +\t\terr = mr->mr_ops->route_create(mlxsw_sp, mr->priv,\n> +\t\t\t\t\t       mr_route->route_priv,\n> +\t\t\t\t\t       &route_params);\n> +\t\tif (err)\n> +\t\t\tkfree(mr_route->route_priv);\n> +\t} else {\n> +\t\terr = mr->mr_ops->route_update(mlxsw_sp, mr_route->route_priv,\n> +\t\t\t\t\t       &route_info);\n> +\t}\n> +out:\n> +\tmlxsw_sp_mr_route_info_destroy(&route_info);\n> +\treturn err;\n> +}\n> +\n> +static void mlxsw_sp_mr_route_erase(struct mlxsw_sp_mr_table *mr_table,\n> +\t\t\t\t    struct mlxsw_sp_mr_route *mr_route)\n> +{\n> +\tstruct mlxsw_sp *mlxsw_sp = mr_table->mlxsw_sp;\n> +\tstruct mlxsw_sp_mr *mr = mlxsw_sp->mr;\n> +\n> +\tmr->mr_ops->route_destroy(mlxsw_sp, mr->priv, mr_route->route_priv);\n> +\tkfree(mr_route->route_priv);\n> +}\n> +\n> +static struct mlxsw_sp_mr_route *\n> +mlxsw_sp_mr_route4_create(struct mlxsw_sp_mr_table *mr_table,\n> +\t\t\t  struct mfc_cache *mfc)\n> +{\n> +\tstruct mlxsw_sp_mr_route_vif_entry *rve, *tmp;\n> +\tstruct mlxsw_sp_mr_route *mr_route;\n> +\tint err;\n> +\tint i;\n> +\n> +\t/* Allocate and init a new route and fill it with parameters */\n> +\tmr_route = kzalloc(sizeof(*mr_table), GFP_KERNEL);\n> +\tif (!mr_route)\n> +\t\treturn ERR_PTR(-ENOMEM);\n> +\tINIT_LIST_HEAD(&mr_route->evif_list);\n> +\tmlxsw_sp_mr_route4_key(mr_table, &mr_route->key, mfc);\n> +\n> +\t/* Find min_mtu and link iVIF and eVIFs */\n> +\tmr_route->min_mtu = ETH_MAX_MTU;\n> +\tipmr_cache_hold(mfc);\n> +\tmr_route->mfc4 = mfc;\n> +\tmr_route->mr_table = mr_table;\n> +\tfor (i = 0; i < MAXVIFS; i++) {\n> +\t\tif (mfc->mfc_un.res.ttls[i] != 255) {\n> +\t\t\terr = mlxsw_sp_mr_route_evif_link(mr_route,\n> +\t\t\t\t\t\t\t  &mr_table->vifs[i]);\n> +\t\t\tif (err)\n> +\t\t\t\tgoto err;\n> +\t\t\tif (mr_table->vifs[i].dev &&\n> +\t\t\t    mr_table->vifs[i].dev->mtu < mr_route->min_mtu)\n> +\t\t\t\tmr_route->min_mtu = mr_table->vifs[i].dev->mtu;\n> +\t\t}\n> +\t}\n> +\tmlxsw_sp_mr_route_ivif_link(mr_route, &mr_table->vifs[mfc->mfc_parent]);\n> +\tif (err)\n> +\t\tgoto err;\n> +\n> +\tmr_route->route_action = mlxsw_sp_mr_route_action(mr_route);\n> +\treturn mr_route;\n> +err:\n> +\tipmr_cache_put(mfc);\n> +\tlist_for_each_entry_safe(rve, tmp, &mr_route->evif_list, route_node)\n> +\t\tmlxsw_sp_mr_route_evif_unlink(rve);\n> +\tkfree(mr_route);\n> +\treturn ERR_PTR(err);\n> +}\n> +\n> +static void mlxsw_sp_mr_route4_destroy(struct mlxsw_sp_mr_table *mr_table,\n> +\t\t\t\t       struct mlxsw_sp_mr_route *mr_route)\n> +{\n> +\tstruct mlxsw_sp_mr_route_vif_entry *rve, *tmp;\n> +\n> +\tmlxsw_sp_mr_route_ivif_unlink(mr_route);\n> +\tipmr_cache_put(mr_route->mfc4);\n> +\tlist_for_each_entry_safe(rve, tmp, &mr_route->evif_list, route_node)\n> +\t\tmlxsw_sp_mr_route_evif_unlink(rve);\n> +\tkfree(mr_route);\n> +}\n> +\n> +static void mlxsw_sp_mr_route_destroy(struct mlxsw_sp_mr_table *mr_table,\n> +\t\t\t\t      struct mlxsw_sp_mr_route *mr_route)\n> +{\n> +\tswitch (mr_table->proto) {\n> +\tcase MLXSW_SP_L3_PROTO_IPV4:\n> +\t\tmlxsw_sp_mr_route4_destroy(mr_table, mr_route);\n> +\t\tbreak;\n> +\tcase MLXSW_SP_L3_PROTO_IPV6:\n> +\t\t/* fall through */\n> +\tdefault:\n> +\t\tWARN_ON_ONCE(1);\n> +\t}\n> +}\n> +\n> +static void mlxsw_sp_mr_mfc_offload_set(struct mlxsw_sp_mr_route *mr_route,\n> +\t\t\t\t\tbool offload)\n> +{\n> +\tswitch (mr_route->mr_table->proto) {\n> +\tcase MLXSW_SP_L3_PROTO_IPV4:\n> +\t\tif (offload)\n> +\t\t\tmr_route->mfc4->mfc_flags |= MFC_OFFLOAD;\n> +\t\telse\n> +\t\t\tmr_route->mfc4->mfc_flags &= ~MFC_OFFLOAD;\n> +\t\tbreak;\n> +\tcase MLXSW_SP_L3_PROTO_IPV6:\n> +\t\t/* fall through */\n> +\tdefault:\n> +\t\tWARN_ON_ONCE(1);\n> +\t}\n> +}\n> +\n> +static void mlxsw_sp_mr_mfc_offload_update(struct mlxsw_sp_mr_route *mr_route)\n> +{\n> +\tbool offload;\n> +\n> +\toffload = mr_route->route_action != MLXSW_SP_MR_ROUTE_ACTION_TRAP;\n> +\tmlxsw_sp_mr_mfc_offload_set(mr_route, offload);\n> +}\n> +\n> +static void __mlxsw_sp_mr_route_del(struct mlxsw_sp_mr_table *mr_table,\n> +\t\t\t\t    struct mlxsw_sp_mr_route *mr_route)\n> +{\n> +\tmlxsw_sp_mr_mfc_offload_set(mr_route, false);\n> +\tmlxsw_sp_mr_route_erase(mr_table, mr_route);\n> +\trhashtable_remove_fast(&mr_table->route_ht, &mr_route->ht_node,\n> +\t\t\t       mlxsw_sp_mr_route_ht_params);\n> +\tlist_del(&mr_route->node);\n> +\tmlxsw_sp_mr_route_destroy(mr_table, mr_route);\n> +}\n> +\n> +int mlxsw_sp_mr_route4_add(struct mlxsw_sp_mr_table *mr_table,\n> +\t\t\t   struct mfc_cache *mfc, bool replace)\n> +{\n> +\tstruct mlxsw_sp_mr_route *mr_orig_route = NULL;\n> +\tstruct mlxsw_sp_mr_route *mr_route;\n> +\tint err;\n> +\n> +\t/* If the route is a (*,*) route, abort, as these kind of routes are\n> +\t * used for proxy routes.\n> +\t */\n> +\tif (mfc->mfc_origin == INADDR_ANY && mfc->mfc_mcastgrp == INADDR_ANY) {\n> +\t\tdev_warn(mr_table->mlxsw_sp->bus_info->dev,\n> +\t\t\t \"Offloading proxy routes is not supported.\\n\");\n\nYou are return err, why not use dev_err?\n\n> +\t\treturn -EINVAL;\n> +\t}\n> +\n> +\t/* Create a new route */\n> +\tmr_route = mlxsw_sp_mr_route4_create(mr_table, mfc);\n> +\tif (IS_ERR(mr_route))\n> +\t\treturn PTR_ERR(mr_route);\n> +\n> +\t/* Find any route with a matching key */\n> +\tmr_orig_route = rhashtable_lookup_fast(&mr_table->route_ht,\n> +\t\t\t\t\t       &mr_route->key,\n> +\t\t\t\t\t       mlxsw_sp_mr_route_ht_params);\n> +\tif (replace) {\n> +\t\t/* On replace case, make the route point to the new route_priv.\n> +\t\t */\n> +\t\tif (WARN_ON(!mr_orig_route)) {\n> +\t\t\terr = -ENOENT;\n> +\t\t\tgoto err_no_orig_route;\n> +\t\t}\n> +\t\tmr_route->route_priv = mr_orig_route->route_priv;\n> +\t} else if (mr_orig_route) {\n> +\t\t/* On non replace case, if another route with the same key was\n> +\t\t * found, abort, as duplicate routes are used for proxy routes.\n> +\t\t */\n> +\t\tdev_warn(mr_table->mlxsw_sp->bus_info->dev,\n> +\t\t\t \"Offloading proxy routes is not supported.\\n\");\n\nSame as here.\n\n> +\t\terr = -EINVAL;\n> +\t\tgoto err_duplicate_route;\n> +\t}\n> +\n> +\t/* Put it in the table data-structures */\n> +\tlist_add_tail(&mr_route->node, &mr_table->route_list);\n> +\terr = rhashtable_insert_fast(&mr_table->route_ht,\n> +\t\t\t\t     &mr_route->ht_node,\n> +\t\t\t\t     mlxsw_sp_mr_route_ht_params);\n> +\tif (err)\n> +\t\tgoto err_rhashtable_insert;\n> +\n> +\t/* Write the route to the hardware */\n> +\terr = mlxsw_sp_mr_route_write(mr_table, mr_route, replace);\n> +\tif (err)\n> +\t\tgoto err_mr_route_write;\n> +\n> +\t/* Destroy the original route */\n> +\tif (replace) {\n> +\t\trhashtable_remove_fast(&mr_table->route_ht,\n> +\t\t\t\t       &mr_orig_route->ht_node,\n> +\t\t\t\t       mlxsw_sp_mr_route_ht_params);\n> +\t\tlist_del(&mr_orig_route->node);\n> +\t\tmlxsw_sp_mr_route4_destroy(mr_table, mr_orig_route);\n> +\t}\n> +\n> +\tmlxsw_sp_mr_mfc_offload_update(mr_route);\n> +\treturn 0;\n> +\n> +err_mr_route_write:\n> +\trhashtable_remove_fast(&mr_table->route_ht, &mr_route->ht_node,\n> +\t\t\t       mlxsw_sp_mr_route_ht_params);\n> +err_rhashtable_insert:\n> +\tlist_del(&mr_route->node);\n> +err_no_orig_route:\n> +err_duplicate_route:\n> +\tmlxsw_sp_mr_route4_destroy(mr_table, mr_route);\n> +\treturn err;\n> +}\n> +\n> +void mlxsw_sp_mr_route4_del(struct mlxsw_sp_mr_table *mr_table,\n> +\t\t\t    struct mfc_cache *mfc)\n> +{\n> +\tstruct mlxsw_sp_mr_route *mr_route;\n> +\tstruct mlxsw_sp_mr_route_key key;\n> +\n> +\tmlxsw_sp_mr_route4_key(mr_table, &key, mfc);\n> +\tmr_route = rhashtable_lookup_fast(&mr_table->route_ht, &key,\n> +\t\t\t\t\t  mlxsw_sp_mr_route_ht_params);\n> +\tif (mr_route)\n> +\t\t__mlxsw_sp_mr_route_del(mr_table, mr_route);\n> +}\n> +\n> +/* Should be called after the VIF struct is updated */\n> +static int\n> +mlxsw_sp_mr_route_ivif_resolve(struct mlxsw_sp_mr_table *mr_table,\n> +\t\t\t       struct mlxsw_sp_mr_route_vif_entry *rve)\n> +{\n> +\tstruct mlxsw_sp *mlxsw_sp = mr_table->mlxsw_sp;\n> +\tenum mlxsw_sp_mr_route_action route_action;\n> +\tstruct mlxsw_sp_mr *mr = mlxsw_sp->mr;\n> +\tu16 irif_index;\n> +\tint err;\n> +\n> +\troute_action = mlxsw_sp_mr_route_action(rve->mr_route);\n> +\tif (route_action == MLXSW_SP_MR_ROUTE_ACTION_TRAP)\n> +\t\treturn 0;\n> +\n> +\t/* rve->mr_vif->rif is guaranteed to be valid at this stage */\n> +\tirif_index = mlxsw_sp_rif_index(rve->mr_vif->rif);\n> +\terr = mr->mr_ops->route_irif_update(mlxsw_sp, rve->mr_route->route_priv,\n> +\t\t\t\t\t    irif_index);\n> +\tif (err)\n> +\t\treturn err;\n> +\n> +\terr = mr->mr_ops->route_action_update(mlxsw_sp,\n> +\t\t\t\t\t      rve->mr_route->route_priv,\n> +\t\t\t\t\t      route_action);\n> +\tif (err)\n> +\t\t/* No need to rollback here because the iRIF change only takes\n> +\t\t * place after the action has been updated.\n> +\t\t */\n> +\t\treturn err;\n> +\n> +\trve->mr_route->route_action = route_action;\n> +\tmlxsw_sp_mr_mfc_offload_update(rve->mr_route);\n> +\treturn 0;\n> +}\n> +\n> +static void\n> +mlxsw_sp_mr_route_ivif_unresolve(struct mlxsw_sp_mr_table *mr_table,\n> +\t\t\t\t struct mlxsw_sp_mr_route_vif_entry *rve)\n> +{\n> +\tstruct mlxsw_sp *mlxsw_sp = mr_table->mlxsw_sp;\n> +\tstruct mlxsw_sp_mr *mr = mlxsw_sp->mr;\n> +\n> +\tmr->mr_ops->route_action_update(mlxsw_sp, rve->mr_route->route_priv,\n> +\t\t\t\t\tMLXSW_SP_MR_ROUTE_ACTION_TRAP);\n> +\trve->mr_route->route_action = MLXSW_SP_MR_ROUTE_ACTION_TRAP;\n> +\tmlxsw_sp_mr_mfc_offload_update(rve->mr_route);\n> +}\n> +\n> +/* Should be called after the RIF struct is updated */\n> +static int\n> +mlxsw_sp_mr_route_evif_resolve(struct mlxsw_sp_mr_table *mr_table,\n> +\t\t\t       struct mlxsw_sp_mr_route_vif_entry *rve)\n> +{\n> +\tstruct mlxsw_sp *mlxsw_sp = mr_table->mlxsw_sp;\n> +\tenum mlxsw_sp_mr_route_action route_action;\n> +\tstruct mlxsw_sp_mr *mr = mlxsw_sp->mr;\n> +\tu16 erif_index = 0;\n> +\tint err;\n> +\n> +\t/* Update the route action, as the new eVIF can be a tunnel or a pimreg\n> +\t * device which will require updating the action.\n> +\t */\n> +\troute_action = mlxsw_sp_mr_route_action(rve->mr_route);\n> +\tif (route_action != rve->mr_route->route_action) {\n> +\t\terr = mr->mr_ops->route_action_update(mlxsw_sp,\n> +\t\t\t\t\t\t      rve->mr_route->route_priv,\n> +\t\t\t\t\t\t      route_action);\n> +\t\tif (err)\n> +\t\t\treturn err;\n> +\t}\n> +\n> +\t/* Add the eRIF */\n> +\tif (mlxsw_sp_mr_vif_valid(rve->mr_vif)) {\n> +\t\terif_index = mlxsw_sp_rif_index(rve->mr_vif->rif);\n> +\t\terr = mr->mr_ops->route_erif_add(mlxsw_sp,\n> +\t\t\t\t\t\t rve->mr_route->route_priv,\n> +\t\t\t\t\t\t erif_index);\n> +\t\tif (err)\n> +\t\t\tgoto err_route_erif_add;\n> +\t}\n> +\n> +\t/* Update the minimum MTU */\n> +\tif (rve->mr_vif->dev->mtu < rve->mr_route->min_mtu) {\n> +\t\trve->mr_route->min_mtu = rve->mr_vif->dev->mtu;\n> +\t\terr = mr->mr_ops->route_min_mtu_update(mlxsw_sp,\n> +\t\t\t\t\t\t       rve->mr_route->route_priv,\n> +\t\t\t\t\t\t       rve->mr_route->min_mtu);\n> +\t\tif (err)\n> +\t\t\tgoto err_route_min_mtu_update;\n> +\t}\n> +\n> +\trve->mr_route->route_action = route_action;\n> +\tmlxsw_sp_mr_mfc_offload_update(rve->mr_route);\n> +\treturn 0;\n> +\n> +err_route_min_mtu_update:\n> +\tif (mlxsw_sp_mr_vif_valid(rve->mr_vif))\n> +\t\tmr->mr_ops->route_erif_del(mlxsw_sp, rve->mr_route->route_priv,\n> +\t\t\t\t\t   erif_index);\n> +err_route_erif_add:\n> +\tif (route_action != rve->mr_route->route_action)\n> +\t\tmr->mr_ops->route_action_update(mlxsw_sp,\n> +\t\t\t\t\t\trve->mr_route->route_priv,\n> +\t\t\t\t\t\trve->mr_route->route_action);\n> +\treturn err;\n> +}\n> +\n> +/* Should be called before the RIF struct is updated */\n> +static void\n> +mlxsw_sp_mr_route_evif_unresolve(struct mlxsw_sp_mr_table *mr_table,\n> +\t\t\t\t struct mlxsw_sp_mr_route_vif_entry *rve)\n> +{\n> +\tstruct mlxsw_sp *mlxsw_sp = mr_table->mlxsw_sp;\n> +\tenum mlxsw_sp_mr_route_action route_action;\n> +\tstruct mlxsw_sp_mr *mr = mlxsw_sp->mr;\n> +\tu16 rifi;\n> +\n> +\t/* If the unresolved RIF was not valid, no need to delete it */\n> +\tif (!mlxsw_sp_mr_vif_valid(rve->mr_vif))\n> +\t\treturn;\n> +\n> +\t/* Update the route action: if there is only one valid eVIF in the\n> +\t * route, set the action to trap as the VIF deletion will lead to zero\n> +\t * valid eVIFs. On any other case, use the mlxsw_sp_mr_route_action to\n> +\t * determine the route action.\n> +\t */\n> +\tif (mlxsw_sp_mr_route_valid_evifs_num(rve->mr_route) == 1)\n> +\t\troute_action = MLXSW_SP_MR_ROUTE_ACTION_TRAP;\n> +\telse\n> +\t\troute_action = mlxsw_sp_mr_route_action(rve->mr_route);\n> +\tif (route_action != rve->mr_route->route_action)\n> +\t\tmr->mr_ops->route_action_update(mlxsw_sp,\n> +\t\t\t\t\t\trve->mr_route->route_priv,\n> +\t\t\t\t\t\troute_action);\n> +\n> +\t/* Delete the erif from the route */\n> +\trifi = mlxsw_sp_rif_index(rve->mr_vif->rif);\n> +\tmr->mr_ops->route_erif_del(mlxsw_sp, rve->mr_route->route_priv, rifi);\n> +\trve->mr_route->route_action = route_action;\n> +\tmlxsw_sp_mr_mfc_offload_update(rve->mr_route);\n> +}\n> +\n> +static int mlxsw_sp_mr_vif_resolve(struct mlxsw_sp_mr_table *mr_table,\n> +\t\t\t\t   struct net_device *dev,\n> +\t\t\t\t   struct mlxsw_sp_mr_vif *mr_vif,\n> +\t\t\t\t   unsigned long vif_flags,\n> +\t\t\t\t   const struct mlxsw_sp_rif *rif)\n> +{\n> +\tstruct mlxsw_sp_mr_route_vif_entry *irve, *erve;\n> +\tint err;\n> +\n> +\t/* Update the VIF */\n> +\tmr_vif->dev = dev;\n> +\tmr_vif->rif = rif;\n> +\tmr_vif->vif_flags = vif_flags;\n> +\n> +\t/* Update all routes where this VIF is used as an unresolved iRIF */\n> +\tlist_for_each_entry(irve, &mr_vif->route_ivif_list, vif_node) {\n> +\t\terr = mlxsw_sp_mr_route_ivif_resolve(mr_table, irve);\n> +\t\tif (err)\n> +\t\t\tgoto err_irif_unresolve;\n> +\t}\n> +\n> +\t/* Update all routes where this VIF is used as an unresolved eRIF */\n> +\tlist_for_each_entry(erve, &mr_vif->route_evif_list, vif_node) {\n> +\t\terr = mlxsw_sp_mr_route_evif_resolve(mr_table, erve);\n> +\t\tif (err)\n> +\t\t\tgoto err_erif_unresolve;\n> +\t}\n> +\treturn 0;\n> +\n> +err_erif_unresolve:\n> +\tlist_for_each_entry_from_reverse(erve, &mr_vif->route_evif_list,\n> +\t\t\t\t\t vif_node)\n> +\t\tmlxsw_sp_mr_route_evif_unresolve(mr_table, erve);\n> +err_irif_unresolve:\n> +\tlist_for_each_entry_from_reverse(irve, &mr_vif->route_ivif_list,\n> +\t\t\t\t\t vif_node)\n> +\t\tmlxsw_sp_mr_route_ivif_unresolve(mr_table, irve);\n> +\tmr_vif->rif = NULL;\n> +\treturn err;\n> +}\n> +\n> +static void mlxsw_sp_mr_vif_unresolve(struct mlxsw_sp_mr_table *mr_table,\n> +\t\t\t\t      struct net_device *dev,\n> +\t\t\t\t      struct mlxsw_sp_mr_vif *mr_vif)\n> +{\n> +\tstruct mlxsw_sp_mr_route_vif_entry *rve;\n> +\n> +\t/* Update all routes where this VIF is used as an unresolved eRIF */\n> +\tlist_for_each_entry(rve, &mr_vif->route_evif_list, vif_node)\n> +\t\tmlxsw_sp_mr_route_evif_unresolve(mr_table, rve);\n> +\n> +\t/* Update all routes where this VIF is used as an unresolved iRIF */\n> +\tlist_for_each_entry(rve, &mr_vif->route_ivif_list, vif_node)\n> +\t\tmlxsw_sp_mr_route_ivif_unresolve(mr_table, rve);\n> +\n> +\t/* Update the VIF */\n> +\tmr_vif->dev = dev;\n> +\tmr_vif->rif = NULL;\n> +}\n> +\n> +int mlxsw_sp_mr_vif_add(struct mlxsw_sp_mr_table *mr_table,\n> +\t\t\tstruct net_device *dev, vifi_t vif_index,\n> +\t\t\tunsigned long vif_flags, const struct mlxsw_sp_rif *rif)\n> +{\n> +\tstruct mlxsw_sp_mr_vif *mr_vif = &mr_table->vifs[vif_index];\n> +\n> +\tif (WARN_ON(vif_index >= MAXVIFS))\n> +\t\treturn -EINVAL;\n> +\tif (mr_vif->dev)\n> +\t\treturn -EEXIST;\n\n-ENODEV?\n\n> +\treturn mlxsw_sp_mr_vif_resolve(mr_table, dev, mr_vif, vif_flags, rif);\n> +}\n> +\n> +void mlxsw_sp_mr_vif_del(struct mlxsw_sp_mr_table *mr_table, vifi_t vif_index)\n> +{\n> +\tstruct mlxsw_sp_mr_vif *mr_vif = &mr_table->vifs[vif_index];\n> +\n> +\tif (WARN_ON(vif_index >= MAXVIFS))\n> +\t\treturn;\n> +\tif (WARN_ON(!mr_vif->dev))\n> +\t\treturn;\n> +\tmlxsw_sp_mr_vif_unresolve(mr_table, NULL, mr_vif);\n> +}\n> +\n> +struct mlxsw_sp_mr_vif *\n> +mlxsw_sp_mr_dev_vif_lookup(struct mlxsw_sp_mr_table *mr_table,\n> +\t\t\t   const struct net_device *dev)\n> +{\n> +\tvifi_t vif_index;\n> +\n> +\tfor (vif_index = 0; vif_index < MAXVIFS; vif_index++)\n> +\t\tif (mr_table->vifs[vif_index].dev == dev)\n> +\t\t\treturn &mr_table->vifs[vif_index];\n> +\treturn NULL;\n> +}\n> +\n> +int mlxsw_sp_mr_rif_add(struct mlxsw_sp_mr_table *mr_table,\n> +\t\t\tconst struct mlxsw_sp_rif *rif)\n> +{\n> +\tconst struct net_device *rif_dev = mlxsw_sp_rif_dev(rif);\n> +\tstruct mlxsw_sp_mr_vif *mr_vif;\n> +\n> +\tif (!rif_dev)\n> +\t\treturn 0;\n> +\n> +\tmr_vif = mlxsw_sp_mr_dev_vif_lookup(mr_table, rif_dev);\n> +\tif (!mr_vif)\n> +\t\treturn 0;\n> +\treturn mlxsw_sp_mr_vif_resolve(mr_table, mr_vif->dev, mr_vif,\n> +\t\t\t\t       mr_vif->vif_flags, rif);\n> +}\n> +\n> +void mlxsw_sp_mr_rif_del(struct mlxsw_sp_mr_table *mr_table,\n> +\t\t\t const struct mlxsw_sp_rif *rif)\n> +{\n> +\tconst struct net_device *rif_dev = mlxsw_sp_rif_dev(rif);\n> +\tstruct mlxsw_sp_mr_vif *mr_vif;\n> +\n> +\tif (!rif_dev)\n> +\t\treturn;\n> +\n> +\tmr_vif = mlxsw_sp_mr_dev_vif_lookup(mr_table, rif_dev);\n> +\tif (!mr_vif)\n> +\t\treturn;\n> +\tmlxsw_sp_mr_vif_unresolve(mr_table, mr_vif->dev, mr_vif);\n> +}\n> +\n> +void mlxsw_sp_mr_rif_mtu_update(struct mlxsw_sp_mr_table *mr_table,\n> +\t\t\t\tconst struct mlxsw_sp_rif *rif, int mtu)\n> +{\n> +\tconst struct net_device *rif_dev = mlxsw_sp_rif_dev(rif);\n> +\tstruct mlxsw_sp *mlxsw_sp = mr_table->mlxsw_sp;\n> +\tstruct mlxsw_sp_mr_route_vif_entry *rve;\n> +\tstruct mlxsw_sp_mr *mr = mlxsw_sp->mr;\n> +\tstruct mlxsw_sp_mr_vif *mr_vif;\n> +\n> +\tif (!rif_dev)\n> +\t\treturn;\n> +\n> +\t/* Search for a VIF that use that RIF */\n> +\tmr_vif = mlxsw_sp_mr_dev_vif_lookup(mr_table, rif_dev);\n> +\tif (!mr_vif)\n> +\t\treturn;\n> +\n> +\t/* Update all the routes that uses that VIF as eVIF */\n> +\tlist_for_each_entry(rve, &mr_vif->route_evif_list, vif_node) {\n> +\t\tif (mtu < rve->mr_route->min_mtu) {\n> +\t\t\trve->mr_route->min_mtu = mtu;\n> +\t\t\tmr->mr_ops->route_min_mtu_update(mlxsw_sp,\n> +\t\t\t\t\t\t\t rve->mr_route->route_priv,\n> +\t\t\t\t\t\t\t mtu);\n> +\t\t}\n> +\t}\n> +}\n> +\n> +struct mlxsw_sp_mr_table *mlxsw_sp_mr_table_create(struct mlxsw_sp *mlxsw_sp,\n> +\t\t\t\t\t\t   u32 vr_id,\n> +\t\t\t\t\t\t   enum mlxsw_sp_l3proto proto)\n> +{\n> +\tstruct mlxsw_sp_mr_route_params catchall_route_params = {\n> +\t\t.prio = MLXSW_SP_MR_ROUTE_PRIO_CATCHALL,\n> +\t\t.key = {\n> +\t\t\t.vrid = vr_id,\n> +\t\t},\n> +\t\t.value = {\n> +\t\t\t.route_action = MLXSW_SP_MR_ROUTE_ACTION_TRAP,\n> +\t\t}\n> +\t};\n> +\tstruct mlxsw_sp_mr *mr = mlxsw_sp->mr;\n> +\tstruct mlxsw_sp_mr_table *mr_table;\n> +\tint err;\n> +\tint i;\n> +\n> +\tmr_table = kzalloc(sizeof(*mr_table) + mr->mr_ops->route_priv_size,\n> +\t\t\t   GFP_KERNEL);\n> +\tif (!mr_table)\n> +\t\treturn ERR_PTR(-ENOMEM);\n> +\n> +\tmr_table->vr_id = vr_id;\n> +\tmr_table->mlxsw_sp = mlxsw_sp;\n> +\tmr_table->proto = proto;\n> +\tINIT_LIST_HEAD(&mr_table->route_list);\n> +\n> +\terr = rhashtable_init(&mr_table->route_ht,\n> +\t\t\t      &mlxsw_sp_mr_route_ht_params);\n> +\tif (err)\n> +\t\tgoto err_route_rhashtable_init;\n> +\n> +\tfor (i = 0; i < MAXVIFS; i++) {\n> +\t\tINIT_LIST_HEAD(&mr_table->vifs[i].route_evif_list);\n> +\t\tINIT_LIST_HEAD(&mr_table->vifs[i].route_ivif_list);\n> +\t}\n> +\n> +\terr = mr->mr_ops->route_create(mlxsw_sp, mr->priv,\n> +\t\t\t\t       mr_table->catchall_route_priv,\n> +\t\t\t\t       &catchall_route_params);\n> +\tif (err)\n> +\t\tgoto err_ops_route_create;\n> +\tlist_add_tail(&mr_table->node, &mr->table_list);\n> +\treturn mr_table;\n> +\n> +err_ops_route_create:\n> +\trhashtable_destroy(&mr_table->route_ht);\n> +err_route_rhashtable_init:\n> +\tkfree(mr_table);\n> +\treturn ERR_PTR(err);\n> +}\n> +\n> +void mlxsw_sp_mr_table_destroy(struct mlxsw_sp_mr_table *mr_table)\n> +{\n> +\tstruct mlxsw_sp *mlxsw_sp = mr_table->mlxsw_sp;\n> +\tstruct mlxsw_sp_mr *mr = mlxsw_sp->mr;\n> +\n> +\tWARN_ON(!mlxsw_sp_mr_table_empty(mr_table));\n> +\tlist_del(&mr_table->node);\n> +\tmr->mr_ops->route_destroy(mlxsw_sp, mr->priv,\n> +\t\t\t\t  &mr_table->catchall_route_priv);\n> +\trhashtable_destroy(&mr_table->route_ht);\n> +\tkfree(mr_table);\n> +}\n> +\n> +void mlxsw_sp_mr_table_flush(struct mlxsw_sp_mr_table *mr_table)\n> +{\n> +\tstruct mlxsw_sp_mr_route *mr_route, *tmp;\n> +\tint i;\n> +\n> +\tlist_for_each_entry_safe(mr_route, tmp, &mr_table->route_list, node)\n> +\t\t__mlxsw_sp_mr_route_del(mr_table, mr_route);\n> +\n> +\tfor (i = 0; i < MAXVIFS; i++) {\n> +\t\tmr_table->vifs[i].dev = NULL;\n> +\t\tmr_table->vifs[i].rif = NULL;\n> +\t}\n> +}\n> +\n> +bool mlxsw_sp_mr_table_empty(const struct mlxsw_sp_mr_table *mr_table)\n> +{\n> +\tint i;\n> +\n> +\tfor (i = 0; i < MAXVIFS; i++)\n> +\t\tif (mr_table->vifs[i].dev)\n> +\t\t\treturn false;\n> +\treturn list_empty(&mr_table->route_list);\n> +}\n> +\n> +static void mlxsw_sp_mr_route_stats_update(struct mlxsw_sp *mlxsw_sp,\n> +\t\t\t\t\t   struct mlxsw_sp_mr_route *mr_route)\n> +{\n> +\tstruct mlxsw_sp_mr *mr = mlxsw_sp->mr;\n> +\tu64 packets, bytes;\n> +\n> +\tif (mr_route->route_action == MLXSW_SP_MR_ROUTE_ACTION_TRAP)\n> +\t\treturn;\n> +\n> +\tmr->mr_ops->route_stats(mlxsw_sp, mr_route->route_priv, &packets,\n> +\t\t\t\t&bytes);\n> +\n> +\tswitch (mr_route->mr_table->proto) {\n> +\tcase MLXSW_SP_L3_PROTO_IPV4:\n> +\t\tif (mr_route->mfc4->mfc_un.res.pkt != packets)\n> +\t\t\tmr_route->mfc4->mfc_un.res.lastuse = jiffies;\n> +\t\tmr_route->mfc4->mfc_un.res.pkt = packets;\n> +\t\tmr_route->mfc4->mfc_un.res.bytes = bytes;\n> +\t\tbreak;\n> +\tcase MLXSW_SP_L3_PROTO_IPV6:\n> +\t\t/* fall through */\n> +\tdefault:\n> +\t\tWARN_ON_ONCE(1);\n> +\t}\n> +}\n> +\n> +static void mlxsw_sp_mr_stats_update(struct work_struct *work)\n> +{\n> +\tstruct mlxsw_sp_mr *mr = container_of(work, struct mlxsw_sp_mr,\n> +\t\t\t\t\t      stats_update_dw.work);\n> +\tstruct mlxsw_sp_mr_table *mr_table;\n> +\tstruct mlxsw_sp_mr_route *mr_route;\n> +\tunsigned long interval;\n> +\n> +\trtnl_lock();\n> +\tlist_for_each_entry(mr_table, &mr->table_list, node)\n> +\t\tlist_for_each_entry(mr_route, &mr_table->route_list, node)\n> +\t\t\tmlxsw_sp_mr_route_stats_update(mr_table->mlxsw_sp,\n> +\t\t\t\t\t\t       mr_route);\n> +\trtnl_unlock();\n> +\n> +\tinterval = msecs_to_jiffies(MLXSW_SP_MR_ROUTES_COUNTER_UPDATE_INTERVAL);\n> +\tmlxsw_core_schedule_dw(&mr->stats_update_dw, interval);\n> +}\n> +\n> +int mlxsw_sp_mr_init(struct mlxsw_sp *mlxsw_sp,\n> +\t\t     const struct mlxsw_sp_mr_ops *mr_ops)\n> +{\n> +\tstruct mlxsw_sp_mr *mr;\n> +\tunsigned long interval;\n> +\tint err;\n> +\n> +\tmr = kzalloc(sizeof(*mr) + mr_ops->priv_size, GFP_KERNEL);\n> +\tif (!mr)\n> +\t\treturn -ENOMEM;\n> +\tmr->mr_ops = mr_ops;\n> +\tmlxsw_sp->mr = mr;\n> +\tINIT_LIST_HEAD(&mr->table_list);\n> +\n> +\terr = mr_ops->init(mlxsw_sp, mr->priv);\n> +\tif (err)\n> +\t\tgoto err;\n> +\n> +\t/* Create the delayed work for counter updates */\n> +\tINIT_DELAYED_WORK(&mr->stats_update_dw, mlxsw_sp_mr_stats_update);\n> +\tinterval = msecs_to_jiffies(MLXSW_SP_MR_ROUTES_COUNTER_UPDATE_INTERVAL);\n> +\tmlxsw_core_schedule_dw(&mr->stats_update_dw, interval);\n> +\treturn 0;\n> +err:\n> +\tkfree(mr);\n> +\treturn err;\n> +}\n> +\n> +void mlxsw_sp_mr_fini(struct mlxsw_sp *mlxsw_sp)\n> +{\n> +\tstruct mlxsw_sp_mr *mr = mlxsw_sp->mr;\n> +\n> +\tcancel_delayed_work_sync(&mr->stats_update_dw);\n> +\tmr->mr_ops->fini(mr->priv);\n> +\tkfree(mr);\n> +}\n> diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.h\n> new file mode 100644\n> index 0000000..c851b23\n> --- /dev/null\n> +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.h\n> @@ -0,0 +1,133 @@\n> +/*\n> + * drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.h\n> + * Copyright (c) 2017 Mellanox Technologies. All rights reserved.\n> + * Copyright (c) 2017 Yotam Gigi <yotamg@mellanox.com>\n> + *\n> + * Redistribution and use in source and binary forms, with or without\n> + * modification, are permitted provided that the following conditions are met:\n> + *\n> + * 1. Redistributions of source code must retain the above copyright\n> + *    notice, this list of conditions and the following disclaimer.\n> + * 2. Redistributions in binary form must reproduce the above copyright\n> + *    notice, this list of conditions and the following disclaimer in the\n> + *    documentation and/or other materials provided with the distribution.\n> + * 3. Neither the names of the copyright holders nor the names of its\n> + *    contributors may be used to endorse or promote products derived from\n> + *    this software without specific prior written permission.\n> + *\n> + * Alternatively, this software may be distributed under the terms of the\n> + * GNU General Public License (\"GPL\") version 2 as published by the Free\n> + * Software Foundation.\n> + *\n> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n> + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n> + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n> + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n> + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n> + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n> + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n> + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n> + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n> + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n> + * POSSIBILITY OF SUCH DAMAGE.\n> + */\n> +\n> +#ifndef _MLXSW_SPECTRUM_MCROUTER_H\n> +#define _MLXSW_SPECTRUM_MCROUTER_H\n> +\n> +#include <linux/mroute.h>\n> +#include \"spectrum_router.h\"\n> +#include \"spectrum.h\"\n> +\n> +enum mlxsw_sp_mr_route_action {\n> +\tMLXSW_SP_MR_ROUTE_ACTION_FORWARD,\n> +\tMLXSW_SP_MR_ROUTE_ACTION_TRAP,\n> +};\n> +\n> +enum mlxsw_sp_mr_route_prio {\n> +\tMLXSW_SP_MR_ROUTE_PRIO_SG,\n> +\tMLXSW_SP_MR_ROUTE_PRIO_STARG,\n> +\tMLXSW_SP_MR_ROUTE_PRIO_CATCHALL,\n> +\t__MLXSW_SP_MR_ROUTE_PRIO_MAX\n> +};\n> +\n> +#define MLXSW_SP_MR_ROUTE_PRIO_MAX (__MLXSW_SP_MR_ROUTE_PRIO_MAX - 1)\n> +\n> +struct mlxsw_sp_mr_route_key {\n> +\tint vrid;\n> +\tenum mlxsw_sp_l3proto proto;\n> +\tunion mlxsw_sp_l3addr group;\n> +\tunion mlxsw_sp_l3addr group_mask;\n> +\tunion mlxsw_sp_l3addr source;\n> +\tunion mlxsw_sp_l3addr source_mask;\n> +};\n> +\n> +struct mlxsw_sp_mr_route_info {\n> +\tenum mlxsw_sp_mr_route_action route_action;\n> +\tu16 irif_index;\n> +\tu16 *erif_indices;\n> +\tsize_t erif_num;\n> +\tu16 min_mtu;\n> +};\n> +\n> +struct mlxsw_sp_mr_route_params {\n> +\tstruct mlxsw_sp_mr_route_key key;\n> +\tstruct mlxsw_sp_mr_route_info value;\n> +\tenum mlxsw_sp_mr_route_prio prio;\n> +};\n> +\n> +struct mlxsw_sp_mr_ops {\n> +\tint priv_size;\n> +\tint route_priv_size;\n> +\tint (*init)(struct mlxsw_sp *mlxsw_sp, void *priv);\n> +\tint (*route_create)(struct mlxsw_sp *mlxsw_sp, void *priv,\n> +\t\t\t    void *route_priv,\n> +\t\t\t    struct mlxsw_sp_mr_route_params *route_params);\n> +\tint (*route_update)(struct mlxsw_sp *mlxsw_sp, void *route_priv,\n> +\t\t\t    struct mlxsw_sp_mr_route_info *route_info);\n> +\tint (*route_stats)(struct mlxsw_sp *mlxsw_sp, void *route_priv,\n> +\t\t\t   u64 *packets, u64 *bytes);\n> +\tint (*route_action_update)(struct mlxsw_sp *mlxsw_sp, void *route_priv,\n> +\t\t\t\t   enum mlxsw_sp_mr_route_action route_action);\n> +\tint (*route_min_mtu_update)(struct mlxsw_sp *mlxsw_sp, void *route_priv,\n> +\t\t\t\t    u16 min_mtu);\n> +\tint (*route_irif_update)(struct mlxsw_sp *mlxsw_sp, void *route_priv,\n> +\t\t\t\t u16 irif_index);\n> +\tint (*route_erif_add)(struct mlxsw_sp *mlxsw_sp, void *route_priv,\n> +\t\t\t      u16 erif_index);\n> +\tint (*route_erif_del)(struct mlxsw_sp *mlxsw_sp, void *route_priv,\n> +\t\t\t      u16 erif_index);\n> +\tvoid (*route_destroy)(struct mlxsw_sp *mlxsw_sp, void *priv,\n> +\t\t\t      void *route_priv);\n> +\tvoid (*fini)(void *priv);\n> +};\n> +\n> +struct mlxsw_sp_mr;\n> +struct mlxsw_sp_mr_table;\n> +\n> +int mlxsw_sp_mr_init(struct mlxsw_sp *mlxsw_sp,\n> +\t\t     const struct mlxsw_sp_mr_ops *mr_ops);\n> +void mlxsw_sp_mr_fini(struct mlxsw_sp *mlxsw_sp);\n> +int mlxsw_sp_mr_route4_add(struct mlxsw_sp_mr_table *mr_table,\n> +\t\t\t   struct mfc_cache *mfc, bool replace);\n> +void mlxsw_sp_mr_route4_del(struct mlxsw_sp_mr_table *mr_table,\n> +\t\t\t    struct mfc_cache *mfc);\n> +int mlxsw_sp_mr_vif_add(struct mlxsw_sp_mr_table *mr_table,\n> +\t\t\tstruct net_device *dev, vifi_t vif_index,\n> +\t\t\tunsigned long vif_flags,\n> +\t\t\tconst struct mlxsw_sp_rif *rif);\n> +void mlxsw_sp_mr_vif_del(struct mlxsw_sp_mr_table *mr_table, vifi_t vif_index);\n> +int mlxsw_sp_mr_rif_add(struct mlxsw_sp_mr_table *mr_table,\n> +\t\t\tconst struct mlxsw_sp_rif *rif);\n> +void mlxsw_sp_mr_rif_del(struct mlxsw_sp_mr_table *mr_table,\n> +\t\t\t const struct mlxsw_sp_rif *rif);\n> +void mlxsw_sp_mr_rif_mtu_update(struct mlxsw_sp_mr_table *mr_table,\n> +\t\t\t\tconst struct mlxsw_sp_rif *rif, int mtu);\n> +struct mlxsw_sp_mr_table *mlxsw_sp_mr_table_create(struct mlxsw_sp *mlxsw_sp,\n> +\t\t\t\t\t\t   u32 tb_id,\n> +\t\t\t\t\t\t   enum mlxsw_sp_l3proto proto);\n> +void mlxsw_sp_mr_table_destroy(struct mlxsw_sp_mr_table *mr_table);\n> +void mlxsw_sp_mr_table_flush(struct mlxsw_sp_mr_table *mr_table);\n> +bool mlxsw_sp_mr_table_empty(const struct mlxsw_sp_mr_table *mr_table);\n> +\n> +#endif\n>","headers":{"Return-Path":"<netdev-owner@vger.kernel.org>","X-Original-To":"patchwork-incoming@ozlabs.org","Delivered-To":"patchwork-incoming@ozlabs.org","Authentication-Results":"ozlabs.org;\n\tspf=none (mailfrom) smtp.mailfrom=vger.kernel.org\n\t(client-ip=209.132.180.67; helo=vger.kernel.org;\n\tenvelope-from=netdev-owner@vger.kernel.org;\n\treceiver=<UNKNOWN>)","Received":["from vger.kernel.org (vger.kernel.org [209.132.180.67])\n\tby ozlabs.org (Postfix) with ESMTP id 3y0n7H4tvTz9t4B\n\tfor <patchwork-incoming@ozlabs.org>;\n\tMon, 25 Sep 2017 11:48:59 +1000 (AEST)","(majordomo@vger.kernel.org) by vger.kernel.org via listexpand\n\tid S1753343AbdIYBs4 (ORCPT <rfc822;patchwork-incoming@ozlabs.org>);\n\tSun, 24 Sep 2017 21:48:56 -0400","from szxga04-in.huawei.com ([45.249.212.190]:6987 \"EHLO\n\tszxga04-in.huawei.com\" rhost-flags-OK-OK-OK-OK) by vger.kernel.org\n\twith ESMTP id S1753164AbdIYBsx (ORCPT\n\t<rfc822;netdev@vger.kernel.org>); Sun, 24 Sep 2017 21:48:53 -0400","from 172.30.72.58 (EHLO DGGEMS410-HUB.china.huawei.com)\n\t([172.30.72.58])\n\tby dggrg04-dlp.huawei.com (MOS 4.4.6-GA FastPath queued)\n\twith ESMTP id DHW68366; Mon, 25 Sep 2017 09:48:45 +0800 (CST)","from [127.0.0.1] (10.74.191.121) by DGGEMS410-HUB.china.huawei.com\n\t(10.3.19.210) with Microsoft SMTP Server id 14.3.301.0;\n\tMon, 25 Sep 2017 09:48:37 +0800"],"Subject":"Re: [patch net-next v2 07/12] mlxsw: spectrum: Add the multicast\n\trouting offloading logic","To":"Jiri Pirko <jiri@resnulli.us>, <netdev@vger.kernel.org>","CC":"<davem@davemloft.net>, <yotamg@mellanox.com>,\n\t<idosch@mellanox.com>, <mlxsw@mellanox.com>,\n\t<nikolay@cumulusnetworks.com>, <andrew@lunn.ch>","References":"<20170924172212.10096-1-jiri@resnulli.us>\n\t<20170924172212.10096-8-jiri@resnulli.us>","From":"Yunsheng Lin <linyunsheng@huawei.com>","Message-ID":"<fb5d1fcc-ad70-d656-f860-7b05cdd2834a@huawei.com>","Date":"Mon, 25 Sep 2017 09:48:38 +0800","User-Agent":"Mozilla/5.0 (Windows NT 6.1; WOW64; rv:52.0) Gecko/20100101\n\tThunderbird/52.2.0","MIME-Version":"1.0","In-Reply-To":"<20170924172212.10096-8-jiri@resnulli.us>","Content-Type":"text/plain; charset=\"utf-8\"","Content-Language":"en-US","Content-Transfer-Encoding":"7bit","X-Originating-IP":"[10.74.191.121]","X-CFilter-Loop":"Reflected","X-Mirapoint-Virus-RAPID-Raw":"score=unknown(0),\n\trefid=str=0001.0A090201.59C8607F.0011, ss=1, re=0.000, recu=0.000,\n\treip=0.000, cl=1, cld=1, fgs=0, ip=0.0.0.0,\n\tso=2014-11-16 11:51:01, dmn=2013-03-21 17:37:32","X-Mirapoint-Loop-Id":"428b914633f2f43c1fee58fc83f69bea","Sender":"netdev-owner@vger.kernel.org","Precedence":"bulk","List-ID":"<netdev.vger.kernel.org>","X-Mailing-List":"netdev@vger.kernel.org"}},{"id":1774431,"web_url":"http://patchwork.ozlabs.org/comment/1774431/","msgid":"<d89064c4-f685-648c-07c2-54cfa8524e7f@mellanox.com>","list_archive_url":null,"date":"2017-09-25T05:55:39","subject":"Re: [patch net-next v2 07/12] mlxsw: spectrum: Add the multicast\n\trouting offloading logic","submitter":{"id":69965,"url":"http://patchwork.ozlabs.org/api/people/69965/","name":"Yotam Gigi","email":"yotamg@mellanox.com"},"content":"On 09/25/2017 04:48 AM, Yunsheng Lin wrote:\n> Hi, Jiri\n>\n> On 2017/9/25 1:22, Jiri Pirko wrote:\n>> From: Yotam Gigi <yotamg@mellanox.com>\n>>\n>> Add the multicast router offloading logic, which is in charge of handling\n>> the VIF and MFC notifications and translating it to the hardware logic API.\n>>\n>> The offloading logic has to overcome several obstacles in order to safely\n>> comply with the kernel multicast router user API:\n>>  - It must keep track of the mapping between VIFs to netdevices. The user\n>>    can add an MFC cache entry pointing to a VIF, delete the VIF and add\n>>    re-add it with a different netdevice. The offloading logic has to handle\n>>    this in order to be compatible with the kernel logic.\n>>  - It must keep track of the mapping between netdevices to spectrum RIFs,\n>>    as the current hardware implementation assume having a RIF for every\n>>    port in a multicast router.\n>>  - It must handle routes pointing to pimreg device to be trapped to the\n>>    kernel, as the packet should be delivered to userspace.\n>>  - It must handle routes pointing tunnel VIFs. The current implementation\n>>    does not support multicast forwarding to tunnels, thus routes that point\n>>    to a tunnel should be trapped to the kernel.\n>>  - It must be aware of proxy multicast routes, which include both (*,*)\n>>    routes and duplicate routes. Currently proxy routes are not offloaded\n>>    and trigger the abort mechanism: removal of all routes from hardware and\n>>    triggering the traffic to go through the kernel.\n>>\n>> The multicast routing offloading logic also updates the counters of the\n>> offloaded MFC routes in a periodic work.\n>>\n>> Signed-off-by: Yotam Gigi <yotamg@mellanox.com>\n>> Reviewed-by: Ido Schimmel <idosch@mellanox.com>\n>> Signed-off-by: Jiri Pirko <jiri@mellanox.com>\n>> ---\n>> v1->v2:\n>>  - Update the lastuse MFC entry field too, in addition to packets an bytes.\n>> ---\n>>  drivers/net/ethernet/mellanox/mlxsw/Makefile      |    3 +-\n>>  drivers/net/ethernet/mellanox/mlxsw/spectrum.h    |    1 +\n>>  drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.c | 1014 +++++++++++++++++++++\n>>  drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.h |  133 +++\n>>  4 files changed, 1150 insertions(+), 1 deletion(-)\n>>  create mode 100644 drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.c\n>>  create mode 100644 drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.h\n>>\n>> diff --git a/drivers/net/ethernet/mellanox/mlxsw/Makefile b/drivers/net/ethernet/mellanox/mlxsw/Makefile\n>> index 4b88158..9b29764 100644\n>> --- a/drivers/net/ethernet/mellanox/mlxsw/Makefile\n>> +++ b/drivers/net/ethernet/mellanox/mlxsw/Makefile\n>> @@ -17,7 +17,8 @@ mlxsw_spectrum-objs\t\t:= spectrum.o spectrum_buffers.o \\\n>>  \t\t\t\t   spectrum_kvdl.o spectrum_acl_tcam.o \\\n>>  \t\t\t\t   spectrum_acl.o spectrum_flower.o \\\n>>  \t\t\t\t   spectrum_cnt.o spectrum_fid.o \\\n>> -\t\t\t\t   spectrum_ipip.o spectrum_acl_flex_actions.o\n>> +\t\t\t\t   spectrum_ipip.o spectrum_acl_flex_actions.o \\\n>> +\t\t\t\t   spectrum_mr.o\n>>  mlxsw_spectrum-$(CONFIG_MLXSW_SPECTRUM_DCB)\t+= spectrum_dcb.o\n>>  mlxsw_spectrum-$(CONFIG_NET_DEVLINK) += spectrum_dpipe.o\n>>  obj-$(CONFIG_MLXSW_MINIMAL)\t+= mlxsw_minimal.o\n>> diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h\n>> index e907ec4..51d8b9f 100644\n>> --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h\n>> +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h\n>> @@ -153,6 +153,7 @@ struct mlxsw_sp {\n>>  \tstruct mlxsw_sp_sb *sb;\n>>  \tstruct mlxsw_sp_bridge *bridge;\n>>  \tstruct mlxsw_sp_router *router;\n>> +\tstruct mlxsw_sp_mr *mr;\n>>  \tstruct mlxsw_afa *afa;\n>>  \tstruct mlxsw_sp_acl *acl;\n>>  \tstruct mlxsw_sp_fid_core *fid_core;\n>> diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.c\n>> new file mode 100644\n>> index 0000000..89b2e60\n>> --- /dev/null\n>> +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.c\n>> @@ -0,0 +1,1014 @@\n>> +/*\n>> + * drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.c\n>> + * Copyright (c) 2017 Mellanox Technologies. All rights reserved.\n>> + * Copyright (c) 2017 Yotam Gigi <yotamg@mellanox.com>\n>> + *\n>> + * Redistribution and use in source and binary forms, with or without\n>> + * modification, are permitted provided that the following conditions are met:\n>> + *\n>> + * 1. Redistributions of source code must retain the above copyright\n>> + *    notice, this list of conditions and the following disclaimer.\n>> + * 2. Redistributions in binary form must reproduce the above copyright\n>> + *    notice, this list of conditions and the following disclaimer in the\n>> + *    documentation and/or other materials provided with the distribution.\n>> + * 3. Neither the names of the copyright holders nor the names of its\n>> + *    contributors may be used to endorse or promote products derived from\n>> + *    this software without specific prior written permission.\n>> + *\n>> + * Alternatively, this software may be distributed under the terms of the\n>> + * GNU General Public License (\"GPL\") version 2 as published by the Free\n>> + * Software Foundation.\n>> + *\n>> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n>> + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n>> + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n>> + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n>> + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n>> + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n>> + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n>> + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n>> + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n>> + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n>> + * POSSIBILITY OF SUCH DAMAGE.\n>> + */\n>> +\n>> +#include <linux/rhashtable.h>\n>> +\n>> +#include \"spectrum_mr.h\"\n>> +#include \"spectrum_router.h\"\n>> +\n>> +struct mlxsw_sp_mr {\n>> +\tconst struct mlxsw_sp_mr_ops *mr_ops;\n>> +\tvoid *catchall_route_priv;\n>> +\tstruct delayed_work stats_update_dw;\n>> +\tstruct list_head table_list;\n>> +#define MLXSW_SP_MR_ROUTES_COUNTER_UPDATE_INTERVAL 5000 /* ms */\n>> +\tunsigned long priv[0];\n>> +\t/* priv has to be always the last item */\n>> +};\n>> +\n>> +struct mlxsw_sp_mr_vif {\n>> +\tstruct net_device *dev;\n>> +\tconst struct mlxsw_sp_rif *rif;\n>> +\tunsigned long vif_flags;\n>> +\n>> +\t/* A list of route_vif_entry structs that point to routes that the VIF\n>> +\t * instance is used as one of the egress VIFs\n>> +\t */\n>> +\tstruct list_head route_evif_list;\n>> +\n>> +\t/* A list of route_vif_entry structs that point to routes that the VIF\n>> +\t * instance is used as an ingress VIF\n>> +\t */\n>> +\tstruct list_head route_ivif_list;\n>> +};\n>> +\n>> +struct mlxsw_sp_mr_route_vif_entry {\n>> +\tstruct list_head vif_node;\n>> +\tstruct list_head route_node;\n>> +\tstruct mlxsw_sp_mr_vif *mr_vif;\n>> +\tstruct mlxsw_sp_mr_route *mr_route;\n>> +};\n>> +\n>> +struct mlxsw_sp_mr_table {\n>> +\tstruct list_head node;\n>> +\tenum mlxsw_sp_l3proto proto;\n>> +\tstruct mlxsw_sp *mlxsw_sp;\n>> +\tu32 vr_id;\n>> +\tstruct mlxsw_sp_mr_vif vifs[MAXVIFS];\n>> +\tstruct list_head route_list;\n>> +\tstruct rhashtable route_ht;\n>> +\tchar catchall_route_priv[0];\n>> +\t/* catchall_route_priv has to be always the last item */\n>> +};\n>> +\n>> +struct mlxsw_sp_mr_route {\n>> +\tstruct list_head node;\n>> +\tstruct rhash_head ht_node;\n>> +\tstruct mlxsw_sp_mr_route_key key;\n>> +\tenum mlxsw_sp_mr_route_action route_action;\n>> +\tu16 min_mtu;\n>> +\tstruct mfc_cache *mfc4;\n>> +\tvoid *route_priv;\n>> +\tconst struct mlxsw_sp_mr_table *mr_table;\n>> +\t/* A list of route_vif_entry structs that point to the egress VIFs */\n>> +\tstruct list_head evif_list;\n>> +\t/* A route_vif_entry struct that point to the ingress VIF */\n>> +\tstruct mlxsw_sp_mr_route_vif_entry ivif;\n>> +};\n>> +\n>> +static const struct rhashtable_params mlxsw_sp_mr_route_ht_params = {\n>> +\t.key_len = sizeof(struct mlxsw_sp_mr_route_key),\n>> +\t.key_offset = offsetof(struct mlxsw_sp_mr_route, key),\n>> +\t.head_offset = offsetof(struct mlxsw_sp_mr_route, ht_node),\n>> +\t.automatic_shrinking = true,\n>> +};\n>> +\n>> +static bool mlxsw_sp_mr_vif_regular(const struct mlxsw_sp_mr_vif *vif)\n>> +{\n>> +\treturn !(vif->vif_flags & (VIFF_TUNNEL | VIFF_REGISTER));\n>> +}\n>> +\n>> +static bool mlxsw_sp_mr_vif_valid(const struct mlxsw_sp_mr_vif *vif)\n>> +{\n>> +\treturn mlxsw_sp_mr_vif_regular(vif) && vif->dev && vif->rif;\n>> +}\n>> +\n>> +static bool mlxsw_sp_mr_vif_rif_invalid(const struct mlxsw_sp_mr_vif *vif)\n>> +{\n>> +\treturn mlxsw_sp_mr_vif_regular(vif) && vif->dev && !vif->rif;\n>> +}\n>> +\n>> +static bool\n>> +mlxsw_sp_mr_route_ivif_in_evifs(const struct mlxsw_sp_mr_route *mr_route)\n>> +{\n>> +\tvifi_t ivif;\n>> +\n>> +\tswitch (mr_route->mr_table->proto) {\n>> +\tcase MLXSW_SP_L3_PROTO_IPV4:\n>> +\t\tivif = mr_route->mfc4->mfc_parent;\n>> +\t\treturn mr_route->mfc4->mfc_un.res.ttls[ivif] != 255;\n>> +\tcase MLXSW_SP_L3_PROTO_IPV6:\n>> +\t\t/* fall through */\n>> +\tdefault:\n>> +\t\tWARN_ON_ONCE(1);\n>> +\t}\n>> +\treturn false;\n>> +}\n>> +\n>> +static int\n>> +mlxsw_sp_mr_route_valid_evifs_num(const struct mlxsw_sp_mr_route *mr_route)\n>> +{\n>> +\tstruct mlxsw_sp_mr_route_vif_entry *rve;\n>> +\tint valid_evifs = 0;\n>> +\n>> +\tvalid_evifs = 0;\n> you are doing valid_evifs = 0 twice.\n\nDidn't notice. I will fix and send v3.\n\nThanks.\n\n>\n>> +\tlist_for_each_entry(rve, &mr_route->evif_list, route_node)\n>> +\t\tif (mlxsw_sp_mr_vif_valid(rve->mr_vif))\n>> +\t\t\tvalid_evifs++;\n>> +\treturn valid_evifs;\n>> +}\n>> +\n>> +static bool mlxsw_sp_mr_route_starg(const struct mlxsw_sp_mr_route *mr_route)\n>> +{\n>> +\tswitch (mr_route->mr_table->proto) {\n>> +\tcase MLXSW_SP_L3_PROTO_IPV4:\n>> +\t\treturn mr_route->key.source_mask.addr4 == INADDR_ANY;\n>> +\tcase MLXSW_SP_L3_PROTO_IPV6:\n>> +\t\t/* fall through */\n>> +\tdefault:\n>> +\t\tWARN_ON_ONCE(1);\n>> +\t}\n>> +\treturn false;\n>> +}\n>> +\n>> +static enum mlxsw_sp_mr_route_action\n>> +mlxsw_sp_mr_route_action(const struct mlxsw_sp_mr_route *mr_route)\n>> +{\n>> +\tstruct mlxsw_sp_mr_route_vif_entry *rve;\n>> +\n>> +\t/* If the ingress port is not regular and resolved, trap the route */\n>> +\tif (!mlxsw_sp_mr_vif_valid(mr_route->ivif.mr_vif))\n>> +\t\treturn MLXSW_SP_MR_ROUTE_ACTION_TRAP;\n>> +\n>> +\t/* The kernel does not match a (*,G) route that the ingress interface is\n>> +\t * not one of the egress interfaces, so trap these kind of routes.\n>> +\t */\n>> +\tif (mlxsw_sp_mr_route_starg(mr_route) &&\n>> +\t    !mlxsw_sp_mr_route_ivif_in_evifs(mr_route))\n>> +\t\treturn MLXSW_SP_MR_ROUTE_ACTION_TRAP;\n>> +\n>> +\t/* If the route has no valid eVIFs, trap it. */\n>> +\tif (!mlxsw_sp_mr_route_valid_evifs_num(mr_route))\n>> +\t\treturn MLXSW_SP_MR_ROUTE_ACTION_TRAP;\n>> +\n>> +\t/* If either one of the eVIFs is not regular (VIF of type pimreg or\n>> +\t * tunnel) or one of the VIFs has no matching RIF, trap the packet.\n>> +\t */\n>> +\tlist_for_each_entry(rve, &mr_route->evif_list, route_node) {\n>> +\t\tif (!mlxsw_sp_mr_vif_regular(rve->mr_vif) ||\n>> +\t\t    mlxsw_sp_mr_vif_rif_invalid(rve->mr_vif))\n>> +\t\t\treturn MLXSW_SP_MR_ROUTE_ACTION_TRAP;\n>> +\t}\n>> +\treturn MLXSW_SP_MR_ROUTE_ACTION_FORWARD;\n>> +}\n>> +\n>> +static enum mlxsw_sp_mr_route_prio\n>> +mlxsw_sp_mr_route_prio(const struct mlxsw_sp_mr_route *mr_route)\n>> +{\n>> +\treturn mlxsw_sp_mr_route_starg(mr_route) ?\n>> +\t\tMLXSW_SP_MR_ROUTE_PRIO_STARG : MLXSW_SP_MR_ROUTE_PRIO_SG;\n>> +}\n>> +\n>> +static void mlxsw_sp_mr_route4_key(struct mlxsw_sp_mr_table *mr_table,\n>> +\t\t\t\t   struct mlxsw_sp_mr_route_key *key,\n>> +\t\t\t\t   const struct mfc_cache *mfc)\n>> +{\n>> +\tbool starg = (mfc->mfc_origin == INADDR_ANY);\n>> +\n>> +\tmemset(key, 0, sizeof(*key));\n>> +\tkey->vrid = mr_table->vr_id;\n>> +\tkey->proto = mr_table->proto;\n>> +\tkey->group.addr4 = mfc->mfc_mcastgrp;\n>> +\tkey->group_mask.addr4 = 0xffffffff;\n>> +\tkey->source.addr4 = mfc->mfc_origin;\n>> +\tkey->source_mask.addr4 = starg ? 0 : 0xffffffff;\n>> +}\n>> +\n>> +static int mlxsw_sp_mr_route_evif_link(struct mlxsw_sp_mr_route *mr_route,\n>> +\t\t\t\t       struct mlxsw_sp_mr_vif *mr_vif)\n>> +{\n>> +\tstruct mlxsw_sp_mr_route_vif_entry *rve;\n>> +\n>> +\trve = kzalloc(sizeof(*rve), GFP_KERNEL);\n>> +\tif (!rve)\n>> +\t\treturn -ENOMEM;\n>> +\trve->mr_route = mr_route;\n>> +\trve->mr_vif = mr_vif;\n>> +\tlist_add_tail(&rve->route_node, &mr_route->evif_list);\n>> +\tlist_add_tail(&rve->vif_node, &mr_vif->route_evif_list);\n>> +\treturn 0;\n>> +}\n>> +\n>> +static void\n>> +mlxsw_sp_mr_route_evif_unlink(struct mlxsw_sp_mr_route_vif_entry *rve)\n>> +{\n>> +\tlist_del(&rve->route_node);\n>> +\tlist_del(&rve->vif_node);\n>> +\tkfree(rve);\n>> +}\n>> +\n>> +static void mlxsw_sp_mr_route_ivif_link(struct mlxsw_sp_mr_route *mr_route,\n>> +\t\t\t\t\tstruct mlxsw_sp_mr_vif *mr_vif)\n>> +{\n>> +\tmr_route->ivif.mr_route = mr_route;\n>> +\tmr_route->ivif.mr_vif = mr_vif;\n>> +\tlist_add_tail(&mr_route->ivif.vif_node, &mr_vif->route_ivif_list);\n>> +}\n>> +\n>> +static void mlxsw_sp_mr_route_ivif_unlink(struct mlxsw_sp_mr_route *mr_route)\n>> +{\n>> +\tlist_del(&mr_route->ivif.vif_node);\n>> +}\n>> +\n>> +static int\n>> +mlxsw_sp_mr_route_info_create(struct mlxsw_sp_mr_table *mr_table,\n>> +\t\t\t      struct mlxsw_sp_mr_route *mr_route,\n>> +\t\t\t      struct mlxsw_sp_mr_route_info *route_info)\n>> +{\n>> +\tstruct mlxsw_sp_mr_route_vif_entry *rve;\n>> +\tu16 *erif_indices;\n>> +\tu16 irif_index;\n>> +\tu16 erif = 0;\n>> +\n>> +\terif_indices = kmalloc_array(MAXVIFS, sizeof(*erif_indices),\n>> +\t\t\t\t     GFP_KERNEL);\n>> +\tif (!erif_indices)\n>> +\t\treturn -ENOMEM;\n>> +\n>> +\tlist_for_each_entry(rve, &mr_route->evif_list, route_node) {\n>> +\t\tif (mlxsw_sp_mr_vif_valid(rve->mr_vif)) {\n>> +\t\t\tu16 rifi = mlxsw_sp_rif_index(rve->mr_vif->rif);\n>> +\n>> +\t\t\terif_indices[erif++] = rifi;\n>> +\t\t}\n>> +\t}\n>> +\n>> +\tif (mlxsw_sp_mr_vif_valid(mr_route->ivif.mr_vif))\n>> +\t\tirif_index = mlxsw_sp_rif_index(mr_route->ivif.mr_vif->rif);\n>> +\telse\n>> +\t\tirif_index = 0;\n>> +\n>> +\troute_info->irif_index = irif_index;\n>> +\troute_info->erif_indices = erif_indices;\n>> +\troute_info->min_mtu = mr_route->min_mtu;\n>> +\troute_info->route_action = mr_route->route_action;\n>> +\troute_info->erif_num = erif;\n>> +\treturn 0;\n>> +}\n>> +\n>> +static void\n>> +mlxsw_sp_mr_route_info_destroy(struct mlxsw_sp_mr_route_info *route_info)\n>> +{\n>> +\tkfree(route_info->erif_indices);\n>> +}\n>> +\n>> +static int mlxsw_sp_mr_route_write(struct mlxsw_sp_mr_table *mr_table,\n>> +\t\t\t\t   struct mlxsw_sp_mr_route *mr_route,\n>> +\t\t\t\t   bool replace)\n>> +{\n>> +\tstruct mlxsw_sp *mlxsw_sp = mr_table->mlxsw_sp;\n>> +\tstruct mlxsw_sp_mr_route_info route_info;\n>> +\tstruct mlxsw_sp_mr *mr = mlxsw_sp->mr;\n>> +\tint err;\n>> +\n>> +\terr = mlxsw_sp_mr_route_info_create(mr_table, mr_route, &route_info);\n>> +\tif (err)\n>> +\t\treturn err;\n>> +\n>> +\tif (!replace) {\n>> +\t\tstruct mlxsw_sp_mr_route_params route_params;\n>> +\n>> +\t\tmr_route->route_priv = kzalloc(mr->mr_ops->route_priv_size,\n>> +\t\t\t\t\t       GFP_KERNEL);\n>> +\t\tif (!mr_route->route_priv) {\n>> +\t\t\terr = -ENOMEM;\n>> +\t\t\tgoto out;\n>> +\t\t}\n>> +\n>> +\t\troute_params.key = mr_route->key;\n>> +\t\troute_params.value = route_info;\n>> +\t\troute_params.prio = mlxsw_sp_mr_route_prio(mr_route);\n>> +\t\terr = mr->mr_ops->route_create(mlxsw_sp, mr->priv,\n>> +\t\t\t\t\t       mr_route->route_priv,\n>> +\t\t\t\t\t       &route_params);\n>> +\t\tif (err)\n>> +\t\t\tkfree(mr_route->route_priv);\n>> +\t} else {\n>> +\t\terr = mr->mr_ops->route_update(mlxsw_sp, mr_route->route_priv,\n>> +\t\t\t\t\t       &route_info);\n>> +\t}\n>> +out:\n>> +\tmlxsw_sp_mr_route_info_destroy(&route_info);\n>> +\treturn err;\n>> +}\n>> +\n>> +static void mlxsw_sp_mr_route_erase(struct mlxsw_sp_mr_table *mr_table,\n>> +\t\t\t\t    struct mlxsw_sp_mr_route *mr_route)\n>> +{\n>> +\tstruct mlxsw_sp *mlxsw_sp = mr_table->mlxsw_sp;\n>> +\tstruct mlxsw_sp_mr *mr = mlxsw_sp->mr;\n>> +\n>> +\tmr->mr_ops->route_destroy(mlxsw_sp, mr->priv, mr_route->route_priv);\n>> +\tkfree(mr_route->route_priv);\n>> +}\n>> +\n>> +static struct mlxsw_sp_mr_route *\n>> +mlxsw_sp_mr_route4_create(struct mlxsw_sp_mr_table *mr_table,\n>> +\t\t\t  struct mfc_cache *mfc)\n>> +{\n>> +\tstruct mlxsw_sp_mr_route_vif_entry *rve, *tmp;\n>> +\tstruct mlxsw_sp_mr_route *mr_route;\n>> +\tint err;\n>> +\tint i;\n>> +\n>> +\t/* Allocate and init a new route and fill it with parameters */\n>> +\tmr_route = kzalloc(sizeof(*mr_table), GFP_KERNEL);\n>> +\tif (!mr_route)\n>> +\t\treturn ERR_PTR(-ENOMEM);\n>> +\tINIT_LIST_HEAD(&mr_route->evif_list);\n>> +\tmlxsw_sp_mr_route4_key(mr_table, &mr_route->key, mfc);\n>> +\n>> +\t/* Find min_mtu and link iVIF and eVIFs */\n>> +\tmr_route->min_mtu = ETH_MAX_MTU;\n>> +\tipmr_cache_hold(mfc);\n>> +\tmr_route->mfc4 = mfc;\n>> +\tmr_route->mr_table = mr_table;\n>> +\tfor (i = 0; i < MAXVIFS; i++) {\n>> +\t\tif (mfc->mfc_un.res.ttls[i] != 255) {\n>> +\t\t\terr = mlxsw_sp_mr_route_evif_link(mr_route,\n>> +\t\t\t\t\t\t\t  &mr_table->vifs[i]);\n>> +\t\t\tif (err)\n>> +\t\t\t\tgoto err;\n>> +\t\t\tif (mr_table->vifs[i].dev &&\n>> +\t\t\t    mr_table->vifs[i].dev->mtu < mr_route->min_mtu)\n>> +\t\t\t\tmr_route->min_mtu = mr_table->vifs[i].dev->mtu;\n>> +\t\t}\n>> +\t}\n>> +\tmlxsw_sp_mr_route_ivif_link(mr_route, &mr_table->vifs[mfc->mfc_parent]);\n>> +\tif (err)\n>> +\t\tgoto err;\n>> +\n>> +\tmr_route->route_action = mlxsw_sp_mr_route_action(mr_route);\n>> +\treturn mr_route;\n>> +err:\n>> +\tipmr_cache_put(mfc);\n>> +\tlist_for_each_entry_safe(rve, tmp, &mr_route->evif_list, route_node)\n>> +\t\tmlxsw_sp_mr_route_evif_unlink(rve);\n>> +\tkfree(mr_route);\n>> +\treturn ERR_PTR(err);\n>> +}\n>> +\n>> +static void mlxsw_sp_mr_route4_destroy(struct mlxsw_sp_mr_table *mr_table,\n>> +\t\t\t\t       struct mlxsw_sp_mr_route *mr_route)\n>> +{\n>> +\tstruct mlxsw_sp_mr_route_vif_entry *rve, *tmp;\n>> +\n>> +\tmlxsw_sp_mr_route_ivif_unlink(mr_route);\n>> +\tipmr_cache_put(mr_route->mfc4);\n>> +\tlist_for_each_entry_safe(rve, tmp, &mr_route->evif_list, route_node)\n>> +\t\tmlxsw_sp_mr_route_evif_unlink(rve);\n>> +\tkfree(mr_route);\n>> +}\n>> +\n>> +static void mlxsw_sp_mr_route_destroy(struct mlxsw_sp_mr_table *mr_table,\n>> +\t\t\t\t      struct mlxsw_sp_mr_route *mr_route)\n>> +{\n>> +\tswitch (mr_table->proto) {\n>> +\tcase MLXSW_SP_L3_PROTO_IPV4:\n>> +\t\tmlxsw_sp_mr_route4_destroy(mr_table, mr_route);\n>> +\t\tbreak;\n>> +\tcase MLXSW_SP_L3_PROTO_IPV6:\n>> +\t\t/* fall through */\n>> +\tdefault:\n>> +\t\tWARN_ON_ONCE(1);\n>> +\t}\n>> +}\n>> +\n>> +static void mlxsw_sp_mr_mfc_offload_set(struct mlxsw_sp_mr_route *mr_route,\n>> +\t\t\t\t\tbool offload)\n>> +{\n>> +\tswitch (mr_route->mr_table->proto) {\n>> +\tcase MLXSW_SP_L3_PROTO_IPV4:\n>> +\t\tif (offload)\n>> +\t\t\tmr_route->mfc4->mfc_flags |= MFC_OFFLOAD;\n>> +\t\telse\n>> +\t\t\tmr_route->mfc4->mfc_flags &= ~MFC_OFFLOAD;\n>> +\t\tbreak;\n>> +\tcase MLXSW_SP_L3_PROTO_IPV6:\n>> +\t\t/* fall through */\n>> +\tdefault:\n>> +\t\tWARN_ON_ONCE(1);\n>> +\t}\n>> +}\n>> +\n>> +static void mlxsw_sp_mr_mfc_offload_update(struct mlxsw_sp_mr_route *mr_route)\n>> +{\n>> +\tbool offload;\n>> +\n>> +\toffload = mr_route->route_action != MLXSW_SP_MR_ROUTE_ACTION_TRAP;\n>> +\tmlxsw_sp_mr_mfc_offload_set(mr_route, offload);\n>> +}\n>> +\n>> +static void __mlxsw_sp_mr_route_del(struct mlxsw_sp_mr_table *mr_table,\n>> +\t\t\t\t    struct mlxsw_sp_mr_route *mr_route)\n>> +{\n>> +\tmlxsw_sp_mr_mfc_offload_set(mr_route, false);\n>> +\tmlxsw_sp_mr_route_erase(mr_table, mr_route);\n>> +\trhashtable_remove_fast(&mr_table->route_ht, &mr_route->ht_node,\n>> +\t\t\t       mlxsw_sp_mr_route_ht_params);\n>> +\tlist_del(&mr_route->node);\n>> +\tmlxsw_sp_mr_route_destroy(mr_table, mr_route);\n>> +}\n>> +\n>> +int mlxsw_sp_mr_route4_add(struct mlxsw_sp_mr_table *mr_table,\n>> +\t\t\t   struct mfc_cache *mfc, bool replace)\n>> +{\n>> +\tstruct mlxsw_sp_mr_route *mr_orig_route = NULL;\n>> +\tstruct mlxsw_sp_mr_route *mr_route;\n>> +\tint err;\n>> +\n>> +\t/* If the route is a (*,*) route, abort, as these kind of routes are\n>> +\t * used for proxy routes.\n>> +\t */\n>> +\tif (mfc->mfc_origin == INADDR_ANY && mfc->mfc_mcastgrp == INADDR_ANY) {\n>> +\t\tdev_warn(mr_table->mlxsw_sp->bus_info->dev,\n>> +\t\t\t \"Offloading proxy routes is not supported.\\n\");\n> You are return err, why not use dev_err?\n\n\nThis err return value is properly handled by the caller (spectrum_router.c),\nwhich will trigger the driver abort mechanism. The kernel will still be\nfunctional, but the driver will stop offloading and eject all current offloaded\nroutes.\n\nIt is totally valid for a user to add a proxy route on machine with Spectrum,\nbut he should be warned that from now on, the routes go through slowpath. This\nis why it is only a warning print.\n\nAgain, this is symmetric to the case of a failure in ipv4 and ipv6 route\noffloading, which does not use dev_err either and only print with dev_warn.\n\n\n>\n>> +\t\treturn -EINVAL;\n>> +\t}\n>> +\n>> +\t/* Create a new route */\n>> +\tmr_route = mlxsw_sp_mr_route4_create(mr_table, mfc);\n>> +\tif (IS_ERR(mr_route))\n>> +\t\treturn PTR_ERR(mr_route);\n>> +\n>> +\t/* Find any route with a matching key */\n>> +\tmr_orig_route = rhashtable_lookup_fast(&mr_table->route_ht,\n>> +\t\t\t\t\t       &mr_route->key,\n>> +\t\t\t\t\t       mlxsw_sp_mr_route_ht_params);\n>> +\tif (replace) {\n>> +\t\t/* On replace case, make the route point to the new route_priv.\n>> +\t\t */\n>> +\t\tif (WARN_ON(!mr_orig_route)) {\n>> +\t\t\terr = -ENOENT;\n>> +\t\t\tgoto err_no_orig_route;\n>> +\t\t}\n>> +\t\tmr_route->route_priv = mr_orig_route->route_priv;\n>> +\t} else if (mr_orig_route) {\n>> +\t\t/* On non replace case, if another route with the same key was\n>> +\t\t * found, abort, as duplicate routes are used for proxy routes.\n>> +\t\t */\n>> +\t\tdev_warn(mr_table->mlxsw_sp->bus_info->dev,\n>> +\t\t\t \"Offloading proxy routes is not supported.\\n\");\n> Same as here.\n\nSame as above.\n\n>\n>> +\t\terr = -EINVAL;\n>> +\t\tgoto err_duplicate_route;\n>> +\t}\n>> +\n>> +\t/* Put it in the table data-structures */\n>> +\tlist_add_tail(&mr_route->node, &mr_table->route_list);\n>> +\terr = rhashtable_insert_fast(&mr_table->route_ht,\n>> +\t\t\t\t     &mr_route->ht_node,\n>> +\t\t\t\t     mlxsw_sp_mr_route_ht_params);\n>> +\tif (err)\n>> +\t\tgoto err_rhashtable_insert;\n>> +\n>> +\t/* Write the route to the hardware */\n>> +\terr = mlxsw_sp_mr_route_write(mr_table, mr_route, replace);\n>> +\tif (err)\n>> +\t\tgoto err_mr_route_write;\n>> +\n>> +\t/* Destroy the original route */\n>> +\tif (replace) {\n>> +\t\trhashtable_remove_fast(&mr_table->route_ht,\n>> +\t\t\t\t       &mr_orig_route->ht_node,\n>> +\t\t\t\t       mlxsw_sp_mr_route_ht_params);\n>> +\t\tlist_del(&mr_orig_route->node);\n>> +\t\tmlxsw_sp_mr_route4_destroy(mr_table, mr_orig_route);\n>> +\t}\n>> +\n>> +\tmlxsw_sp_mr_mfc_offload_update(mr_route);\n>> +\treturn 0;\n>> +\n>> +err_mr_route_write:\n>> +\trhashtable_remove_fast(&mr_table->route_ht, &mr_route->ht_node,\n>> +\t\t\t       mlxsw_sp_mr_route_ht_params);\n>> +err_rhashtable_insert:\n>> +\tlist_del(&mr_route->node);\n>> +err_no_orig_route:\n>> +err_duplicate_route:\n>> +\tmlxsw_sp_mr_route4_destroy(mr_table, mr_route);\n>> +\treturn err;\n>> +}\n>> +\n>> +void mlxsw_sp_mr_route4_del(struct mlxsw_sp_mr_table *mr_table,\n>> +\t\t\t    struct mfc_cache *mfc)\n>> +{\n>> +\tstruct mlxsw_sp_mr_route *mr_route;\n>> +\tstruct mlxsw_sp_mr_route_key key;\n>> +\n>> +\tmlxsw_sp_mr_route4_key(mr_table, &key, mfc);\n>> +\tmr_route = rhashtable_lookup_fast(&mr_table->route_ht, &key,\n>> +\t\t\t\t\t  mlxsw_sp_mr_route_ht_params);\n>> +\tif (mr_route)\n>> +\t\t__mlxsw_sp_mr_route_del(mr_table, mr_route);\n>> +}\n>> +\n>> +/* Should be called after the VIF struct is updated */\n>> +static int\n>> +mlxsw_sp_mr_route_ivif_resolve(struct mlxsw_sp_mr_table *mr_table,\n>> +\t\t\t       struct mlxsw_sp_mr_route_vif_entry *rve)\n>> +{\n>> +\tstruct mlxsw_sp *mlxsw_sp = mr_table->mlxsw_sp;\n>> +\tenum mlxsw_sp_mr_route_action route_action;\n>> +\tstruct mlxsw_sp_mr *mr = mlxsw_sp->mr;\n>> +\tu16 irif_index;\n>> +\tint err;\n>> +\n>> +\troute_action = mlxsw_sp_mr_route_action(rve->mr_route);\n>> +\tif (route_action == MLXSW_SP_MR_ROUTE_ACTION_TRAP)\n>> +\t\treturn 0;\n>> +\n>> +\t/* rve->mr_vif->rif is guaranteed to be valid at this stage */\n>> +\tirif_index = mlxsw_sp_rif_index(rve->mr_vif->rif);\n>> +\terr = mr->mr_ops->route_irif_update(mlxsw_sp, rve->mr_route->route_priv,\n>> +\t\t\t\t\t    irif_index);\n>> +\tif (err)\n>> +\t\treturn err;\n>> +\n>> +\terr = mr->mr_ops->route_action_update(mlxsw_sp,\n>> +\t\t\t\t\t      rve->mr_route->route_priv,\n>> +\t\t\t\t\t      route_action);\n>> +\tif (err)\n>> +\t\t/* No need to rollback here because the iRIF change only takes\n>> +\t\t * place after the action has been updated.\n>> +\t\t */\n>> +\t\treturn err;\n>> +\n>> +\trve->mr_route->route_action = route_action;\n>> +\tmlxsw_sp_mr_mfc_offload_update(rve->mr_route);\n>> +\treturn 0;\n>> +}\n>> +\n>> +static void\n>> +mlxsw_sp_mr_route_ivif_unresolve(struct mlxsw_sp_mr_table *mr_table,\n>> +\t\t\t\t struct mlxsw_sp_mr_route_vif_entry *rve)\n>> +{\n>> +\tstruct mlxsw_sp *mlxsw_sp = mr_table->mlxsw_sp;\n>> +\tstruct mlxsw_sp_mr *mr = mlxsw_sp->mr;\n>> +\n>> +\tmr->mr_ops->route_action_update(mlxsw_sp, rve->mr_route->route_priv,\n>> +\t\t\t\t\tMLXSW_SP_MR_ROUTE_ACTION_TRAP);\n>> +\trve->mr_route->route_action = MLXSW_SP_MR_ROUTE_ACTION_TRAP;\n>> +\tmlxsw_sp_mr_mfc_offload_update(rve->mr_route);\n>> +}\n>> +\n>> +/* Should be called after the RIF struct is updated */\n>> +static int\n>> +mlxsw_sp_mr_route_evif_resolve(struct mlxsw_sp_mr_table *mr_table,\n>> +\t\t\t       struct mlxsw_sp_mr_route_vif_entry *rve)\n>> +{\n>> +\tstruct mlxsw_sp *mlxsw_sp = mr_table->mlxsw_sp;\n>> +\tenum mlxsw_sp_mr_route_action route_action;\n>> +\tstruct mlxsw_sp_mr *mr = mlxsw_sp->mr;\n>> +\tu16 erif_index = 0;\n>> +\tint err;\n>> +\n>> +\t/* Update the route action, as the new eVIF can be a tunnel or a pimreg\n>> +\t * device which will require updating the action.\n>> +\t */\n>> +\troute_action = mlxsw_sp_mr_route_action(rve->mr_route);\n>> +\tif (route_action != rve->mr_route->route_action) {\n>> +\t\terr = mr->mr_ops->route_action_update(mlxsw_sp,\n>> +\t\t\t\t\t\t      rve->mr_route->route_priv,\n>> +\t\t\t\t\t\t      route_action);\n>> +\t\tif (err)\n>> +\t\t\treturn err;\n>> +\t}\n>> +\n>> +\t/* Add the eRIF */\n>> +\tif (mlxsw_sp_mr_vif_valid(rve->mr_vif)) {\n>> +\t\terif_index = mlxsw_sp_rif_index(rve->mr_vif->rif);\n>> +\t\terr = mr->mr_ops->route_erif_add(mlxsw_sp,\n>> +\t\t\t\t\t\t rve->mr_route->route_priv,\n>> +\t\t\t\t\t\t erif_index);\n>> +\t\tif (err)\n>> +\t\t\tgoto err_route_erif_add;\n>> +\t}\n>> +\n>> +\t/* Update the minimum MTU */\n>> +\tif (rve->mr_vif->dev->mtu < rve->mr_route->min_mtu) {\n>> +\t\trve->mr_route->min_mtu = rve->mr_vif->dev->mtu;\n>> +\t\terr = mr->mr_ops->route_min_mtu_update(mlxsw_sp,\n>> +\t\t\t\t\t\t       rve->mr_route->route_priv,\n>> +\t\t\t\t\t\t       rve->mr_route->min_mtu);\n>> +\t\tif (err)\n>> +\t\t\tgoto err_route_min_mtu_update;\n>> +\t}\n>> +\n>> +\trve->mr_route->route_action = route_action;\n>> +\tmlxsw_sp_mr_mfc_offload_update(rve->mr_route);\n>> +\treturn 0;\n>> +\n>> +err_route_min_mtu_update:\n>> +\tif (mlxsw_sp_mr_vif_valid(rve->mr_vif))\n>> +\t\tmr->mr_ops->route_erif_del(mlxsw_sp, rve->mr_route->route_priv,\n>> +\t\t\t\t\t   erif_index);\n>> +err_route_erif_add:\n>> +\tif (route_action != rve->mr_route->route_action)\n>> +\t\tmr->mr_ops->route_action_update(mlxsw_sp,\n>> +\t\t\t\t\t\trve->mr_route->route_priv,\n>> +\t\t\t\t\t\trve->mr_route->route_action);\n>> +\treturn err;\n>> +}\n>> +\n>> +/* Should be called before the RIF struct is updated */\n>> +static void\n>> +mlxsw_sp_mr_route_evif_unresolve(struct mlxsw_sp_mr_table *mr_table,\n>> +\t\t\t\t struct mlxsw_sp_mr_route_vif_entry *rve)\n>> +{\n>> +\tstruct mlxsw_sp *mlxsw_sp = mr_table->mlxsw_sp;\n>> +\tenum mlxsw_sp_mr_route_action route_action;\n>> +\tstruct mlxsw_sp_mr *mr = mlxsw_sp->mr;\n>> +\tu16 rifi;\n>> +\n>> +\t/* If the unresolved RIF was not valid, no need to delete it */\n>> +\tif (!mlxsw_sp_mr_vif_valid(rve->mr_vif))\n>> +\t\treturn;\n>> +\n>> +\t/* Update the route action: if there is only one valid eVIF in the\n>> +\t * route, set the action to trap as the VIF deletion will lead to zero\n>> +\t * valid eVIFs. On any other case, use the mlxsw_sp_mr_route_action to\n>> +\t * determine the route action.\n>> +\t */\n>> +\tif (mlxsw_sp_mr_route_valid_evifs_num(rve->mr_route) == 1)\n>> +\t\troute_action = MLXSW_SP_MR_ROUTE_ACTION_TRAP;\n>> +\telse\n>> +\t\troute_action = mlxsw_sp_mr_route_action(rve->mr_route);\n>> +\tif (route_action != rve->mr_route->route_action)\n>> +\t\tmr->mr_ops->route_action_update(mlxsw_sp,\n>> +\t\t\t\t\t\trve->mr_route->route_priv,\n>> +\t\t\t\t\t\troute_action);\n>> +\n>> +\t/* Delete the erif from the route */\n>> +\trifi = mlxsw_sp_rif_index(rve->mr_vif->rif);\n>> +\tmr->mr_ops->route_erif_del(mlxsw_sp, rve->mr_route->route_priv, rifi);\n>> +\trve->mr_route->route_action = route_action;\n>> +\tmlxsw_sp_mr_mfc_offload_update(rve->mr_route);\n>> +}\n>> +\n>> +static int mlxsw_sp_mr_vif_resolve(struct mlxsw_sp_mr_table *mr_table,\n>> +\t\t\t\t   struct net_device *dev,\n>> +\t\t\t\t   struct mlxsw_sp_mr_vif *mr_vif,\n>> +\t\t\t\t   unsigned long vif_flags,\n>> +\t\t\t\t   const struct mlxsw_sp_rif *rif)\n>> +{\n>> +\tstruct mlxsw_sp_mr_route_vif_entry *irve, *erve;\n>> +\tint err;\n>> +\n>> +\t/* Update the VIF */\n>> +\tmr_vif->dev = dev;\n>> +\tmr_vif->rif = rif;\n>> +\tmr_vif->vif_flags = vif_flags;\n>> +\n>> +\t/* Update all routes where this VIF is used as an unresolved iRIF */\n>> +\tlist_for_each_entry(irve, &mr_vif->route_ivif_list, vif_node) {\n>> +\t\terr = mlxsw_sp_mr_route_ivif_resolve(mr_table, irve);\n>> +\t\tif (err)\n>> +\t\t\tgoto err_irif_unresolve;\n>> +\t}\n>> +\n>> +\t/* Update all routes where this VIF is used as an unresolved eRIF */\n>> +\tlist_for_each_entry(erve, &mr_vif->route_evif_list, vif_node) {\n>> +\t\terr = mlxsw_sp_mr_route_evif_resolve(mr_table, erve);\n>> +\t\tif (err)\n>> +\t\t\tgoto err_erif_unresolve;\n>> +\t}\n>> +\treturn 0;\n>> +\n>> +err_erif_unresolve:\n>> +\tlist_for_each_entry_from_reverse(erve, &mr_vif->route_evif_list,\n>> +\t\t\t\t\t vif_node)\n>> +\t\tmlxsw_sp_mr_route_evif_unresolve(mr_table, erve);\n>> +err_irif_unresolve:\n>> +\tlist_for_each_entry_from_reverse(irve, &mr_vif->route_ivif_list,\n>> +\t\t\t\t\t vif_node)\n>> +\t\tmlxsw_sp_mr_route_ivif_unresolve(mr_table, irve);\n>> +\tmr_vif->rif = NULL;\n>> +\treturn err;\n>> +}\n>> +\n>> +static void mlxsw_sp_mr_vif_unresolve(struct mlxsw_sp_mr_table *mr_table,\n>> +\t\t\t\t      struct net_device *dev,\n>> +\t\t\t\t      struct mlxsw_sp_mr_vif *mr_vif)\n>> +{\n>> +\tstruct mlxsw_sp_mr_route_vif_entry *rve;\n>> +\n>> +\t/* Update all routes where this VIF is used as an unresolved eRIF */\n>> +\tlist_for_each_entry(rve, &mr_vif->route_evif_list, vif_node)\n>> +\t\tmlxsw_sp_mr_route_evif_unresolve(mr_table, rve);\n>> +\n>> +\t/* Update all routes where this VIF is used as an unresolved iRIF */\n>> +\tlist_for_each_entry(rve, &mr_vif->route_ivif_list, vif_node)\n>> +\t\tmlxsw_sp_mr_route_ivif_unresolve(mr_table, rve);\n>> +\n>> +\t/* Update the VIF */\n>> +\tmr_vif->dev = dev;\n>> +\tmr_vif->rif = NULL;\n>> +}\n>> +\n>> +int mlxsw_sp_mr_vif_add(struct mlxsw_sp_mr_table *mr_table,\n>> +\t\t\tstruct net_device *dev, vifi_t vif_index,\n>> +\t\t\tunsigned long vif_flags, const struct mlxsw_sp_rif *rif)\n>> +{\n>> +\tstruct mlxsw_sp_mr_vif *mr_vif = &mr_table->vifs[vif_index];\n>> +\n>> +\tif (WARN_ON(vif_index >= MAXVIFS))\n>> +\t\treturn -EINVAL;\n>> +\tif (mr_vif->dev)\n>> +\t\treturn -EEXIST;\n> -ENODEV?\n\n\nNo, Look carefully. The error is returned if mr_vif->dev is *not* NULL, which\nmeans that the VIF was already added before, hence -EEXIST.\n\nThis error will happen if the mlxsw_sp_mr_vif_add function is called twice with\nthe same VIF index.\n\n\n>\n>> +\treturn mlxsw_sp_mr_vif_resolve(mr_table, dev, mr_vif, vif_flags, rif);\n>> +}\n>> +\n>> +void mlxsw_sp_mr_vif_del(struct mlxsw_sp_mr_table *mr_table, vifi_t vif_index)\n>> +{\n>> +\tstruct mlxsw_sp_mr_vif *mr_vif = &mr_table->vifs[vif_index];\n>> +\n>> +\tif (WARN_ON(vif_index >= MAXVIFS))\n>> +\t\treturn;\n>> +\tif (WARN_ON(!mr_vif->dev))\n>> +\t\treturn;\n>> +\tmlxsw_sp_mr_vif_unresolve(mr_table, NULL, mr_vif);\n>> +}\n>> +\n>> +struct mlxsw_sp_mr_vif *\n>> +mlxsw_sp_mr_dev_vif_lookup(struct mlxsw_sp_mr_table *mr_table,\n>> +\t\t\t   const struct net_device *dev)\n>> +{\n>> +\tvifi_t vif_index;\n>> +\n>> +\tfor (vif_index = 0; vif_index < MAXVIFS; vif_index++)\n>> +\t\tif (mr_table->vifs[vif_index].dev == dev)\n>> +\t\t\treturn &mr_table->vifs[vif_index];\n>> +\treturn NULL;\n>> +}\n>> +\n>> +int mlxsw_sp_mr_rif_add(struct mlxsw_sp_mr_table *mr_table,\n>> +\t\t\tconst struct mlxsw_sp_rif *rif)\n>> +{\n>> +\tconst struct net_device *rif_dev = mlxsw_sp_rif_dev(rif);\n>> +\tstruct mlxsw_sp_mr_vif *mr_vif;\n>> +\n>> +\tif (!rif_dev)\n>> +\t\treturn 0;\n>> +\n>> +\tmr_vif = mlxsw_sp_mr_dev_vif_lookup(mr_table, rif_dev);\n>> +\tif (!mr_vif)\n>> +\t\treturn 0;\n>> +\treturn mlxsw_sp_mr_vif_resolve(mr_table, mr_vif->dev, mr_vif,\n>> +\t\t\t\t       mr_vif->vif_flags, rif);\n>> +}\n>> +\n>> +void mlxsw_sp_mr_rif_del(struct mlxsw_sp_mr_table *mr_table,\n>> +\t\t\t const struct mlxsw_sp_rif *rif)\n>> +{\n>> +\tconst struct net_device *rif_dev = mlxsw_sp_rif_dev(rif);\n>> +\tstruct mlxsw_sp_mr_vif *mr_vif;\n>> +\n>> +\tif (!rif_dev)\n>> +\t\treturn;\n>> +\n>> +\tmr_vif = mlxsw_sp_mr_dev_vif_lookup(mr_table, rif_dev);\n>> +\tif (!mr_vif)\n>> +\t\treturn;\n>> +\tmlxsw_sp_mr_vif_unresolve(mr_table, mr_vif->dev, mr_vif);\n>> +}\n>> +\n>> +void mlxsw_sp_mr_rif_mtu_update(struct mlxsw_sp_mr_table *mr_table,\n>> +\t\t\t\tconst struct mlxsw_sp_rif *rif, int mtu)\n>> +{\n>> +\tconst struct net_device *rif_dev = mlxsw_sp_rif_dev(rif);\n>> +\tstruct mlxsw_sp *mlxsw_sp = mr_table->mlxsw_sp;\n>> +\tstruct mlxsw_sp_mr_route_vif_entry *rve;\n>> +\tstruct mlxsw_sp_mr *mr = mlxsw_sp->mr;\n>> +\tstruct mlxsw_sp_mr_vif *mr_vif;\n>> +\n>> +\tif (!rif_dev)\n>> +\t\treturn;\n>> +\n>> +\t/* Search for a VIF that use that RIF */\n>> +\tmr_vif = mlxsw_sp_mr_dev_vif_lookup(mr_table, rif_dev);\n>> +\tif (!mr_vif)\n>> +\t\treturn;\n>> +\n>> +\t/* Update all the routes that uses that VIF as eVIF */\n>> +\tlist_for_each_entry(rve, &mr_vif->route_evif_list, vif_node) {\n>> +\t\tif (mtu < rve->mr_route->min_mtu) {\n>> +\t\t\trve->mr_route->min_mtu = mtu;\n>> +\t\t\tmr->mr_ops->route_min_mtu_update(mlxsw_sp,\n>> +\t\t\t\t\t\t\t rve->mr_route->route_priv,\n>> +\t\t\t\t\t\t\t mtu);\n>> +\t\t}\n>> +\t}\n>> +}\n>> +\n>> +struct mlxsw_sp_mr_table *mlxsw_sp_mr_table_create(struct mlxsw_sp *mlxsw_sp,\n>> +\t\t\t\t\t\t   u32 vr_id,\n>> +\t\t\t\t\t\t   enum mlxsw_sp_l3proto proto)\n>> +{\n>> +\tstruct mlxsw_sp_mr_route_params catchall_route_params = {\n>> +\t\t.prio = MLXSW_SP_MR_ROUTE_PRIO_CATCHALL,\n>> +\t\t.key = {\n>> +\t\t\t.vrid = vr_id,\n>> +\t\t},\n>> +\t\t.value = {\n>> +\t\t\t.route_action = MLXSW_SP_MR_ROUTE_ACTION_TRAP,\n>> +\t\t}\n>> +\t};\n>> +\tstruct mlxsw_sp_mr *mr = mlxsw_sp->mr;\n>> +\tstruct mlxsw_sp_mr_table *mr_table;\n>> +\tint err;\n>> +\tint i;\n>> +\n>> +\tmr_table = kzalloc(sizeof(*mr_table) + mr->mr_ops->route_priv_size,\n>> +\t\t\t   GFP_KERNEL);\n>> +\tif (!mr_table)\n>> +\t\treturn ERR_PTR(-ENOMEM);\n>> +\n>> +\tmr_table->vr_id = vr_id;\n>> +\tmr_table->mlxsw_sp = mlxsw_sp;\n>> +\tmr_table->proto = proto;\n>> +\tINIT_LIST_HEAD(&mr_table->route_list);\n>> +\n>> +\terr = rhashtable_init(&mr_table->route_ht,\n>> +\t\t\t      &mlxsw_sp_mr_route_ht_params);\n>> +\tif (err)\n>> +\t\tgoto err_route_rhashtable_init;\n>> +\n>> +\tfor (i = 0; i < MAXVIFS; i++) {\n>> +\t\tINIT_LIST_HEAD(&mr_table->vifs[i].route_evif_list);\n>> +\t\tINIT_LIST_HEAD(&mr_table->vifs[i].route_ivif_list);\n>> +\t}\n>> +\n>> +\terr = mr->mr_ops->route_create(mlxsw_sp, mr->priv,\n>> +\t\t\t\t       mr_table->catchall_route_priv,\n>> +\t\t\t\t       &catchall_route_params);\n>> +\tif (err)\n>> +\t\tgoto err_ops_route_create;\n>> +\tlist_add_tail(&mr_table->node, &mr->table_list);\n>> +\treturn mr_table;\n>> +\n>> +err_ops_route_create:\n>> +\trhashtable_destroy(&mr_table->route_ht);\n>> +err_route_rhashtable_init:\n>> +\tkfree(mr_table);\n>> +\treturn ERR_PTR(err);\n>> +}\n>> +\n>> +void mlxsw_sp_mr_table_destroy(struct mlxsw_sp_mr_table *mr_table)\n>> +{\n>> +\tstruct mlxsw_sp *mlxsw_sp = mr_table->mlxsw_sp;\n>> +\tstruct mlxsw_sp_mr *mr = mlxsw_sp->mr;\n>> +\n>> +\tWARN_ON(!mlxsw_sp_mr_table_empty(mr_table));\n>> +\tlist_del(&mr_table->node);\n>> +\tmr->mr_ops->route_destroy(mlxsw_sp, mr->priv,\n>> +\t\t\t\t  &mr_table->catchall_route_priv);\n>> +\trhashtable_destroy(&mr_table->route_ht);\n>> +\tkfree(mr_table);\n>> +}\n>> +\n>> +void mlxsw_sp_mr_table_flush(struct mlxsw_sp_mr_table *mr_table)\n>> +{\n>> +\tstruct mlxsw_sp_mr_route *mr_route, *tmp;\n>> +\tint i;\n>> +\n>> +\tlist_for_each_entry_safe(mr_route, tmp, &mr_table->route_list, node)\n>> +\t\t__mlxsw_sp_mr_route_del(mr_table, mr_route);\n>> +\n>> +\tfor (i = 0; i < MAXVIFS; i++) {\n>> +\t\tmr_table->vifs[i].dev = NULL;\n>> +\t\tmr_table->vifs[i].rif = NULL;\n>> +\t}\n>> +}\n>> +\n>> +bool mlxsw_sp_mr_table_empty(const struct mlxsw_sp_mr_table *mr_table)\n>> +{\n>> +\tint i;\n>> +\n>> +\tfor (i = 0; i < MAXVIFS; i++)\n>> +\t\tif (mr_table->vifs[i].dev)\n>> +\t\t\treturn false;\n>> +\treturn list_empty(&mr_table->route_list);\n>> +}\n>> +\n>> +static void mlxsw_sp_mr_route_stats_update(struct mlxsw_sp *mlxsw_sp,\n>> +\t\t\t\t\t   struct mlxsw_sp_mr_route *mr_route)\n>> +{\n>> +\tstruct mlxsw_sp_mr *mr = mlxsw_sp->mr;\n>> +\tu64 packets, bytes;\n>> +\n>> +\tif (mr_route->route_action == MLXSW_SP_MR_ROUTE_ACTION_TRAP)\n>> +\t\treturn;\n>> +\n>> +\tmr->mr_ops->route_stats(mlxsw_sp, mr_route->route_priv, &packets,\n>> +\t\t\t\t&bytes);\n>> +\n>> +\tswitch (mr_route->mr_table->proto) {\n>> +\tcase MLXSW_SP_L3_PROTO_IPV4:\n>> +\t\tif (mr_route->mfc4->mfc_un.res.pkt != packets)\n>> +\t\t\tmr_route->mfc4->mfc_un.res.lastuse = jiffies;\n>> +\t\tmr_route->mfc4->mfc_un.res.pkt = packets;\n>> +\t\tmr_route->mfc4->mfc_un.res.bytes = bytes;\n>> +\t\tbreak;\n>> +\tcase MLXSW_SP_L3_PROTO_IPV6:\n>> +\t\t/* fall through */\n>> +\tdefault:\n>> +\t\tWARN_ON_ONCE(1);\n>> +\t}\n>> +}\n>> +\n>> +static void mlxsw_sp_mr_stats_update(struct work_struct *work)\n>> +{\n>> +\tstruct mlxsw_sp_mr *mr = container_of(work, struct mlxsw_sp_mr,\n>> +\t\t\t\t\t      stats_update_dw.work);\n>> +\tstruct mlxsw_sp_mr_table *mr_table;\n>> +\tstruct mlxsw_sp_mr_route *mr_route;\n>> +\tunsigned long interval;\n>> +\n>> +\trtnl_lock();\n>> +\tlist_for_each_entry(mr_table, &mr->table_list, node)\n>> +\t\tlist_for_each_entry(mr_route, &mr_table->route_list, node)\n>> +\t\t\tmlxsw_sp_mr_route_stats_update(mr_table->mlxsw_sp,\n>> +\t\t\t\t\t\t       mr_route);\n>> +\trtnl_unlock();\n>> +\n>> +\tinterval = msecs_to_jiffies(MLXSW_SP_MR_ROUTES_COUNTER_UPDATE_INTERVAL);\n>> +\tmlxsw_core_schedule_dw(&mr->stats_update_dw, interval);\n>> +}\n>> +\n>> +int mlxsw_sp_mr_init(struct mlxsw_sp *mlxsw_sp,\n>> +\t\t     const struct mlxsw_sp_mr_ops *mr_ops)\n>> +{\n>> +\tstruct mlxsw_sp_mr *mr;\n>> +\tunsigned long interval;\n>> +\tint err;\n>> +\n>> +\tmr = kzalloc(sizeof(*mr) + mr_ops->priv_size, GFP_KERNEL);\n>> +\tif (!mr)\n>> +\t\treturn -ENOMEM;\n>> +\tmr->mr_ops = mr_ops;\n>> +\tmlxsw_sp->mr = mr;\n>> +\tINIT_LIST_HEAD(&mr->table_list);\n>> +\n>> +\terr = mr_ops->init(mlxsw_sp, mr->priv);\n>> +\tif (err)\n>> +\t\tgoto err;\n>> +\n>> +\t/* Create the delayed work for counter updates */\n>> +\tINIT_DELAYED_WORK(&mr->stats_update_dw, mlxsw_sp_mr_stats_update);\n>> +\tinterval = msecs_to_jiffies(MLXSW_SP_MR_ROUTES_COUNTER_UPDATE_INTERVAL);\n>> +\tmlxsw_core_schedule_dw(&mr->stats_update_dw, interval);\n>> +\treturn 0;\n>> +err:\n>> +\tkfree(mr);\n>> +\treturn err;\n>> +}\n>> +\n>> +void mlxsw_sp_mr_fini(struct mlxsw_sp *mlxsw_sp)\n>> +{\n>> +\tstruct mlxsw_sp_mr *mr = mlxsw_sp->mr;\n>> +\n>> +\tcancel_delayed_work_sync(&mr->stats_update_dw);\n>> +\tmr->mr_ops->fini(mr->priv);\n>> +\tkfree(mr);\n>> +}\n>> diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.h\n>> new file mode 100644\n>> index 0000000..c851b23\n>> --- /dev/null\n>> +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.h\n>> @@ -0,0 +1,133 @@\n>> +/*\n>> + * drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.h\n>> + * Copyright (c) 2017 Mellanox Technologies. All rights reserved.\n>> + * Copyright (c) 2017 Yotam Gigi <yotamg@mellanox.com>\n>> + *\n>> + * Redistribution and use in source and binary forms, with or without\n>> + * modification, are permitted provided that the following conditions are met:\n>> + *\n>> + * 1. Redistributions of source code must retain the above copyright\n>> + *    notice, this list of conditions and the following disclaimer.\n>> + * 2. Redistributions in binary form must reproduce the above copyright\n>> + *    notice, this list of conditions and the following disclaimer in the\n>> + *    documentation and/or other materials provided with the distribution.\n>> + * 3. Neither the names of the copyright holders nor the names of its\n>> + *    contributors may be used to endorse or promote products derived from\n>> + *    this software without specific prior written permission.\n>> + *\n>> + * Alternatively, this software may be distributed under the terms of the\n>> + * GNU General Public License (\"GPL\") version 2 as published by the Free\n>> + * Software Foundation.\n>> + *\n>> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n>> + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n>> + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n>> + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n>> + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n>> + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n>> + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n>> + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n>> + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n>> + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n>> + * POSSIBILITY OF SUCH DAMAGE.\n>> + */\n>> +\n>> +#ifndef _MLXSW_SPECTRUM_MCROUTER_H\n>> +#define _MLXSW_SPECTRUM_MCROUTER_H\n>> +\n>> +#include <linux/mroute.h>\n>> +#include \"spectrum_router.h\"\n>> +#include \"spectrum.h\"\n>> +\n>> +enum mlxsw_sp_mr_route_action {\n>> +\tMLXSW_SP_MR_ROUTE_ACTION_FORWARD,\n>> +\tMLXSW_SP_MR_ROUTE_ACTION_TRAP,\n>> +};\n>> +\n>> +enum mlxsw_sp_mr_route_prio {\n>> +\tMLXSW_SP_MR_ROUTE_PRIO_SG,\n>> +\tMLXSW_SP_MR_ROUTE_PRIO_STARG,\n>> +\tMLXSW_SP_MR_ROUTE_PRIO_CATCHALL,\n>> +\t__MLXSW_SP_MR_ROUTE_PRIO_MAX\n>> +};\n>> +\n>> +#define MLXSW_SP_MR_ROUTE_PRIO_MAX (__MLXSW_SP_MR_ROUTE_PRIO_MAX - 1)\n>> +\n>> +struct mlxsw_sp_mr_route_key {\n>> +\tint vrid;\n>> +\tenum mlxsw_sp_l3proto proto;\n>> +\tunion mlxsw_sp_l3addr group;\n>> +\tunion mlxsw_sp_l3addr group_mask;\n>> +\tunion mlxsw_sp_l3addr source;\n>> +\tunion mlxsw_sp_l3addr source_mask;\n>> +};\n>> +\n>> +struct mlxsw_sp_mr_route_info {\n>> +\tenum mlxsw_sp_mr_route_action route_action;\n>> +\tu16 irif_index;\n>> +\tu16 *erif_indices;\n>> +\tsize_t erif_num;\n>> +\tu16 min_mtu;\n>> +};\n>> +\n>> +struct mlxsw_sp_mr_route_params {\n>> +\tstruct mlxsw_sp_mr_route_key key;\n>> +\tstruct mlxsw_sp_mr_route_info value;\n>> +\tenum mlxsw_sp_mr_route_prio prio;\n>> +};\n>> +\n>> +struct mlxsw_sp_mr_ops {\n>> +\tint priv_size;\n>> +\tint route_priv_size;\n>> +\tint (*init)(struct mlxsw_sp *mlxsw_sp, void *priv);\n>> +\tint (*route_create)(struct mlxsw_sp *mlxsw_sp, void *priv,\n>> +\t\t\t    void *route_priv,\n>> +\t\t\t    struct mlxsw_sp_mr_route_params *route_params);\n>> +\tint (*route_update)(struct mlxsw_sp *mlxsw_sp, void *route_priv,\n>> +\t\t\t    struct mlxsw_sp_mr_route_info *route_info);\n>> +\tint (*route_stats)(struct mlxsw_sp *mlxsw_sp, void *route_priv,\n>> +\t\t\t   u64 *packets, u64 *bytes);\n>> +\tint (*route_action_update)(struct mlxsw_sp *mlxsw_sp, void *route_priv,\n>> +\t\t\t\t   enum mlxsw_sp_mr_route_action route_action);\n>> +\tint (*route_min_mtu_update)(struct mlxsw_sp *mlxsw_sp, void *route_priv,\n>> +\t\t\t\t    u16 min_mtu);\n>> +\tint (*route_irif_update)(struct mlxsw_sp *mlxsw_sp, void *route_priv,\n>> +\t\t\t\t u16 irif_index);\n>> +\tint (*route_erif_add)(struct mlxsw_sp *mlxsw_sp, void *route_priv,\n>> +\t\t\t      u16 erif_index);\n>> +\tint (*route_erif_del)(struct mlxsw_sp *mlxsw_sp, void *route_priv,\n>> +\t\t\t      u16 erif_index);\n>> +\tvoid (*route_destroy)(struct mlxsw_sp *mlxsw_sp, void *priv,\n>> +\t\t\t      void *route_priv);\n>> +\tvoid (*fini)(void *priv);\n>> +};\n>> +\n>> +struct mlxsw_sp_mr;\n>> +struct mlxsw_sp_mr_table;\n>> +\n>> +int mlxsw_sp_mr_init(struct mlxsw_sp *mlxsw_sp,\n>> +\t\t     const struct mlxsw_sp_mr_ops *mr_ops);\n>> +void mlxsw_sp_mr_fini(struct mlxsw_sp *mlxsw_sp);\n>> +int mlxsw_sp_mr_route4_add(struct mlxsw_sp_mr_table *mr_table,\n>> +\t\t\t   struct mfc_cache *mfc, bool replace);\n>> +void mlxsw_sp_mr_route4_del(struct mlxsw_sp_mr_table *mr_table,\n>> +\t\t\t    struct mfc_cache *mfc);\n>> +int mlxsw_sp_mr_vif_add(struct mlxsw_sp_mr_table *mr_table,\n>> +\t\t\tstruct net_device *dev, vifi_t vif_index,\n>> +\t\t\tunsigned long vif_flags,\n>> +\t\t\tconst struct mlxsw_sp_rif *rif);\n>> +void mlxsw_sp_mr_vif_del(struct mlxsw_sp_mr_table *mr_table, vifi_t vif_index);\n>> +int mlxsw_sp_mr_rif_add(struct mlxsw_sp_mr_table *mr_table,\n>> +\t\t\tconst struct mlxsw_sp_rif *rif);\n>> +void mlxsw_sp_mr_rif_del(struct mlxsw_sp_mr_table *mr_table,\n>> +\t\t\t const struct mlxsw_sp_rif *rif);\n>> +void mlxsw_sp_mr_rif_mtu_update(struct mlxsw_sp_mr_table *mr_table,\n>> +\t\t\t\tconst struct mlxsw_sp_rif *rif, int mtu);\n>> +struct mlxsw_sp_mr_table *mlxsw_sp_mr_table_create(struct mlxsw_sp *mlxsw_sp,\n>> +\t\t\t\t\t\t   u32 tb_id,\n>> +\t\t\t\t\t\t   enum mlxsw_sp_l3proto proto);\n>> +void mlxsw_sp_mr_table_destroy(struct mlxsw_sp_mr_table *mr_table);\n>> +void mlxsw_sp_mr_table_flush(struct mlxsw_sp_mr_table *mr_table);\n>> +bool mlxsw_sp_mr_table_empty(const struct mlxsw_sp_mr_table *mr_table);\n>> +\n>> +#endif\n>>","headers":{"Return-Path":"<netdev-owner@vger.kernel.org>","X-Original-To":"patchwork-incoming@ozlabs.org","Delivered-To":"patchwork-incoming@ozlabs.org","Authentication-Results":["ozlabs.org;\n\tspf=none (mailfrom) smtp.mailfrom=vger.kernel.org\n\t(client-ip=209.132.180.67; helo=vger.kernel.org;\n\tenvelope-from=netdev-owner@vger.kernel.org;\n\treceiver=<UNKNOWN>)","ozlabs.org; dkim=pass (1024-bit key;\n\tunprotected) header.d=Mellanox.com header.i=@Mellanox.com\n\theader.b=\"yQO151gD\"; dkim-atps=neutral","spf=none (sender IP is )\n\tsmtp.mailfrom=yotamg@mellanox.com; "],"Received":["from vger.kernel.org (vger.kernel.org [209.132.180.67])\n\tby ozlabs.org (Postfix) with ESMTP id 3y0tcD2gChz9t4Z\n\tfor <patchwork-incoming@ozlabs.org>;\n\tMon, 25 Sep 2017 15:55:56 +1000 (AEST)","(majordomo@vger.kernel.org) by vger.kernel.org via listexpand\n\tid S932129AbdIYFzy (ORCPT <rfc822;patchwork-incoming@ozlabs.org>);\n\tMon, 25 Sep 2017 01:55:54 -0400","from mail-ve1eur01on0044.outbound.protection.outlook.com\n\t([104.47.1.44]:11245\n\t\"EHLO EUR01-VE1-obe.outbound.protection.outlook.com\"\n\trhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP\n\tid S1753195AbdIYFzv (ORCPT <rfc822;netdev@vger.kernel.org>);\n\tMon, 25 Sep 2017 01:55:51 -0400","from localhost.localdomain (79.181.49.40) by\n\tHE1PR0502MB2970.eurprd05.prod.outlook.com (2603:10a6:3:d7::11) with\n\tMicrosoft SMTP Server (version=TLS1_2,\n\tcipher=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384_P256) id 15.20.77.7;\n\tMon, 25 Sep 2017 05:55:45 +0000"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=Mellanox.com;\n\ts=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version;\n\tbh=KFY7AUt6w4y5YlCeeR8FiNIKvOUsJPfgyBxEnTelGMk=;\n\tb=yQO151gDlKpt+TNZATRw1D/d1PRmYoIyzMlqiv4xpMfZuMq4GdoXkO7W+kcYIr/3j38/gW6GeWkEUIo4G5M3JMXWwoRXxBknaajJ4UKtZDkKW0y8f99qS/EZ4obzfWGx/I2zdD8Yg3c4jHFwvJjoOFDvQIgFW29zv08QLlk2guA=","Subject":"Re: [patch net-next v2 07/12] mlxsw: spectrum: Add the multicast\n\trouting offloading logic","To":"Yunsheng Lin <linyunsheng@huawei.com>,\n\tJiri Pirko <jiri@resnulli.us>, netdev@vger.kernel.org","Cc":"davem@davemloft.net, idosch@mellanox.com, mlxsw@mellanox.com,\n\tnikolay@cumulusnetworks.com, andrew@lunn.ch","References":"<20170924172212.10096-1-jiri@resnulli.us>\n\t<20170924172212.10096-8-jiri@resnulli.us>\n\t<fb5d1fcc-ad70-d656-f860-7b05cdd2834a@huawei.com>","From":"Yotam Gigi <yotamg@mellanox.com>","Message-ID":"<d89064c4-f685-648c-07c2-54cfa8524e7f@mellanox.com>","Date":"Mon, 25 Sep 2017 08:55:39 +0300","User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101\n\tThunderbird/52.2.1","MIME-Version":"1.0","In-Reply-To":"<fb5d1fcc-ad70-d656-f860-7b05cdd2834a@huawei.com>","Content-Type":"text/plain; charset=utf-8","Content-Transfer-Encoding":"7bit","Content-Language":"en-US","X-Originating-IP":"[79.181.49.40]","X-ClientProxiedBy":"HE1PR0102CA0039.eurprd01.prod.exchangelabs.com\n\t(2603:10a6:7:7d::16) To HE1PR0502MB2970.eurprd05.prod.outlook.com\n\t(2603:10a6:3:d7::11)","X-MS-PublicTrafficType":"Email","X-MS-Office365-Filtering-Correlation-Id":"c364dfb5-6a14-4b71-bd71-08d503da11ac","X-MS-Office365-Filtering-HT":"Tenant","X-Microsoft-Antispam":"UriScan:; BCL:0; PCL:0;\n\tRULEID:(22001)(2017030254152)(48565401081)(2017052603199)(201703131423075)(201703031133081)(201702281549075);\n\tSRVR:HE1PR0502MB2970; ","X-Microsoft-Exchange-Diagnostics":["1; HE1PR0502MB2970;\n\t3:q3VfMPWqLdAh8Gqnh/l6mUfRkeQ51kFQanlThFNf5EBUT9IwLE2ZXc/Z1283GOSiQaftI6NdyMjhmuuJdzXxEwPXUB+eDus4ENHa7yYjaXeZ3UBPfHS8+20n0YNeu8aKzqEckYrku6T2BfIg6fJbl3GuKYYb5X62e7tstXZwfykd8WbYwFvI34LO9mUSE1jmW/vJfRg2iUNAyJMH+T/Y2jqDut0UkiEekuBlbOoB6w+aZFafVDadZLVa5DYH1Njx;\n\t25:tnC+IOqKd3fs78JseJ8jBUoo33uFpRndtpUVZGkC2zjeCVv4fHi+sS+jNp/y6nZsccD+AASLChSc3H75+717rhVS8CFc7kaYGO3eAgy41/13xjsVOQMrqRmmjKplbjZ5+xIT0oUZ1sc8UJ1AQxi0pO8FyejX/25PMpmAf2Edz+Q7nAv3815WwaqaXv53splr3vXnbkpbDikAkVJZlPVNPVpLI+X1CL8Jrfa7OoYCS+qrpnGHEORoHSOCwmdBKP+SEORQfsK5kZVjptrnkkDS9hBBqzEzxIJI8LiOlOKct9Gc+WnBMJP3JYFZUA95RuOxtI3nsVfePLFfxzbJu2I/nQ==;\n\t31:P4I6RIW+i+c2WxedfEE7sOi0oTiboLqdS+Ou6i2E4UlPQmbWxiap/8Ay6DjAvfQU1hwb0AfkGSiOMuWvadK0oxxGnikHlSzgetfk8rs54Xhp87Ym28vZCQptsj2mw3wvvyCPMfqBoCg2xj4xfX+XaRSEZv/Ot+xsJ7xB0cN62JrZsCbhYH85CltMMIRl1BVVjuTAFIvYcUxJ5YMgwpsb0ASESyxrrr+NLS/ldiK2TWA=","1; HE1PR0502MB2970;\n\t20:4B6cdKEiOxEg7SjSfKjIf8dQMYMeRjClBrBlE1fVYWnaxzH8qDCNF/HKeXfYlvBtJw6s0AvK9cQ5dMUjSen/vXEu4PpJQ3EmCFZBEp8Kgls/9E7qPjaFFHO+CHQGuXJ7qEV47DhTrwSk1P3XiuhBqKC5XwpTCHgdDM6pBs2GQzsKEFkV06gXEHkFQwXu0ZZuEIche2SSFee9vhxK0Ks3u3c0cEch8hX3rc9jj4NTDngTPXnEWYwOFzCPEEyel10ETzkeRiKkRHQ+ufQ/BrVJZTjErpSutPbOmz5rb2/kGFc+3TsWHXoNL8zu/xlun5/d7wTwdJN9y90es8zZKSrfPiGflDbdupnIwcdN5gKeXxtPii/J/9qBj/X3ZfeHySGv3iiFMBWqiCUxGdKSaNHAAEdf9j0lwTan9+1x4tjt7P29kvhVgDLkdaVfYsKC8b8G/pH9y0NC+nDUxbP9ysRpXOaSWz85wHxSKnNU4wlurvABfWf3t4fXrmsgrHaHK5lK;\n\t4:UUMIr8R/skR5Wl1+xD7dGjzd9oaJTo2Tz9Pu2KqNLQl0efeb6s55tzrjHdPdDYK6CwCF24FGBT4WNGgGHVShhbpdyR/wz5d5lpOGBdAN9DPZwMHUZQmpdfbiMiVRdWyMM+/kaGVDmp7h7uuuI1mjskynlsAExWbkdtEqoWfD4T6+y3tZMz11tfPmMmIdtTxBiheamiKXiDMqK61HiaT36rx1ZhHegf+BFS5QGajh/KpQQnPBr1cHnGvfIBFHvtZwvcTkjjyJqcWUsPWRcoua9VsQUlYM7fOW4ppIudtZZTk=","=?utf-8?q?1=3BHE1PR0502MB2970=3B23=3AY8?=\n\t=?utf-8?q?Ff1tVZnFaNyvpFILEwmiJIN7UUFK/Wnslojubef/KiSf9QuiNQUHeAGd?=\n\t=?utf-8?q?/evNN1vvS9aLTsj9QpgsjJyElkdBASeUCvgjWmC41XrELrfMpGWkiRYE?=\n\t=?utf-8?q?nre0XclOoKep5SBb5uAtJgAWSm5Hui8Czzw7d96cpovl34ABIRkSWAg9?=\n\t=?utf-8?q?3i5hUgI2fX4qKtQm/Bpuvpqg+nOQl97gka2k0DsV8MIQq+pJ/CmlzEHO?=\n\t=?utf-8?q?vnHM150uHtO3w00fS3Hrn8Rqz8IofPJ+zTLMIrSrBKsBINgLcNNiCjMG?=\n\t=?utf-8?q?bxnyiWTel8r1GkcU12pXca9dvmWz5SVFu40kvTrDBBuqLzLZ+Z7zpseC?=\n\t=?utf-8?q?i+5gOni1X25q+5/jgH5XqpiynksLKQ4xmWfY2aiUtzfyqeaY+aVIMx29?=\n\t=?utf-8?q?aY2VdbJ14cLU+tyIFDnZlZLDLwCfiIrPtjimmdQUShQG+/Qtpby0r52l?=\n\t=?utf-8?q?Fe7LtdwP4UlUjqHrHs0Hbx+kLI3YKaSOH3vKq7FVUUHHfystydrKTXoN?=\n\t=?utf-8?q?0AQJpE+wuFQ/wC+AhrYE/sQYU5kRp8vlnDteIipr/P47900lQ/X2gdqS?=\n\t=?utf-8?q?Dzkfj0h9F8Fr1ojMyGwkceEmCXMeL29KfzMLau503zU/nllMSQXjLcbo?=\n\t=?utf-8?q?5cmzCN5mJ1o0vV59cvr/2ZLTEMe5Jg16PTGl1W6o1nqb/DGTpmUrcPf0?=\n\t=?utf-8?q?mfQ7ZW/E3txabpBkutK3WHRd+5uWAUob6hm1Ngd5WUiAHW17DYWDayHq?=\n\t=?utf-8?q?SgaRi6g7JAZPNyHfQ82ASmLiii3fkXlryE4dlzMqdWskrSQHR878Gyv1?=\n\t=?utf-8?q?Vx9sW3i2QUSISq608MtgRBfP7RnPeZde0PJjwXmXHClWtwgAZgsBtLvR?=\n\t=?utf-8?q?MkftKJdCSiYyT0Q7/HlUo0KjdEqq9Op5S7YzzuKjcR50SatCT+86H8pj?=\n\t=?utf-8?q?1WlmhgH42MksvRpx4bap/UyQ5bShrpFx1Cg0FVKQGtLLIr/QDB4M5sdo?=\n\t=?utf-8?q?DRu8WQqF2t5AnQX8v/3e5JTP11m+mL3WmK5La1ndAWqI7NTn9vBGCuDC?=\n\t=?utf-8?q?U3m2/5BZSY0ExksKr88wqaSzSV/+43g3+zvn61Ny9UOWUfPeaVvSK2Vj?=\n\t=?utf-8?q?4tgvIUWGEWUrfiqNfQjWF/P81XzSvE5fywa+84pSIfdGqNWwApplO6on?=\n\t=?utf-8?q?WBPmFpIu+f8z5HOZ1lDITl0vripkYbXyU6bNXq3zoz7SThcxCuLLf3E3?=\n\t=?utf-8?q?EbwCBbDe8kNUvbsev9GU5fjOhU2KVszWeZb16NVEmBNRKc/MWaKj53vB?=\n\t=?utf-8?q?q4TZSamdkSG28kptkdkwZEStxkw/dAoJS/WqB20VHMlLdCAphJ9b/wu2?=\n\t=?utf-8?q?ZboVjyovYXNi5WKmX5nGEERHPbu1kgYEDvSZcsQP8+OVbHs/kyfkopGg?=\n\t=?utf-8?q?9Y0/Vfk1JJJGOilwgaI0EXYr9j2tgSHmXKJCAG6zzv2JnPheCaXyeRoe?=\n\t=?utf-8?q?+/j+2u?=","1; HE1PR0502MB2970;\n\t6:aKti+Y9UM7bBr93KV25LXzl3//ibGgoPJXGSKipXUDPkslgCkdS1r6col4IOi6UqnVhDvr8Krzecm+UPNEpivKKocMmrEBkWlxUAOwV3S7nZ0+M+ENqOoNLRlZKHj+NXRNPnGowl2D98zec88y+LU1ph4pfFP01L/3V/xQhQVg9IBsCTLTAkr5CbvvnRnhMxOogbyhUU+P+LCwkmIBHLwfei4a1nBK36KNZiczsMomrvlZ6fqIN6H3u8kX2kR5nxWACiZbAwufLXBr0Nt5NibAvpUHe3u9IwTuNo1ZQz9Z+GZtN78PeF4DdBcUVlu9Ydd/JdT9Nu0u10bGqiw1YwAQ==;\n\t5:16pHyLAQrQhg4syzN3HzQa+/CQ/KY1JfpFzqu3jkckK/X4K4dfNZGLzmEMWDseEYTC6j+95NewqbleisJie+ne/fjgQRXY5OH2bh8dVWo7qVW3b7GSSQRihSHpVvzOPS8CbQRQKbPtYuPB9D/SOnCA==;\n\t24:OqCeN316/RKokxz4JfcIOvL/cfhLErcoZXKcw3lcazoP0f5nrmP1pVyAOzm8mvezQfeqQ2cELMuMETx20XmtSmujeeyJ3fSovOqTPhyMUMw=;\n\t7:VwYGkyXrsqL8xEXXwQPdOuG2bCgKQbrf0ze+Rqa/xlbuwQeRyRt2K0Ssb43Plba8d8XRHLOOS9R56O5Xj2j11+hsUqm3821S8pisSuH7XHKdTSuIg5ab2grVmqXpovVYb2k3bHQSbsCsFIyvBV/A7AkTKxDOWZILYkAphz4IMNNW+vmnGXmFZG11RizmdfRbt8/lqKWA1QuPMDngbYWMVe5TdCMvh9HcEnlT1M6K0Rk="],"X-MS-TrafficTypeDiagnostic":"HE1PR0502MB2970:","X-Exchange-Antispam-Report-Test":"UriScan:(788757137089);","X-Microsoft-Antispam-PRVS":"<HE1PR0502MB2970EDEFEA76E3F9C6B39147AC7A0@HE1PR0502MB2970.eurprd05.prod.outlook.com>","X-Exchange-Antispam-Report-CFA-Test":"BCL:0; PCL:0;\n\tRULEID:(100000700101)(100105000095)(100000701101)(100105300095)(100000702101)(100105100095)(6040450)(2401047)(5005006)(8121501046)(10201501046)(3002001)(100000703101)(100105400095)(93006095)(93001095)(6055026)(6041248)(20161123560025)(20161123555025)(20161123564025)(201703131423075)(201702281528075)(201703061421075)(201703061406153)(20161123562025)(20161123558100)(6072148)(201708071742011)(100000704101)(100105200095)(100000705101)(100105500095);\n\tSRVR:HE1PR0502MB2970; BCL:0; PCL:0;\n\tRULEID:(100000800101)(100110000095)(100000801101)(100110300095)(100000802101)(100110100095)(100000803101)(100110400095)(100000804101)(100110200095)(100000805101)(100110500095);\n\tSRVR:HE1PR0502MB2970; ","X-Forefront-PRVS":"04410E544A","X-Forefront-Antispam-Report":"SFV:NSPM;\n\tSFS:(10009020)(6069001)(6009001)(376002)(346002)(39860400002)(199003)(189002)(377454003)(24454002)(305945005)(36756003)(58126008)(8676002)(8936002)(229853002)(6666003)(53936002)(81166006)(81156014)(101416001)(76176999)(54356999)(6506006)(7736002)(2906002)(316002)(6486002)(16526017)(6246003)(106356001)(53946003)(31686004)(189998001)(6512007)(478600001)(33646002)(50986999)(53546010)(65806001)(65956001)(110136005)(105586002)(64126003)(25786009)(47776003)(66066001)(2950100002)(5660300001)(50466002)(97736004)(23676002)(65826007)(230700001)(83506001)(4326008)(6116002)(3846002)(86362001)(31696002)(68736007)(579004);\n\tDIR:OUT; SFP:1101; SCL:1; SRVR:HE1PR0502MB2970;\n\tH:localhost.localdomain; FPR:; SPF:None; PTR:InfoNoRecords;\n\tA:1; MX:1; LANG:en; ","Received-SPF":"None (protection.outlook.com: mellanox.com does not designate\n\tpermitted sender hosts)","SpamDiagnosticOutput":"1:99","SpamDiagnosticMetadata":"NSPM","X-OriginatorOrg":"Mellanox.com","X-MS-Exchange-CrossTenant-OriginalArrivalTime":"25 Sep 2017 05:55:45.8955\n\t(UTC)","X-MS-Exchange-CrossTenant-FromEntityHeader":"Hosted","X-MS-Exchange-CrossTenant-Id":"a652971c-7d2e-4d9b-a6a4-d149256f461b","X-MS-Exchange-Transport-CrossTenantHeadersStamped":"HE1PR0502MB2970","Sender":"netdev-owner@vger.kernel.org","Precedence":"bulk","List-ID":"<netdev.vger.kernel.org>","X-Mailing-List":"netdev@vger.kernel.org"}},{"id":1774639,"web_url":"http://patchwork.ozlabs.org/comment/1774639/","msgid":"<5ded28ba-0a0c-927c-5a5e-c0fc761ca49e@cumulusnetworks.com>","list_archive_url":null,"date":"2017-09-25T10:40:37","subject":"Re: [patch net-next v2 07/12] mlxsw: spectrum: Add the multicast\n\trouting offloading logic","submitter":{"id":66448,"url":"http://patchwork.ozlabs.org/api/people/66448/","name":"Nikolay Aleksandrov","email":"nikolay@cumulusnetworks.com"},"content":"On 24/09/17 20:22, Jiri Pirko wrote:\n> From: Yotam Gigi <yotamg@mellanox.com>\n> \n> Add the multicast router offloading logic, which is in charge of handling\n> the VIF and MFC notifications and translating it to the hardware logic API.\n> \n> The offloading logic has to overcome several obstacles in order to safely\n> comply with the kernel multicast router user API:\n>  - It must keep track of the mapping between VIFs to netdevices. The user\n>    can add an MFC cache entry pointing to a VIF, delete the VIF and add\n>    re-add it with a different netdevice. The offloading logic has to handle\n>    this in order to be compatible with the kernel logic.\n>  - It must keep track of the mapping between netdevices to spectrum RIFs,\n>    as the current hardware implementation assume having a RIF for every\n>    port in a multicast router.\n>  - It must handle routes pointing to pimreg device to be trapped to the\n>    kernel, as the packet should be delivered to userspace.\n>  - It must handle routes pointing tunnel VIFs. The current implementation\n>    does not support multicast forwarding to tunnels, thus routes that point\n>    to a tunnel should be trapped to the kernel.\n>  - It must be aware of proxy multicast routes, which include both (*,*)\n>    routes and duplicate routes. Currently proxy routes are not offloaded\n>    and trigger the abort mechanism: removal of all routes from hardware and\n>    triggering the traffic to go through the kernel.\n> \n> The multicast routing offloading logic also updates the counters of the\n> offloaded MFC routes in a periodic work.\n> \n> Signed-off-by: Yotam Gigi <yotamg@mellanox.com>\n> Reviewed-by: Ido Schimmel <idosch@mellanox.com>\n> Signed-off-by: Jiri Pirko <jiri@mellanox.com>\n> ---\n> v1->v2:\n>  - Update the lastuse MFC entry field too, in addition to packets an bytes.\n> ---\n>  drivers/net/ethernet/mellanox/mlxsw/Makefile      |    3 +-\n>  drivers/net/ethernet/mellanox/mlxsw/spectrum.h    |    1 +\n>  drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.c | 1014 +++++++++++++++++++++\n>  drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.h |  133 +++\n>  4 files changed, 1150 insertions(+), 1 deletion(-)\n>  create mode 100644 drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.c\n>  create mode 100644 drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.h\n> \n[snip]\n> +static void mlxsw_sp_mr_route_erase(struct mlxsw_sp_mr_table *mr_table,\n> +\t\t\t\t    struct mlxsw_sp_mr_route *mr_route)\n> +{\n> +\tstruct mlxsw_sp *mlxsw_sp = mr_table->mlxsw_sp;\n> +\tstruct mlxsw_sp_mr *mr = mlxsw_sp->mr;\n> +\n> +\tmr->mr_ops->route_destroy(mlxsw_sp, mr->priv, mr_route->route_priv);\n> +\tkfree(mr_route->route_priv);\n> +}\n> +\n> +static struct mlxsw_sp_mr_route *\n> +mlxsw_sp_mr_route4_create(struct mlxsw_sp_mr_table *mr_table,\n> +\t\t\t  struct mfc_cache *mfc)\n> +{\n> +\tstruct mlxsw_sp_mr_route_vif_entry *rve, *tmp;\n> +\tstruct mlxsw_sp_mr_route *mr_route;\n> +\tint err;\n> +\tint i;\n> +\n> +\t/* Allocate and init a new route and fill it with parameters */\n> +\tmr_route = kzalloc(sizeof(*mr_table), GFP_KERNEL);\n\nsizeof(*mr_table) ? Shouldn't you allocate sizeof struct mlsw_sp_mr_route (*mr_route) here ?\n\n> +\tif (!mr_route)\n> +\t\treturn ERR_PTR(-ENOMEM);\n> +\tINIT_LIST_HEAD(&mr_route->evif_list);\n> +\tmlxsw_sp_mr_route4_key(mr_table, &mr_route->key, mfc);\n> +\n> +\t/* Find min_mtu and link iVIF and eVIFs */\n> +\tmr_route->min_mtu = ETH_MAX_MTU;\n> +\tipmr_cache_hold(mfc);\n> +\tmr_route->mfc4 = mfc;\n> +\tmr_route->mr_table = mr_table;\n> +\tfor (i = 0; i < MAXVIFS; i++) {\n> +\t\tif (mfc->mfc_un.res.ttls[i] != 255) {\n> +\t\t\terr = mlxsw_sp_mr_route_evif_link(mr_route,\n> +\t\t\t\t\t\t\t  &mr_table->vifs[i]);\n> +\t\t\tif (err)\n> +\t\t\t\tgoto err;\n> +\t\t\tif (mr_table->vifs[i].dev &&\n> +\t\t\t    mr_table->vifs[i].dev->mtu < mr_route->min_mtu)\n> +\t\t\t\tmr_route->min_mtu = mr_table->vifs[i].dev->mtu;\n> +\t\t}\n> +\t}\n> +\tmlxsw_sp_mr_route_ivif_link(mr_route, &mr_table->vifs[mfc->mfc_parent]);\n> +\tif (err)\n> +\t\tgoto err;\n> +\n> +\tmr_route->route_action = mlxsw_sp_mr_route_action(mr_route);\n> +\treturn mr_route;\n> +err:\n> +\tipmr_cache_put(mfc);\n> +\tlist_for_each_entry_safe(rve, tmp, &mr_route->evif_list, route_node)\n> +\t\tmlxsw_sp_mr_route_evif_unlink(rve);\n> +\tkfree(mr_route);\n> +\treturn ERR_PTR(err);\n> +}\n> +\n> +static void mlxsw_sp_mr_route4_destroy(struct mlxsw_sp_mr_table *mr_table,\n> +\t\t\t\t       struct mlxsw_sp_mr_route *mr_route)\n> +{\n> +\tstruct mlxsw_sp_mr_route_vif_entry *rve, *tmp;\n> +\n> +\tmlxsw_sp_mr_route_ivif_unlink(mr_route);\n> +\tipmr_cache_put(mr_route->mfc4);\n> +\tlist_for_each_entry_safe(rve, tmp, &mr_route->evif_list, route_node)\n> +\t\tmlxsw_sp_mr_route_evif_unlink(rve);\n> +\tkfree(mr_route);\n> +}\n[snip]","headers":{"Return-Path":"<netdev-owner@vger.kernel.org>","X-Original-To":"patchwork-incoming@ozlabs.org","Delivered-To":"patchwork-incoming@ozlabs.org","Authentication-Results":["ozlabs.org;\n\tspf=none (mailfrom) smtp.mailfrom=vger.kernel.org\n\t(client-ip=209.132.180.67; helo=vger.kernel.org;\n\tenvelope-from=netdev-owner@vger.kernel.org;\n\treceiver=<UNKNOWN>)","ozlabs.org; dkim=pass (1024-bit key;\n\tunprotected) header.d=cumulusnetworks.com\n\theader.i=@cumulusnetworks.com header.b=\"QgkUP5R0\"; \n\tdkim-atps=neutral"],"Received":["from vger.kernel.org (vger.kernel.org [209.132.180.67])\n\tby ozlabs.org (Postfix) with ESMTP id 3y10ws0NpXz9tXF\n\tfor <patchwork-incoming@ozlabs.org>;\n\tMon, 25 Sep 2017 20:40:45 +1000 (AEST)","(majordomo@vger.kernel.org) by vger.kernel.org via listexpand\n\tid S934703AbdIYKkm (ORCPT <rfc822;patchwork-incoming@ozlabs.org>);\n\tMon, 25 Sep 2017 06:40:42 -0400","from mail-wm0-f47.google.com ([74.125.82.47]:49785 \"EHLO\n\tmail-wm0-f47.google.com\" rhost-flags-OK-OK-OK-OK) by vger.kernel.org\n\twith ESMTP id S934575AbdIYKkl (ORCPT\n\t<rfc822;netdev@vger.kernel.org>); Mon, 25 Sep 2017 06:40:41 -0400","by mail-wm0-f47.google.com with SMTP id r74so18105310wme.4\n\tfor <netdev@vger.kernel.org>; Mon, 25 Sep 2017 03:40:40 -0700 (PDT)","from [192.168.0.103] (46-10-142-144.ip.btc-net.bg. [46.10.142.144])\n\tby smtp.googlemail.com with ESMTPSA id\n\th56sm4433335ede.15.2017.09.25.03.40.38\n\t(version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128);\n\tMon, 25 Sep 2017 03:40:38 -0700 (PDT)"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=cumulusnetworks.com; s=google;\n\th=subject:to:references:cc:from:message-id:date:user-agent\n\t:mime-version:in-reply-to:content-transfer-encoding;\n\tbh=sW1n5T1S+Vup475vm1TJYd5ZYFNsjAceIUsTKk0GdlI=;\n\tb=QgkUP5R0kR/p1+HUQ7x+jw3b/lePPKlbDyUFM5gYfRUUHuBqeSn9KnbK92UrGT4Wmr\n\tqq1WZWco2BP7H01o2NQg2u/K09RUiIx1A3II6lJNDM4Vyw8W0/w06wrIZjiV+iGgjmJR\n\te60vKaF6ACIZaV1DyPOYboNjiVOlVMz47ogoU=","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:subject:to:references:cc:from:message-id:date\n\t:user-agent:mime-version:in-reply-to:content-transfer-encoding;\n\tbh=sW1n5T1S+Vup475vm1TJYd5ZYFNsjAceIUsTKk0GdlI=;\n\tb=PY0m6f2h32LcbsBnmCEVVpmgxidI+K9gWJ8hgyJ81UhjoBtTgGcq2swYkz6+KYh0yW\n\tdR5zdno6r8XL8JxBOPBZ9Ylo/oxiq0M6JzlonuJDf9yFQ6esLbQSrsDLsUGEU3XWWwwP\n\typwvQxwZ17bl+K6N0FjlsWtMiQHCWQj0E4icUnxDgzpSGJun36KeZqK7/cHfsFwAJkgz\n\tQw35udCJFSDrNtaKZjL66c+cCdnWYKrWAkrzCcDZCo666J8pKS4B+rheb6BHCDnizyCz\n\tVOOW6V5kqCPfWf737mazupHPCMXaF1d4qGnbFlnWmX+7Q1h3Hixe963jcFGTNrpGaN1C\n\tuKAw==","X-Gm-Message-State":"AHPjjUg3cKX1OJqBMPDLaxKV2FAuPXf6SUYrLq71FgyOYyrQL7rxe2Cs\n\t0T3zYk5DXOumcmLbE/O3qhRRrA==","X-Google-Smtp-Source":"AOwi7QBJKygEPLqDvcQ5MeeigM6jjFoN+wyW5r4w22tUIMwZ4JQ0jamRb7O+fxJ6oMRblYdzSETMYA==","X-Received":"by 10.80.149.180 with SMTP id w49mr13392833eda.301.1506336039771;\n\tMon, 25 Sep 2017 03:40:39 -0700 (PDT)","Subject":"Re: [patch net-next v2 07/12] mlxsw: spectrum: Add the multicast\n\trouting offloading logic","To":"Jiri Pirko <jiri@resnulli.us>, netdev@vger.kernel.org","References":"<20170924172212.10096-1-jiri@resnulli.us>\n\t<20170924172212.10096-8-jiri@resnulli.us>","Cc":"davem@davemloft.net, yotamg@mellanox.com, idosch@mellanox.com,\n\tmlxsw@mellanox.com, andrew@lunn.ch","From":"Nikolay Aleksandrov <nikolay@cumulusnetworks.com>","X-Enigmail-Draft-Status":"N1110","Message-ID":"<5ded28ba-0a0c-927c-5a5e-c0fc761ca49e@cumulusnetworks.com>","Date":"Mon, 25 Sep 2017 13:40:37 +0300","User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101\n\tIcedove/45.6.0","MIME-Version":"1.0","In-Reply-To":"<20170924172212.10096-8-jiri@resnulli.us>","Content-Type":"text/plain; charset=windows-1252","Content-Transfer-Encoding":"7bit","Sender":"netdev-owner@vger.kernel.org","Precedence":"bulk","List-ID":"<netdev.vger.kernel.org>","X-Mailing-List":"netdev@vger.kernel.org"}},{"id":1774646,"web_url":"http://patchwork.ozlabs.org/comment/1774646/","msgid":"<5e6ba4b6-66c0-b0c8-e786-c734fb9131c6@mellanox.com>","list_archive_url":null,"date":"2017-09-25T10:53:08","subject":"Re: [patch net-next v2 07/12] mlxsw: spectrum: Add the multicast\n\trouting offloading logic","submitter":{"id":69965,"url":"http://patchwork.ozlabs.org/api/people/69965/","name":"Yotam Gigi","email":"yotamg@mellanox.com"},"content":"On 09/25/2017 01:40 PM, Nikolay Aleksandrov wrote:\n> On 24/09/17 20:22, Jiri Pirko wrote:\n>> From: Yotam Gigi <yotamg@mellanox.com>\n>>\n>> Add the multicast router offloading logic, which is in charge of handling\n>> the VIF and MFC notifications and translating it to the hardware logic API.\n>>\n>> The offloading logic has to overcome several obstacles in order to safely\n>> comply with the kernel multicast router user API:\n>>  - It must keep track of the mapping between VIFs to netdevices. The user\n>>    can add an MFC cache entry pointing to a VIF, delete the VIF and add\n>>    re-add it with a different netdevice. The offloading logic has to handle\n>>    this in order to be compatible with the kernel logic.\n>>  - It must keep track of the mapping between netdevices to spectrum RIFs,\n>>    as the current hardware implementation assume having a RIF for every\n>>    port in a multicast router.\n>>  - It must handle routes pointing to pimreg device to be trapped to the\n>>    kernel, as the packet should be delivered to userspace.\n>>  - It must handle routes pointing tunnel VIFs. The current implementation\n>>    does not support multicast forwarding to tunnels, thus routes that point\n>>    to a tunnel should be trapped to the kernel.\n>>  - It must be aware of proxy multicast routes, which include both (*,*)\n>>    routes and duplicate routes. Currently proxy routes are not offloaded\n>>    and trigger the abort mechanism: removal of all routes from hardware and\n>>    triggering the traffic to go through the kernel.\n>>\n>> The multicast routing offloading logic also updates the counters of the\n>> offloaded MFC routes in a periodic work.\n>>\n>> Signed-off-by: Yotam Gigi <yotamg@mellanox.com>\n>> Reviewed-by: Ido Schimmel <idosch@mellanox.com>\n>> Signed-off-by: Jiri Pirko <jiri@mellanox.com>\n>> ---\n>> v1->v2:\n>>  - Update the lastuse MFC entry field too, in addition to packets an bytes.\n>> ---\n>>  drivers/net/ethernet/mellanox/mlxsw/Makefile      |    3 +-\n>>  drivers/net/ethernet/mellanox/mlxsw/spectrum.h    |    1 +\n>>  drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.c | 1014 +++++++++++++++++++++\n>>  drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.h |  133 +++\n>>  4 files changed, 1150 insertions(+), 1 deletion(-)\n>>  create mode 100644 drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.c\n>>  create mode 100644 drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.h\n>>\n> [snip]\n>> +static void mlxsw_sp_mr_route_erase(struct mlxsw_sp_mr_table *mr_table,\n>> +\t\t\t\t    struct mlxsw_sp_mr_route *mr_route)\n>> +{\n>> +\tstruct mlxsw_sp *mlxsw_sp = mr_table->mlxsw_sp;\n>> +\tstruct mlxsw_sp_mr *mr = mlxsw_sp->mr;\n>> +\n>> +\tmr->mr_ops->route_destroy(mlxsw_sp, mr->priv, mr_route->route_priv);\n>> +\tkfree(mr_route->route_priv);\n>> +}\n>> +\n>> +static struct mlxsw_sp_mr_route *\n>> +mlxsw_sp_mr_route4_create(struct mlxsw_sp_mr_table *mr_table,\n>> +\t\t\t  struct mfc_cache *mfc)\n>> +{\n>> +\tstruct mlxsw_sp_mr_route_vif_entry *rve, *tmp;\n>> +\tstruct mlxsw_sp_mr_route *mr_route;\n>> +\tint err;\n>> +\tint i;\n>> +\n>> +\t/* Allocate and init a new route and fill it with parameters */\n>> +\tmr_route = kzalloc(sizeof(*mr_table), GFP_KERNEL);\n> sizeof(*mr_table) ? Shouldn't you allocate sizeof struct mlsw_sp_mr_route (*mr_route) here ?\n>\n\nSeems like you are right. Because of the fact that sizeof(*mr_table) is much\nbigger than sizeof(*mr_route), all our tests did not notice it.\n\nThanks for that!\n\n>> +\tif (!mr_route)\n>> +\t\treturn ERR_PTR(-ENOMEM);\n>> +\tINIT_LIST_HEAD(&mr_route->evif_list);\n>> +\tmlxsw_sp_mr_route4_key(mr_table, &mr_route->key, mfc);\n>> +\n>> +\t/* Find min_mtu and link iVIF and eVIFs */\n>> +\tmr_route->min_mtu = ETH_MAX_MTU;\n>> +\tipmr_cache_hold(mfc);\n>> +\tmr_route->mfc4 = mfc;\n>> +\tmr_route->mr_table = mr_table;\n>> +\tfor (i = 0; i < MAXVIFS; i++) {\n>> +\t\tif (mfc->mfc_un.res.ttls[i] != 255) {\n>> +\t\t\terr = mlxsw_sp_mr_route_evif_link(mr_route,\n>> +\t\t\t\t\t\t\t  &mr_table->vifs[i]);\n>> +\t\t\tif (err)\n>> +\t\t\t\tgoto err;\n>> +\t\t\tif (mr_table->vifs[i].dev &&\n>> +\t\t\t    mr_table->vifs[i].dev->mtu < mr_route->min_mtu)\n>> +\t\t\t\tmr_route->min_mtu = mr_table->vifs[i].dev->mtu;\n>> +\t\t}\n>> +\t}\n>> +\tmlxsw_sp_mr_route_ivif_link(mr_route, &mr_table->vifs[mfc->mfc_parent]);\n>> +\tif (err)\n>> +\t\tgoto err;\n>> +\n>> +\tmr_route->route_action = mlxsw_sp_mr_route_action(mr_route);\n>> +\treturn mr_route;\n>> +err:\n>> +\tipmr_cache_put(mfc);\n>> +\tlist_for_each_entry_safe(rve, tmp, &mr_route->evif_list, route_node)\n>> +\t\tmlxsw_sp_mr_route_evif_unlink(rve);\n>> +\tkfree(mr_route);\n>> +\treturn ERR_PTR(err);\n>> +}\n>> +\n>> +static void mlxsw_sp_mr_route4_destroy(struct mlxsw_sp_mr_table *mr_table,\n>> +\t\t\t\t       struct mlxsw_sp_mr_route *mr_route)\n>> +{\n>> +\tstruct mlxsw_sp_mr_route_vif_entry *rve, *tmp;\n>> +\n>> +\tmlxsw_sp_mr_route_ivif_unlink(mr_route);\n>> +\tipmr_cache_put(mr_route->mfc4);\n>> +\tlist_for_each_entry_safe(rve, tmp, &mr_route->evif_list, route_node)\n>> +\t\tmlxsw_sp_mr_route_evif_unlink(rve);\n>> +\tkfree(mr_route);\n>> +}\n> [snip]\n>","headers":{"Return-Path":"<netdev-owner@vger.kernel.org>","X-Original-To":"patchwork-incoming@ozlabs.org","Delivered-To":"patchwork-incoming@ozlabs.org","Authentication-Results":["ozlabs.org;\n\tspf=none (mailfrom) smtp.mailfrom=vger.kernel.org\n\t(client-ip=209.132.180.67; helo=vger.kernel.org;\n\tenvelope-from=netdev-owner@vger.kernel.org;\n\treceiver=<UNKNOWN>)","ozlabs.org; dkim=pass (1024-bit key;\n\tunprotected) header.d=Mellanox.com header.i=@Mellanox.com\n\theader.b=\"l5vMZWAu\"; dkim-atps=neutral","spf=none (sender IP is )\n\tsmtp.mailfrom=yotamg@mellanox.com; "],"Received":["from vger.kernel.org (vger.kernel.org [209.132.180.67])\n\tby ozlabs.org (Postfix) with ESMTP id 3y11CQ4fJRz9t3R\n\tfor <patchwork-incoming@ozlabs.org>;\n\tMon, 25 Sep 2017 20:53:22 +1000 (AEST)","(majordomo@vger.kernel.org) by vger.kernel.org via listexpand\n\tid S934822AbdIYKxU (ORCPT <rfc822;patchwork-incoming@ozlabs.org>);\n\tMon, 25 Sep 2017 06:53:20 -0400","from mail-ve1eur01on0064.outbound.protection.outlook.com\n\t([104.47.1.64]:11502\n\t\"EHLO EUR01-VE1-obe.outbound.protection.outlook.com\"\n\trhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP\n\tid S934668AbdIYKxS (ORCPT <rfc822;netdev@vger.kernel.org>);\n\tMon, 25 Sep 2017 06:53:18 -0400","from localhost.localdomain (79.179.78.224) by\n\tAM5PR0502MB2961.eurprd05.prod.outlook.com (2603:10a6:203:96::11) with\n\tMicrosoft SMTP Server (version=TLS1_2,\n\tcipher=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384_P256) id 15.20.77.7;\n\tMon, 25 Sep 2017 10:53:15 +0000"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=Mellanox.com;\n\ts=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version;\n\tbh=zMEbWUa0Y3D+JsAcpXBz/rdQvr/oKz9wubXJnI9Dei8=;\n\tb=l5vMZWAuw0CbBAnMaizw/nQ6pgM3uMT6VDjg9QXrrmhylBj85Y8jIoFK4O/nJG3iKcGz7u1pJhZqMlcRskVBGujN2DNIhsBkX/GgQ4Bo0S/1AMdw8Jm/Wm0iWOGb576pB1UA5tXmcYLHNTDGg1o+toiRLQvxBnL7ZnfS45FoBQI=","Subject":"Re: [patch net-next v2 07/12] mlxsw: spectrum: Add the multicast\n\trouting offloading logic","To":"Nikolay Aleksandrov <nikolay@cumulusnetworks.com>,\n\tJiri Pirko <jiri@resnulli.us>, netdev@vger.kernel.org","Cc":"davem@davemloft.net, idosch@mellanox.com, mlxsw@mellanox.com,\n\tandrew@lunn.ch","References":"<20170924172212.10096-1-jiri@resnulli.us>\n\t<20170924172212.10096-8-jiri@resnulli.us>\n\t<5ded28ba-0a0c-927c-5a5e-c0fc761ca49e@cumulusnetworks.com>","From":"Yotam Gigi <yotamg@mellanox.com>","Message-ID":"<5e6ba4b6-66c0-b0c8-e786-c734fb9131c6@mellanox.com>","Date":"Mon, 25 Sep 2017 13:53:08 +0300","User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101\n\tThunderbird/52.2.1","MIME-Version":"1.0","In-Reply-To":"<5ded28ba-0a0c-927c-5a5e-c0fc761ca49e@cumulusnetworks.com>","Content-Type":"text/plain; charset=windows-1252","Content-Transfer-Encoding":"7bit","Content-Language":"en-US","X-Originating-IP":"[79.179.78.224]","X-ClientProxiedBy":"DB6PR02CA0012.eurprd02.prod.outlook.com\n\t(2603:10a6:6:15::25) To AM5PR0502MB2961.eurprd05.prod.outlook.com\n\t(2603:10a6:203:96::11)","X-MS-PublicTrafficType":"Email","X-MS-Office365-Filtering-Correlation-Id":"6638196e-d0b0-4e6a-0b05-08d50403a021","X-MS-Office365-Filtering-HT":"Tenant","X-Microsoft-Antispam":"UriScan:; BCL:0; PCL:0;\n\tRULEID:(22001)(2017030254152)(48565401081)(2017052603199)(201703131423075)(201703031133081)(201702281549075);\n\tSRVR:AM5PR0502MB2961; ","X-Microsoft-Exchange-Diagnostics":["1; AM5PR0502MB2961;\n\t3:mgiMFeFwIaODgv1esIfSvPyu7d3eaIvHtMsUfgFxqKp975pDr/TtmrMD0lX44fchbbfUd3f+rEandAOxpvgjV4kbXSTDfC4/+EX/lTnKd8xuji71wAszHILqoN903NR+9sEVNxecQ8ajaEf1ahNFfgMZWiPTC2VqUQGMUeVRozcW9R31Spwa0VeQ+R4QEL6GdebAQArp0tO6UWrhqsaavCG2EfpXncpxewm3nPL2AArhp7H2tXw+uFU2z2HX9K5f;\n\t25:214kBuYk8+2G2+fVmgaTq0ZmK6R4DrLkK1sEZuXi2r1LurYt6VFGxhWU0Th8MIcvvmZ+XYCVgeR1SwoHrvE6xcMSwQwYLiRlAzG+BpKfKjgybEOc57skHOFrX+fqhFE4R2ltPJ1e8Zyq9xMEwQTVOlKzA5E4U0KfXfCI+vSved4k1ExwgGmi2xUxb8fh4AofSWxYZiAsdvFfh/v8MxyTXDY33taij10XKc4OgVIL3FADM1JUxsfTDKrQuRbqqqm3LigyAguH4RT+sC8RsNYxEhkaUMi7aCam2Qs2k5ib9lo0KGLIKuWFIXT3havXgzSJ8TGi2OTdX8TNwVzYK2WZDw==;\n\t31:TqxCEFAI3dQ9s8lnB4jX7lhWguva8+kRRSNTvGWz2P64ZLK7IhmutfLMZCsyO6+reQiw6X/V1RqfMFUi/dbMu80eaweuS8Wi4FNfgR6u9TYkK6YToLWI80iQd/FeWI9tFMSvRukTqgi/LLXkuI2tdsrEBQcHwLBUv6FDWwQWDT61XV7XHX9HZyn8uVbFBuTfrTehNt+WbokcZHkJNrbLGvW6g7XH4ezp/KQuOM+6c/I=","1; AM5PR0502MB2961;\n\t20:MxX8Rh3N3j4uEvfIops7v+33R2lmHa38NBU5FdZLm3IegPjsDAkiQL2bBvp58kskIgoUoq9zu2vk3UrD+IGg89+Exewm+Ef4ZfOg+yF740IFNdKSpV4cZ9DhEFHTYjGrW/uQfpDpN/SQZAUk1hZImLj8SkXJOfKl7MSBed8P/YvrOszH5m9hGMmYmPqyrcf8vErekJljpWsYNrC7zBQO/acRnfyxMSvq6kN5iTp6YOvBfxO8rwdSSOtG1hECIt/EC0MbTnZfTEdWZI8fYoIELllQtqfOkFKmuoBbDdcw4Vx+2lz+xfByhp4MnfNodopNg74No9fbUomMNmmjoRdI0EJPAL9SVWKt+avEwCjofx0DEbnHZfdUuShR+4OgwB9n2gxAZvn13m7/FbxsnWlCs9z1LXhu70AtX632p6uU0LaoWjdFKVvB5OZTFk11xhN6ZSvMlNJ8Pnyu9IRjkp+l+q9kTxcMD3rY5yJm/7dvt+wFndzZ5phFJkumPLYtuvBO;\n\t4:thawJpxd5xo3zySsZbaTAoN3GLoJcM6v8892OYbgk4KFwSu6vNnsFMg4tCYVOP6GRWYNTT8tuOh+STgu8zZjsq4KTrO1qoqkuZG0/NZYBYHqcqIbXp2wHonWSmY1Guoun2eR6ZwwsPEm1vTnXALAY5E/0Em6fEqyhkKXtMNsX8owdZWHtg5Hi7Dzd5Jn8fHLQ1C6/UL4vQ5bBiSTnfpZe+y8XFJm9bv/CvzyX7KJoec4/jag+6pXafXHNtMVcBpx","=?windows-1252?q?1=3BAM5PR0502MB2961=3B?=\n\t=?windows-1252?q?23=3AV+Sm8AV0u8c11aHbULyNxKLdDfx29TPUNdrvMdYVXxX+?=\n\t=?windows-1252?q?bow6uFjRge5Rhq9qXOEAroaLAJ8i1yWrnXkabTRmPNWjhWia7?=\n\t=?windows-1252?q?nEWgNgjmyeaQAaePCV0bItg3q1G5dLy40wgZZAmCxjH76Tnf3?=\n\t=?windows-1252?q?vo9uerx9W+5ocN5fmZwTdqNZipZ+JTM43E+nJfdbRKKaJgWav?=\n\t=?windows-1252?q?Ablu8Lc4K0kQYDqa+loVxXo640F7/x2C5vVqbtBqPkvCR9V1T?=\n\t=?windows-1252?q?jioSJkMu750LCJrW3JvHfwqmKD76HZJM5gGS/dNPa7kg4PPyQ?=\n\t=?windows-1252?q?ce/0jiWmevxSa4B5+EzNfMDcHxX2vBMu2W5+WHbiR19ttc42q?=\n\t=?windows-1252?q?9W1nQfgI01T+53gEmdV/bC1dYNTUHWucxQ6YPgAY2hg5M0OZT?=\n\t=?windows-1252?q?5ZO87rqip/lrnpjG4mg54NLx23Aby9AJBlYl4zGtpl/+UQNvp?=\n\t=?windows-1252?q?qzN1qzsxsCcGc3cHIfmg7lTWMH0TyI1aFgZ//uwKdYlLLKXO5?=\n\t=?windows-1252?q?bJJgzpNqVVqrNQveGv+mcfeCv6u6yjpDeCJ1wNS3he3LkUT19?=\n\t=?windows-1252?q?wYYaa/rBU4vHSG8nR/fUKrK5T+JeuIcgoTEAcJBoIE+Krbi21?=\n\t=?windows-1252?q?dsaX8nHHZQRPfhNMzuM2f8upWhPC24PovlBA3F9pBJSbZ4zn0?=\n\t=?windows-1252?q?/EtMDgi0/KDA/UV0WpXCgUiHgvTqkHnk6BtBU7+HvJ24ouz1L?=\n\t=?windows-1252?q?U/6FJO+om7n3WZxJY7XHzKs7xOZHBwHGElvebri7e3huLUxee?=\n\t=?windows-1252?q?XJs+dQtcsVosL1kwyk8ka2SoFTqespiTc8TBJh4doxxrtFz60?=\n\t=?windows-1252?q?aidPqbUms3O9CRERfO26MqT5RX6zWt5IyMbK1xFXnrCXCOcs9?=\n\t=?windows-1252?q?Tlsy5G6jZFP6yLynbcwgsmwsHoyo1zKjDr5h+00cWVXKTpGBu?=\n\t=?windows-1252?q?QeMAAt3bDlB1tK/4yRrxjCnLrbfKiwQsz7RNaBelLV4ch94jt?=\n\t=?windows-1252?q?2RXzq9yeyTscE8tDOs2eA9D52aNYnvY6G737kMqNjz2ihoLK/?=\n\t=?windows-1252?q?ElRYBYpzaYhe/hBuDP/aGZhrn1vQdXrYkKyaXgluk5hnbsk8l?=\n\t=?windows-1252?q?EqHQrRnU8hmv3WOyMbfP1++TaVwFhXAiaG7ogJBrUylDFGP9P?=\n\t=?windows-1252?q?EPBSczYCA4rwPFrLmuLVHMI5sb1sNzpnHxw9uNPPWvbVXYPoS?=\n\t=?windows-1252?q?j8JuiNMqD8J1cOjyTcSrbkHHBrnsHtNF6/0oUFeaQ6yIP7u0N?=\n\t=?windows-1252?q?qrBCww+hYDzUlOpxLU3RrKcdRLEcnWaZqkzQqEG5pPZtLeg+a?=\n\t=?windows-1252?q?3iJemPjk9ZwvPEy6hwwwkDcGJtl7wpcWk352p/5HaX7DEMy9g?=\n\t=?windows-1252?q?+pU8X9I3R0NrTQkmCHUaoLuHRAy9OUFwMbYZ26OExQNxFvNmJ?=\n\t=?windows-1252?q?pz7qYbK0wekTXEa7Mqm9sXIfFk3sxqKYbQcopZ6jtNRVVADw?=\n\t=?windows-1252?q?=3D=3D?=","1; AM5PR0502MB2961;\n\t6:QscE3dm5odr+MATUoPP+q0/VspuQnUk+r0EGWeeyvQYVP2xgGX8t3euYns7ZgYE61ix8I2ZcKwQvTNfIoYWShT16vkQglfSIn4zIyBDZRt1F+2ehnQRfZEMmSlpoPEi+c1fc3BSQ+leZaDl9wOx7+kNoP9sFktPK/743tqQcOFIbeu9e9cRts9xwFyGyYi2DNnVTO46oe0HWpoRWTUzMGaLrkYjmhkVwdZR0QvP7fzp9GLr7bAJN7RLXyU2MBvHLm1t7/WzmK4DLQ/m8LZu2h8v+iOPF7Z4s55FZYGqUyGJ4bJhUSnxUvRNHmw67r+JRMt1xwcU4jC6WczYxN+1O5w==;\n\t5:r+BFHj/GkGN+VCS4rs6QRNYQh6OqZdgdUzDlHx7xQozorZJvsNwGs3+ccgU7XARBsub6RNOyVrYD+yWzUWhkWNRrsm7bsPL3QzY3ZMrqMcUNpi7fBCvPzIFNUVsgH5V+HduNtHxQgUecO0MYRDjhFA==;\n\t24:LS4VWdArbgIRsrZxzfNhW5AsfqSP1ftsI8U5bTPG/lbZke3k0qe2r6yZo9TORfZqEhB6fJHqZNefSKZXrQXxL9cQiDmr7mcLDFvr2psDqaA=;\n\t7:Hwc+VEtwj/cRQjtjkN4YtZ164qTnaDXO6X/teHqUTYlXWEz3N2Au1AT9RJDLL5C3+ecBFzp2s6RoAq6rQqjH9cSHaze8+JhOZIictVwGTCsnQf6AvSeQATpjOIiX4gpRKZCRSlySGtOkPbCmGuM0l7erV5Y54X+sIWnR4UgkXKfADvC01rhxG/UIbbcB5MwZ9Xnp99ErDBcS8VS+e+d1YnB9VVIBT0H0RlQvVSny+PY="],"X-MS-TrafficTypeDiagnostic":"AM5PR0502MB2961:","X-Exchange-Antispam-Report-Test":"UriScan:;","X-Microsoft-Antispam-PRVS":"<AM5PR0502MB296170A03B565DB4B827F55BAC7A0@AM5PR0502MB2961.eurprd05.prod.outlook.com>","X-Exchange-Antispam-Report-CFA-Test":"BCL:0; PCL:0;\n\tRULEID:(100000700101)(100105000095)(100000701101)(100105300095)(100000702101)(100105100095)(6040450)(2401047)(8121501046)(5005006)(3002001)(10201501046)(100000703101)(100105400095)(93006095)(93001095)(6055026)(6041248)(20161123558100)(20161123555025)(20161123564025)(20161123560025)(20161123562025)(201703131423075)(201702281528075)(201703061421075)(201703061406153)(6072148)(201708071742011)(100000704101)(100105200095)(100000705101)(100105500095);\n\tSRVR:AM5PR0502MB2961; BCL:0; PCL:0;\n\tRULEID:(100000800101)(100110000095)(100000801101)(100110300095)(100000802101)(100110100095)(100000803101)(100110400095)(100000804101)(100110200095)(100000805101)(100110500095);\n\tSRVR:AM5PR0502MB2961; ","X-Forefront-PRVS":"04410E544A","X-Forefront-Antispam-Report":"SFV:NSPM;\n\tSFS:(10009020)(6069001)(6009001)(346002)(376002)(39860400002)(377454003)(199003)(24454002)(189002)(6666003)(83506001)(65826007)(66066001)(6116002)(8676002)(6512007)(53936002)(64126003)(16526017)(81166006)(47776003)(65956001)(5660300001)(6246003)(54356999)(76176999)(50986999)(2906002)(81156014)(2950100002)(31686004)(50466002)(97736004)(65806001)(106356001)(229853002)(8936002)(4326008)(36756003)(105586002)(53546010)(101416001)(110136005)(189998001)(316002)(7736002)(6506006)(25786009)(6486002)(68736007)(478600001)(305945005)(31696002)(58126008)(23746002)(230700001)(86362001)(33646002)(3846002);\n\tDIR:OUT; SFP:1101; SCL:1; SRVR:AM5PR0502MB2961;\n\tH:localhost.localdomain; FPR:; SPF:None; PTR:InfoNoRecords;\n\tA:1; MX:1; LANG:en; ","Received-SPF":"None (protection.outlook.com: mellanox.com does not designate\n\tpermitted sender hosts)","SpamDiagnosticOutput":"1:99","SpamDiagnosticMetadata":"NSPM","X-OriginatorOrg":"Mellanox.com","X-MS-Exchange-CrossTenant-OriginalArrivalTime":"25 Sep 2017 10:53:15.0257\n\t(UTC)","X-MS-Exchange-CrossTenant-FromEntityHeader":"Hosted","X-MS-Exchange-CrossTenant-Id":"a652971c-7d2e-4d9b-a6a4-d149256f461b","X-MS-Exchange-Transport-CrossTenantHeadersStamped":"AM5PR0502MB2961","Sender":"netdev-owner@vger.kernel.org","Precedence":"bulk","List-ID":"<netdev.vger.kernel.org>","X-Mailing-List":"netdev@vger.kernel.org"}}]