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); ...@@ -4225,6 +4225,12 @@ MLXSW_ITEM32(reg, ritr, ipv6, 0x00, 28, 1);
*/ */
MLXSW_ITEM32(reg, ritr, ipv4_mc, 0x00, 27, 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 { enum mlxsw_reg_ritr_if_type {
/* VLAN interface. */ /* VLAN interface. */
MLXSW_REG_RITR_VLAN_IF, MLXSW_REG_RITR_VLAN_IF,
...@@ -4290,6 +4296,14 @@ MLXSW_ITEM32(reg, ritr, ipv6_fe, 0x04, 28, 1); ...@@ -4290,6 +4296,14 @@ MLXSW_ITEM32(reg, ritr, ipv6_fe, 0x04, 28, 1);
*/ */
MLXSW_ITEM32(reg, ritr, ipv4_mc_fe, 0x04, 27, 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 /* reg_ritr_lb_en
* Loop-back filter enable for unicast packets. * Loop-back filter enable for unicast packets.
* If the flag is set then loop-back filter for unicast packets is * 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, ...@@ -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_ipv4_set(payload, 1);
mlxsw_reg_ritr_ipv6_set(payload, 1); mlxsw_reg_ritr_ipv6_set(payload, 1);
mlxsw_reg_ritr_ipv4_mc_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_type_set(payload, type);
mlxsw_reg_ritr_op_set(payload, op); mlxsw_reg_ritr_op_set(payload, op);
mlxsw_reg_ritr_rif_set(payload, rif); mlxsw_reg_ritr_rif_set(payload, rif);
mlxsw_reg_ritr_ipv4_fe_set(payload, 1); mlxsw_reg_ritr_ipv4_fe_set(payload, 1);
mlxsw_reg_ritr_ipv6_fe_set(payload, 1); mlxsw_reg_ritr_ipv6_fe_set(payload, 1);
mlxsw_reg_ritr_ipv4_mc_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_lb_en_set(payload, 1);
mlxsw_reg_ritr_virtual_router_set(payload, vr_id); mlxsw_reg_ritr_virtual_router_set(payload, vr_id);
mlxsw_reg_ritr_mtu_set(payload, mtu); mlxsw_reg_ritr_mtu_set(payload, mtu);
...@@ -6302,30 +6318,34 @@ MLXSW_ITEM32(reg, rmft2, irif_mask, 0x08, 24, 1); ...@@ -6302,30 +6318,34 @@ MLXSW_ITEM32(reg, rmft2, irif_mask, 0x08, 24, 1);
*/ */
MLXSW_ITEM32(reg, rmft2, irif, 0x08, 0, 16); MLXSW_ITEM32(reg, rmft2, irif, 0x08, 0, 16);
/* reg_rmft2_dip4 /* reg_rmft2_dip{4,6}
* Destination IPv4 address * Destination IPv4/6 address
* Access: RW * Access: RW
*/ */
MLXSW_ITEM_BUF(reg, rmft2, dip6, 0x10, 16);
MLXSW_ITEM32(reg, rmft2, dip4, 0x1C, 0, 32); 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 * 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. * bit that is clear directs the TCAM to ignore the corresponding bit in key.
* Access: RW * Access: RW
*/ */
MLXSW_ITEM_BUF(reg, rmft2, dip6_mask, 0x20, 16);
MLXSW_ITEM32(reg, rmft2, dip4_mask, 0x2C, 0, 32); MLXSW_ITEM32(reg, rmft2, dip4_mask, 0x2C, 0, 32);
/* reg_rmft2_sip4 /* reg_rmft2_sip{4,6}
* Source IPv4 address * Source IPv4/6 address
* Access: RW * Access: RW
*/ */
MLXSW_ITEM_BUF(reg, rmft2, sip6, 0x30, 16);
MLXSW_ITEM32(reg, rmft2, sip4, 0x3C, 0, 32); 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 * 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. * bit that is clear directs the TCAM to ignore the corresponding bit in key.
* Access: RW * Access: RW
*/ */
MLXSW_ITEM_BUF(reg, rmft2, sip6_mask, 0x40, 16);
MLXSW_ITEM32(reg, rmft2, sip4_mask, 0x4C, 0, 32); MLXSW_ITEM32(reg, rmft2, sip4_mask, 0x4C, 0, 32);
/* reg_rmft2_flexible_action_set /* reg_rmft2_flexible_action_set
...@@ -6343,26 +6363,52 @@ MLXSW_ITEM_BUF(reg, rmft2, flexible_action_set, 0x80, ...@@ -6343,26 +6363,52 @@ MLXSW_ITEM_BUF(reg, rmft2, flexible_action_set, 0x80,
MLXSW_REG_FLEX_ACTION_SET_LEN); MLXSW_REG_FLEX_ACTION_SET_LEN);
static inline void 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, enum mlxsw_reg_rmft2_irif_mask irif_mask, u16 irif,
u32 dip4, u32 dip4_mask, u32 sip4, u32 sip4_mask, const char *flex_action_set)
const char *flexible_action_set)
{ {
MLXSW_REG_ZERO(rmft2, payload); MLXSW_REG_ZERO(rmft2, payload);
mlxsw_reg_rmft2_v_set(payload, v); 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_op_set(payload, MLXSW_REG_RMFT2_OP_READ_WRITE);
mlxsw_reg_rmft2_offset_set(payload, offset); mlxsw_reg_rmft2_offset_set(payload, offset);
mlxsw_reg_rmft2_virtual_router_set(payload, virtual_router); mlxsw_reg_rmft2_virtual_router_set(payload, virtual_router);
mlxsw_reg_rmft2_irif_mask_set(payload, irif_mask); mlxsw_reg_rmft2_irif_mask_set(payload, irif_mask);
mlxsw_reg_rmft2_irif_set(payload, irif); 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_set(payload, dip4);
mlxsw_reg_rmft2_dip4_mask_set(payload, dip4_mask); mlxsw_reg_rmft2_dip4_mask_set(payload, dip4_mask);
mlxsw_reg_rmft2_sip4_set(payload, sip4); mlxsw_reg_rmft2_sip4_set(payload, sip4);
mlxsw_reg_rmft2_sip4_mask_set(payload, sip4_mask); 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 /* MFCR - Management Fan Control Register
......
...@@ -3380,6 +3380,7 @@ static const struct mlxsw_listener mlxsw_sp_listener[] = { ...@@ -3380,6 +3380,7 @@ static const struct mlxsw_listener mlxsw_sp_listener[] = {
MLXSW_SP_RXL_NO_MARK(ACL0, TRAP_TO_CPU, IP2ME, false), MLXSW_SP_RXL_NO_MARK(ACL0, TRAP_TO_CPU, IP2ME, false),
/* Multicast Router Traps */ /* Multicast Router Traps */
MLXSW_SP_RXL_MARK(IPV4_PIM, TRAP_TO_CPU, PIM, false), 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(RPF, TRAP_TO_CPU, RPF, false),
MLXSW_SP_RXL_MARK(ACL1, TRAP_TO_CPU, MULTICAST, false), MLXSW_SP_RXL_MARK(ACL1, TRAP_TO_CPU, MULTICAST, false),
MLXSW_SP_RXL_MR_MARK(ACL2, TRAP_TO_CPU, MULTICAST, false), MLXSW_SP_RXL_MR_MARK(ACL2, TRAP_TO_CPU, MULTICAST, false),
......
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#define _MLXSW_SPECTRUM_MCROUTER_H #define _MLXSW_SPECTRUM_MCROUTER_H
#include <linux/mroute.h> #include <linux/mroute.h>
#include <linux/mroute6.h>
#include "spectrum_router.h" #include "spectrum_router.h"
#include "spectrum.h" #include "spectrum.h"
...@@ -109,10 +110,10 @@ struct mlxsw_sp_mr_table; ...@@ -109,10 +110,10 @@ struct mlxsw_sp_mr_table;
int mlxsw_sp_mr_init(struct mlxsw_sp *mlxsw_sp, int mlxsw_sp_mr_init(struct mlxsw_sp *mlxsw_sp,
const struct mlxsw_sp_mr_ops *mr_ops); const struct mlxsw_sp_mr_ops *mr_ops);
void mlxsw_sp_mr_fini(struct mlxsw_sp *mlxsw_sp); void mlxsw_sp_mr_fini(struct mlxsw_sp *mlxsw_sp);
int mlxsw_sp_mr_route4_add(struct mlxsw_sp_mr_table *mr_table, int mlxsw_sp_mr_route_add(struct mlxsw_sp_mr_table *mr_table,
struct mfc_cache *mfc, bool replace); struct mr_mfc *mfc, bool replace);
void mlxsw_sp_mr_route4_del(struct mlxsw_sp_mr_table *mr_table, void mlxsw_sp_mr_route_del(struct mlxsw_sp_mr_table *mr_table,
struct mfc_cache *mfc); struct mr_mfc *mfc);
int mlxsw_sp_mr_vif_add(struct mlxsw_sp_mr_table *mr_table, int mlxsw_sp_mr_vif_add(struct mlxsw_sp_mr_table *mr_table,
struct net_device *dev, vifi_t vif_index, struct net_device *dev, vifi_t vif_index,
unsigned long vif_flags, unsigned long vif_flags,
......
...@@ -51,7 +51,7 @@ struct mlxsw_sp_mr_tcam_region { ...@@ -51,7 +51,7 @@ struct mlxsw_sp_mr_tcam_region {
}; };
struct mlxsw_sp_mr_tcam { 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 */ /* 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, ...@@ -316,20 +316,37 @@ static int mlxsw_sp_mr_tcam_route_replace(struct mlxsw_sp *mlxsw_sp,
mlxsw_afa_block_first_set(afa_block)); mlxsw_afa_block_first_set(afa_block));
break; break;
case MLXSW_SP_L3_PROTO_IPV6: case MLXSW_SP_L3_PROTO_IPV6:
default: mlxsw_reg_rmft2_ipv6_pack(rmft2_pl, true, parman_item->index,
WARN_ON_ONCE(1); 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); 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, 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 parman_item *parman_item)
{ {
struct in6_addr zero_addr = IN6ADDR_ANY_INIT;
char rmft2_pl[MLXSW_REG_RMFT2_LEN]; char rmft2_pl[MLXSW_REG_RMFT2_LEN];
mlxsw_reg_rmft2_ipv4_pack(rmft2_pl, false, parman_item->index, vrid, switch (key->proto) {
0, 0, 0, 0, 0, 0, NULL); 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); 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, ...@@ -353,27 +370,30 @@ mlxsw_sp_mr_tcam_erif_populate(struct mlxsw_sp *mlxsw_sp,
return 0; 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 static int
mlxsw_sp_mr_tcam_route_parman_item_add(struct mlxsw_sp_mr_tcam *mr_tcam, mlxsw_sp_mr_tcam_route_parman_item_add(struct mlxsw_sp_mr_tcam *mr_tcam,
struct mlxsw_sp_mr_tcam_route *route, struct mlxsw_sp_mr_tcam_route *route,
enum mlxsw_sp_mr_route_prio prio) enum mlxsw_sp_mr_route_prio prio)
{ {
struct parman_prio *parman_prio = NULL; struct mlxsw_sp_mr_tcam_region *tcam_region;
int err; int err;
switch (route->key.proto) { tcam_region = mlxsw_sp_mr_tcam_protocol_region(mr_tcam,
case MLXSW_SP_L3_PROTO_IPV4: route->key.proto);
parman_prio = &mr_tcam->ipv4_tcam_region.parman_prios[prio]; err = parman_item_add(tcam_region->parman,
err = parman_item_add(mr_tcam->ipv4_tcam_region.parman, &tcam_region->parman_prios[prio],
parman_prio, &route->parman_item); &route->parman_item);
if (err) if (err)
return err; return err;
break;
case MLXSW_SP_L3_PROTO_IPV6: route->parman_prio = &tcam_region->parman_prios[prio];
default:
WARN_ON_ONCE(1);
}
route->parman_prio = parman_prio;
return 0; return 0;
} }
...@@ -381,15 +401,13 @@ static void ...@@ -381,15 +401,13 @@ static void
mlxsw_sp_mr_tcam_route_parman_item_remove(struct mlxsw_sp_mr_tcam *mr_tcam, mlxsw_sp_mr_tcam_route_parman_item_remove(struct mlxsw_sp_mr_tcam *mr_tcam,
struct mlxsw_sp_mr_tcam_route *route) struct mlxsw_sp_mr_tcam_route *route)
{ {
switch (route->key.proto) { struct mlxsw_sp_mr_tcam_region *tcam_region;
case MLXSW_SP_L3_PROTO_IPV4:
parman_item_remove(mr_tcam->ipv4_tcam_region.parman, 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); route->parman_prio, &route->parman_item);
break;
case MLXSW_SP_L3_PROTO_IPV6:
default:
WARN_ON_ONCE(1);
}
} }
static int static int
...@@ -462,7 +480,7 @@ static void mlxsw_sp_mr_tcam_route_destroy(struct mlxsw_sp *mlxsw_sp, ...@@ -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; struct mlxsw_sp_mr_tcam *mr_tcam = priv;
mlxsw_sp_mr_tcam_route_remove(mlxsw_sp, route->key.vrid, 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_route_parman_item_remove(mr_tcam, route);
mlxsw_sp_mr_tcam_afa_block_destroy(route->afa_block); mlxsw_sp_mr_tcam_afa_block_destroy(route->afa_block);
mlxsw_sp_flow_counter_free(mlxsw_sp, route->counter_index); 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) ...@@ -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) 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 *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) || if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MC_ERIF_LIST_ENTRIES) ||
!MLXSW_CORE_RES_VALID(mlxsw_sp->core, ACL_MAX_TCAM_RULES)) !MLXSW_CORE_RES_VALID(mlxsw_sp->core, ACL_MAX_TCAM_RULES))
return -EIO; return -EIO;
return mlxsw_sp_mr_tcam_region_init(mlxsw_sp, rtar_key = MLXSW_REG_RTAR_KEY_TYPE_IPV4_MULTICAST;
&mr_tcam->ipv4_tcam_region, err = mlxsw_sp_mr_tcam_region_init(mlxsw_sp,
MLXSW_REG_RTAR_KEY_TYPE_IPV4_MULTICAST); &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) static void mlxsw_sp_mr_tcam_fini(void *priv)
{ {
struct mlxsw_sp_mr_tcam *mr_tcam = 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 = { const struct mlxsw_sp_mr_ops mlxsw_sp_mr_tcam_ops = {
......
...@@ -41,6 +41,7 @@ ...@@ -41,6 +41,7 @@
enum mlxsw_sp_l3proto { enum mlxsw_sp_l3proto {
MLXSW_SP_L3_PROTO_IPV4, MLXSW_SP_L3_PROTO_IPV4,
MLXSW_SP_L3_PROTO_IPV6, MLXSW_SP_L3_PROTO_IPV6,
#define MLXSW_SP_L3_PROTO_MAX (MLXSW_SP_L3_PROTO_IPV6 + 1)
}; };
union mlxsw_sp_l3addr { union mlxsw_sp_l3addr {
......
...@@ -77,6 +77,7 @@ enum { ...@@ -77,6 +77,7 @@ enum {
MLXSW_TRAP_ID_IPV6_DHCP = 0x69, MLXSW_TRAP_ID_IPV6_DHCP = 0x69,
MLXSW_TRAP_ID_IPV6_ALL_ROUTERS_LINK = 0x6F, MLXSW_TRAP_ID_IPV6_ALL_ROUTERS_LINK = 0x6F,
MLXSW_TRAP_ID_RTR_INGRESS0 = 0x70, MLXSW_TRAP_ID_RTR_INGRESS0 = 0x70,
MLXSW_TRAP_ID_IPV6_PIM = 0x79,
MLXSW_TRAP_ID_IPV4_BGP = 0x88, MLXSW_TRAP_ID_IPV4_BGP = 0x88,
MLXSW_TRAP_ID_IPV6_BGP = 0x89, MLXSW_TRAP_ID_IPV6_BGP = 0x89,
MLXSW_TRAP_ID_L3_IPV6_ROUTER_SOLICITATION = 0x8A, MLXSW_TRAP_ID_L3_IPV6_ROUTER_SOLICITATION = 0x8A,
......
...@@ -55,14 +55,6 @@ static inline bool ipmr_rule_default(const struct fib_rule *rule) ...@@ -55,14 +55,6 @@ static inline bool ipmr_rule_default(const struct fib_rule *rule)
} }
#endif #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 #define VIFF_STATIC 0x8000
struct mfc_cache_cmp_arg { struct mfc_cache_cmp_arg {
...@@ -88,33 +80,8 @@ struct mfc_cache { ...@@ -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; struct rtmsg;
int ipmr_get_route(struct net *net, struct sk_buff *skb, int ipmr_get_route(struct net *net, struct sk_buff *skb,
__be32 saddr, __be32 daddr, __be32 saddr, __be32 daddr,
struct rtmsg *rtm, u32 portid); 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 #endif
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <net/net_namespace.h> #include <net/net_namespace.h>
#include <uapi/linux/mroute6.h> #include <uapi/linux/mroute6.h>
#include <linux/mroute_base.h> #include <linux/mroute_base.h>
#include <net/fib_rules.h>
#ifdef CONFIG_IPV6_MROUTE #ifdef CONFIG_IPV6_MROUTE
static inline int ip6_mroute_opt(int opt) static inline int ip6_mroute_opt(int opt)
...@@ -63,6 +64,15 @@ static inline void ip6_mr_cleanup(void) ...@@ -63,6 +64,15 @@ static inline void ip6_mr_cleanup(void)
} }
#endif #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 #define VIFF_STATIC 0x8000
struct mfc6_cache_cmp_arg { struct mfc6_cache_cmp_arg {
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <net/net_namespace.h> #include <net/net_namespace.h>
#include <net/sock.h> #include <net/sock.h>
#include <net/fib_notifier.h>
/** /**
* struct vif_device - interface representor for multicast routing * struct vif_device - interface representor for multicast routing
...@@ -36,6 +37,58 @@ struct vif_device { ...@@ -36,6 +37,58 @@ struct vif_device {
__be32 local, remote; __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 #ifndef MAXVIFS
/* This one is nasty; value is defined in uapi using different symbols for /* This one is nasty; value is defined in uapi using different symbols for
* mroute and morute6 but both map into same 32. * mroute and morute6 but both map into same 32.
...@@ -72,6 +125,7 @@ enum { ...@@ -72,6 +125,7 @@ enum {
* @refcount: reference count for this entry * @refcount: reference count for this entry
* @list: global entry list * @list: global entry list
* @rcu: used for entry destruction * @rcu: used for entry destruction
* @free: Operation used for freeing an entry under RCU
*/ */
struct mr_mfc { struct mr_mfc {
struct rhlist_head mnode; struct rhlist_head mnode;
...@@ -97,8 +151,64 @@ struct mr_mfc { ...@@ -97,8 +151,64 @@ struct mr_mfc {
} mfc_un; } mfc_un;
struct list_head list; struct list_head list;
struct rcu_head rcu; 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; struct mr_table;
/** /**
...@@ -180,6 +290,13 @@ int mr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb, ...@@ -180,6 +290,13 @@ int mr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb,
u32 portid, u32 seq, struct mr_mfc *c, u32 portid, u32 seq, struct mr_mfc *c,
int cmd, int flags), int cmd, int flags),
spinlock_t *lock); 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 #else
static inline void vif_device_init(struct vif_device *v, static inline void vif_device_init(struct vif_device *v,
struct net_device *dev, struct net_device *dev,
...@@ -236,6 +353,17 @@ mr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb, ...@@ -236,6 +353,17 @@ mr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb,
{ {
return -EINVAL; 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 #endif
static inline void *mr_mfc_find(struct mr_table *mrt, void *hasharg) static inline void *mr_mfc_find(struct mr_table *mrt, void *hasharg)
......
...@@ -96,6 +96,8 @@ struct netns_ipv6 { ...@@ -96,6 +96,8 @@ struct netns_ipv6 {
atomic_t fib6_sernum; atomic_t fib6_sernum;
struct seg6_pernet_data *seg6_data; struct seg6_pernet_data *seg6_data;
struct fib_notifier_ops *notifier_ops; struct fib_notifier_ops *notifier_ops;
struct fib_notifier_ops *ip6mr_notifier_ops;
unsigned int ipmr_seq; /* protected by rtnl_mutex */
struct { struct {
struct hlist_head head; struct hlist_head head;
spinlock_t lock; spinlock_t lock;
......
...@@ -644,80 +644,22 @@ static struct net_device *ipmr_reg_vif(struct net *net, struct mr_table *mrt) ...@@ -644,80 +644,22 @@ static struct net_device *ipmr_reg_vif(struct net *net, struct mr_table *mrt)
} }
#endif #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, static int call_ipmr_vif_entry_notifiers(struct net *net,
enum fib_event_type event_type, enum fib_event_type event_type,
struct vif_device *vif, struct vif_device *vif,
vifi_t vif_index, u32 tb_id) vifi_t vif_index, u32 tb_id)
{ {
struct vif_entry_notifier_info info = { return mr_call_vif_notifiers(net, RTNL_FAMILY_IPMR, event_type,
.info = { vif, vif_index, tb_id,
.family = RTNL_FAMILY_IPMR, &net->ipv4.ipmr_seq);
.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);
} }
static int call_ipmr_mfc_entry_notifiers(struct net *net, static int call_ipmr_mfc_entry_notifiers(struct net *net,
enum fib_event_type event_type, enum fib_event_type event_type,
struct mfc_cache *mfc, u32 tb_id) struct mfc_cache *mfc, u32 tb_id)
{ {
struct mfc_entry_notifier_info info = { return mr_call_mfc_notifiers(net, RTNL_FAMILY_IPMR, event_type,
.info = { &mfc->_c, tb_id, &net->ipv4.ipmr_seq);
.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);
} }
/** /**
...@@ -790,11 +732,10 @@ static void ipmr_cache_free_rcu(struct rcu_head *head) ...@@ -790,11 +732,10 @@ static void ipmr_cache_free_rcu(struct rcu_head *head)
kmem_cache_free(mrt_cachep, (struct mfc_cache *)c); 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); call_rcu(&c->_c.rcu, ipmr_cache_free_rcu);
} }
EXPORT_SYMBOL(ipmr_cache_free);
/* Destroy an unresolved cache entry, killing queued skbs /* Destroy an unresolved cache entry, killing queued skbs
* and reporting error to netlink readers. * and reporting error to netlink readers.
...@@ -1045,6 +986,7 @@ static struct mfc_cache *ipmr_cache_alloc(void) ...@@ -1045,6 +986,7 @@ static struct mfc_cache *ipmr_cache_alloc(void)
if (c) { if (c) {
c->_c.mfc_un.res.last_assert = jiffies - MFC_ASSERT_THRESH - 1; c->_c.mfc_un.res.last_assert = jiffies - MFC_ASSERT_THRESH - 1;
c->_c.mfc_un.res.minvif = MAXVIFS; c->_c.mfc_un.res.minvif = MAXVIFS;
c->_c.free = ipmr_cache_free_rcu;
refcount_set(&c->_c.mfc_un.res.refcount, 1); refcount_set(&c->_c.mfc_un.res.refcount, 1);
} }
return c; return c;
...@@ -1264,7 +1206,7 @@ static int ipmr_mfc_delete(struct mr_table *mrt, struct mfcctl *mfc, int parent) ...@@ -1264,7 +1206,7 @@ static int ipmr_mfc_delete(struct mr_table *mrt, struct mfcctl *mfc, int parent)
list_del_rcu(&c->_c.list); list_del_rcu(&c->_c.list);
call_ipmr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, c, mrt->id); call_ipmr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, c, mrt->id);
mroute_netlink_event(mrt, c, RTM_DELROUTE); mroute_netlink_event(mrt, c, RTM_DELROUTE);
ipmr_cache_put(c); mr_cache_put(&c->_c);
return 0; return 0;
} }
...@@ -1376,7 +1318,7 @@ static void mroute_clean_tables(struct mr_table *mrt, bool all) ...@@ -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, call_ipmr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, cache,
mrt->id); mrt->id);
mroute_netlink_event(mrt, cache, RTM_DELROUTE); mroute_netlink_event(mrt, cache, RTM_DELROUTE);
ipmr_cache_put(cache); mr_cache_put(c);
} }
if (atomic_read(&mrt->cache_resolve_queue_len) != 0) { if (atomic_read(&mrt->cache_resolve_queue_len) != 0) {
...@@ -2989,38 +2931,8 @@ static unsigned int ipmr_seq_read(struct net *net) ...@@ -2989,38 +2931,8 @@ static unsigned int ipmr_seq_read(struct net *net)
static int ipmr_dump(struct net *net, struct notifier_block *nb) static int ipmr_dump(struct net *net, struct notifier_block *nb)
{ {
struct mr_table *mrt; return mr_dump(net, nb, RTNL_FAMILY_IPMR, ipmr_rules_dump,
int err; ipmr_mr_table_iter, &mrt_lock);
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;
} }
static const struct fib_notifier_ops ipmr_notifier_ops_template = { 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, ...@@ -321,3 +321,45 @@ int mr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb,
return skb->len; return skb->len;
} }
EXPORT_SYMBOL(mr_rtm_dumproute); 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) ...@@ -258,6 +258,23 @@ static void __net_exit ip6mr_rules_exit(struct net *net)
fib_rules_unregister(net->ipv6.mr6_rules_ops); fib_rules_unregister(net->ipv6.mr6_rules_ops);
rtnl_unlock(); 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 #else
#define ip6mr_for_each_table(mrt, net) \ #define ip6mr_for_each_table(mrt, net) \
for (mrt = net->ipv6.mrt6; mrt; mrt = NULL) for (mrt = net->ipv6.mrt6; mrt; mrt = NULL)
...@@ -295,6 +312,16 @@ static void __net_exit ip6mr_rules_exit(struct net *net) ...@@ -295,6 +312,16 @@ static void __net_exit ip6mr_rules_exit(struct net *net)
net->ipv6.mrt6 = NULL; net->ipv6.mrt6 = NULL;
rtnl_unlock(); 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 #endif
static int ip6mr_hash_cmp(struct rhashtable_compare_arg *arg, 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) ...@@ -653,10 +680,25 @@ static struct net_device *ip6mr_reg_vif(struct net *net, struct mr_table *mrt)
} }
#endif #endif
/* static int call_ip6mr_vif_entry_notifiers(struct net *net,
* Delete a VIF entry 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, static int mif6_delete(struct mr_table *mrt, int vifi, int notify,
struct list_head *head) struct list_head *head)
{ {
...@@ -669,6 +711,11 @@ static int mif6_delete(struct mr_table *mrt, int vifi, int notify, ...@@ -669,6 +711,11 @@ static int mif6_delete(struct mr_table *mrt, int vifi, int notify,
v = &mrt->vif_table[vifi]; 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); write_lock_bh(&mrt_lock);
dev = v->dev; dev = v->dev;
v->dev = NULL; v->dev = NULL;
...@@ -887,6 +934,8 @@ static int mif6_add(struct net *net, struct mr_table *mrt, ...@@ -887,6 +934,8 @@ static int mif6_add(struct net *net, struct mr_table *mrt,
if (vifi + 1 > mrt->maxvif) if (vifi + 1 > mrt->maxvif)
mrt->maxvif = vifi + 1; mrt->maxvif = vifi + 1;
write_unlock_bh(&mrt_lock); write_unlock_bh(&mrt_lock);
call_ip6mr_vif_entry_notifiers(net, FIB_EVENT_VIF_ADD,
v, vifi, mrt->id);
return 0; return 0;
} }
...@@ -940,6 +989,8 @@ static struct mfc6_cache *ip6mr_cache_alloc(void) ...@@ -940,6 +989,8 @@ static struct mfc6_cache *ip6mr_cache_alloc(void)
return NULL; return NULL;
c->_c.mfc_un.res.last_assert = jiffies - MFC_ASSERT_THRESH - 1; c->_c.mfc_un.res.last_assert = jiffies - MFC_ASSERT_THRESH - 1;
c->_c.mfc_un.res.minvif = MAXMIFS; 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; return c;
} }
...@@ -1175,8 +1226,10 @@ static int ip6mr_mfc_delete(struct mr_table *mrt, struct mf6cctl *mfc, ...@@ -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); rhltable_remove(&mrt->mfc_hash, &c->_c.mnode, ip6mr_rht_params);
list_del_rcu(&c->_c.list); 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); mr6_netlink_event(mrt, c, RTM_DELROUTE);
ip6mr_cache_free(c); mr_cache_put(&c->_c);
return 0; return 0;
} }
...@@ -1203,21 +1256,63 @@ static int ip6mr_device_event(struct notifier_block *this, ...@@ -1203,21 +1256,63 @@ static int ip6mr_device_event(struct notifier_block *this,
return NOTIFY_DONE; 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 = { static struct notifier_block ip6_mr_notifier = {
.notifier_call = ip6mr_device_event .notifier_call = ip6mr_device_event
}; };
/* static const struct fib_notifier_ops ip6mr_notifier_ops_template = {
* Setup for IP multicast routing .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) static int __net_init ip6mr_net_init(struct net *net)
{ {
int err; int err;
err = ip6mr_notifier_init(net);
if (err)
return err;
err = ip6mr_rules_init(net); err = ip6mr_rules_init(net);
if (err < 0) if (err < 0)
goto fail; goto ip6mr_rules_fail;
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
err = -ENOMEM; err = -ENOMEM;
...@@ -1235,7 +1330,8 @@ static int __net_init ip6mr_net_init(struct net *net) ...@@ -1235,7 +1330,8 @@ static int __net_init ip6mr_net_init(struct net *net)
proc_vif_fail: proc_vif_fail:
ip6mr_rules_exit(net); ip6mr_rules_exit(net);
#endif #endif
fail: ip6mr_rules_fail:
ip6mr_notifier_exit(net);
return err; return err;
} }
...@@ -1246,6 +1342,7 @@ static void __net_exit ip6mr_net_exit(struct net *net) ...@@ -1246,6 +1342,7 @@ static void __net_exit ip6mr_net_exit(struct net *net)
remove_proc_entry("ip6_mr_vif", net->proc_net); remove_proc_entry("ip6_mr_vif", net->proc_net);
#endif #endif
ip6mr_rules_exit(net); ip6mr_rules_exit(net);
ip6mr_notifier_exit(net);
} }
static struct pernet_operations ip6mr_net_ops = { static struct pernet_operations ip6mr_net_ops = {
...@@ -1337,6 +1434,8 @@ static int ip6mr_mfc_add(struct net *net, struct mr_table *mrt, ...@@ -1337,6 +1434,8 @@ static int ip6mr_mfc_add(struct net *net, struct mr_table *mrt,
if (!mrtsock) if (!mrtsock)
c->_c.mfc_flags |= MFC_STATIC; c->_c.mfc_flags |= MFC_STATIC;
write_unlock_bh(&mrt_lock); 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); mr6_netlink_event(mrt, c, RTM_NEWROUTE);
return 0; return 0;
} }
...@@ -1388,6 +1487,8 @@ static int ip6mr_mfc_add(struct net *net, struct mr_table *mrt, ...@@ -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_resolve(net, mrt, uc, c);
ip6mr_cache_free(uc); ip6mr_cache_free(uc);
} }
call_ip6mr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_ADD,
c, mrt->id);
mr6_netlink_event(mrt, c, RTM_NEWROUTE); mr6_netlink_event(mrt, c, RTM_NEWROUTE);
return 0; return 0;
} }
...@@ -1417,13 +1518,17 @@ static void mroute_clean_tables(struct mr_table *mrt, bool all) ...@@ -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); rhltable_remove(&mrt->mfc_hash, &c->mnode, ip6mr_rht_params);
list_del_rcu(&c->list); list_del_rcu(&c->list);
mr6_netlink_event(mrt, (struct mfc6_cache *)c, RTM_DELROUTE); 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) { if (atomic_read(&mrt->cache_resolve_queue_len) != 0) {
spin_lock_bh(&mfc_unres_lock); spin_lock_bh(&mfc_unres_lock);
list_for_each_entry_safe(c, tmp, &mrt->mfc_unres_queue, list) { list_for_each_entry_safe(c, tmp, &mrt->mfc_unres_queue, list) {
list_del(&c->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, mr6_netlink_event(mrt, (struct mfc6_cache *)c,
RTM_DELROUTE); RTM_DELROUTE);
ip6mr_destroy_unres(mrt, (struct mfc6_cache *)c); 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