Commit 7d2a055b authored by David S. Miller's avatar David S. Miller

Merge branch 'mlxsw-Preparations-for-IPv6-UC-router'

Jiri Pirko says:

====================
mlxsw: Preparations for IPv6 UC router

Ido says:

The purpose of this set is to prepare the driver for the introduction of
IPv6 FIB offload. It's mainly composed of small and non-functional
changes, that either add the IPv6 equivalent of existing IPv4 code or
aimed at making the introduction of IPv6-specific code easier.

The first five patches enable IPv6 forwarding in the device and allow us
to configure router interfaces (RIFs) based on inet6addr notifications.

The next six patches add support for programming IPv6 neighbours into
the device's table as well as dumping their activity and updating the
kernel accordingly.

The last 11 patches extend current infrastructure to allow us to program
IPv6 routes, set catch-all IPv6 trap in case of abort and make the code
more receptive towards up-coming changes.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 7f81ff04 7dcc18ad
......@@ -3679,16 +3679,17 @@ enum mlxsw_reg_htgt_trap_group {
MLXSW_REG_HTGT_TRAP_GROUP_SP_LACP,
MLXSW_REG_HTGT_TRAP_GROUP_SP_LLDP,
MLXSW_REG_HTGT_TRAP_GROUP_SP_IGMP,
MLXSW_REG_HTGT_TRAP_GROUP_SP_BGP_IPV4,
MLXSW_REG_HTGT_TRAP_GROUP_SP_BGP,
MLXSW_REG_HTGT_TRAP_GROUP_SP_OSPF,
MLXSW_REG_HTGT_TRAP_GROUP_SP_ARP,
MLXSW_REG_HTGT_TRAP_GROUP_SP_ARP_MISS,
MLXSW_REG_HTGT_TRAP_GROUP_SP_HOST_MISS,
MLXSW_REG_HTGT_TRAP_GROUP_SP_ROUTER_EXP,
MLXSW_REG_HTGT_TRAP_GROUP_SP_REMOTE_ROUTE,
MLXSW_REG_HTGT_TRAP_GROUP_SP_IP2ME,
MLXSW_REG_HTGT_TRAP_GROUP_SP_DHCP,
MLXSW_REG_HTGT_TRAP_GROUP_SP_EVENT,
MLXSW_REG_HTGT_TRAP_GROUP_SP_IPV6_MLD,
MLXSW_REG_HTGT_TRAP_GROUP_SP_IPV6_ND,
};
/* reg_htgt_trap_group
......@@ -3953,10 +3954,12 @@ MLXSW_ITEM32(reg, rgcr, pcp_rw, 0x18, 16, 2);
*/
MLXSW_ITEM32(reg, rgcr, activity_dis, 0x20, 0, 8);
static inline void mlxsw_reg_rgcr_pack(char *payload, bool ipv4_en)
static inline void mlxsw_reg_rgcr_pack(char *payload, bool ipv4_en,
bool ipv6_en)
{
MLXSW_REG_ZERO(rgcr, payload);
mlxsw_reg_rgcr_ipv4_en_set(payload, ipv4_en);
mlxsw_reg_rgcr_ipv6_en_set(payload, ipv6_en);
}
/* RITR - Router Interface Table Register
......@@ -4204,10 +4207,12 @@ static inline void mlxsw_reg_ritr_pack(char *payload, bool enable,
MLXSW_REG_ZERO(ritr, payload);
mlxsw_reg_ritr_enable_set(payload, enable);
mlxsw_reg_ritr_ipv4_set(payload, 1);
mlxsw_reg_ritr_ipv6_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_lb_en_set(payload, 1);
mlxsw_reg_ritr_virtual_router_set(payload, vr_id);
mlxsw_reg_ritr_mtu_set(payload, mtu);
......@@ -4719,6 +4724,7 @@ MLXSW_ITEM32(reg, ralue, prefix_len, 0x08, 0, 8);
* Access: Index
*/
MLXSW_ITEM32(reg, ralue, dip4, 0x18, 0, 32);
MLXSW_ITEM_BUF(reg, ralue, dip6, 0x0C, 16);
enum mlxsw_reg_ralue_entry_type {
MLXSW_REG_RALUE_ENTRY_TYPE_MARKER_ENTRY = 1,
......@@ -4852,6 +4858,16 @@ static inline void mlxsw_reg_ralue_pack4(char *payload,
mlxsw_reg_ralue_dip4_set(payload, dip);
}
static inline void mlxsw_reg_ralue_pack6(char *payload,
enum mlxsw_reg_ralxx_protocol protocol,
enum mlxsw_reg_ralue_op op,
u16 virtual_router, u8 prefix_len,
const void *dip)
{
mlxsw_reg_ralue_pack(payload, protocol, op, virtual_router, prefix_len);
mlxsw_reg_ralue_dip6_memcpy_to(payload, dip);
}
static inline void
mlxsw_reg_ralue_act_remote_pack(char *payload,
enum mlxsw_reg_ralue_trap_action trap_action,
......@@ -4955,6 +4971,7 @@ MLXSW_ITEM32(reg, rauht, rif, 0x00, 0, 16);
* Access: Index
*/
MLXSW_ITEM32(reg, rauht, dip4, 0x1C, 0x0, 32);
MLXSW_ITEM_BUF(reg, rauht, dip6, 0x10, 16);
enum mlxsw_reg_rauht_trap_action {
MLXSW_REG_RAUHT_TRAP_ACTION_NOP,
......@@ -5019,6 +5036,15 @@ static inline void mlxsw_reg_rauht_pack4(char *payload,
mlxsw_reg_rauht_dip4_set(payload, dip);
}
static inline void mlxsw_reg_rauht_pack6(char *payload,
enum mlxsw_reg_rauht_op op, u16 rif,
const char *mac, const char *dip)
{
mlxsw_reg_rauht_pack(payload, op, rif, mac);
mlxsw_reg_rauht_type_set(payload, MLXSW_REG_RAUHT_TYPE_IPV6);
mlxsw_reg_rauht_dip6_memcpy_to(payload, dip);
}
/* RALEU - Router Algorithmic LPM ECMP Update Register
* ---------------------------------------------------
* The register enables updating the ECMP section in the action for multiple
......@@ -5217,6 +5243,30 @@ MLXSW_ITEM32_INDEXED(reg, rauhtd, ipv4_ent_rif, MLXSW_REG_RAUHTD_BASE_LEN, 0,
MLXSW_ITEM32_INDEXED(reg, rauhtd, ipv4_ent_dip, MLXSW_REG_RAUHTD_BASE_LEN, 0,
32, MLXSW_REG_RAUHTD_IPV4_ENT_LEN, 0x04, false);
#define MLXSW_REG_RAUHTD_IPV6_ENT_LEN 0x20
/* reg_rauhtd_ipv6_ent_a
* Activity. Set for new entries. Set if a packet lookup has hit on the
* specific entry.
* Access: RO
*/
MLXSW_ITEM32_INDEXED(reg, rauhtd, ipv6_ent_a, MLXSW_REG_RAUHTD_BASE_LEN, 16, 1,
MLXSW_REG_RAUHTD_IPV6_ENT_LEN, 0x00, false);
/* reg_rauhtd_ipv6_ent_rif
* Router interface.
* Access: RO
*/
MLXSW_ITEM32_INDEXED(reg, rauhtd, ipv6_ent_rif, MLXSW_REG_RAUHTD_BASE_LEN, 0,
16, MLXSW_REG_RAUHTD_IPV6_ENT_LEN, 0x00, false);
/* reg_rauhtd_ipv6_ent_dip
* Destination IPv6 address.
* Access: RO
*/
MLXSW_ITEM_BUF_INDEXED(reg, rauhtd, ipv6_ent_dip, MLXSW_REG_RAUHTD_BASE_LEN,
16, MLXSW_REG_RAUHTD_IPV6_ENT_LEN, 0x10);
static inline void mlxsw_reg_rauhtd_ent_ipv4_unpack(char *payload,
int ent_index, u16 *p_rif,
u32 *p_dip)
......@@ -5225,6 +5275,14 @@ static inline void mlxsw_reg_rauhtd_ent_ipv4_unpack(char *payload,
*p_dip = mlxsw_reg_rauhtd_ipv4_ent_dip_get(payload, ent_index);
}
static inline void mlxsw_reg_rauhtd_ent_ipv6_unpack(char *payload,
int rec_index, u16 *p_rif,
char *p_dip)
{
*p_rif = mlxsw_reg_rauhtd_ipv6_ent_rif_get(payload, rec_index);
mlxsw_reg_rauhtd_ipv6_ent_dip_memcpy_from(payload, rec_index, p_dip);
}
/* MFCR - Management Fan Control Register
* --------------------------------------
* This register controls the settings of the Fan Speed PWM mechanism.
......
......@@ -58,6 +58,7 @@
#include <net/tc_act/tc_mirred.h>
#include <net/netevent.h>
#include <net/tc_act/tc_sample.h>
#include <net/addrconf.h>
#include "spectrum.h"
#include "pci.h"
......@@ -3345,12 +3346,35 @@ static const struct mlxsw_listener mlxsw_sp_listener[] = {
MLXSW_SP_RXL_MARK(MTUERROR, TRAP_TO_CPU, ROUTER_EXP, false),
MLXSW_SP_RXL_MARK(TTLERROR, TRAP_TO_CPU, ROUTER_EXP, false),
MLXSW_SP_RXL_MARK(LBERROR, TRAP_TO_CPU, ROUTER_EXP, false),
MLXSW_SP_RXL_MARK(OSPF, TRAP_TO_CPU, OSPF, false),
MLXSW_SP_RXL_MARK(IP2ME, TRAP_TO_CPU, IP2ME, false),
MLXSW_SP_RXL_MARK(IPV6_UNSPECIFIED_ADDRESS, TRAP_TO_CPU, ROUTER_EXP,
false),
MLXSW_SP_RXL_MARK(IPV6_LINK_LOCAL_DEST, TRAP_TO_CPU, ROUTER_EXP, false),
MLXSW_SP_RXL_MARK(IPV6_LINK_LOCAL_SRC, TRAP_TO_CPU, ROUTER_EXP, false),
MLXSW_SP_RXL_MARK(IPV6_ALL_NODES_LINK, TRAP_TO_CPU, ROUTER_EXP, false),
MLXSW_SP_RXL_MARK(IPV6_ALL_ROUTERS_LINK, TRAP_TO_CPU, ROUTER_EXP,
false),
MLXSW_SP_RXL_MARK(IPV4_OSPF, TRAP_TO_CPU, OSPF, false),
MLXSW_SP_RXL_MARK(IPV6_OSPF, TRAP_TO_CPU, OSPF, false),
MLXSW_SP_RXL_MARK(IPV6_DHCP, TRAP_TO_CPU, DHCP, false),
MLXSW_SP_RXL_MARK(RTR_INGRESS0, TRAP_TO_CPU, REMOTE_ROUTE, false),
MLXSW_SP_RXL_MARK(HOST_MISS_IPV4, TRAP_TO_CPU, ARP_MISS, false),
MLXSW_SP_RXL_MARK(BGP_IPV4, TRAP_TO_CPU, BGP_IPV4, false),
MLXSW_SP_RXL_MARK(IPV4_BGP, TRAP_TO_CPU, BGP, false),
MLXSW_SP_RXL_MARK(IPV6_BGP, TRAP_TO_CPU, BGP, false),
MLXSW_SP_RXL_MARK(L3_IPV6_ROUTER_SOLICITATION, TRAP_TO_CPU, IPV6_ND,
false),
MLXSW_SP_RXL_MARK(L3_IPV6_ROUTER_ADVERTISMENT, TRAP_TO_CPU, IPV6_ND,
false),
MLXSW_SP_RXL_MARK(L3_IPV6_NEIGHBOR_SOLICITATION, TRAP_TO_CPU, IPV6_ND,
false),
MLXSW_SP_RXL_MARK(L3_IPV6_NEIGHBOR_ADVERTISMENT, TRAP_TO_CPU, IPV6_ND,
false),
MLXSW_SP_RXL_MARK(L3_IPV6_REDIRECTION, TRAP_TO_CPU, IPV6_ND, false),
MLXSW_SP_RXL_MARK(IPV6_MC_LINK_LOCAL_DEST, TRAP_TO_CPU, ROUTER_EXP,
false),
MLXSW_SP_RXL_MARK(HOST_MISS_IPV4, TRAP_TO_CPU, HOST_MISS, false),
MLXSW_SP_RXL_MARK(HOST_MISS_IPV6, TRAP_TO_CPU, HOST_MISS, false),
MLXSW_SP_RXL_MARK(ROUTER_ALERT_IPV4, TRAP_TO_CPU, ROUTER_EXP, false),
MLXSW_SP_RXL_MARK(ROUTER_ALERT_IPV6, TRAP_TO_CPU, ROUTER_EXP, false),
/* PKT Sample trap */
MLXSW_RXL(mlxsw_sp_rx_listener_sample_func, PKT_SAMPLE, MIRROR_TO_CPU,
false, SP_IP2ME, DISCARD),
......@@ -3389,12 +3413,13 @@ static int mlxsw_sp_cpu_policers_set(struct mlxsw_core *mlxsw_core)
rate = 16 * 1024;
burst_size = 10;
break;
case MLXSW_REG_HTGT_TRAP_GROUP_SP_BGP_IPV4:
case MLXSW_REG_HTGT_TRAP_GROUP_SP_BGP:
case MLXSW_REG_HTGT_TRAP_GROUP_SP_ARP:
case MLXSW_REG_HTGT_TRAP_GROUP_SP_DHCP:
case MLXSW_REG_HTGT_TRAP_GROUP_SP_ARP_MISS:
case MLXSW_REG_HTGT_TRAP_GROUP_SP_HOST_MISS:
case MLXSW_REG_HTGT_TRAP_GROUP_SP_ROUTER_EXP:
case MLXSW_REG_HTGT_TRAP_GROUP_SP_REMOTE_ROUTE:
case MLXSW_REG_HTGT_TRAP_GROUP_SP_IPV6_ND:
rate = 1024;
burst_size = 7;
break;
......@@ -3443,7 +3468,7 @@ static int mlxsw_sp_trap_groups_set(struct mlxsw_core *mlxsw_core)
priority = 5;
tc = 5;
break;
case MLXSW_REG_HTGT_TRAP_GROUP_SP_BGP_IPV4:
case MLXSW_REG_HTGT_TRAP_GROUP_SP_BGP:
case MLXSW_REG_HTGT_TRAP_GROUP_SP_DHCP:
priority = 4;
tc = 4;
......@@ -3455,10 +3480,11 @@ static int mlxsw_sp_trap_groups_set(struct mlxsw_core *mlxsw_core)
tc = 3;
break;
case MLXSW_REG_HTGT_TRAP_GROUP_SP_ARP:
case MLXSW_REG_HTGT_TRAP_GROUP_SP_IPV6_ND:
priority = 2;
tc = 2;
break;
case MLXSW_REG_HTGT_TRAP_GROUP_SP_ARP_MISS:
case MLXSW_REG_HTGT_TRAP_GROUP_SP_HOST_MISS:
case MLXSW_REG_HTGT_TRAP_GROUP_SP_ROUTER_EXP:
case MLXSW_REG_HTGT_TRAP_GROUP_SP_REMOTE_ROUTE:
priority = 1;
......@@ -4368,6 +4394,10 @@ static struct notifier_block mlxsw_sp_inetaddr_nb __read_mostly = {
.priority = 10, /* Must be called before FIB notifier block */
};
static struct notifier_block mlxsw_sp_inet6addr_nb __read_mostly = {
.notifier_call = mlxsw_sp_inet6addr_event,
};
static struct notifier_block mlxsw_sp_router_netevent_nb __read_mostly = {
.notifier_call = mlxsw_sp_router_netevent_event,
};
......@@ -4388,6 +4418,7 @@ static int __init mlxsw_sp_module_init(void)
register_netdevice_notifier(&mlxsw_sp_netdevice_nb);
register_inetaddr_notifier(&mlxsw_sp_inetaddr_nb);
register_inet6addr_notifier(&mlxsw_sp_inet6addr_nb);
register_netevent_notifier(&mlxsw_sp_router_netevent_nb);
err = mlxsw_core_driver_register(&mlxsw_sp_driver);
......@@ -4404,6 +4435,7 @@ static int __init mlxsw_sp_module_init(void)
mlxsw_core_driver_unregister(&mlxsw_sp_driver);
err_core_driver_register:
unregister_netevent_notifier(&mlxsw_sp_router_netevent_nb);
unregister_inet6addr_notifier(&mlxsw_sp_inet6addr_nb);
unregister_inetaddr_notifier(&mlxsw_sp_inetaddr_nb);
unregister_netdevice_notifier(&mlxsw_sp_netdevice_nb);
return err;
......@@ -4414,6 +4446,7 @@ static void __exit mlxsw_sp_module_exit(void)
mlxsw_pci_driver_unregister(&mlxsw_sp_pci_driver);
mlxsw_core_driver_unregister(&mlxsw_sp_driver);
unregister_netevent_notifier(&mlxsw_sp_router_netevent_nb);
unregister_inet6addr_notifier(&mlxsw_sp_inet6addr_nb);
unregister_inetaddr_notifier(&mlxsw_sp_inetaddr_nb);
unregister_netdevice_notifier(&mlxsw_sp_netdevice_nb);
}
......
......@@ -384,6 +384,8 @@ int mlxsw_sp_router_netevent_event(struct notifier_block *unused,
int mlxsw_sp_netdevice_router_port_event(struct net_device *dev);
int mlxsw_sp_inetaddr_event(struct notifier_block *unused,
unsigned long event, void *ptr);
int mlxsw_sp_inet6addr_event(struct notifier_block *unused,
unsigned long event, void *ptr);
int mlxsw_sp_netdevice_vrf_event(struct net_device *l3_dev, unsigned long event,
struct netdev_notifier_changeupper_info *info);
void
......
......@@ -49,6 +49,9 @@
#include <net/ip_fib.h>
#include <net/fib_rules.h>
#include <net/l3mdev.h>
#include <net/addrconf.h>
#include <net/ndisc.h>
#include <net/ipv6.h>
#include "spectrum.h"
#include "core.h"
......@@ -304,7 +307,7 @@ static struct mlxsw_sp_rif *
mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp,
const struct net_device *dev);
#define MLXSW_SP_PREFIX_COUNT (sizeof(struct in6_addr) * BITS_PER_BYTE)
#define MLXSW_SP_PREFIX_COUNT (sizeof(struct in6_addr) * BITS_PER_BYTE + 1)
struct mlxsw_sp_prefix_usage {
DECLARE_BITMAP(b, MLXSW_SP_PREFIX_COUNT);
......@@ -384,23 +387,23 @@ struct mlxsw_sp_fib_node {
struct mlxsw_sp_fib_key key;
};
struct mlxsw_sp_fib_entry_params {
u32 tb_id;
u32 prio;
u8 tos;
u8 type;
};
struct mlxsw_sp_fib_entry {
struct list_head list;
struct mlxsw_sp_fib_node *fib_node;
enum mlxsw_sp_fib_entry_type type;
struct list_head nexthop_group_node;
struct mlxsw_sp_nexthop_group *nh_group;
struct mlxsw_sp_fib_entry_params params;
bool offloaded;
};
struct mlxsw_sp_fib4_entry {
struct mlxsw_sp_fib_entry common;
u32 tb_id;
u32 prio;
u8 tos;
u8 type;
};
enum mlxsw_sp_l3proto {
MLXSW_SP_L3_PROTO_IPV4,
MLXSW_SP_L3_PROTO_IPV6,
......@@ -428,6 +431,7 @@ struct mlxsw_sp_vr {
u32 tb_id; /* kernel fib table id */
unsigned int rif_count;
struct mlxsw_sp_fib *fib4;
struct mlxsw_sp_fib *fib6;
};
static const struct rhashtable_params mlxsw_sp_fib_ht_params;
......@@ -625,7 +629,7 @@ 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;
return !!vr->fib4 || !!vr->fib6;
}
static struct mlxsw_sp_vr *mlxsw_sp_vr_find_unused(struct mlxsw_sp *mlxsw_sp)
......@@ -694,7 +698,7 @@ static struct mlxsw_sp_fib *mlxsw_sp_vr_fib(const struct mlxsw_sp_vr *vr,
case MLXSW_SP_L3_PROTO_IPV4:
return vr->fib4;
case MLXSW_SP_L3_PROTO_IPV6:
BUG_ON(1);
return vr->fib6;
}
return NULL;
}
......@@ -703,6 +707,7 @@ static struct mlxsw_sp_vr *mlxsw_sp_vr_create(struct mlxsw_sp *mlxsw_sp,
u32 tb_id)
{
struct mlxsw_sp_vr *vr;
int err;
vr = mlxsw_sp_vr_find_unused(mlxsw_sp);
if (!vr)
......@@ -710,12 +715,24 @@ static struct mlxsw_sp_vr *mlxsw_sp_vr_create(struct mlxsw_sp *mlxsw_sp,
vr->fib4 = mlxsw_sp_fib_create(vr, MLXSW_SP_L3_PROTO_IPV4);
if (IS_ERR(vr->fib4))
return ERR_CAST(vr->fib4);
vr->fib6 = mlxsw_sp_fib_create(vr, MLXSW_SP_L3_PROTO_IPV6);
if (IS_ERR(vr->fib6)) {
err = PTR_ERR(vr->fib6);
goto err_fib6_create;
}
vr->tb_id = tb_id;
return vr;
err_fib6_create:
mlxsw_sp_fib_destroy(vr->fib4);
vr->fib4 = NULL;
return ERR_PTR(err);
}
static void mlxsw_sp_vr_destroy(struct mlxsw_sp_vr *vr)
{
mlxsw_sp_fib_destroy(vr->fib6);
vr->fib6 = NULL;
mlxsw_sp_fib_destroy(vr->fib4);
vr->fib4 = NULL;
}
......@@ -773,7 +790,8 @@ static struct mlxsw_sp_vr *mlxsw_sp_vr_get(struct mlxsw_sp *mlxsw_sp, u32 tb_id)
static void mlxsw_sp_vr_put(struct mlxsw_sp_vr *vr)
{
if (!vr->rif_count && list_empty(&vr->fib4->node_list))
if (!vr->rif_count && list_empty(&vr->fib4->node_list) &&
list_empty(&vr->fib6->node_list))
mlxsw_sp_vr_destroy(vr);
}
......@@ -929,8 +947,11 @@ mlxsw_sp_neigh_entry_lookup(struct mlxsw_sp *mlxsw_sp, struct neighbour *n)
static void
mlxsw_sp_router_neighs_update_interval_init(struct mlxsw_sp *mlxsw_sp)
{
unsigned long interval = NEIGH_VAR(&arp_tbl.parms, DELAY_PROBE_TIME);
unsigned long interval;
interval = min_t(unsigned long,
NEIGH_VAR(&arp_tbl.parms, DELAY_PROBE_TIME),
NEIGH_VAR(&nd_tbl.parms, DELAY_PROBE_TIME));
mlxsw_sp->router->neighs_update.interval = jiffies_to_msecs(interval);
}
......@@ -965,6 +986,36 @@ static void mlxsw_sp_router_neigh_ent_ipv4_process(struct mlxsw_sp *mlxsw_sp,
neigh_release(n);
}
static void mlxsw_sp_router_neigh_ent_ipv6_process(struct mlxsw_sp *mlxsw_sp,
char *rauhtd_pl,
int rec_index)
{
struct net_device *dev;
struct neighbour *n;
struct in6_addr dip;
u16 rif;
mlxsw_reg_rauhtd_ent_ipv6_unpack(rauhtd_pl, rec_index, &rif,
(char *) &dip);
if (!mlxsw_sp->router->rifs[rif]) {
dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect RIF in neighbour entry\n");
return;
}
dev = mlxsw_sp->router->rifs[rif]->dev;
n = neigh_lookup(&nd_tbl, &dip, dev);
if (!n) {
netdev_err(dev, "Failed to find matching neighbour for IP=%pI6c\n",
&dip);
return;
}
netdev_dbg(dev, "Updating neighbour with IP=%pI6c\n", &dip);
neigh_event_send(n, NULL);
neigh_release(n);
}
static void mlxsw_sp_router_neigh_rec_ipv4_process(struct mlxsw_sp *mlxsw_sp,
char *rauhtd_pl,
int rec_index)
......@@ -988,6 +1039,15 @@ static void mlxsw_sp_router_neigh_rec_ipv4_process(struct mlxsw_sp *mlxsw_sp,
}
static void mlxsw_sp_router_neigh_rec_ipv6_process(struct mlxsw_sp *mlxsw_sp,
char *rauhtd_pl,
int rec_index)
{
/* One record contains one entry. */
mlxsw_sp_router_neigh_ent_ipv6_process(mlxsw_sp, rauhtd_pl,
rec_index);
}
static void mlxsw_sp_router_neigh_rec_process(struct mlxsw_sp *mlxsw_sp,
char *rauhtd_pl, int rec_index)
{
......@@ -997,7 +1057,8 @@ static void mlxsw_sp_router_neigh_rec_process(struct mlxsw_sp *mlxsw_sp,
rec_index);
break;
case MLXSW_REG_RAUHTD_TYPE_IPV6:
WARN_ON_ONCE(1);
mlxsw_sp_router_neigh_rec_ipv6_process(mlxsw_sp, rauhtd_pl,
rec_index);
break;
}
}
......@@ -1022,22 +1083,20 @@ static bool mlxsw_sp_router_rauhtd_is_full(char *rauhtd_pl)
return false;
}
static int mlxsw_sp_router_neighs_update_rauhtd(struct mlxsw_sp *mlxsw_sp)
static int
__mlxsw_sp_router_neighs_update_rauhtd(struct mlxsw_sp *mlxsw_sp,
char *rauhtd_pl,
enum mlxsw_reg_rauhtd_type type)
{
char *rauhtd_pl;
u8 num_rec;
int i, err;
rauhtd_pl = kmalloc(MLXSW_REG_RAUHTD_LEN, GFP_KERNEL);
if (!rauhtd_pl)
return -ENOMEM;
int i, num_rec;
int err;
/* Make sure the neighbour's netdev isn't removed in the
* process.
*/
rtnl_lock();
do {
mlxsw_reg_rauhtd_pack(rauhtd_pl, MLXSW_REG_RAUHTD_TYPE_IPV4);
mlxsw_reg_rauhtd_pack(rauhtd_pl, type);
err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(rauhtd),
rauhtd_pl);
if (err) {
......@@ -1051,6 +1110,27 @@ static int mlxsw_sp_router_neighs_update_rauhtd(struct mlxsw_sp *mlxsw_sp)
} while (mlxsw_sp_router_rauhtd_is_full(rauhtd_pl));
rtnl_unlock();
return err;
}
static int mlxsw_sp_router_neighs_update_rauhtd(struct mlxsw_sp *mlxsw_sp)
{
enum mlxsw_reg_rauhtd_type type;
char *rauhtd_pl;
int err;
rauhtd_pl = kmalloc(MLXSW_REG_RAUHTD_LEN, GFP_KERNEL);
if (!rauhtd_pl)
return -ENOMEM;
type = MLXSW_REG_RAUHTD_TYPE_IPV4;
err = __mlxsw_sp_router_neighs_update_rauhtd(mlxsw_sp, rauhtd_pl, type);
if (err)
goto out;
type = MLXSW_REG_RAUHTD_TYPE_IPV6;
err = __mlxsw_sp_router_neighs_update_rauhtd(mlxsw_sp, rauhtd_pl, type);
out:
kfree(rauhtd_pl);
return err;
}
......@@ -1146,6 +1226,32 @@ mlxsw_sp_router_neigh_entry_op4(struct mlxsw_sp *mlxsw_sp,
mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht), rauht_pl);
}
static void
mlxsw_sp_router_neigh_entry_op6(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_neigh_entry *neigh_entry,
enum mlxsw_reg_rauht_op op)
{
struct neighbour *n = neigh_entry->key.n;
char rauht_pl[MLXSW_REG_RAUHT_LEN];
const char *dip = n->primary_key;
mlxsw_reg_rauht_pack6(rauht_pl, op, neigh_entry->rif, neigh_entry->ha,
dip);
mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht), rauht_pl);
}
static bool mlxsw_sp_neigh_ipv6_ignore(struct neighbour *n)
{
/* Packets with a link-local destination address are trapped
* after LPM lookup and never reach the neighbour table, so
* there is no need to program such neighbours to the device.
*/
if (ipv6_addr_type((struct in6_addr *) &n->primary_key) &
IPV6_ADDR_LINKLOCAL)
return true;
return false;
}
static void
mlxsw_sp_neigh_entry_update(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_neigh_entry *neigh_entry,
......@@ -1154,11 +1260,17 @@ mlxsw_sp_neigh_entry_update(struct mlxsw_sp *mlxsw_sp,
if (!adding && !neigh_entry->connected)
return;
neigh_entry->connected = adding;
if (neigh_entry->key.n->tbl == &arp_tbl)
if (neigh_entry->key.n->tbl == &arp_tbl) {
mlxsw_sp_router_neigh_entry_op4(mlxsw_sp, neigh_entry,
mlxsw_sp_rauht_op(adding));
else
} else if (neigh_entry->key.n->tbl == &nd_tbl) {
if (mlxsw_sp_neigh_ipv6_ignore(neigh_entry->key.n))
return;
mlxsw_sp_router_neigh_entry_op6(mlxsw_sp, neigh_entry,
mlxsw_sp_rauht_op(adding));
} else {
WARN_ON_ONCE(1);
}
}
struct mlxsw_sp_neigh_event_work {
......@@ -1227,7 +1339,7 @@ int mlxsw_sp_router_netevent_event(struct notifier_block *unused,
p = ptr;
/* We don't care about changes in the default table. */
if (!p->dev || p->tbl != &arp_tbl)
if (!p->dev || (p->tbl != &arp_tbl && p->tbl != &nd_tbl))
return NOTIFY_DONE;
/* We are in atomic context and can't take RTNL mutex,
......@@ -1246,7 +1358,7 @@ int mlxsw_sp_router_netevent_event(struct notifier_block *unused,
case NETEVENT_NEIGH_UPDATE:
n = ptr;
if (n->tbl != &arp_tbl)
if (n->tbl != &arp_tbl && n->tbl != &nd_tbl)
return NOTIFY_DONE;
mlxsw_sp_port = mlxsw_sp_port_lower_dev_hold(n->dev);
......@@ -1340,6 +1452,7 @@ struct mlxsw_sp_nexthop {
*/
struct rhash_head ht_node;
struct mlxsw_sp_nexthop_key key;
unsigned char gw_addr[sizeof(struct in6_addr)];
struct mlxsw_sp_rif *rif;
u8 should_offload:1, /* set indicates this neigh is connected and
* should be put to KVD linear area of this group.
......@@ -1360,6 +1473,7 @@ struct mlxsw_sp_nexthop_group_key {
struct mlxsw_sp_nexthop_group {
struct rhash_head ht_node;
struct list_head fib_list; /* list of fib entries that use this group */
struct neigh_table *neigh_tbl;
struct mlxsw_sp_nexthop_group_key key;
u8 adj_index_valid:1,
gateway:1; /* routes using the group use a gateway */
......@@ -1677,7 +1791,6 @@ static int mlxsw_sp_nexthop_neigh_init(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_nexthop *nh)
{
struct mlxsw_sp_neigh_entry *neigh_entry;
struct fib_nh *fib_nh = nh->key.fib_nh;
struct neighbour *n;
u8 nud_state, dead;
int err;
......@@ -1690,9 +1803,10 @@ static int mlxsw_sp_nexthop_neigh_init(struct mlxsw_sp *mlxsw_sp,
* The reference is taken either in neigh_lookup() or
* in neigh_create() in case n is not found.
*/
n = neigh_lookup(&arp_tbl, &fib_nh->nh_gw, fib_nh->nh_dev);
n = neigh_lookup(nh->nh_grp->neigh_tbl, &nh->gw_addr, nh->rif->dev);
if (!n) {
n = neigh_create(&arp_tbl, &fib_nh->nh_gw, fib_nh->nh_dev);
n = neigh_create(nh->nh_grp->neigh_tbl, &nh->gw_addr,
nh->rif->dev);
if (IS_ERR(n))
return PTR_ERR(n);
neigh_event_send(n, NULL);
......@@ -1754,7 +1868,7 @@ static void mlxsw_sp_nexthop_neigh_fini(struct mlxsw_sp *mlxsw_sp,
neigh_release(n);
}
static int mlxsw_sp_nexthop_init(struct mlxsw_sp *mlxsw_sp,
static int mlxsw_sp_nexthop4_init(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_nexthop_group *nh_grp,
struct mlxsw_sp_nexthop *nh,
struct fib_nh *fib_nh)
......@@ -1766,6 +1880,7 @@ static int mlxsw_sp_nexthop_init(struct mlxsw_sp *mlxsw_sp,
nh->nh_grp = nh_grp;
nh->key.fib_nh = fib_nh;
memcpy(&nh->gw_addr, &fib_nh->nh_gw, sizeof(fib_nh->nh_gw));
err = mlxsw_sp_nexthop_insert(mlxsw_sp, nh);
if (err)
return err;
......@@ -1795,7 +1910,7 @@ static int mlxsw_sp_nexthop_init(struct mlxsw_sp *mlxsw_sp,
return err;
}
static void mlxsw_sp_nexthop_fini(struct mlxsw_sp *mlxsw_sp,
static void mlxsw_sp_nexthop4_fini(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_nexthop *nh)
{
mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
......@@ -1803,7 +1918,7 @@ static void mlxsw_sp_nexthop_fini(struct mlxsw_sp *mlxsw_sp,
mlxsw_sp_nexthop_remove(mlxsw_sp, nh);
}
static void mlxsw_sp_nexthop_event(struct mlxsw_sp *mlxsw_sp,
static void mlxsw_sp_nexthop4_event(struct mlxsw_sp *mlxsw_sp,
unsigned long event, struct fib_nh *fib_nh)
{
struct mlxsw_sp_nexthop_key key;
......@@ -1849,7 +1964,7 @@ static void mlxsw_sp_nexthop_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
}
static struct mlxsw_sp_nexthop_group *
mlxsw_sp_nexthop_group_create(struct mlxsw_sp *mlxsw_sp, struct fib_info *fi)
mlxsw_sp_nexthop4_group_create(struct mlxsw_sp *mlxsw_sp, struct fib_info *fi)
{
struct mlxsw_sp_nexthop_group *nh_grp;
struct mlxsw_sp_nexthop *nh;
......@@ -1864,6 +1979,8 @@ mlxsw_sp_nexthop_group_create(struct mlxsw_sp *mlxsw_sp, struct fib_info *fi)
if (!nh_grp)
return ERR_PTR(-ENOMEM);
INIT_LIST_HEAD(&nh_grp->fib_list);
nh_grp->neigh_tbl = &arp_tbl;
nh_grp->gateway = fi->fib_nh->nh_scope == RT_SCOPE_LINK;
nh_grp->count = fi->fib_nhs;
nh_grp->key.fi = fi;
......@@ -1871,9 +1988,9 @@ mlxsw_sp_nexthop_group_create(struct mlxsw_sp *mlxsw_sp, struct fib_info *fi)
for (i = 0; i < nh_grp->count; i++) {
nh = &nh_grp->nexthops[i];
fib_nh = &fi->fib_nh[i];
err = mlxsw_sp_nexthop_init(mlxsw_sp, nh_grp, nh, fib_nh);
err = mlxsw_sp_nexthop4_init(mlxsw_sp, nh_grp, nh, fib_nh);
if (err)
goto err_nexthop_init;
goto err_nexthop4_init;
}
err = mlxsw_sp_nexthop_group_insert(mlxsw_sp, nh_grp);
if (err)
......@@ -1882,10 +1999,10 @@ mlxsw_sp_nexthop_group_create(struct mlxsw_sp *mlxsw_sp, struct fib_info *fi)
return nh_grp;
err_nexthop_group_insert:
err_nexthop_init:
err_nexthop4_init:
for (i--; i >= 0; i--) {
nh = &nh_grp->nexthops[i];
mlxsw_sp_nexthop_fini(mlxsw_sp, nh);
mlxsw_sp_nexthop4_fini(mlxsw_sp, nh);
}
fib_info_put(nh_grp->key.fi);
kfree(nh_grp);
......@@ -1893,7 +2010,7 @@ mlxsw_sp_nexthop_group_create(struct mlxsw_sp *mlxsw_sp, struct fib_info *fi)
}
static void
mlxsw_sp_nexthop_group_destroy(struct mlxsw_sp *mlxsw_sp,
mlxsw_sp_nexthop4_group_destroy(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_nexthop_group *nh_grp)
{
struct mlxsw_sp_nexthop *nh;
......@@ -1902,7 +2019,7 @@ mlxsw_sp_nexthop_group_destroy(struct mlxsw_sp *mlxsw_sp,
mlxsw_sp_nexthop_group_remove(mlxsw_sp, nh_grp);
for (i = 0; i < nh_grp->count; i++) {
nh = &nh_grp->nexthops[i];
mlxsw_sp_nexthop_fini(mlxsw_sp, nh);
mlxsw_sp_nexthop4_fini(mlxsw_sp, nh);
}
mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
WARN_ON_ONCE(nh_grp->adj_index_valid);
......@@ -1910,7 +2027,7 @@ mlxsw_sp_nexthop_group_destroy(struct mlxsw_sp *mlxsw_sp,
kfree(nh_grp);
}
static int mlxsw_sp_nexthop_group_get(struct mlxsw_sp *mlxsw_sp,
static int mlxsw_sp_nexthop4_group_get(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_fib_entry *fib_entry,
struct fib_info *fi)
{
......@@ -1920,7 +2037,7 @@ static int mlxsw_sp_nexthop_group_get(struct mlxsw_sp *mlxsw_sp,
key.fi = fi;
nh_grp = mlxsw_sp_nexthop_group_lookup(mlxsw_sp, key);
if (!nh_grp) {
nh_grp = mlxsw_sp_nexthop_group_create(mlxsw_sp, fi);
nh_grp = mlxsw_sp_nexthop4_group_create(mlxsw_sp, fi);
if (IS_ERR(nh_grp))
return PTR_ERR(nh_grp);
}
......@@ -1929,7 +2046,7 @@ static int mlxsw_sp_nexthop_group_get(struct mlxsw_sp *mlxsw_sp,
return 0;
}
static void mlxsw_sp_nexthop_group_put(struct mlxsw_sp *mlxsw_sp,
static void mlxsw_sp_nexthop4_group_put(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_fib_entry *fib_entry)
{
struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
......@@ -1937,7 +2054,17 @@ static void mlxsw_sp_nexthop_group_put(struct mlxsw_sp *mlxsw_sp,
list_del(&fib_entry->nexthop_group_node);
if (!list_empty(&nh_grp->fib_list))
return;
mlxsw_sp_nexthop_group_destroy(mlxsw_sp, nh_grp);
mlxsw_sp_nexthop4_group_destroy(mlxsw_sp, nh_grp);
}
static bool
mlxsw_sp_fib4_entry_should_offload(const struct mlxsw_sp_fib_entry *fib_entry)
{
struct mlxsw_sp_fib4_entry *fib4_entry;
fib4_entry = container_of(fib_entry, struct mlxsw_sp_fib4_entry,
common);
return !fib4_entry->tos;
}
static bool
......@@ -1945,8 +2072,14 @@ mlxsw_sp_fib_entry_should_offload(const struct mlxsw_sp_fib_entry *fib_entry)
{
struct mlxsw_sp_nexthop_group *nh_group = fib_entry->nh_group;
if (fib_entry->params.tos)
switch (fib_entry->fib_node->fib->proto) {
case MLXSW_SP_L3_PROTO_IPV4:
if (!mlxsw_sp_fib4_entry_should_offload(fib_entry))
return false;
break;
case MLXSW_SP_L3_PROTO_IPV6:
break;
}
switch (fib_entry->type) {
case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE:
......@@ -2009,13 +2142,37 @@ mlxsw_sp_fib_entry_offload_refresh(struct mlxsw_sp_fib_entry *fib_entry,
}
}
static int mlxsw_sp_fib_entry_op4_remote(struct mlxsw_sp *mlxsw_sp,
static void
mlxsw_sp_fib_entry_ralue_pack(char *ralue_pl,
const struct mlxsw_sp_fib_entry *fib_entry,
enum mlxsw_reg_ralue_op op)
{
struct mlxsw_sp_fib *fib = fib_entry->fib_node->fib;
enum mlxsw_reg_ralxx_protocol proto;
u32 *p_dip;
proto = (enum mlxsw_reg_ralxx_protocol) fib->proto;
switch (fib->proto) {
case MLXSW_SP_L3_PROTO_IPV4:
p_dip = (u32 *) fib_entry->fib_node->key.addr;
mlxsw_reg_ralue_pack4(ralue_pl, proto, op, fib->vr->id,
fib_entry->fib_node->key.prefix_len,
*p_dip);
break;
case MLXSW_SP_L3_PROTO_IPV6:
mlxsw_reg_ralue_pack6(ralue_pl, proto, op, fib->vr->id,
fib_entry->fib_node->key.prefix_len,
fib_entry->fib_node->key.addr);
break;
}
}
static int mlxsw_sp_fib_entry_op_remote(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_fib_entry *fib_entry,
enum mlxsw_reg_ralue_op op)
{
char ralue_pl[MLXSW_REG_RALUE_LEN];
struct mlxsw_sp_fib *fib = fib_entry->fib_node->fib;
u32 *p_dip = (u32 *) fib_entry->fib_node->key.addr;
enum mlxsw_reg_ralue_trap_action trap_action;
u16 trap_id = 0;
u32 adjacency_index = 0;
......@@ -2034,24 +2191,19 @@ static int mlxsw_sp_fib_entry_op4_remote(struct mlxsw_sp *mlxsw_sp,
trap_id = MLXSW_TRAP_ID_RTR_INGRESS0;
}
mlxsw_reg_ralue_pack4(ralue_pl,
(enum mlxsw_reg_ralxx_protocol) fib->proto, op,
fib->vr->id, fib_entry->fib_node->key.prefix_len,
*p_dip);
mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
mlxsw_reg_ralue_act_remote_pack(ralue_pl, trap_action, trap_id,
adjacency_index, ecmp_size);
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
}
static int mlxsw_sp_fib_entry_op4_local(struct mlxsw_sp *mlxsw_sp,
static int mlxsw_sp_fib_entry_op_local(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_fib_entry *fib_entry,
enum mlxsw_reg_ralue_op op)
{
struct mlxsw_sp_rif *rif = fib_entry->nh_group->nh_rif;
struct mlxsw_sp_fib *fib = fib_entry->fib_node->fib;
enum mlxsw_reg_ralue_trap_action trap_action;
char ralue_pl[MLXSW_REG_RALUE_LEN];
u32 *p_dip = (u32 *) fib_entry->fib_node->key.addr;
u16 trap_id = 0;
u16 rif_index = 0;
......@@ -2063,42 +2215,34 @@ static int mlxsw_sp_fib_entry_op4_local(struct mlxsw_sp *mlxsw_sp,
trap_id = MLXSW_TRAP_ID_RTR_INGRESS0;
}
mlxsw_reg_ralue_pack4(ralue_pl,
(enum mlxsw_reg_ralxx_protocol) fib->proto, op,
fib->vr->id, fib_entry->fib_node->key.prefix_len,
*p_dip);
mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
mlxsw_reg_ralue_act_local_pack(ralue_pl, trap_action, trap_id,
rif_index);
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
}
static int mlxsw_sp_fib_entry_op4_trap(struct mlxsw_sp *mlxsw_sp,
static int mlxsw_sp_fib_entry_op_trap(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_fib_entry *fib_entry,
enum mlxsw_reg_ralue_op op)
{
struct mlxsw_sp_fib *fib = fib_entry->fib_node->fib;
char ralue_pl[MLXSW_REG_RALUE_LEN];
u32 *p_dip = (u32 *) fib_entry->fib_node->key.addr;
mlxsw_reg_ralue_pack4(ralue_pl,
(enum mlxsw_reg_ralxx_protocol) fib->proto, op,
fib->vr->id, fib_entry->fib_node->key.prefix_len,
*p_dip);
mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
mlxsw_reg_ralue_act_ip2me_pack(ralue_pl);
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
}
static int mlxsw_sp_fib_entry_op4(struct mlxsw_sp *mlxsw_sp,
static int __mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_fib_entry *fib_entry,
enum mlxsw_reg_ralue_op op)
{
switch (fib_entry->type) {
case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE:
return mlxsw_sp_fib_entry_op4_remote(mlxsw_sp, fib_entry, op);
return mlxsw_sp_fib_entry_op_remote(mlxsw_sp, fib_entry, op);
case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL:
return mlxsw_sp_fib_entry_op4_local(mlxsw_sp, fib_entry, op);
return mlxsw_sp_fib_entry_op_local(mlxsw_sp, fib_entry, op);
case MLXSW_SP_FIB_ENTRY_TYPE_TRAP:
return mlxsw_sp_fib_entry_op4_trap(mlxsw_sp, fib_entry, op);
return mlxsw_sp_fib_entry_op_trap(mlxsw_sp, fib_entry, op);
}
return -EINVAL;
}
......@@ -2107,16 +2251,10 @@ static int mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_fib_entry *fib_entry,
enum mlxsw_reg_ralue_op op)
{
int err = -EINVAL;
int err = __mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry, op);
switch (fib_entry->fib_node->fib->proto) {
case MLXSW_SP_L3_PROTO_IPV4:
err = mlxsw_sp_fib_entry_op4(mlxsw_sp, fib_entry, op);
break;
case MLXSW_SP_L3_PROTO_IPV6:
return err;
}
mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, err);
return err;
}
......@@ -2166,72 +2304,80 @@ mlxsw_sp_fib4_entry_type_set(struct mlxsw_sp *mlxsw_sp,
}
}
static struct mlxsw_sp_fib_entry *
static struct mlxsw_sp_fib4_entry *
mlxsw_sp_fib4_entry_create(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_fib_node *fib_node,
const struct fib_entry_notifier_info *fen_info)
{
struct mlxsw_sp_fib4_entry *fib4_entry;
struct mlxsw_sp_fib_entry *fib_entry;
int err;
fib_entry = kzalloc(sizeof(*fib_entry), GFP_KERNEL);
if (!fib_entry) {
err = -ENOMEM;
goto err_fib_entry_alloc;
}
fib4_entry = kzalloc(sizeof(*fib4_entry), GFP_KERNEL);
if (!fib4_entry)
return ERR_PTR(-ENOMEM);
fib_entry = &fib4_entry->common;
err = mlxsw_sp_fib4_entry_type_set(mlxsw_sp, fen_info, fib_entry);
if (err)
goto err_fib4_entry_type_set;
err = mlxsw_sp_nexthop_group_get(mlxsw_sp, fib_entry, fen_info->fi);
err = mlxsw_sp_nexthop4_group_get(mlxsw_sp, fib_entry, fen_info->fi);
if (err)
goto err_nexthop_group_get;
goto err_nexthop4_group_get;
fib_entry->params.prio = fen_info->fi->fib_priority;
fib_entry->params.tb_id = fen_info->tb_id;
fib_entry->params.type = fen_info->type;
fib_entry->params.tos = fen_info->tos;
fib4_entry->prio = fen_info->fi->fib_priority;
fib4_entry->tb_id = fen_info->tb_id;
fib4_entry->type = fen_info->type;
fib4_entry->tos = fen_info->tos;
fib_entry->fib_node = fib_node;
return fib_entry;
return fib4_entry;
err_nexthop_group_get:
err_nexthop4_group_get:
err_fib4_entry_type_set:
kfree(fib_entry);
err_fib_entry_alloc:
kfree(fib4_entry);
return ERR_PTR(err);
}
static void mlxsw_sp_fib4_entry_destroy(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_fib_entry *fib_entry)
struct mlxsw_sp_fib4_entry *fib4_entry)
{
mlxsw_sp_nexthop_group_put(mlxsw_sp, fib_entry);
kfree(fib_entry);
mlxsw_sp_nexthop4_group_put(mlxsw_sp, &fib4_entry->common);
kfree(fib4_entry);
}
static struct mlxsw_sp_fib_node *
mlxsw_sp_fib4_node_get(struct mlxsw_sp *mlxsw_sp,
const struct fib_entry_notifier_info *fen_info);
mlxsw_sp_fib_node_lookup(struct mlxsw_sp_fib *fib, const void *addr,
size_t addr_len, unsigned char prefix_len);
static struct mlxsw_sp_fib_entry *
static struct mlxsw_sp_fib4_entry *
mlxsw_sp_fib4_entry_lookup(struct mlxsw_sp *mlxsw_sp,
const struct fib_entry_notifier_info *fen_info)
{
struct mlxsw_sp_fib_entry *fib_entry;
struct mlxsw_sp_fib4_entry *fib4_entry;
struct mlxsw_sp_fib_node *fib_node;
struct mlxsw_sp_fib *fib;
struct mlxsw_sp_vr *vr;
fib_node = mlxsw_sp_fib4_node_get(mlxsw_sp, fen_info);
if (IS_ERR(fib_node))
vr = mlxsw_sp_vr_find(mlxsw_sp, fen_info->tb_id);
if (!vr)
return NULL;
fib = mlxsw_sp_vr_fib(vr, MLXSW_SP_L3_PROTO_IPV4);
fib_node = mlxsw_sp_fib_node_lookup(fib, &fen_info->dst,
sizeof(fen_info->dst),
fen_info->dst_len);
if (!fib_node)
return NULL;
list_for_each_entry(fib_entry, &fib_node->entry_list, list) {
if (fib_entry->params.tb_id == fen_info->tb_id &&
fib_entry->params.tos == fen_info->tos &&
fib_entry->params.type == fen_info->type &&
fib_entry->nh_group->key.fi == fen_info->fi) {
return fib_entry;
list_for_each_entry(fib4_entry, &fib_node->entry_list, common.list) {
if (fib4_entry->tb_id == fen_info->tb_id &&
fib4_entry->tos == fen_info->tos &&
fib4_entry->type == fen_info->type &&
fib4_entry->common.nh_group->key.fi == fen_info->fi) {
return fib4_entry;
}
}
......@@ -2388,28 +2534,25 @@ static void mlxsw_sp_fib_node_fini(struct mlxsw_sp *mlxsw_sp,
}
static struct mlxsw_sp_fib_node *
mlxsw_sp_fib4_node_get(struct mlxsw_sp *mlxsw_sp,
const struct fib_entry_notifier_info *fen_info)
mlxsw_sp_fib_node_get(struct mlxsw_sp *mlxsw_sp, u32 tb_id, const void *addr,
size_t addr_len, unsigned char prefix_len,
enum mlxsw_sp_l3proto proto)
{
struct mlxsw_sp_fib_node *fib_node;
struct mlxsw_sp_fib *fib;
struct mlxsw_sp_vr *vr;
int err;
vr = mlxsw_sp_vr_get(mlxsw_sp, fen_info->tb_id);
vr = mlxsw_sp_vr_get(mlxsw_sp, tb_id);
if (IS_ERR(vr))
return ERR_CAST(vr);
fib = mlxsw_sp_vr_fib(vr, MLXSW_SP_L3_PROTO_IPV4);
fib = mlxsw_sp_vr_fib(vr, proto);
fib_node = mlxsw_sp_fib_node_lookup(fib, &fen_info->dst,
sizeof(fen_info->dst),
fen_info->dst_len);
fib_node = mlxsw_sp_fib_node_lookup(fib, addr, addr_len, prefix_len);
if (fib_node)
return fib_node;
fib_node = mlxsw_sp_fib_node_create(fib, &fen_info->dst,
sizeof(fen_info->dst),
fen_info->dst_len);
fib_node = mlxsw_sp_fib_node_create(fib, addr, addr_len, prefix_len);
if (!fib_node) {
err = -ENOMEM;
goto err_fib_node_create;
......@@ -2428,7 +2571,7 @@ mlxsw_sp_fib4_node_get(struct mlxsw_sp *mlxsw_sp,
return ERR_PTR(err);
}
static void mlxsw_sp_fib4_node_put(struct mlxsw_sp *mlxsw_sp,
static void mlxsw_sp_fib_node_put(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_fib_node *fib_node)
{
struct mlxsw_sp_vr *vr = fib_node->fib->vr;
......@@ -2440,95 +2583,100 @@ static void mlxsw_sp_fib4_node_put(struct mlxsw_sp *mlxsw_sp,
mlxsw_sp_vr_put(vr);
}
static struct mlxsw_sp_fib_entry *
static struct mlxsw_sp_fib4_entry *
mlxsw_sp_fib4_node_entry_find(const struct mlxsw_sp_fib_node *fib_node,
const struct mlxsw_sp_fib_entry_params *params)
const struct mlxsw_sp_fib4_entry *new4_entry)
{
struct mlxsw_sp_fib_entry *fib_entry;
struct mlxsw_sp_fib4_entry *fib4_entry;
list_for_each_entry(fib_entry, &fib_node->entry_list, list) {
if (fib_entry->params.tb_id > params->tb_id)
list_for_each_entry(fib4_entry, &fib_node->entry_list, common.list) {
if (fib4_entry->tb_id > new4_entry->tb_id)
continue;
if (fib_entry->params.tb_id != params->tb_id)
if (fib4_entry->tb_id != new4_entry->tb_id)
break;
if (fib_entry->params.tos > params->tos)
if (fib4_entry->tos > new4_entry->tos)
continue;
if (fib_entry->params.prio >= params->prio ||
fib_entry->params.tos < params->tos)
return fib_entry;
if (fib4_entry->prio >= new4_entry->prio ||
fib4_entry->tos < new4_entry->tos)
return fib4_entry;
}
return NULL;
}
static int mlxsw_sp_fib4_node_list_append(struct mlxsw_sp_fib_entry *fib_entry,
struct mlxsw_sp_fib_entry *new_entry)
static int
mlxsw_sp_fib4_node_list_append(struct mlxsw_sp_fib4_entry *fib4_entry,
struct mlxsw_sp_fib4_entry *new4_entry)
{
struct mlxsw_sp_fib_node *fib_node;
if (WARN_ON(!fib_entry))
if (WARN_ON(!fib4_entry))
return -EINVAL;
fib_node = fib_entry->fib_node;
list_for_each_entry_from(fib_entry, &fib_node->entry_list, list) {
if (fib_entry->params.tb_id != new_entry->params.tb_id ||
fib_entry->params.tos != new_entry->params.tos ||
fib_entry->params.prio != new_entry->params.prio)
fib_node = fib4_entry->common.fib_node;
list_for_each_entry_from(fib4_entry, &fib_node->entry_list,
common.list) {
if (fib4_entry->tb_id != new4_entry->tb_id ||
fib4_entry->tos != new4_entry->tos ||
fib4_entry->prio != new4_entry->prio)
break;
}
list_add_tail(&new_entry->list, &fib_entry->list);
list_add_tail(&new4_entry->common.list, &fib4_entry->common.list);
return 0;
}
static int
mlxsw_sp_fib4_node_list_insert(struct mlxsw_sp_fib_node *fib_node,
struct mlxsw_sp_fib_entry *new_entry,
mlxsw_sp_fib4_node_list_insert(struct mlxsw_sp_fib4_entry *new4_entry,
bool replace, bool append)
{
struct mlxsw_sp_fib_entry *fib_entry;
struct mlxsw_sp_fib_node *fib_node = new4_entry->common.fib_node;
struct mlxsw_sp_fib4_entry *fib4_entry;
fib_entry = mlxsw_sp_fib4_node_entry_find(fib_node, &new_entry->params);
fib4_entry = mlxsw_sp_fib4_node_entry_find(fib_node, new4_entry);
if (append)
return mlxsw_sp_fib4_node_list_append(fib_entry, new_entry);
if (replace && WARN_ON(!fib_entry))
return mlxsw_sp_fib4_node_list_append(fib4_entry, new4_entry);
if (replace && WARN_ON(!fib4_entry))
return -EINVAL;
/* Insert new entry before replaced one, so that we can later
* remove the second.
*/
if (fib_entry) {
list_add_tail(&new_entry->list, &fib_entry->list);
if (fib4_entry) {
list_add_tail(&new4_entry->common.list,
&fib4_entry->common.list);
} else {
struct mlxsw_sp_fib_entry *last;
struct mlxsw_sp_fib4_entry *last;
list_for_each_entry(last, &fib_node->entry_list, list) {
if (new_entry->params.tb_id > last->params.tb_id)
list_for_each_entry(last, &fib_node->entry_list, common.list) {
if (new4_entry->tb_id > last->tb_id)
break;
fib_entry = last;
fib4_entry = last;
}
if (fib_entry)
list_add(&new_entry->list, &fib_entry->list);
if (fib4_entry)
list_add(&new4_entry->common.list,
&fib4_entry->common.list);
else
list_add(&new_entry->list, &fib_node->entry_list);
list_add(&new4_entry->common.list,
&fib_node->entry_list);
}
return 0;
}
static void
mlxsw_sp_fib4_node_list_remove(struct mlxsw_sp_fib_entry *fib_entry)
mlxsw_sp_fib4_node_list_remove(struct mlxsw_sp_fib4_entry *fib4_entry)
{
list_del(&fib_entry->list);
list_del(&fib4_entry->common.list);
}
static int
mlxsw_sp_fib4_node_entry_add(struct mlxsw_sp *mlxsw_sp,
const struct mlxsw_sp_fib_node *fib_node,
static int mlxsw_sp_fib_node_entry_add(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_fib_entry *fib_entry)
{
struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node;
if (!mlxsw_sp_fib_node_entry_is_first(fib_node, fib_entry))
return 0;
......@@ -2545,11 +2693,11 @@ mlxsw_sp_fib4_node_entry_add(struct mlxsw_sp *mlxsw_sp,
return mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
}
static void
mlxsw_sp_fib4_node_entry_del(struct mlxsw_sp *mlxsw_sp,
const struct mlxsw_sp_fib_node *fib_node,
static void mlxsw_sp_fib_node_entry_del(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_fib_entry *fib_entry)
{
struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node;
if (!mlxsw_sp_fib_node_entry_is_first(fib_node, fib_entry))
return;
......@@ -2567,54 +2715,50 @@ mlxsw_sp_fib4_node_entry_del(struct mlxsw_sp *mlxsw_sp,
}
static int mlxsw_sp_fib4_node_entry_link(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_fib_entry *fib_entry,
struct mlxsw_sp_fib4_entry *fib4_entry,
bool replace, bool append)
{
struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node;
int err;
err = mlxsw_sp_fib4_node_list_insert(fib_node, fib_entry, replace,
append);
err = mlxsw_sp_fib4_node_list_insert(fib4_entry, replace, append);
if (err)
return err;
err = mlxsw_sp_fib4_node_entry_add(mlxsw_sp, fib_node, fib_entry);
err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib4_entry->common);
if (err)
goto err_fib4_node_entry_add;
goto err_fib_node_entry_add;
return 0;
err_fib4_node_entry_add:
mlxsw_sp_fib4_node_list_remove(fib_entry);
err_fib_node_entry_add:
mlxsw_sp_fib4_node_list_remove(fib4_entry);
return err;
}
static void
mlxsw_sp_fib4_node_entry_unlink(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_fib_entry *fib_entry)
struct mlxsw_sp_fib4_entry *fib4_entry)
{
struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node;
mlxsw_sp_fib4_node_entry_del(mlxsw_sp, fib_node, fib_entry);
mlxsw_sp_fib4_node_list_remove(fib_entry);
mlxsw_sp_fib_node_entry_del(mlxsw_sp, &fib4_entry->common);
mlxsw_sp_fib4_node_list_remove(fib4_entry);
}
static void mlxsw_sp_fib4_entry_replace(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_fib_entry *fib_entry,
struct mlxsw_sp_fib4_entry *fib4_entry,
bool replace)
{
struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node;
struct mlxsw_sp_fib_entry *replaced;
struct mlxsw_sp_fib_node *fib_node = fib4_entry->common.fib_node;
struct mlxsw_sp_fib4_entry *replaced;
if (!replace)
return;
/* We inserted the new entry before replaced one */
replaced = list_next_entry(fib_entry, list);
replaced = list_next_entry(fib4_entry, common.list);
mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, replaced);
mlxsw_sp_fib4_entry_destroy(mlxsw_sp, replaced);
mlxsw_sp_fib4_node_put(mlxsw_sp, fib_node);
mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
}
static int
......@@ -2622,76 +2766,80 @@ mlxsw_sp_router_fib4_add(struct mlxsw_sp *mlxsw_sp,
const struct fib_entry_notifier_info *fen_info,
bool replace, bool append)
{
struct mlxsw_sp_fib_entry *fib_entry;
struct mlxsw_sp_fib4_entry *fib4_entry;
struct mlxsw_sp_fib_node *fib_node;
int err;
if (mlxsw_sp->router->aborted)
return 0;
fib_node = mlxsw_sp_fib4_node_get(mlxsw_sp, fen_info);
fib_node = mlxsw_sp_fib_node_get(mlxsw_sp, fen_info->tb_id,
&fen_info->dst, sizeof(fen_info->dst),
fen_info->dst_len,
MLXSW_SP_L3_PROTO_IPV4);
if (IS_ERR(fib_node)) {
dev_warn(mlxsw_sp->bus_info->dev, "Failed to get FIB node\n");
return PTR_ERR(fib_node);
}
fib_entry = mlxsw_sp_fib4_entry_create(mlxsw_sp, fib_node, fen_info);
if (IS_ERR(fib_entry)) {
fib4_entry = mlxsw_sp_fib4_entry_create(mlxsw_sp, fib_node, fen_info);
if (IS_ERR(fib4_entry)) {
dev_warn(mlxsw_sp->bus_info->dev, "Failed to create FIB entry\n");
err = PTR_ERR(fib_entry);
err = PTR_ERR(fib4_entry);
goto err_fib4_entry_create;
}
err = mlxsw_sp_fib4_node_entry_link(mlxsw_sp, fib_entry, replace,
err = mlxsw_sp_fib4_node_entry_link(mlxsw_sp, fib4_entry, replace,
append);
if (err) {
dev_warn(mlxsw_sp->bus_info->dev, "Failed to link FIB entry to node\n");
goto err_fib4_node_entry_link;
}
mlxsw_sp_fib4_entry_replace(mlxsw_sp, fib_entry, replace);
mlxsw_sp_fib4_entry_replace(mlxsw_sp, fib4_entry, replace);
return 0;
err_fib4_node_entry_link:
mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib_entry);
mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
err_fib4_entry_create:
mlxsw_sp_fib4_node_put(mlxsw_sp, fib_node);
mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
return err;
}
static void mlxsw_sp_router_fib4_del(struct mlxsw_sp *mlxsw_sp,
struct fib_entry_notifier_info *fen_info)
{
struct mlxsw_sp_fib_entry *fib_entry;
struct mlxsw_sp_fib4_entry *fib4_entry;
struct mlxsw_sp_fib_node *fib_node;
if (mlxsw_sp->router->aborted)
return;
fib_entry = mlxsw_sp_fib4_entry_lookup(mlxsw_sp, fen_info);
if (WARN_ON(!fib_entry))
fib4_entry = mlxsw_sp_fib4_entry_lookup(mlxsw_sp, fen_info);
if (WARN_ON(!fib4_entry))
return;
fib_node = fib_entry->fib_node;
fib_node = fib4_entry->common.fib_node;
mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib_entry);
mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib_entry);
mlxsw_sp_fib4_node_put(mlxsw_sp, fib_node);
mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib4_entry);
mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
}
static int mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp)
static int __mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp,
enum mlxsw_reg_ralxx_protocol proto,
u8 tree_id)
{
char ralta_pl[MLXSW_REG_RALTA_LEN];
char ralst_pl[MLXSW_REG_RALST_LEN];
int i, err;
mlxsw_reg_ralta_pack(ralta_pl, true, MLXSW_REG_RALXX_PROTOCOL_IPV4,
MLXSW_SP_LPM_TREE_MIN);
mlxsw_reg_ralta_pack(ralta_pl, true, proto, tree_id);
err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
if (err)
return err;
mlxsw_reg_ralst_pack(ralst_pl, 0xff, MLXSW_SP_LPM_TREE_MIN);
mlxsw_reg_ralst_pack(ralst_pl, 0xff, tree_id);
err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralst), ralst_pl);
if (err)
return err;
......@@ -2704,17 +2852,14 @@ static int mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp)
if (!mlxsw_sp_vr_is_used(vr))
continue;
mlxsw_reg_raltb_pack(raltb_pl, vr->id,
MLXSW_REG_RALXX_PROTOCOL_IPV4,
MLXSW_SP_LPM_TREE_MIN);
mlxsw_reg_raltb_pack(raltb_pl, vr->id, proto, tree_id);
err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb),
raltb_pl);
if (err)
return err;
mlxsw_reg_ralue_pack4(ralue_pl, MLXSW_SP_L3_PROTO_IPV4,
MLXSW_REG_RALUE_OP_WRITE_WRITE, vr->id, 0,
0);
mlxsw_reg_ralue_pack(ralue_pl, proto,
MLXSW_REG_RALUE_OP_WRITE_WRITE, vr->id, 0);
mlxsw_reg_ralue_act_ip2me_pack(ralue_pl);
err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue),
ralue_pl);
......@@ -2725,17 +2870,33 @@ static int mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp)
return 0;
}
static int mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp)
{
enum mlxsw_reg_ralxx_protocol proto = MLXSW_REG_RALXX_PROTOCOL_IPV4;
int err;
err = __mlxsw_sp_router_set_abort_trap(mlxsw_sp, proto,
MLXSW_SP_LPM_TREE_MIN);
if (err)
return err;
proto = MLXSW_REG_RALXX_PROTOCOL_IPV6;
return __mlxsw_sp_router_set_abort_trap(mlxsw_sp, proto,
MLXSW_SP_LPM_TREE_MIN + 1);
}
static void mlxsw_sp_fib4_node_flush(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_fib_node *fib_node)
{
struct mlxsw_sp_fib_entry *fib_entry, *tmp;
struct mlxsw_sp_fib4_entry *fib4_entry, *tmp;
list_for_each_entry_safe(fib_entry, tmp, &fib_node->entry_list, list) {
bool do_break = &tmp->list == &fib_node->entry_list;
list_for_each_entry_safe(fib4_entry, tmp, &fib_node->entry_list,
common.list) {
bool do_break = &tmp->common.list == &fib_node->entry_list;
mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib_entry);
mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib_entry);
mlxsw_sp_fib4_node_put(mlxsw_sp, fib_node);
mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib4_entry);
mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
/* Break when entry list is empty and node was freed.
* Otherwise, we'll access freed memory in the next
* iteration.
......@@ -2784,10 +2945,17 @@ static void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp)
if (!mlxsw_sp_vr_is_used(vr))
continue;
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
* longer used.
*/
if (!mlxsw_sp_vr_is_used(vr))
continue;
mlxsw_sp_vr_fib_flush(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV6);
}
}
static void mlxsw_sp_router_fib4_abort(struct mlxsw_sp *mlxsw_sp)
static void mlxsw_sp_router_fib_abort(struct mlxsw_sp *mlxsw_sp)
{
int err;
......@@ -2832,7 +3000,7 @@ static void mlxsw_sp_router_fib_event_work(struct work_struct *work)
err = mlxsw_sp_router_fib4_add(mlxsw_sp, &fib_work->fen_info,
replace, append);
if (err)
mlxsw_sp_router_fib4_abort(mlxsw_sp);
mlxsw_sp_router_fib_abort(mlxsw_sp);
fib_info_put(fib_work->fen_info.fi);
break;
case FIB_EVENT_ENTRY_DEL:
......@@ -2843,12 +3011,12 @@ static void mlxsw_sp_router_fib_event_work(struct work_struct *work)
case FIB_EVENT_RULE_DEL:
rule = fib_work->fr_info.rule;
if (!fib4_rule_default(rule) && !rule->l3mdev)
mlxsw_sp_router_fib4_abort(mlxsw_sp);
mlxsw_sp_router_fib_abort(mlxsw_sp);
fib_rule_put(rule);
break;
case FIB_EVENT_NH_ADD: /* fall through */
case FIB_EVENT_NH_DEL:
mlxsw_sp_nexthop_event(mlxsw_sp, fib_work->event,
mlxsw_sp_nexthop4_event(mlxsw_sp, fib_work->event,
fib_work->fnh_info.fib_nh);
fib_info_put(fib_work->fnh_info.fib_nh->nh_parent);
break;
......@@ -2941,17 +3109,30 @@ static void mlxsw_sp_router_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
mlxsw_sp_neigh_rif_gone_sync(mlxsw_sp, rif);
}
static bool mlxsw_sp_rif_should_config(struct mlxsw_sp_rif *rif,
const struct in_device *in_dev,
static bool
mlxsw_sp_rif_should_config(struct mlxsw_sp_rif *rif, struct net_device *dev,
unsigned long event)
{
struct inet6_dev *inet6_dev;
bool addr_list_empty = true;
struct in_device *idev;
switch (event) {
case NETDEV_UP:
if (!rif)
return true;
return false;
case NETDEV_DOWN:
if (rif && !in_dev->ifa_list &&
idev = __in_dev_get_rtnl(dev);
if (idev && idev->ifa_list)
addr_list_empty = false;
inet6_dev = __in6_dev_get(dev);
if (addr_list_empty && inet6_dev &&
!list_empty(&inet6_dev->addr_list))
addr_list_empty = false;
if (rif && addr_list_empty &&
!netif_is_l3_slave(rif->dev))
return true;
/* It is possible we already removed the RIF ourselves
......@@ -3349,7 +3530,7 @@ int mlxsw_sp_inetaddr_event(struct notifier_block *unused,
goto out;
rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
if (!mlxsw_sp_rif_should_config(rif, ifa->ifa_dev, event))
if (!mlxsw_sp_rif_should_config(rif, dev, event))
goto out;
err = __mlxsw_sp_inetaddr_event(dev, event);
......@@ -3357,6 +3538,61 @@ int mlxsw_sp_inetaddr_event(struct notifier_block *unused,
return notifier_from_errno(err);
}
struct mlxsw_sp_inet6addr_event_work {
struct work_struct work;
struct net_device *dev;
unsigned long event;
};
static void mlxsw_sp_inet6addr_event_work(struct work_struct *work)
{
struct mlxsw_sp_inet6addr_event_work *inet6addr_work =
container_of(work, struct mlxsw_sp_inet6addr_event_work, work);
struct net_device *dev = inet6addr_work->dev;
unsigned long event = inet6addr_work->event;
struct mlxsw_sp *mlxsw_sp;
struct mlxsw_sp_rif *rif;
rtnl_lock();
mlxsw_sp = mlxsw_sp_lower_get(dev);
if (!mlxsw_sp)
goto out;
rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
if (!mlxsw_sp_rif_should_config(rif, dev, event))
goto out;
__mlxsw_sp_inetaddr_event(dev, event);
out:
rtnl_unlock();
dev_put(dev);
kfree(inet6addr_work);
}
/* Called with rcu_read_lock() */
int mlxsw_sp_inet6addr_event(struct notifier_block *unused,
unsigned long event, void *ptr)
{
struct inet6_ifaddr *if6 = (struct inet6_ifaddr *) ptr;
struct mlxsw_sp_inet6addr_event_work *inet6addr_work;
struct net_device *dev = if6->idev->dev;
if (!mlxsw_sp_port_dev_lower_find_rcu(dev))
return NOTIFY_DONE;
inet6addr_work = kzalloc(sizeof(*inet6addr_work), GFP_ATOMIC);
if (!inet6addr_work)
return NOTIFY_BAD;
INIT_WORK(&inet6addr_work->work, mlxsw_sp_inet6addr_event_work);
inet6addr_work->dev = dev;
inet6addr_work->event = event;
dev_hold(dev);
mlxsw_core_schedule_work(&inet6addr_work->work);
return NOTIFY_DONE;
}
static int mlxsw_sp_rif_edit(struct mlxsw_sp *mlxsw_sp, u16 rif_index,
const char *mac, int mtu)
{
......@@ -3558,6 +3794,11 @@ static int mlxsw_sp_rif_vlan_configure(struct mlxsw_sp_rif *rif)
if (err)
return err;
err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
mlxsw_sp_router_port(mlxsw_sp), true);
if (err)
goto err_fid_mc_flood_set;
err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
mlxsw_sp_router_port(mlxsw_sp), true);
if (err)
......@@ -3566,6 +3807,9 @@ static int mlxsw_sp_rif_vlan_configure(struct mlxsw_sp_rif *rif)
return 0;
err_fid_bc_flood_set:
mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
mlxsw_sp_router_port(mlxsw_sp), false);
err_fid_mc_flood_set:
mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, false);
return err;
}
......@@ -3577,6 +3821,8 @@ static void mlxsw_sp_rif_vlan_deconfigure(struct mlxsw_sp_rif *rif)
mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
mlxsw_sp_router_port(mlxsw_sp), false);
mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
mlxsw_sp_router_port(mlxsw_sp), false);
mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, false);
}
......@@ -3607,6 +3853,11 @@ static int mlxsw_sp_rif_fid_configure(struct mlxsw_sp_rif *rif)
if (err)
return err;
err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
mlxsw_sp_router_port(mlxsw_sp), true);
if (err)
goto err_fid_mc_flood_set;
err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
mlxsw_sp_router_port(mlxsw_sp), true);
if (err)
......@@ -3615,6 +3866,9 @@ static int mlxsw_sp_rif_fid_configure(struct mlxsw_sp_rif *rif)
return 0;
err_fid_bc_flood_set:
mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
mlxsw_sp_router_port(mlxsw_sp), false);
err_fid_mc_flood_set:
mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index, false);
return err;
}
......@@ -3626,6 +3880,8 @@ static void mlxsw_sp_rif_fid_deconfigure(struct mlxsw_sp_rif *rif)
mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
mlxsw_sp_router_port(mlxsw_sp), false);
mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
mlxsw_sp_router_port(mlxsw_sp), false);
mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index, false);
}
......@@ -3697,7 +3953,7 @@ static int __mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
return -EIO;
max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);
mlxsw_reg_rgcr_pack(rgcr_pl, true);
mlxsw_reg_rgcr_pack(rgcr_pl, true, true);
mlxsw_reg_rgcr_max_router_interfaces_set(rgcr_pl, max_rifs);
err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
if (err)
......@@ -3709,7 +3965,7 @@ static void __mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
{
char rgcr_pl[MLXSW_REG_RGCR_LEN];
mlxsw_reg_rgcr_pack(rgcr_pl, false);
mlxsw_reg_rgcr_pack(rgcr_pl, false, false);
mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
}
......
......@@ -61,16 +61,32 @@ enum {
MLXSW_TRAP_ID_MTUERROR = 0x52,
MLXSW_TRAP_ID_TTLERROR = 0x53,
MLXSW_TRAP_ID_LBERROR = 0x54,
MLXSW_TRAP_ID_OSPF = 0x55,
MLXSW_TRAP_ID_IPV4_OSPF = 0x55,
MLXSW_TRAP_ID_IP2ME = 0x5F,
MLXSW_TRAP_ID_IPV6_UNSPECIFIED_ADDRESS = 0x60,
MLXSW_TRAP_ID_IPV6_LINK_LOCAL_DEST = 0x61,
MLXSW_TRAP_ID_IPV6_LINK_LOCAL_SRC = 0x62,
MLXSW_TRAP_ID_IPV6_ALL_NODES_LINK = 0x63,
MLXSW_TRAP_ID_IPV6_OSPF = 0x64,
MLXSW_TRAP_ID_IPV6_MLDV12_LISTENER_QUERY = 0x65,
MLXSW_TRAP_ID_IPV6_MLDV1_LISTENER_REPORT = 0x66,
MLXSW_TRAP_ID_IPV6_MLDV1_LISTENER_DONE = 0x67,
MLXSW_TRAP_ID_IPV6_MLDV2_LISTENER_REPORT = 0x68,
MLXSW_TRAP_ID_IPV6_DHCP = 0x69,
MLXSW_TRAP_ID_IPV6_ALL_ROUTERS_LINK = 0x6F,
MLXSW_TRAP_ID_RTR_INGRESS0 = 0x70,
MLXSW_TRAP_ID_BGP_IPV4 = 0x88,
MLXSW_TRAP_ID_IPV4_BGP = 0x88,
MLXSW_TRAP_ID_IPV6_BGP = 0x89,
MLXSW_TRAP_ID_L3_IPV6_ROUTER_SOLICITATION = 0x8A,
MLXSW_TRAP_ID_L3_IPV6_ROUTER_ADVERTISMENT = 0x8B,
MLXSW_TRAP_ID_L3_IPV6_NEIGHBOR_SOLICITATION = 0x8C,
MLXSW_TRAP_ID_L3_IPV6_NEIGHBOR_ADVERTISMENT = 0x8D,
MLXSW_TRAP_ID_L3_IPV6_REDIRECTION = 0x8E,
MLXSW_TRAP_ID_HOST_MISS_IPV4 = 0x90,
MLXSW_TRAP_ID_IPV6_MC_LINK_LOCAL_DEST = 0x91,
MLXSW_TRAP_ID_HOST_MISS_IPV6 = 0x92,
MLXSW_TRAP_ID_ROUTER_ALERT_IPV4 = 0xD6,
MLXSW_TRAP_ID_ROUTER_ALERT_IPV6 = 0xD7,
MLXSW_TRAP_ID_ACL0 = 0x1C0,
MLXSW_TRAP_ID_MAX = 0x1FF
......
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