net: lan966x: Implement the callback SWITCHDEV_ATTR_ID_BRIDGE_MC_DISABLED
The callback allows to enable/disable multicast snooping. When the snooping is enabled, all IGMP and MLD frames are redirected to the CPU, therefore make sure not to set the skb flag 'offload_fwd_mark'. The HW will not flood multicast ipv4/ipv6 data frames. When the snooping is disabled, the HW will flood IGMP, MLD and multicast ipv4/ipv6 frames according to the mcast_flood flag. Signed-off-by: Horatiu Vultur <horatiu.vultur@microchip.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
1c213f05a3
commit
47aeea0d57
@ -4,11 +4,13 @@
|
||||
#include <linux/if_bridge.h>
|
||||
#include <linux/if_vlan.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/of_net.h>
|
||||
#include <linux/packing.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/reset.h>
|
||||
#include <net/addrconf.h>
|
||||
|
||||
#include "lan966x_main.h"
|
||||
|
||||
@ -419,6 +421,32 @@ bool lan966x_netdevice_check(const struct net_device *dev)
|
||||
return dev->netdev_ops == &lan966x_port_netdev_ops;
|
||||
}
|
||||
|
||||
static bool lan966x_hw_offload(struct lan966x *lan966x, u32 port,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
/* The IGMP and MLD frames are not forward by the HW if
|
||||
* multicast snooping is enabled, therefor don't mark as
|
||||
* offload to allow the SW to forward the frames accordingly.
|
||||
*/
|
||||
val = lan_rd(lan966x, ANA_CPU_FWD_CFG(port));
|
||||
if (!(val & (ANA_CPU_FWD_CFG_IGMP_REDIR_ENA |
|
||||
ANA_CPU_FWD_CFG_MLD_REDIR_ENA)))
|
||||
return true;
|
||||
|
||||
if (skb->protocol == htons(ETH_P_IP) &&
|
||||
ip_hdr(skb)->protocol == IPPROTO_IGMP)
|
||||
return false;
|
||||
|
||||
if (skb->protocol == htons(ETH_P_IPV6) &&
|
||||
ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr) &&
|
||||
!ipv6_mc_check_mld(skb))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int lan966x_port_xtr_status(struct lan966x *lan966x, u8 grp)
|
||||
{
|
||||
return lan_rd(lan966x, QS_XTR_RD(grp));
|
||||
@ -563,9 +591,14 @@ static irqreturn_t lan966x_xtr_irq_handler(int irq, void *args)
|
||||
lan966x_ptp_rxtstamp(lan966x, skb, timestamp);
|
||||
skb->protocol = eth_type_trans(skb, dev);
|
||||
|
||||
if (lan966x->bridge_mask & BIT(src_port))
|
||||
if (lan966x->bridge_mask & BIT(src_port)) {
|
||||
skb->offload_fwd_mark = 1;
|
||||
|
||||
skb_reset_network_header(skb);
|
||||
if (!lan966x_hw_offload(lan966x, src_port, skb))
|
||||
skb->offload_fwd_mark = 0;
|
||||
}
|
||||
|
||||
netif_rx_ni(skb);
|
||||
dev->stats.rx_bytes += len;
|
||||
dev->stats.rx_packets++;
|
||||
|
@ -170,6 +170,7 @@ struct lan966x_port {
|
||||
bool vlan_aware;
|
||||
|
||||
bool learn_ena;
|
||||
bool mcast_ena;
|
||||
|
||||
struct phylink_config phylink_config;
|
||||
struct phylink_pcs phylink_pcs;
|
||||
|
@ -299,6 +299,24 @@ enum lan966x_target {
|
||||
/* ANA:PORT:CPU_FWD_CFG */
|
||||
#define ANA_CPU_FWD_CFG(g) __REG(TARGET_ANA, 0, 1, 28672, g, 9, 128, 96, 0, 1, 4)
|
||||
|
||||
#define ANA_CPU_FWD_CFG_MLD_REDIR_ENA BIT(6)
|
||||
#define ANA_CPU_FWD_CFG_MLD_REDIR_ENA_SET(x)\
|
||||
FIELD_PREP(ANA_CPU_FWD_CFG_MLD_REDIR_ENA, x)
|
||||
#define ANA_CPU_FWD_CFG_MLD_REDIR_ENA_GET(x)\
|
||||
FIELD_GET(ANA_CPU_FWD_CFG_MLD_REDIR_ENA, x)
|
||||
|
||||
#define ANA_CPU_FWD_CFG_IGMP_REDIR_ENA BIT(5)
|
||||
#define ANA_CPU_FWD_CFG_IGMP_REDIR_ENA_SET(x)\
|
||||
FIELD_PREP(ANA_CPU_FWD_CFG_IGMP_REDIR_ENA, x)
|
||||
#define ANA_CPU_FWD_CFG_IGMP_REDIR_ENA_GET(x)\
|
||||
FIELD_GET(ANA_CPU_FWD_CFG_IGMP_REDIR_ENA, x)
|
||||
|
||||
#define ANA_CPU_FWD_CFG_IPMC_CTRL_COPY_ENA BIT(4)
|
||||
#define ANA_CPU_FWD_CFG_IPMC_CTRL_COPY_ENA_SET(x)\
|
||||
FIELD_PREP(ANA_CPU_FWD_CFG_IPMC_CTRL_COPY_ENA, x)
|
||||
#define ANA_CPU_FWD_CFG_IPMC_CTRL_COPY_ENA_GET(x)\
|
||||
FIELD_GET(ANA_CPU_FWD_CFG_IPMC_CTRL_COPY_ENA, x)
|
||||
|
||||
#define ANA_CPU_FWD_CFG_SRC_COPY_ENA BIT(3)
|
||||
#define ANA_CPU_FWD_CFG_SRC_COPY_ENA_SET(x)\
|
||||
FIELD_PREP(ANA_CPU_FWD_CFG_SRC_COPY_ENA, x)
|
||||
|
@ -9,6 +9,37 @@ static struct notifier_block lan966x_netdevice_nb __read_mostly;
|
||||
static struct notifier_block lan966x_switchdev_nb __read_mostly;
|
||||
static struct notifier_block lan966x_switchdev_blocking_nb __read_mostly;
|
||||
|
||||
static void lan966x_port_set_mcast_ip_flood(struct lan966x_port *port,
|
||||
u32 pgid_ip)
|
||||
{
|
||||
struct lan966x *lan966x = port->lan966x;
|
||||
u32 flood_mask_ip;
|
||||
|
||||
flood_mask_ip = lan_rd(lan966x, ANA_PGID(pgid_ip));
|
||||
flood_mask_ip = ANA_PGID_PGID_GET(flood_mask_ip);
|
||||
|
||||
/* If mcast snooping is not enabled then use mcast flood mask
|
||||
* to decide to enable multicast flooding or not.
|
||||
*/
|
||||
if (!port->mcast_ena) {
|
||||
u32 flood_mask;
|
||||
|
||||
flood_mask = lan_rd(lan966x, ANA_PGID(PGID_MC));
|
||||
flood_mask = ANA_PGID_PGID_GET(flood_mask);
|
||||
|
||||
if (flood_mask & BIT(port->chip_port))
|
||||
flood_mask_ip |= BIT(port->chip_port);
|
||||
else
|
||||
flood_mask_ip &= ~BIT(port->chip_port);
|
||||
} else {
|
||||
flood_mask_ip &= ~BIT(port->chip_port);
|
||||
}
|
||||
|
||||
lan_rmw(ANA_PGID_PGID_SET(flood_mask_ip),
|
||||
ANA_PGID_PGID,
|
||||
lan966x, ANA_PGID(pgid_ip));
|
||||
}
|
||||
|
||||
static void lan966x_port_set_mcast_flood(struct lan966x_port *port,
|
||||
bool enabled)
|
||||
{
|
||||
@ -23,6 +54,11 @@ static void lan966x_port_set_mcast_flood(struct lan966x_port *port,
|
||||
lan_rmw(ANA_PGID_PGID_SET(val),
|
||||
ANA_PGID_PGID,
|
||||
port->lan966x, ANA_PGID(PGID_MC));
|
||||
|
||||
if (!port->mcast_ena) {
|
||||
lan966x_port_set_mcast_ip_flood(port, PGID_MCIPV4);
|
||||
lan966x_port_set_mcast_ip_flood(port, PGID_MCIPV6);
|
||||
}
|
||||
}
|
||||
|
||||
static void lan966x_port_set_ucast_flood(struct lan966x_port *port,
|
||||
@ -144,6 +180,24 @@ static void lan966x_port_ageing_set(struct lan966x_port *port,
|
||||
lan966x_mac_set_ageing(port->lan966x, ageing_time);
|
||||
}
|
||||
|
||||
static void lan966x_port_mc_set(struct lan966x_port *port, bool mcast_ena)
|
||||
{
|
||||
struct lan966x *lan966x = port->lan966x;
|
||||
|
||||
port->mcast_ena = mcast_ena;
|
||||
|
||||
lan_rmw(ANA_CPU_FWD_CFG_IGMP_REDIR_ENA_SET(mcast_ena) |
|
||||
ANA_CPU_FWD_CFG_MLD_REDIR_ENA_SET(mcast_ena) |
|
||||
ANA_CPU_FWD_CFG_IPMC_CTRL_COPY_ENA_SET(mcast_ena),
|
||||
ANA_CPU_FWD_CFG_IGMP_REDIR_ENA |
|
||||
ANA_CPU_FWD_CFG_MLD_REDIR_ENA |
|
||||
ANA_CPU_FWD_CFG_IPMC_CTRL_COPY_ENA,
|
||||
lan966x, ANA_CPU_FWD_CFG(port->chip_port));
|
||||
|
||||
lan966x_port_set_mcast_ip_flood(port, PGID_MCIPV4);
|
||||
lan966x_port_set_mcast_ip_flood(port, PGID_MCIPV6);
|
||||
}
|
||||
|
||||
static int lan966x_port_attr_set(struct net_device *dev, const void *ctx,
|
||||
const struct switchdev_attr *attr,
|
||||
struct netlink_ext_ack *extack)
|
||||
@ -171,6 +225,9 @@ static int lan966x_port_attr_set(struct net_device *dev, const void *ctx,
|
||||
lan966x_vlan_port_set_vlan_aware(port, attr->u.vlan_filtering);
|
||||
lan966x_vlan_port_apply(port);
|
||||
break;
|
||||
case SWITCHDEV_ATTR_ID_BRIDGE_MC_DISABLED:
|
||||
lan966x_port_mc_set(port, !attr->u.mc_disabled);
|
||||
break;
|
||||
default:
|
||||
err = -EOPNOTSUPP;
|
||||
break;
|
||||
|
Loading…
Reference in New Issue
Block a user