mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-11-25 03:50:53 +07:00
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: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
336f2c038d
@ -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,
|
||||
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(char *payload, bool v, u16 offset,
|
||||
u16 virtual_router,
|
||||
enum mlxsw_reg_rmft2_irif_mask irif_mask, u16 irif,
|
||||
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,
|
||||
struct mlxsw_sp_mr_route *mr_route)
|
||||
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;
|
||||
else
|
||||
mr_route->mfc4->_c.mfc_flags &= ~MFC_OFFLOAD;
|
||||
break;
|
||||
case MLXSW_SP_L3_PROTO_IPV6:
|
||||
/* fall through */
|
||||
default:
|
||||
WARN_ON_ONCE(1);
|
||||
}
|
||||
if (offload)
|
||||
mr_route->mfc->mfc_flags |= MFC_OFFLOAD;
|
||||
else
|
||||
mr_route->mfc->mfc_flags &= ~MFC_OFFLOAD;
|
||||
}
|
||||
|
||||
static void mlxsw_sp_mr_mfc_offload_update(struct mlxsw_sp_mr_route *mr_route)
|
||||
@ -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);
|
||||
if (err)
|
||||
return err;
|
||||
break;
|
||||
case MLXSW_SP_L3_PROTO_IPV6:
|
||||
default:
|
||||
WARN_ON_ONCE(1);
|
||||
}
|
||||
route->parman_prio = parman_prio;
|
||||
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;
|
||||
|
||||
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,
|
||||
route->parman_prio, &route->parman_item);
|
||||
break;
|
||||
case MLXSW_SP_L3_PROTO_IPV6:
|
||||
default:
|
||||
WARN_ON_ONCE(1);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
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,
|
||||
®ion[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,
|
||||
®ion[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(®ion[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(®ion[MLXSW_SP_L3_PROTO_IPV6]);
|
||||
mlxsw_sp_mr_tcam_region_fini(®ion[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);
|
||||
if (err)
|
||||
goto err_mr_rif_add;
|
||||
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;
|
||||
|
110
net/ipv4/ipmr.c
110
net/ipv4/ipmr.c
@ -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);
|
||||
|
125
net/ipv6/ip6mr.c
125
net/ipv6/ip6mr.c
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user