Commit 336f2c03 authored by David S. Miller's avatar David S. Miller

Merge branch 'mlxsw-Offload-IPv6-multicast-routes'

Ido Schimmel says:

====================
mlxsw: Offload IPv6 multicast routes

Yuval says:

The series is intended to allow offloading IPv6 multicast routes
and is split into two parts:

  - First half of the patches continue extending ip6mr [& refactor ipmr]
    with missing bits necessary for the offloading - fib-notifications,
    mfc refcounting and default rule identification.

  - Second half of the patches extend functionality inside mlxsw,
    beginning with extending lower-parts to support IPv6 mroutes
    to host and later extending the router/mr internal APIs within
    the driver to accommodate support in ipv6 configurations.
    Lastly it adds support in the RTNL_FAMILY_IP6MR notifications,
    allowing driver to react and offload related routes.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 02a21de9 6a170d32
......@@ -4225,6 +4225,12 @@ MLXSW_ITEM32(reg, ritr, ipv6, 0x00, 28, 1);
*/
MLXSW_ITEM32(reg, ritr, ipv4_mc, 0x00, 27, 1);
/* reg_ritr_ipv6_mc
* IPv6 multicast routing enable.
* Access: RW
*/
MLXSW_ITEM32(reg, ritr, ipv6_mc, 0x00, 26, 1);
enum mlxsw_reg_ritr_if_type {
/* VLAN interface. */
MLXSW_REG_RITR_VLAN_IF,
......@@ -4290,6 +4296,14 @@ MLXSW_ITEM32(reg, ritr, ipv6_fe, 0x04, 28, 1);
*/
MLXSW_ITEM32(reg, ritr, ipv4_mc_fe, 0x04, 27, 1);
/* reg_ritr_ipv6_mc_fe
* IPv6 Multicast Forwarding Enable.
* When disabled, forwarding is blocked but local traffic (traps and IP to me)
* will be enabled.
* Access: RW
*/
MLXSW_ITEM32(reg, ritr, ipv6_mc_fe, 0x04, 26, 1);
/* reg_ritr_lb_en
* Loop-back filter enable for unicast packets.
* If the flag is set then loop-back filter for unicast packets is
......@@ -4513,12 +4527,14 @@ static inline void mlxsw_reg_ritr_pack(char *payload, bool enable,
mlxsw_reg_ritr_ipv4_set(payload, 1);
mlxsw_reg_ritr_ipv6_set(payload, 1);
mlxsw_reg_ritr_ipv4_mc_set(payload, 1);
mlxsw_reg_ritr_ipv6_mc_set(payload, 1);
mlxsw_reg_ritr_type_set(payload, type);
mlxsw_reg_ritr_op_set(payload, op);
mlxsw_reg_ritr_rif_set(payload, rif);
mlxsw_reg_ritr_ipv4_fe_set(payload, 1);
mlxsw_reg_ritr_ipv6_fe_set(payload, 1);
mlxsw_reg_ritr_ipv4_mc_fe_set(payload, 1);
mlxsw_reg_ritr_ipv6_mc_fe_set(payload, 1);
mlxsw_reg_ritr_lb_en_set(payload, 1);
mlxsw_reg_ritr_virtual_router_set(payload, vr_id);
mlxsw_reg_ritr_mtu_set(payload, mtu);
......@@ -6302,30 +6318,34 @@ MLXSW_ITEM32(reg, rmft2, irif_mask, 0x08, 24, 1);
*/
MLXSW_ITEM32(reg, rmft2, irif, 0x08, 0, 16);
/* reg_rmft2_dip4
* Destination IPv4 address
/* reg_rmft2_dip{4,6}
* Destination IPv4/6 address
* Access: RW
*/
MLXSW_ITEM_BUF(reg, rmft2, dip6, 0x10, 16);
MLXSW_ITEM32(reg, rmft2, dip4, 0x1C, 0, 32);
/* reg_rmft2_dip4_mask
/* reg_rmft2_dip{4,6}_mask
* A bit that is set directs the TCAM to compare the corresponding bit in key. A
* bit that is clear directs the TCAM to ignore the corresponding bit in key.
* Access: RW
*/
MLXSW_ITEM_BUF(reg, rmft2, dip6_mask, 0x20, 16);
MLXSW_ITEM32(reg, rmft2, dip4_mask, 0x2C, 0, 32);
/* reg_rmft2_sip4
* Source IPv4 address
/* reg_rmft2_sip{4,6}
* Source IPv4/6 address
* Access: RW
*/
MLXSW_ITEM_BUF(reg, rmft2, sip6, 0x30, 16);
MLXSW_ITEM32(reg, rmft2, sip4, 0x3C, 0, 32);
/* reg_rmft2_sip4_mask
/* reg_rmft2_sip{4,6}_mask
* A bit that is set directs the TCAM to compare the corresponding bit in key. A
* bit that is clear directs the TCAM to ignore the corresponding bit in key.
* Access: RW
*/
MLXSW_ITEM_BUF(reg, rmft2, sip6_mask, 0x40, 16);
MLXSW_ITEM32(reg, rmft2, sip4_mask, 0x4C, 0, 32);
/* reg_rmft2_flexible_action_set
......@@ -6343,26 +6363,52 @@ MLXSW_ITEM_BUF(reg, rmft2, flexible_action_set, 0x80,
MLXSW_REG_FLEX_ACTION_SET_LEN);
static inline void
mlxsw_reg_rmft2_ipv4_pack(char *payload, bool v, u16 offset, u16 virtual_router,
mlxsw_reg_rmft2_common_pack(char *payload, bool v, u16 offset,
u16 virtual_router,
enum mlxsw_reg_rmft2_irif_mask irif_mask, u16 irif,
u32 dip4, u32 dip4_mask, u32 sip4, u32 sip4_mask,
const char *flexible_action_set)
const char *flex_action_set)
{
MLXSW_REG_ZERO(rmft2, payload);
mlxsw_reg_rmft2_v_set(payload, v);
mlxsw_reg_rmft2_type_set(payload, MLXSW_REG_RMFT2_TYPE_IPV4);
mlxsw_reg_rmft2_op_set(payload, MLXSW_REG_RMFT2_OP_READ_WRITE);
mlxsw_reg_rmft2_offset_set(payload, offset);
mlxsw_reg_rmft2_virtual_router_set(payload, virtual_router);
mlxsw_reg_rmft2_irif_mask_set(payload, irif_mask);
mlxsw_reg_rmft2_irif_set(payload, irif);
if (flex_action_set)
mlxsw_reg_rmft2_flexible_action_set_memcpy_to(payload,
flex_action_set);
}
static inline void
mlxsw_reg_rmft2_ipv4_pack(char *payload, bool v, u16 offset, u16 virtual_router,
enum mlxsw_reg_rmft2_irif_mask irif_mask, u16 irif,
u32 dip4, u32 dip4_mask, u32 sip4, u32 sip4_mask,
const char *flexible_action_set)
{
mlxsw_reg_rmft2_common_pack(payload, v, offset, virtual_router,
irif_mask, irif, flexible_action_set);
mlxsw_reg_rmft2_type_set(payload, MLXSW_REG_RMFT2_TYPE_IPV4);
mlxsw_reg_rmft2_dip4_set(payload, dip4);
mlxsw_reg_rmft2_dip4_mask_set(payload, dip4_mask);
mlxsw_reg_rmft2_sip4_set(payload, sip4);
mlxsw_reg_rmft2_sip4_mask_set(payload, sip4_mask);
if (flexible_action_set)
mlxsw_reg_rmft2_flexible_action_set_memcpy_to(payload,
flexible_action_set);
}
static inline void
mlxsw_reg_rmft2_ipv6_pack(char *payload, bool v, u16 offset, u16 virtual_router,
enum mlxsw_reg_rmft2_irif_mask irif_mask, u16 irif,
struct in6_addr dip6, struct in6_addr dip6_mask,
struct in6_addr sip6, struct in6_addr sip6_mask,
const char *flexible_action_set)
{
mlxsw_reg_rmft2_common_pack(payload, v, offset, virtual_router,
irif_mask, irif, flexible_action_set);
mlxsw_reg_rmft2_type_set(payload, MLXSW_REG_RMFT2_TYPE_IPV6);
mlxsw_reg_rmft2_dip6_memcpy_to(payload, (void *)&dip6);
mlxsw_reg_rmft2_dip6_mask_memcpy_to(payload, (void *)&dip6_mask);
mlxsw_reg_rmft2_sip6_memcpy_to(payload, (void *)&sip6);
mlxsw_reg_rmft2_sip6_mask_memcpy_to(payload, (void *)&sip6_mask);
}
/* MFCR - Management Fan Control Register
......
......@@ -3380,6 +3380,7 @@ static const struct mlxsw_listener mlxsw_sp_listener[] = {
MLXSW_SP_RXL_NO_MARK(ACL0, TRAP_TO_CPU, IP2ME, false),
/* Multicast Router Traps */
MLXSW_SP_RXL_MARK(IPV4_PIM, TRAP_TO_CPU, PIM, false),
MLXSW_SP_RXL_MARK(IPV6_PIM, TRAP_TO_CPU, PIM, false),
MLXSW_SP_RXL_MARK(RPF, TRAP_TO_CPU, RPF, false),
MLXSW_SP_RXL_MARK(ACL1, TRAP_TO_CPU, MULTICAST, false),
MLXSW_SP_RXL_MR_MARK(ACL2, TRAP_TO_CPU, MULTICAST, false),
......
......@@ -33,6 +33,7 @@
*/
#include <linux/rhashtable.h>
#include <net/ipv6.h>
#include "spectrum_mr.h"
#include "spectrum_router.h"
......@@ -47,6 +48,11 @@ struct mlxsw_sp_mr {
/* priv has to be always the last item */
};
struct mlxsw_sp_mr_vif;
struct mlxsw_sp_mr_vif_ops {
bool (*is_regular)(const struct mlxsw_sp_mr_vif *vif);
};
struct mlxsw_sp_mr_vif {
struct net_device *dev;
const struct mlxsw_sp_rif *rif;
......@@ -61,6 +67,9 @@ struct mlxsw_sp_mr_vif {
* instance is used as an ingress VIF
*/
struct list_head route_ivif_list;
/* Protocol specific operations for a VIF */
const struct mlxsw_sp_mr_vif_ops *ops;
};
struct mlxsw_sp_mr_route_vif_entry {
......@@ -70,6 +79,17 @@ struct mlxsw_sp_mr_route_vif_entry {
struct mlxsw_sp_mr_route *mr_route;
};
struct mlxsw_sp_mr_table;
struct mlxsw_sp_mr_table_ops {
bool (*is_route_valid)(const struct mlxsw_sp_mr_table *mr_table,
const struct mr_mfc *mfc);
void (*key_create)(struct mlxsw_sp_mr_table *mr_table,
struct mlxsw_sp_mr_route_key *key,
struct mr_mfc *mfc);
bool (*is_route_starg)(const struct mlxsw_sp_mr_table *mr_table,
const struct mlxsw_sp_mr_route *mr_route);
};
struct mlxsw_sp_mr_table {
struct list_head node;
enum mlxsw_sp_l3proto proto;
......@@ -78,6 +98,7 @@ struct mlxsw_sp_mr_table {
struct mlxsw_sp_mr_vif vifs[MAXVIFS];
struct list_head route_list;
struct rhashtable route_ht;
const struct mlxsw_sp_mr_table_ops *ops;
char catchall_route_priv[0];
/* catchall_route_priv has to be always the last item */
};
......@@ -88,7 +109,7 @@ struct mlxsw_sp_mr_route {
struct mlxsw_sp_mr_route_key key;
enum mlxsw_sp_mr_route_action route_action;
u16 min_mtu;
struct mfc_cache *mfc4;
struct mr_mfc *mfc;
void *route_priv;
const struct mlxsw_sp_mr_table *mr_table;
/* A list of route_vif_entry structs that point to the egress VIFs */
......@@ -104,14 +125,9 @@ static const struct rhashtable_params mlxsw_sp_mr_route_ht_params = {
.automatic_shrinking = true,
};
static bool mlxsw_sp_mr_vif_regular(const struct mlxsw_sp_mr_vif *vif)
{
return !(vif->vif_flags & (VIFF_TUNNEL | VIFF_REGISTER));
}
static bool mlxsw_sp_mr_vif_valid(const struct mlxsw_sp_mr_vif *vif)
{
return mlxsw_sp_mr_vif_regular(vif) && vif->dev && vif->rif;
return vif->ops->is_regular(vif) && vif->dev && vif->rif;
}
static bool mlxsw_sp_mr_vif_exists(const struct mlxsw_sp_mr_vif *vif)
......@@ -122,18 +138,9 @@ static bool mlxsw_sp_mr_vif_exists(const struct mlxsw_sp_mr_vif *vif)
static bool
mlxsw_sp_mr_route_ivif_in_evifs(const struct mlxsw_sp_mr_route *mr_route)
{
vifi_t ivif;
vifi_t ivif = mr_route->mfc->mfc_parent;
switch (mr_route->mr_table->proto) {
case MLXSW_SP_L3_PROTO_IPV4:
ivif = mr_route->mfc4->_c.mfc_parent;
return mr_route->mfc4->_c.mfc_un.res.ttls[ivif] != 255;
case MLXSW_SP_L3_PROTO_IPV6:
/* fall through */
default:
WARN_ON_ONCE(1);
}
return false;
return mr_route->mfc->mfc_un.res.ttls[ivif] != 255;
}
static int
......@@ -149,19 +156,6 @@ mlxsw_sp_mr_route_valid_evifs_num(const struct mlxsw_sp_mr_route *mr_route)
return valid_evifs;
}
static bool mlxsw_sp_mr_route_starg(const struct mlxsw_sp_mr_route *mr_route)
{
switch (mr_route->mr_table->proto) {
case MLXSW_SP_L3_PROTO_IPV4:
return mr_route->key.source_mask.addr4 == htonl(INADDR_ANY);
case MLXSW_SP_L3_PROTO_IPV6:
/* fall through */
default:
WARN_ON_ONCE(1);
}
return false;
}
static enum mlxsw_sp_mr_route_action
mlxsw_sp_mr_route_action(const struct mlxsw_sp_mr_route *mr_route)
{
......@@ -174,7 +168,8 @@ mlxsw_sp_mr_route_action(const struct mlxsw_sp_mr_route *mr_route)
/* The kernel does not match a (*,G) route that the ingress interface is
* not one of the egress interfaces, so trap these kind of routes.
*/
if (mlxsw_sp_mr_route_starg(mr_route) &&
if (mr_route->mr_table->ops->is_route_starg(mr_route->mr_table,
mr_route) &&
!mlxsw_sp_mr_route_ivif_in_evifs(mr_route))
return MLXSW_SP_MR_ROUTE_ACTION_TRAP;
......@@ -195,25 +190,11 @@ mlxsw_sp_mr_route_action(const struct mlxsw_sp_mr_route *mr_route)
static enum mlxsw_sp_mr_route_prio
mlxsw_sp_mr_route_prio(const struct mlxsw_sp_mr_route *mr_route)
{
return mlxsw_sp_mr_route_starg(mr_route) ?
return mr_route->mr_table->ops->is_route_starg(mr_route->mr_table,
mr_route) ?
MLXSW_SP_MR_ROUTE_PRIO_STARG : MLXSW_SP_MR_ROUTE_PRIO_SG;
}
static void mlxsw_sp_mr_route4_key(struct mlxsw_sp_mr_table *mr_table,
struct mlxsw_sp_mr_route_key *key,
const struct mfc_cache *mfc)
{
bool starg = (mfc->mfc_origin == htonl(INADDR_ANY));
memset(key, 0, sizeof(*key));
key->vrid = mr_table->vr_id;
key->proto = mr_table->proto;
key->group.addr4 = mfc->mfc_mcastgrp;
key->group_mask.addr4 = htonl(0xffffffff);
key->source.addr4 = mfc->mfc_origin;
key->source_mask.addr4 = htonl(starg ? 0 : 0xffffffff);
}
static int mlxsw_sp_mr_route_evif_link(struct mlxsw_sp_mr_route *mr_route,
struct mlxsw_sp_mr_vif *mr_vif)
{
......@@ -343,8 +324,8 @@ static void mlxsw_sp_mr_route_erase(struct mlxsw_sp_mr_table *mr_table,
}
static struct mlxsw_sp_mr_route *
mlxsw_sp_mr_route4_create(struct mlxsw_sp_mr_table *mr_table,
struct mfc_cache *mfc)
mlxsw_sp_mr_route_create(struct mlxsw_sp_mr_table *mr_table,
struct mr_mfc *mfc)
{
struct mlxsw_sp_mr_route_vif_entry *rve, *tmp;
struct mlxsw_sp_mr_route *mr_route;
......@@ -356,15 +337,16 @@ mlxsw_sp_mr_route4_create(struct mlxsw_sp_mr_table *mr_table,
if (!mr_route)
return ERR_PTR(-ENOMEM);
INIT_LIST_HEAD(&mr_route->evif_list);
mlxsw_sp_mr_route4_key(mr_table, &mr_route->key, mfc);
/* Find min_mtu and link iVIF and eVIFs */
mr_route->min_mtu = ETH_MAX_MTU;
ipmr_cache_hold(mfc);
mr_route->mfc4 = mfc;
mr_cache_hold(mfc);
mr_route->mfc = mfc;
mr_table->ops->key_create(mr_table, &mr_route->key, mr_route->mfc);
mr_route->mr_table = mr_table;
for (i = 0; i < MAXVIFS; i++) {
if (mfc->_c.mfc_un.res.ttls[i] != 255) {
if (mfc->mfc_un.res.ttls[i] != 255) {
err = mlxsw_sp_mr_route_evif_link(mr_route,
&mr_table->vifs[i]);
if (err)
......@@ -375,59 +357,37 @@ mlxsw_sp_mr_route4_create(struct mlxsw_sp_mr_table *mr_table,
}
}
mlxsw_sp_mr_route_ivif_link(mr_route,
&mr_table->vifs[mfc->_c.mfc_parent]);
&mr_table->vifs[mfc->mfc_parent]);
mr_route->route_action = mlxsw_sp_mr_route_action(mr_route);
return mr_route;
err:
ipmr_cache_put(mfc);
mr_cache_put(mfc);
list_for_each_entry_safe(rve, tmp, &mr_route->evif_list, route_node)
mlxsw_sp_mr_route_evif_unlink(rve);
kfree(mr_route);
return ERR_PTR(err);
}
static void mlxsw_sp_mr_route4_destroy(struct mlxsw_sp_mr_table *mr_table,
static void mlxsw_sp_mr_route_destroy(struct mlxsw_sp_mr_table *mr_table,
struct mlxsw_sp_mr_route *mr_route)
{
struct mlxsw_sp_mr_route_vif_entry *rve, *tmp;
mlxsw_sp_mr_route_ivif_unlink(mr_route);
ipmr_cache_put(mr_route->mfc4);
mr_cache_put(mr_route->mfc);
list_for_each_entry_safe(rve, tmp, &mr_route->evif_list, route_node)
mlxsw_sp_mr_route_evif_unlink(rve);
kfree(mr_route);
}
static void mlxsw_sp_mr_route_destroy(struct mlxsw_sp_mr_table *mr_table,
struct mlxsw_sp_mr_route *mr_route)
{
switch (mr_table->proto) {
case MLXSW_SP_L3_PROTO_IPV4:
mlxsw_sp_mr_route4_destroy(mr_table, mr_route);
break;
case MLXSW_SP_L3_PROTO_IPV6:
/* fall through */
default:
WARN_ON_ONCE(1);
}
}
static void mlxsw_sp_mr_mfc_offload_set(struct mlxsw_sp_mr_route *mr_route,
bool offload)
{
switch (mr_route->mr_table->proto) {
case MLXSW_SP_L3_PROTO_IPV4:
if (offload)
mr_route->mfc4->_c.mfc_flags |= MFC_OFFLOAD;
mr_route->mfc->mfc_flags |= MFC_OFFLOAD;
else
mr_route->mfc4->_c.mfc_flags &= ~MFC_OFFLOAD;
break;
case MLXSW_SP_L3_PROTO_IPV6:
/* fall through */
default:
WARN_ON_ONCE(1);
}
mr_route->mfc->mfc_flags &= ~MFC_OFFLOAD;
}
static void mlxsw_sp_mr_mfc_offload_update(struct mlxsw_sp_mr_route *mr_route)
......@@ -449,25 +409,18 @@ static void __mlxsw_sp_mr_route_del(struct mlxsw_sp_mr_table *mr_table,
mlxsw_sp_mr_route_destroy(mr_table, mr_route);
}
int mlxsw_sp_mr_route4_add(struct mlxsw_sp_mr_table *mr_table,
struct mfc_cache *mfc, bool replace)
int mlxsw_sp_mr_route_add(struct mlxsw_sp_mr_table *mr_table,
struct mr_mfc *mfc, bool replace)
{
struct mlxsw_sp_mr_route *mr_orig_route = NULL;
struct mlxsw_sp_mr_route *mr_route;
int err;
/* If the route is a (*,*) route, abort, as these kind of routes are
* used for proxy routes.
*/
if (mfc->mfc_origin == htonl(INADDR_ANY) &&
mfc->mfc_mcastgrp == htonl(INADDR_ANY)) {
dev_warn(mr_table->mlxsw_sp->bus_info->dev,
"Offloading proxy routes is not supported.\n");
if (!mr_table->ops->is_route_valid(mr_table, mfc))
return -EINVAL;
}
/* Create a new route */
mr_route = mlxsw_sp_mr_route4_create(mr_table, mfc);
mr_route = mlxsw_sp_mr_route_create(mr_table, mfc);
if (IS_ERR(mr_route))
return PTR_ERR(mr_route);
......@@ -512,7 +465,7 @@ int mlxsw_sp_mr_route4_add(struct mlxsw_sp_mr_table *mr_table,
&mr_orig_route->ht_node,
mlxsw_sp_mr_route_ht_params);
list_del(&mr_orig_route->node);
mlxsw_sp_mr_route4_destroy(mr_table, mr_orig_route);
mlxsw_sp_mr_route_destroy(mr_table, mr_orig_route);
}
mlxsw_sp_mr_mfc_offload_update(mr_route);
......@@ -525,17 +478,17 @@ int mlxsw_sp_mr_route4_add(struct mlxsw_sp_mr_table *mr_table,
list_del(&mr_route->node);
err_no_orig_route:
err_duplicate_route:
mlxsw_sp_mr_route4_destroy(mr_table, mr_route);
mlxsw_sp_mr_route_destroy(mr_table, mr_route);
return err;
}
void mlxsw_sp_mr_route4_del(struct mlxsw_sp_mr_table *mr_table,
struct mfc_cache *mfc)
void mlxsw_sp_mr_route_del(struct mlxsw_sp_mr_table *mr_table,
struct mr_mfc *mfc)
{
struct mlxsw_sp_mr_route *mr_route;
struct mlxsw_sp_mr_route_key key;
mlxsw_sp_mr_route4_key(mr_table, &key, mfc);
mr_table->ops->key_create(mr_table, &key, mfc);
mr_route = rhashtable_lookup_fast(&mr_table->route_ht, &key,
mlxsw_sp_mr_route_ht_params);
if (mr_route)
......@@ -840,6 +793,125 @@ void mlxsw_sp_mr_rif_mtu_update(struct mlxsw_sp_mr_table *mr_table,
}
}
/* Protocol specific functions */
static bool
mlxsw_sp_mr_route4_validate(const struct mlxsw_sp_mr_table *mr_table,
const struct mr_mfc *c)
{
struct mfc_cache *mfc = (struct mfc_cache *) c;
/* If the route is a (*,*) route, abort, as these kind of routes are
* used for proxy routes.
*/
if (mfc->mfc_origin == htonl(INADDR_ANY) &&
mfc->mfc_mcastgrp == htonl(INADDR_ANY)) {
dev_warn(mr_table->mlxsw_sp->bus_info->dev,
"Offloading proxy routes is not supported.\n");
return false;
}
return true;
}
static void mlxsw_sp_mr_route4_key(struct mlxsw_sp_mr_table *mr_table,
struct mlxsw_sp_mr_route_key *key,
struct mr_mfc *c)
{
const struct mfc_cache *mfc = (struct mfc_cache *) c;
bool starg;
starg = (mfc->mfc_origin == htonl(INADDR_ANY));
memset(key, 0, sizeof(*key));
key->vrid = mr_table->vr_id;
key->proto = MLXSW_SP_L3_PROTO_IPV4;
key->group.addr4 = mfc->mfc_mcastgrp;
key->group_mask.addr4 = htonl(0xffffffff);
key->source.addr4 = mfc->mfc_origin;
key->source_mask.addr4 = htonl(starg ? 0 : 0xffffffff);
}
static bool mlxsw_sp_mr_route4_starg(const struct mlxsw_sp_mr_table *mr_table,
const struct mlxsw_sp_mr_route *mr_route)
{
return mr_route->key.source_mask.addr4 == htonl(INADDR_ANY);
}
static bool mlxsw_sp_mr_vif4_is_regular(const struct mlxsw_sp_mr_vif *vif)
{
return !(vif->vif_flags & (VIFF_TUNNEL | VIFF_REGISTER));
}
static bool
mlxsw_sp_mr_route6_validate(const struct mlxsw_sp_mr_table *mr_table,
const struct mr_mfc *c)
{
struct mfc6_cache *mfc = (struct mfc6_cache *) c;
/* If the route is a (*,*) route, abort, as these kind of routes are
* used for proxy routes.
*/
if (ipv6_addr_any(&mfc->mf6c_origin) &&
ipv6_addr_any(&mfc->mf6c_mcastgrp)) {
dev_warn(mr_table->mlxsw_sp->bus_info->dev,
"Offloading proxy routes is not supported.\n");
return false;
}
return true;
}
static void mlxsw_sp_mr_route6_key(struct mlxsw_sp_mr_table *mr_table,
struct mlxsw_sp_mr_route_key *key,
struct mr_mfc *c)
{
const struct mfc6_cache *mfc = (struct mfc6_cache *) c;
memset(key, 0, sizeof(*key));
key->vrid = mr_table->vr_id;
key->proto = MLXSW_SP_L3_PROTO_IPV6;
key->group.addr6 = mfc->mf6c_mcastgrp;
memset(&key->group_mask.addr6, 0xff, sizeof(key->group_mask.addr6));
key->source.addr6 = mfc->mf6c_origin;
if (!ipv6_addr_any(&mfc->mf6c_origin))
memset(&key->source_mask.addr6, 0xff,
sizeof(key->source_mask.addr6));
}
static bool mlxsw_sp_mr_route6_starg(const struct mlxsw_sp_mr_table *mr_table,
const struct mlxsw_sp_mr_route *mr_route)
{
return ipv6_addr_any(&mr_route->key.source_mask.addr6);
}
static bool mlxsw_sp_mr_vif6_is_regular(const struct mlxsw_sp_mr_vif *vif)
{
return !(vif->vif_flags & MIFF_REGISTER);
}
static struct
mlxsw_sp_mr_vif_ops mlxsw_sp_mr_vif_ops_arr[] = {
{
.is_regular = mlxsw_sp_mr_vif4_is_regular,
},
{
.is_regular = mlxsw_sp_mr_vif6_is_regular,
},
};
static struct
mlxsw_sp_mr_table_ops mlxsw_sp_mr_table_ops_arr[] = {
{
.is_route_valid = mlxsw_sp_mr_route4_validate,
.key_create = mlxsw_sp_mr_route4_key,
.is_route_starg = mlxsw_sp_mr_route4_starg,
},
{
.is_route_valid = mlxsw_sp_mr_route6_validate,
.key_create = mlxsw_sp_mr_route6_key,
.is_route_starg = mlxsw_sp_mr_route6_starg,
},
};
struct mlxsw_sp_mr_table *mlxsw_sp_mr_table_create(struct mlxsw_sp *mlxsw_sp,
u32 vr_id,
enum mlxsw_sp_l3proto proto)
......@@ -848,6 +920,7 @@ struct mlxsw_sp_mr_table *mlxsw_sp_mr_table_create(struct mlxsw_sp *mlxsw_sp,
.prio = MLXSW_SP_MR_ROUTE_PRIO_CATCHALL,
.key = {
.vrid = vr_id,
.proto = proto,
},
.value = {
.route_action = MLXSW_SP_MR_ROUTE_ACTION_TRAP,
......@@ -866,6 +939,7 @@ struct mlxsw_sp_mr_table *mlxsw_sp_mr_table_create(struct mlxsw_sp *mlxsw_sp,
mr_table->vr_id = vr_id;
mr_table->mlxsw_sp = mlxsw_sp;
mr_table->proto = proto;
mr_table->ops = &mlxsw_sp_mr_table_ops_arr[proto];
INIT_LIST_HEAD(&mr_table->route_list);
err = rhashtable_init(&mr_table->route_ht,
......@@ -876,6 +950,7 @@ struct mlxsw_sp_mr_table *mlxsw_sp_mr_table_create(struct mlxsw_sp *mlxsw_sp,
for (i = 0; i < MAXVIFS; i++) {
INIT_LIST_HEAD(&mr_table->vifs[i].route_evif_list);
INIT_LIST_HEAD(&mr_table->vifs[i].route_ivif_list);
mr_table->vifs[i].ops = &mlxsw_sp_mr_vif_ops_arr[proto];
}
err = mr->mr_ops->route_create(mlxsw_sp, mr->priv,
......@@ -942,18 +1017,10 @@ static void mlxsw_sp_mr_route_stats_update(struct mlxsw_sp *mlxsw_sp,
mr->mr_ops->route_stats(mlxsw_sp, mr_route->route_priv, &packets,
&bytes);
switch (mr_route->mr_table->proto) {
case MLXSW_SP_L3_PROTO_IPV4:
if (mr_route->mfc4->_c.mfc_un.res.pkt != packets)
mr_route->mfc4->_c.mfc_un.res.lastuse = jiffies;
mr_route->mfc4->_c.mfc_un.res.pkt = packets;
mr_route->mfc4->_c.mfc_un.res.bytes = bytes;
break;
case MLXSW_SP_L3_PROTO_IPV6:
/* fall through */
default:
WARN_ON_ONCE(1);
}
if (mr_route->mfc->mfc_un.res.pkt != packets)
mr_route->mfc->mfc_un.res.lastuse = jiffies;
mr_route->mfc->mfc_un.res.pkt = packets;
mr_route->mfc->mfc_un.res.bytes = bytes;
}
static void mlxsw_sp_mr_stats_update(struct work_struct *work)
......
......@@ -36,6 +36,7 @@
#define _MLXSW_SPECTRUM_MCROUTER_H
#include <linux/mroute.h>
#include <linux/mroute6.h>
#include "spectrum_router.h"
#include "spectrum.h"
......@@ -109,10 +110,10 @@ struct mlxsw_sp_mr_table;
int mlxsw_sp_mr_init(struct mlxsw_sp *mlxsw_sp,
const struct mlxsw_sp_mr_ops *mr_ops);
void mlxsw_sp_mr_fini(struct mlxsw_sp *mlxsw_sp);
int mlxsw_sp_mr_route4_add(struct mlxsw_sp_mr_table *mr_table,
struct mfc_cache *mfc, bool replace);
void mlxsw_sp_mr_route4_del(struct mlxsw_sp_mr_table *mr_table,
struct mfc_cache *mfc);
int mlxsw_sp_mr_route_add(struct mlxsw_sp_mr_table *mr_table,
struct mr_mfc *mfc, bool replace);
void mlxsw_sp_mr_route_del(struct mlxsw_sp_mr_table *mr_table,
struct mr_mfc *mfc);
int mlxsw_sp_mr_vif_add(struct mlxsw_sp_mr_table *mr_table,
struct net_device *dev, vifi_t vif_index,
unsigned long vif_flags,
......
......@@ -51,7 +51,7 @@ struct mlxsw_sp_mr_tcam_region {
};
struct mlxsw_sp_mr_tcam {
struct mlxsw_sp_mr_tcam_region ipv4_tcam_region;
struct mlxsw_sp_mr_tcam_region tcam_regions[MLXSW_SP_L3_PROTO_MAX];
};
/* This struct maps to one RIGR2 register entry */
......@@ -316,20 +316,37 @@ static int mlxsw_sp_mr_tcam_route_replace(struct mlxsw_sp *mlxsw_sp,
mlxsw_afa_block_first_set(afa_block));
break;
case MLXSW_SP_L3_PROTO_IPV6:
default:
WARN_ON_ONCE(1);
mlxsw_reg_rmft2_ipv6_pack(rmft2_pl, true, parman_item->index,
key->vrid,
MLXSW_REG_RMFT2_IRIF_MASK_IGNORE, 0,
key->group.addr6,
key->group_mask.addr6,
key->source.addr6,
key->source_mask.addr6,
mlxsw_afa_block_first_set(afa_block));
}
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rmft2), rmft2_pl);
}
static int mlxsw_sp_mr_tcam_route_remove(struct mlxsw_sp *mlxsw_sp, int vrid,
struct mlxsw_sp_mr_route_key *key,
struct parman_item *parman_item)
{
struct in6_addr zero_addr = IN6ADDR_ANY_INIT;
char rmft2_pl[MLXSW_REG_RMFT2_LEN];
mlxsw_reg_rmft2_ipv4_pack(rmft2_pl, false, parman_item->index, vrid,
0, 0, 0, 0, 0, 0, NULL);
switch (key->proto) {
case MLXSW_SP_L3_PROTO_IPV4:
mlxsw_reg_rmft2_ipv4_pack(rmft2_pl, false, parman_item->index,
vrid, 0, 0, 0, 0, 0, 0, NULL);
break;
case MLXSW_SP_L3_PROTO_IPV6:
mlxsw_reg_rmft2_ipv6_pack(rmft2_pl, false, parman_item->index,
vrid, 0, 0, zero_addr, zero_addr,
zero_addr, zero_addr, NULL);
break;
}
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rmft2), rmft2_pl);
}
......@@ -353,27 +370,30 @@ mlxsw_sp_mr_tcam_erif_populate(struct mlxsw_sp *mlxsw_sp,
return 0;
}
static struct mlxsw_sp_mr_tcam_region *
mlxsw_sp_mr_tcam_protocol_region(struct mlxsw_sp_mr_tcam *mr_tcam,
enum mlxsw_sp_l3proto proto)
{
return &mr_tcam->tcam_regions[proto];
}
static int
mlxsw_sp_mr_tcam_route_parman_item_add(struct mlxsw_sp_mr_tcam *mr_tcam,
struct mlxsw_sp_mr_tcam_route *route,
enum mlxsw_sp_mr_route_prio prio)
{
struct parman_prio *parman_prio = NULL;
struct mlxsw_sp_mr_tcam_region *tcam_region;
int err;
switch (route->key.proto) {
case MLXSW_SP_L3_PROTO_IPV4:
parman_prio = &mr_tcam->ipv4_tcam_region.parman_prios[prio];
err = parman_item_add(mr_tcam->ipv4_tcam_region.parman,
parman_prio, &route->parman_item);
tcam_region = mlxsw_sp_mr_tcam_protocol_region(mr_tcam,
route->key.proto);
err = parman_item_add(tcam_region->parman,
&tcam_region->parman_prios[prio],
&route->parman_item);
if (err)
return err;
break;
case MLXSW_SP_L3_PROTO_IPV6:
default:
WARN_ON_ONCE(1);
}
route->parman_prio = parman_prio;
route->parman_prio = &tcam_region->parman_prios[prio];
return 0;
}
......@@ -381,15 +401,13 @@ static void
mlxsw_sp_mr_tcam_route_parman_item_remove(struct mlxsw_sp_mr_tcam *mr_tcam,
struct mlxsw_sp_mr_tcam_route *route)
{
switch (route->key.proto) {
case MLXSW_SP_L3_PROTO_IPV4:
parman_item_remove(mr_tcam->ipv4_tcam_region.parman,
struct mlxsw_sp_mr_tcam_region *tcam_region;
tcam_region = mlxsw_sp_mr_tcam_protocol_region(mr_tcam,
route->key.proto);
parman_item_remove(tcam_region->parman,
route->parman_prio, &route->parman_item);
break;
case MLXSW_SP_L3_PROTO_IPV6:
default:
WARN_ON_ONCE(1);
}
}
static int
......@@ -462,7 +480,7 @@ static void mlxsw_sp_mr_tcam_route_destroy(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_mr_tcam *mr_tcam = priv;
mlxsw_sp_mr_tcam_route_remove(mlxsw_sp, route->key.vrid,
&route->parman_item);
&route->key, &route->parman_item);
mlxsw_sp_mr_tcam_route_parman_item_remove(mr_tcam, route);
mlxsw_sp_mr_tcam_afa_block_destroy(route->afa_block);
mlxsw_sp_flow_counter_free(mlxsw_sp, route->counter_index);
......@@ -806,21 +824,42 @@ mlxsw_sp_mr_tcam_region_fini(struct mlxsw_sp_mr_tcam_region *mr_tcam_region)
static int mlxsw_sp_mr_tcam_init(struct mlxsw_sp *mlxsw_sp, void *priv)
{
struct mlxsw_sp_mr_tcam *mr_tcam = priv;
struct mlxsw_sp_mr_tcam_region *region = &mr_tcam->tcam_regions[0];
u32 rtar_key;
int err;
if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MC_ERIF_LIST_ENTRIES) ||
!MLXSW_CORE_RES_VALID(mlxsw_sp->core, ACL_MAX_TCAM_RULES))
return -EIO;
return mlxsw_sp_mr_tcam_region_init(mlxsw_sp,
&mr_tcam->ipv4_tcam_region,
MLXSW_REG_RTAR_KEY_TYPE_IPV4_MULTICAST);
rtar_key = MLXSW_REG_RTAR_KEY_TYPE_IPV4_MULTICAST;
err = mlxsw_sp_mr_tcam_region_init(mlxsw_sp,
&region[MLXSW_SP_L3_PROTO_IPV4],
rtar_key);
if (err)
return err;
rtar_key = MLXSW_REG_RTAR_KEY_TYPE_IPV6_MULTICAST;
err = mlxsw_sp_mr_tcam_region_init(mlxsw_sp,
&region[MLXSW_SP_L3_PROTO_IPV6],
rtar_key);
if (err)
goto err_ipv6_region_init;
return 0;
err_ipv6_region_init:
mlxsw_sp_mr_tcam_region_fini(&region[MLXSW_SP_L3_PROTO_IPV4]);
return err;
}
static void mlxsw_sp_mr_tcam_fini(void *priv)
{
struct mlxsw_sp_mr_tcam *mr_tcam = priv;
struct mlxsw_sp_mr_tcam_region *region = &mr_tcam->tcam_regions[0];
mlxsw_sp_mr_tcam_region_fini(&mr_tcam->ipv4_tcam_region);
mlxsw_sp_mr_tcam_region_fini(&region[MLXSW_SP_L3_PROTO_IPV6]);
mlxsw_sp_mr_tcam_region_fini(&region[MLXSW_SP_L3_PROTO_IPV4]);
}
const struct mlxsw_sp_mr_ops mlxsw_sp_mr_tcam_ops = {
......
......@@ -467,7 +467,7 @@ struct mlxsw_sp_vr {
unsigned int rif_count;
struct mlxsw_sp_fib *fib4;
struct mlxsw_sp_fib *fib6;
struct mlxsw_sp_mr_table *mr4_table;
struct mlxsw_sp_mr_table *mr_table[MLXSW_SP_L3_PROTO_MAX];
};
static const struct rhashtable_params mlxsw_sp_fib_ht_params;
......@@ -711,7 +711,9 @@ static void mlxsw_sp_lpm_fini(struct mlxsw_sp *mlxsw_sp)
static bool mlxsw_sp_vr_is_used(const struct mlxsw_sp_vr *vr)
{
return !!vr->fib4 || !!vr->fib6 || !!vr->mr4_table;
return !!vr->fib4 || !!vr->fib6 ||
!!vr->mr_table[MLXSW_SP_L3_PROTO_IPV4] ||
!!vr->mr_table[MLXSW_SP_L3_PROTO_IPV6];
}
static struct mlxsw_sp_vr *mlxsw_sp_vr_find_unused(struct mlxsw_sp *mlxsw_sp)
......@@ -789,7 +791,7 @@ static struct mlxsw_sp_vr *mlxsw_sp_vr_create(struct mlxsw_sp *mlxsw_sp,
u32 tb_id,
struct netlink_ext_ack *extack)
{
struct mlxsw_sp_mr_table *mr4_table;
struct mlxsw_sp_mr_table *mr4_table, *mr6_table;
struct mlxsw_sp_fib *fib4;
struct mlxsw_sp_fib *fib6;
struct mlxsw_sp_vr *vr;
......@@ -812,15 +814,25 @@ static struct mlxsw_sp_vr *mlxsw_sp_vr_create(struct mlxsw_sp *mlxsw_sp,
MLXSW_SP_L3_PROTO_IPV4);
if (IS_ERR(mr4_table)) {
err = PTR_ERR(mr4_table);
goto err_mr_table_create;
goto err_mr4_table_create;
}
mr6_table = mlxsw_sp_mr_table_create(mlxsw_sp, vr->id,
MLXSW_SP_L3_PROTO_IPV6);
if (IS_ERR(mr6_table)) {
err = PTR_ERR(mr6_table);
goto err_mr6_table_create;
}
vr->fib4 = fib4;
vr->fib6 = fib6;
vr->mr4_table = mr4_table;
vr->mr_table[MLXSW_SP_L3_PROTO_IPV4] = mr4_table;
vr->mr_table[MLXSW_SP_L3_PROTO_IPV6] = mr6_table;
vr->tb_id = tb_id;
return vr;
err_mr_table_create:
err_mr6_table_create:
mlxsw_sp_mr_table_destroy(mr4_table);
err_mr4_table_create:
mlxsw_sp_fib_destroy(mlxsw_sp, fib6);
err_fib6_create:
mlxsw_sp_fib_destroy(mlxsw_sp, fib4);
......@@ -830,8 +842,10 @@ static struct mlxsw_sp_vr *mlxsw_sp_vr_create(struct mlxsw_sp *mlxsw_sp,
static void mlxsw_sp_vr_destroy(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_vr *vr)
{
mlxsw_sp_mr_table_destroy(vr->mr4_table);
vr->mr4_table = NULL;
mlxsw_sp_mr_table_destroy(vr->mr_table[MLXSW_SP_L3_PROTO_IPV6]);
vr->mr_table[MLXSW_SP_L3_PROTO_IPV6] = NULL;
mlxsw_sp_mr_table_destroy(vr->mr_table[MLXSW_SP_L3_PROTO_IPV4]);
vr->mr_table[MLXSW_SP_L3_PROTO_IPV4] = NULL;
mlxsw_sp_fib_destroy(mlxsw_sp, vr->fib6);
vr->fib6 = NULL;
mlxsw_sp_fib_destroy(mlxsw_sp, vr->fib4);
......@@ -854,7 +868,8 @@ static void mlxsw_sp_vr_put(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_vr *vr)
{
if (!vr->rif_count && list_empty(&vr->fib4->node_list) &&
list_empty(&vr->fib6->node_list) &&
mlxsw_sp_mr_table_empty(vr->mr4_table))
mlxsw_sp_mr_table_empty(vr->mr_table[MLXSW_SP_L3_PROTO_IPV4]) &&
mlxsw_sp_mr_table_empty(vr->mr_table[MLXSW_SP_L3_PROTO_IPV6]))
mlxsw_sp_vr_destroy(mlxsw_sp, vr);
}
......@@ -5378,10 +5393,20 @@ static int __mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp,
return 0;
}
static struct mlxsw_sp_mr_table *
mlxsw_sp_router_fibmr_family_to_table(struct mlxsw_sp_vr *vr, int family)
{
if (family == RTNL_FAMILY_IPMR)
return vr->mr_table[MLXSW_SP_L3_PROTO_IPV4];
else
return vr->mr_table[MLXSW_SP_L3_PROTO_IPV6];
}
static int mlxsw_sp_router_fibmr_add(struct mlxsw_sp *mlxsw_sp,
struct mfc_entry_notifier_info *men_info,
bool replace)
{
struct mlxsw_sp_mr_table *mrt;
struct mlxsw_sp_vr *vr;
if (mlxsw_sp->router->aborted)
......@@ -5391,12 +5416,14 @@ static int mlxsw_sp_router_fibmr_add(struct mlxsw_sp *mlxsw_sp,
if (IS_ERR(vr))
return PTR_ERR(vr);
return mlxsw_sp_mr_route4_add(vr->mr4_table, men_info->mfc, replace);
mrt = mlxsw_sp_router_fibmr_family_to_table(vr, men_info->info.family);
return mlxsw_sp_mr_route_add(mrt, men_info->mfc, replace);
}
static void mlxsw_sp_router_fibmr_del(struct mlxsw_sp *mlxsw_sp,
struct mfc_entry_notifier_info *men_info)
{
struct mlxsw_sp_mr_table *mrt;
struct mlxsw_sp_vr *vr;
if (mlxsw_sp->router->aborted)
......@@ -5406,7 +5433,8 @@ static void mlxsw_sp_router_fibmr_del(struct mlxsw_sp *mlxsw_sp,
if (WARN_ON(!vr))
return;
mlxsw_sp_mr_route4_del(vr->mr4_table, men_info->mfc);
mrt = mlxsw_sp_router_fibmr_family_to_table(vr, men_info->info.family);
mlxsw_sp_mr_route_del(mrt, men_info->mfc);
mlxsw_sp_vr_put(mlxsw_sp, vr);
}
......@@ -5414,6 +5442,7 @@ static int
mlxsw_sp_router_fibmr_vif_add(struct mlxsw_sp *mlxsw_sp,
struct vif_entry_notifier_info *ven_info)
{
struct mlxsw_sp_mr_table *mrt;
struct mlxsw_sp_rif *rif;
struct mlxsw_sp_vr *vr;
......@@ -5424,8 +5453,9 @@ mlxsw_sp_router_fibmr_vif_add(struct mlxsw_sp *mlxsw_sp,
if (IS_ERR(vr))
return PTR_ERR(vr);
mrt = mlxsw_sp_router_fibmr_family_to_table(vr, ven_info->info.family);
rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, ven_info->dev);
return mlxsw_sp_mr_vif_add(vr->mr4_table, ven_info->dev,
return mlxsw_sp_mr_vif_add(mrt, ven_info->dev,
ven_info->vif_index,
ven_info->vif_flags, rif);
}
......@@ -5434,6 +5464,7 @@ static void
mlxsw_sp_router_fibmr_vif_del(struct mlxsw_sp *mlxsw_sp,
struct vif_entry_notifier_info *ven_info)
{
struct mlxsw_sp_mr_table *mrt;
struct mlxsw_sp_vr *vr;
if (mlxsw_sp->router->aborted)
......@@ -5443,7 +5474,8 @@ mlxsw_sp_router_fibmr_vif_del(struct mlxsw_sp *mlxsw_sp,
if (WARN_ON(!vr))
return;
mlxsw_sp_mr_vif_del(vr->mr4_table, ven_info->vif_index);
mrt = mlxsw_sp_router_fibmr_family_to_table(vr, ven_info->info.family);
mlxsw_sp_mr_vif_del(mrt, ven_info->vif_index);
mlxsw_sp_vr_put(mlxsw_sp, vr);
}
......@@ -5535,7 +5567,7 @@ static void mlxsw_sp_vr_fib_flush(struct mlxsw_sp *mlxsw_sp,
static void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp)
{
int i;
int i, j;
for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i];
......@@ -5543,7 +5575,8 @@ static void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp)
if (!mlxsw_sp_vr_is_used(vr))
continue;
mlxsw_sp_mr_table_flush(vr->mr4_table);
for (j = 0; j < MLXSW_SP_L3_PROTO_MAX; j++)
mlxsw_sp_mr_table_flush(vr->mr_table[j]);
mlxsw_sp_vr_fib_flush(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV4);
/* If virtual router was only used for IPv4, then it's no
......@@ -5682,11 +5715,11 @@ static void mlxsw_sp_router_fibmr_event_work(struct work_struct *work)
replace);
if (err)
mlxsw_sp_router_fib_abort(mlxsw_sp);
ipmr_cache_put(fib_work->men_info.mfc);
mr_cache_put(fib_work->men_info.mfc);
break;
case FIB_EVENT_ENTRY_DEL:
mlxsw_sp_router_fibmr_del(mlxsw_sp, &fib_work->men_info);
ipmr_cache_put(fib_work->men_info.mfc);
mr_cache_put(fib_work->men_info.mfc);
break;
case FIB_EVENT_VIF_ADD:
err = mlxsw_sp_router_fibmr_vif_add(mlxsw_sp,
......@@ -5766,7 +5799,7 @@ mlxsw_sp_router_fibmr_event(struct mlxsw_sp_fib_event_work *fib_work,
case FIB_EVENT_ENTRY_ADD: /* fall through */
case FIB_EVENT_ENTRY_DEL:
memcpy(&fib_work->men_info, info, sizeof(fib_work->men_info));
ipmr_cache_hold(fib_work->men_info.mfc);
mr_cache_hold(fib_work->men_info.mfc);
break;
case FIB_EVENT_VIF_ADD: /* fall through */
case FIB_EVENT_VIF_DEL:
......@@ -5808,6 +5841,10 @@ static int mlxsw_sp_router_fib_rule_event(unsigned long event,
if (!ipmr_rule_default(rule) && !rule->l3mdev)
err = -1;
break;
case RTNL_FAMILY_IP6MR:
if (!ip6mr_rule_default(rule) && !rule->l3mdev)
err = -1;
break;
}
if (err < 0)
......@@ -5827,7 +5864,8 @@ static int mlxsw_sp_router_fib_event(struct notifier_block *nb,
if (!net_eq(info->net, &init_net) ||
(info->family != AF_INET && info->family != AF_INET6 &&
info->family != RTNL_FAMILY_IPMR))
info->family != RTNL_FAMILY_IPMR &&
info->family != RTNL_FAMILY_IP6MR))
return NOTIFY_DONE;
router = container_of(nb, struct mlxsw_sp_router, fib_nb);
......@@ -5857,6 +5895,7 @@ static int mlxsw_sp_router_fib_event(struct notifier_block *nb,
INIT_WORK(&fib_work->work, mlxsw_sp_router_fib6_event_work);
mlxsw_sp_router_fib6_event(fib_work, info);
break;
case RTNL_FAMILY_IP6MR:
case RTNL_FAMILY_IPMR:
INIT_WORK(&fib_work->work, mlxsw_sp_router_fibmr_event_work);
mlxsw_sp_router_fibmr_event(fib_work, info);
......@@ -6038,7 +6077,7 @@ mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_rif *rif;
struct mlxsw_sp_vr *vr;
u16 rif_index;
int err;
int i, err;
type = mlxsw_sp_dev_rif_type(mlxsw_sp, params->dev);
ops = mlxsw_sp->router->rif_ops_arr[type];
......@@ -6078,9 +6117,11 @@ mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp,
if (err)
goto err_configure;
err = mlxsw_sp_mr_rif_add(vr->mr4_table, rif);
for (i = 0; i < MLXSW_SP_L3_PROTO_MAX; i++) {
err = mlxsw_sp_mr_rif_add(vr->mr_table[i], rif);
if (err)
goto err_mr_rif_add;
}
mlxsw_sp_rif_counters_alloc(rif);
mlxsw_sp->router->rifs[rif_index] = rif;
......@@ -6088,6 +6129,8 @@ mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp,
return rif;
err_mr_rif_add:
for (i--; i >= 0; i--)
mlxsw_sp_mr_rif_del(vr->mr_table[i], rif);
ops->deconfigure(rif);
err_configure:
if (fid)
......@@ -6107,13 +6150,15 @@ void mlxsw_sp_rif_destroy(struct mlxsw_sp_rif *rif)
struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
struct mlxsw_sp_fid *fid = rif->fid;
struct mlxsw_sp_vr *vr;
int i;
mlxsw_sp_router_rif_gone_sync(mlxsw_sp, rif);
vr = &mlxsw_sp->router->vrs[rif->vr_id];
mlxsw_sp->router->rifs[rif->rif_index] = NULL;
mlxsw_sp_rif_counters_free(rif);
mlxsw_sp_mr_rif_del(vr->mr4_table, rif);
for (i = 0; i < MLXSW_SP_L3_PROTO_MAX; i++)
mlxsw_sp_mr_rif_del(vr->mr_table[i], rif);
ops->deconfigure(rif);
if (fid)
/* Loopback RIFs are not associated with a FID. */
......@@ -6520,13 +6565,16 @@ int mlxsw_sp_netdevice_router_port_event(struct net_device *dev)
if (rif->mtu != dev->mtu) {
struct mlxsw_sp_vr *vr;
int i;
/* The RIF is relevant only to its mr_table instance, as unlike
* unicast routing, in multicast routing a RIF cannot be shared
* between several multicast routing tables.
*/
vr = &mlxsw_sp->router->vrs[rif->vr_id];
mlxsw_sp_mr_rif_mtu_update(vr->mr4_table, rif, dev->mtu);
for (i = 0; i < MLXSW_SP_L3_PROTO_MAX; i++)
mlxsw_sp_mr_rif_mtu_update(vr->mr_table[i],
rif, dev->mtu);
}
ether_addr_copy(rif->addr, dev->dev_addr);
......
......@@ -41,6 +41,7 @@
enum mlxsw_sp_l3proto {
MLXSW_SP_L3_PROTO_IPV4,
MLXSW_SP_L3_PROTO_IPV6,
#define MLXSW_SP_L3_PROTO_MAX (MLXSW_SP_L3_PROTO_IPV6 + 1)
};
union mlxsw_sp_l3addr {
......
......@@ -77,6 +77,7 @@ enum {
MLXSW_TRAP_ID_IPV6_DHCP = 0x69,
MLXSW_TRAP_ID_IPV6_ALL_ROUTERS_LINK = 0x6F,
MLXSW_TRAP_ID_RTR_INGRESS0 = 0x70,
MLXSW_TRAP_ID_IPV6_PIM = 0x79,
MLXSW_TRAP_ID_IPV4_BGP = 0x88,
MLXSW_TRAP_ID_IPV6_BGP = 0x89,
MLXSW_TRAP_ID_L3_IPV6_ROUTER_SOLICITATION = 0x8A,
......
......@@ -55,14 +55,6 @@ static inline bool ipmr_rule_default(const struct fib_rule *rule)
}
#endif
struct vif_entry_notifier_info {
struct fib_notifier_info info;
struct net_device *dev;
vifi_t vif_index;
unsigned short vif_flags;
u32 tb_id;
};
#define VIFF_STATIC 0x8000
struct mfc_cache_cmp_arg {
......@@ -88,33 +80,8 @@ struct mfc_cache {
};
};
struct mfc_entry_notifier_info {
struct fib_notifier_info info;
struct mfc_cache *mfc;
u32 tb_id;
};
struct rtmsg;
int ipmr_get_route(struct net *net, struct sk_buff *skb,
__be32 saddr, __be32 daddr,
struct rtmsg *rtm, u32 portid);
#ifdef CONFIG_IP_MROUTE
void ipmr_cache_free(struct mfc_cache *mfc_cache);
#else
static inline void ipmr_cache_free(struct mfc_cache *mfc_cache)
{
}
#endif
static inline void ipmr_cache_put(struct mfc_cache *c)
{
if (refcount_dec_and_test(&c->_c.mfc_un.res.refcount))
ipmr_cache_free(c);
}
static inline void ipmr_cache_hold(struct mfc_cache *c)
{
refcount_inc(&c->_c.mfc_un.res.refcount);
}
#endif
......@@ -8,6 +8,7 @@
#include <net/net_namespace.h>
#include <uapi/linux/mroute6.h>
#include <linux/mroute_base.h>
#include <net/fib_rules.h>
#ifdef CONFIG_IPV6_MROUTE
static inline int ip6_mroute_opt(int opt)
......@@ -63,6 +64,15 @@ static inline void ip6_mr_cleanup(void)
}
#endif
#ifdef CONFIG_IPV6_MROUTE_MULTIPLE_TABLES
bool ip6mr_rule_default(const struct fib_rule *rule);
#else
static inline bool ip6mr_rule_default(const struct fib_rule *rule)
{
return true;
}
#endif
#define VIFF_STATIC 0x8000
struct mfc6_cache_cmp_arg {
......
......@@ -6,6 +6,7 @@
#include <linux/spinlock.h>
#include <net/net_namespace.h>
#include <net/sock.h>
#include <net/fib_notifier.h>
/**
* struct vif_device - interface representor for multicast routing
......@@ -36,6 +37,58 @@ struct vif_device {
__be32 local, remote;
};
struct vif_entry_notifier_info {
struct fib_notifier_info info;
struct net_device *dev;
unsigned short vif_index;
unsigned short vif_flags;
u32 tb_id;
};
static inline int mr_call_vif_notifier(struct notifier_block *nb,
struct net *net,
unsigned short family,
enum fib_event_type event_type,
struct vif_device *vif,
unsigned short vif_index, u32 tb_id)
{
struct vif_entry_notifier_info info = {
.info = {
.family = family,
.net = net,
},
.dev = vif->dev,
.vif_index = vif_index,
.vif_flags = vif->flags,
.tb_id = tb_id,
};
return call_fib_notifier(nb, net, event_type, &info.info);
}
static inline int mr_call_vif_notifiers(struct net *net,
unsigned short family,
enum fib_event_type event_type,
struct vif_device *vif,
unsigned short vif_index, u32 tb_id,
unsigned int *ipmr_seq)
{
struct vif_entry_notifier_info info = {
.info = {
.family = family,
.net = net,
},
.dev = vif->dev,
.vif_index = vif_index,
.vif_flags = vif->flags,
.tb_id = tb_id,
};
ASSERT_RTNL();
(*ipmr_seq)++;
return call_fib_notifiers(net, event_type, &info.info);
}
#ifndef MAXVIFS
/* This one is nasty; value is defined in uapi using different symbols for
* mroute and morute6 but both map into same 32.
......@@ -72,6 +125,7 @@ enum {
* @refcount: reference count for this entry
* @list: global entry list
* @rcu: used for entry destruction
* @free: Operation used for freeing an entry under RCU
*/
struct mr_mfc {
struct rhlist_head mnode;
......@@ -97,8 +151,64 @@ struct mr_mfc {
} mfc_un;
struct list_head list;
struct rcu_head rcu;
void (*free)(struct rcu_head *head);
};
static inline void mr_cache_put(struct mr_mfc *c)
{
if (refcount_dec_and_test(&c->mfc_un.res.refcount))
call_rcu(&c->rcu, c->free);
}
static inline void mr_cache_hold(struct mr_mfc *c)
{
refcount_inc(&c->mfc_un.res.refcount);
}
struct mfc_entry_notifier_info {
struct fib_notifier_info info;
struct mr_mfc *mfc;
u32 tb_id;
};
static inline int mr_call_mfc_notifier(struct notifier_block *nb,
struct net *net,
unsigned short family,
enum fib_event_type event_type,
struct mr_mfc *mfc, u32 tb_id)
{
struct mfc_entry_notifier_info info = {
.info = {
.family = family,
.net = net,
},
.mfc = mfc,
.tb_id = tb_id
};
return call_fib_notifier(nb, net, event_type, &info.info);
}
static inline int mr_call_mfc_notifiers(struct net *net,
unsigned short family,
enum fib_event_type event_type,
struct mr_mfc *mfc, u32 tb_id,
unsigned int *ipmr_seq)
{
struct mfc_entry_notifier_info info = {
.info = {
.family = family,
.net = net,
},
.mfc = mfc,
.tb_id = tb_id
};
ASSERT_RTNL();
(*ipmr_seq)++;
return call_fib_notifiers(net, event_type, &info.info);
}
struct mr_table;
/**
......@@ -180,6 +290,13 @@ int mr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb,
u32 portid, u32 seq, struct mr_mfc *c,
int cmd, int flags),
spinlock_t *lock);
int mr_dump(struct net *net, struct notifier_block *nb, unsigned short family,
int (*rules_dump)(struct net *net,
struct notifier_block *nb),
struct mr_table *(*mr_iter)(struct net *net,
struct mr_table *mrt),
rwlock_t *mrt_lock);
#else
static inline void vif_device_init(struct vif_device *v,
struct net_device *dev,
......@@ -236,6 +353,17 @@ mr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb,
{
return -EINVAL;
}
static inline int mr_dump(struct net *net, struct notifier_block *nb,
unsigned short family,
int (*rules_dump)(struct net *net,
struct notifier_block *nb),
struct mr_table *(*mr_iter)(struct net *net,
struct mr_table *mrt),
rwlock_t *mrt_lock)
{
return -EINVAL;
}
#endif
static inline void *mr_mfc_find(struct mr_table *mrt, void *hasharg)
......
......@@ -96,6 +96,8 @@ struct netns_ipv6 {
atomic_t fib6_sernum;
struct seg6_pernet_data *seg6_data;
struct fib_notifier_ops *notifier_ops;
struct fib_notifier_ops *ip6mr_notifier_ops;
unsigned int ipmr_seq; /* protected by rtnl_mutex */
struct {
struct hlist_head head;
spinlock_t lock;
......
......@@ -644,80 +644,22 @@ static struct net_device *ipmr_reg_vif(struct net *net, struct mr_table *mrt)
}
#endif
static int call_ipmr_vif_entry_notifier(struct notifier_block *nb,
struct net *net,
enum fib_event_type event_type,
struct vif_device *vif,
vifi_t vif_index, u32 tb_id)
{
struct vif_entry_notifier_info info = {
.info = {
.family = RTNL_FAMILY_IPMR,
.net = net,
},
.dev = vif->dev,
.vif_index = vif_index,
.vif_flags = vif->flags,
.tb_id = tb_id,
};
return call_fib_notifier(nb, net, event_type, &info.info);
}
static int call_ipmr_vif_entry_notifiers(struct net *net,
enum fib_event_type event_type,
struct vif_device *vif,
vifi_t vif_index, u32 tb_id)
{
struct vif_entry_notifier_info info = {
.info = {
.family = RTNL_FAMILY_IPMR,
.net = net,
},
.dev = vif->dev,
.vif_index = vif_index,
.vif_flags = vif->flags,
.tb_id = tb_id,
};
ASSERT_RTNL();
net->ipv4.ipmr_seq++;
return call_fib_notifiers(net, event_type, &info.info);
}
static int call_ipmr_mfc_entry_notifier(struct notifier_block *nb,
struct net *net,
enum fib_event_type event_type,
struct mfc_cache *mfc, u32 tb_id)
{
struct mfc_entry_notifier_info info = {
.info = {
.family = RTNL_FAMILY_IPMR,
.net = net,
},
.mfc = mfc,
.tb_id = tb_id
};
return call_fib_notifier(nb, net, event_type, &info.info);
return mr_call_vif_notifiers(net, RTNL_FAMILY_IPMR, event_type,
vif, vif_index, tb_id,
&net->ipv4.ipmr_seq);
}
static int call_ipmr_mfc_entry_notifiers(struct net *net,
enum fib_event_type event_type,
struct mfc_cache *mfc, u32 tb_id)
{
struct mfc_entry_notifier_info info = {
.info = {
.family = RTNL_FAMILY_IPMR,
.net = net,
},
.mfc = mfc,
.tb_id = tb_id
};
ASSERT_RTNL();
net->ipv4.ipmr_seq++;
return call_fib_notifiers(net, event_type, &info.info);
return mr_call_mfc_notifiers(net, RTNL_FAMILY_IPMR, event_type,
&mfc->_c, tb_id, &net->ipv4.ipmr_seq);
}
/**
......@@ -790,11 +732,10 @@ static void ipmr_cache_free_rcu(struct rcu_head *head)
kmem_cache_free(mrt_cachep, (struct mfc_cache *)c);
}
void ipmr_cache_free(struct mfc_cache *c)
static void ipmr_cache_free(struct mfc_cache *c)
{
call_rcu(&c->_c.rcu, ipmr_cache_free_rcu);
}
EXPORT_SYMBOL(ipmr_cache_free);
/* Destroy an unresolved cache entry, killing queued skbs
* and reporting error to netlink readers.
......@@ -1045,6 +986,7 @@ static struct mfc_cache *ipmr_cache_alloc(void)
if (c) {
c->_c.mfc_un.res.last_assert = jiffies - MFC_ASSERT_THRESH - 1;
c->_c.mfc_un.res.minvif = MAXVIFS;
c->_c.free = ipmr_cache_free_rcu;
refcount_set(&c->_c.mfc_un.res.refcount, 1);
}
return c;
......@@ -1264,7 +1206,7 @@ static int ipmr_mfc_delete(struct mr_table *mrt, struct mfcctl *mfc, int parent)
list_del_rcu(&c->_c.list);
call_ipmr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, c, mrt->id);
mroute_netlink_event(mrt, c, RTM_DELROUTE);
ipmr_cache_put(c);
mr_cache_put(&c->_c);
return 0;
}
......@@ -1376,7 +1318,7 @@ static void mroute_clean_tables(struct mr_table *mrt, bool all)
call_ipmr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, cache,
mrt->id);
mroute_netlink_event(mrt, cache, RTM_DELROUTE);
ipmr_cache_put(cache);
mr_cache_put(c);
}
if (atomic_read(&mrt->cache_resolve_queue_len) != 0) {
......@@ -2989,38 +2931,8 @@ static unsigned int ipmr_seq_read(struct net *net)
static int ipmr_dump(struct net *net, struct notifier_block *nb)
{
struct mr_table *mrt;
int err;
err = ipmr_rules_dump(net, nb);
if (err)
return err;
ipmr_for_each_table(mrt, net) {
struct vif_device *v = &mrt->vif_table[0];
struct mr_mfc *mfc;
int vifi;
/* Notifiy on table VIF entries */
read_lock(&mrt_lock);
for (vifi = 0; vifi < mrt->maxvif; vifi++, v++) {
if (!v->dev)
continue;
call_ipmr_vif_entry_notifier(nb, net, FIB_EVENT_VIF_ADD,
v, vifi, mrt->id);
}
read_unlock(&mrt_lock);
/* Notify on table MFC entries */
list_for_each_entry_rcu(mfc, &mrt->mfc_cache_list, list)
call_ipmr_mfc_entry_notifier(nb, net,
FIB_EVENT_ENTRY_ADD,
(struct mfc_cache *)mfc,
mrt->id);
}
return 0;
return mr_dump(net, nb, RTNL_FAMILY_IPMR, ipmr_rules_dump,
ipmr_mr_table_iter, &mrt_lock);
}
static const struct fib_notifier_ops ipmr_notifier_ops_template = {
......
......@@ -321,3 +321,45 @@ int mr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb,
return skb->len;
}
EXPORT_SYMBOL(mr_rtm_dumproute);
int mr_dump(struct net *net, struct notifier_block *nb, unsigned short family,
int (*rules_dump)(struct net *net,
struct notifier_block *nb),
struct mr_table *(*mr_iter)(struct net *net,
struct mr_table *mrt),
rwlock_t *mrt_lock)
{
struct mr_table *mrt;
int err;
err = rules_dump(net, nb);
if (err)
return err;
for (mrt = mr_iter(net, NULL); mrt; mrt = mr_iter(net, mrt)) {
struct vif_device *v = &mrt->vif_table[0];
struct mr_mfc *mfc;
int vifi;
/* Notifiy on table VIF entries */
read_lock(mrt_lock);
for (vifi = 0; vifi < mrt->maxvif; vifi++, v++) {
if (!v->dev)
continue;
mr_call_vif_notifier(nb, net, family,
FIB_EVENT_VIF_ADD,
v, vifi, mrt->id);
}
read_unlock(mrt_lock);
/* Notify on table MFC entries */
list_for_each_entry_rcu(mfc, &mrt->mfc_cache_list, list)
mr_call_mfc_notifier(nb, net, family,
FIB_EVENT_ENTRY_ADD,
mfc, mrt->id);
}
return 0;
}
EXPORT_SYMBOL(mr_dump);
......@@ -258,6 +258,23 @@ static void __net_exit ip6mr_rules_exit(struct net *net)
fib_rules_unregister(net->ipv6.mr6_rules_ops);
rtnl_unlock();
}
static int ip6mr_rules_dump(struct net *net, struct notifier_block *nb)
{
return fib_rules_dump(net, nb, RTNL_FAMILY_IP6MR);
}
static unsigned int ip6mr_rules_seq_read(struct net *net)
{
return fib_rules_seq_read(net, RTNL_FAMILY_IP6MR);
}
bool ip6mr_rule_default(const struct fib_rule *rule)
{
return fib_rule_matchall(rule) && rule->action == FR_ACT_TO_TBL &&
rule->table == RT6_TABLE_DFLT && !rule->l3mdev;
}
EXPORT_SYMBOL(ip6mr_rule_default);
#else
#define ip6mr_for_each_table(mrt, net) \
for (mrt = net->ipv6.mrt6; mrt; mrt = NULL)
......@@ -295,6 +312,16 @@ static void __net_exit ip6mr_rules_exit(struct net *net)
net->ipv6.mrt6 = NULL;
rtnl_unlock();
}
static int ip6mr_rules_dump(struct net *net, struct notifier_block *nb)
{
return 0;
}
static unsigned int ip6mr_rules_seq_read(struct net *net)
{
return 0;
}
#endif
static int ip6mr_hash_cmp(struct rhashtable_compare_arg *arg,
......@@ -653,10 +680,25 @@ static struct net_device *ip6mr_reg_vif(struct net *net, struct mr_table *mrt)
}
#endif
/*
* Delete a VIF entry
*/
static int call_ip6mr_vif_entry_notifiers(struct net *net,
enum fib_event_type event_type,
struct vif_device *vif,
mifi_t vif_index, u32 tb_id)
{
return mr_call_vif_notifiers(net, RTNL_FAMILY_IP6MR, event_type,
vif, vif_index, tb_id,
&net->ipv6.ipmr_seq);
}
static int call_ip6mr_mfc_entry_notifiers(struct net *net,
enum fib_event_type event_type,
struct mfc6_cache *mfc, u32 tb_id)
{
return mr_call_mfc_notifiers(net, RTNL_FAMILY_IP6MR, event_type,
&mfc->_c, tb_id, &net->ipv6.ipmr_seq);
}
/* Delete a VIF entry */
static int mif6_delete(struct mr_table *mrt, int vifi, int notify,
struct list_head *head)
{
......@@ -669,6 +711,11 @@ static int mif6_delete(struct mr_table *mrt, int vifi, int notify,
v = &mrt->vif_table[vifi];
if (VIF_EXISTS(mrt, vifi))
call_ip6mr_vif_entry_notifiers(read_pnet(&mrt->net),
FIB_EVENT_VIF_DEL, v, vifi,
mrt->id);
write_lock_bh(&mrt_lock);
dev = v->dev;
v->dev = NULL;
......@@ -887,6 +934,8 @@ static int mif6_add(struct net *net, struct mr_table *mrt,
if (vifi + 1 > mrt->maxvif)
mrt->maxvif = vifi + 1;
write_unlock_bh(&mrt_lock);
call_ip6mr_vif_entry_notifiers(net, FIB_EVENT_VIF_ADD,
v, vifi, mrt->id);
return 0;
}
......@@ -940,6 +989,8 @@ static struct mfc6_cache *ip6mr_cache_alloc(void)
return NULL;
c->_c.mfc_un.res.last_assert = jiffies - MFC_ASSERT_THRESH - 1;
c->_c.mfc_un.res.minvif = MAXMIFS;
c->_c.free = ip6mr_cache_free_rcu;
refcount_set(&c->_c.mfc_un.res.refcount, 1);
return c;
}
......@@ -1175,8 +1226,10 @@ static int ip6mr_mfc_delete(struct mr_table *mrt, struct mf6cctl *mfc,
rhltable_remove(&mrt->mfc_hash, &c->_c.mnode, ip6mr_rht_params);
list_del_rcu(&c->_c.list);
call_ip6mr_mfc_entry_notifiers(read_pnet(&mrt->net),
FIB_EVENT_ENTRY_DEL, c, mrt->id);
mr6_netlink_event(mrt, c, RTM_DELROUTE);
ip6mr_cache_free(c);
mr_cache_put(&c->_c);
return 0;
}
......@@ -1203,21 +1256,63 @@ static int ip6mr_device_event(struct notifier_block *this,
return NOTIFY_DONE;
}
static unsigned int ip6mr_seq_read(struct net *net)
{
ASSERT_RTNL();
return net->ipv6.ipmr_seq + ip6mr_rules_seq_read(net);
}
static int ip6mr_dump(struct net *net, struct notifier_block *nb)
{
return mr_dump(net, nb, RTNL_FAMILY_IP6MR, ip6mr_rules_dump,
ip6mr_mr_table_iter, &mrt_lock);
}
static struct notifier_block ip6_mr_notifier = {
.notifier_call = ip6mr_device_event
};
/*
* Setup for IP multicast routing
*/
static const struct fib_notifier_ops ip6mr_notifier_ops_template = {
.family = RTNL_FAMILY_IP6MR,
.fib_seq_read = ip6mr_seq_read,
.fib_dump = ip6mr_dump,
.owner = THIS_MODULE,
};
static int __net_init ip6mr_notifier_init(struct net *net)
{
struct fib_notifier_ops *ops;
net->ipv6.ipmr_seq = 0;
ops = fib_notifier_ops_register(&ip6mr_notifier_ops_template, net);
if (IS_ERR(ops))
return PTR_ERR(ops);
net->ipv6.ip6mr_notifier_ops = ops;
return 0;
}
static void __net_exit ip6mr_notifier_exit(struct net *net)
{
fib_notifier_ops_unregister(net->ipv6.ip6mr_notifier_ops);
net->ipv6.ip6mr_notifier_ops = NULL;
}
/* Setup for IP multicast routing */
static int __net_init ip6mr_net_init(struct net *net)
{
int err;
err = ip6mr_notifier_init(net);
if (err)
return err;
err = ip6mr_rules_init(net);
if (err < 0)
goto fail;
goto ip6mr_rules_fail;
#ifdef CONFIG_PROC_FS
err = -ENOMEM;
......@@ -1235,7 +1330,8 @@ static int __net_init ip6mr_net_init(struct net *net)
proc_vif_fail:
ip6mr_rules_exit(net);
#endif
fail:
ip6mr_rules_fail:
ip6mr_notifier_exit(net);
return err;
}
......@@ -1246,6 +1342,7 @@ static void __net_exit ip6mr_net_exit(struct net *net)
remove_proc_entry("ip6_mr_vif", net->proc_net);
#endif
ip6mr_rules_exit(net);
ip6mr_notifier_exit(net);
}
static struct pernet_operations ip6mr_net_ops = {
......@@ -1337,6 +1434,8 @@ static int ip6mr_mfc_add(struct net *net, struct mr_table *mrt,
if (!mrtsock)
c->_c.mfc_flags |= MFC_STATIC;
write_unlock_bh(&mrt_lock);
call_ip6mr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_REPLACE,
c, mrt->id);
mr6_netlink_event(mrt, c, RTM_NEWROUTE);
return 0;
}
......@@ -1388,6 +1487,8 @@ static int ip6mr_mfc_add(struct net *net, struct mr_table *mrt,
ip6mr_cache_resolve(net, mrt, uc, c);
ip6mr_cache_free(uc);
}
call_ip6mr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_ADD,
c, mrt->id);
mr6_netlink_event(mrt, c, RTM_NEWROUTE);
return 0;
}
......@@ -1417,13 +1518,17 @@ static void mroute_clean_tables(struct mr_table *mrt, bool all)
rhltable_remove(&mrt->mfc_hash, &c->mnode, ip6mr_rht_params);
list_del_rcu(&c->list);
mr6_netlink_event(mrt, (struct mfc6_cache *)c, RTM_DELROUTE);
ip6mr_cache_free((struct mfc6_cache *)c);
mr_cache_put(c);
}
if (atomic_read(&mrt->cache_resolve_queue_len) != 0) {
spin_lock_bh(&mfc_unres_lock);
list_for_each_entry_safe(c, tmp, &mrt->mfc_unres_queue, list) {
list_del(&c->list);
call_ip6mr_mfc_entry_notifiers(read_pnet(&mrt->net),
FIB_EVENT_ENTRY_DEL,
(struct mfc6_cache *)c,
mrt->id);
mr6_netlink_event(mrt, (struct mfc6_cache *)c,
RTM_DELROUTE);
ip6mr_destroy_unres(mrt, (struct mfc6_cache *)c);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment