Merge branch 'add-multicast-filtering-support-for-vlan-interface'

MD Danish Anwar says:

====================
Add Multicast Filtering support for VLAN interface

This series adds Multicast filtering support for VLAN interfaces in dual
EMAC and HSR offload mode for ICSSG driver.

Patch 1/4 - Adds support for VLAN in dual EMAC mode
Patch 2/4 - Adds MC filtering support for VLAN in dual EMAC mode
Patch 3/4 - Create and export hsr_get_port_ndev() in hsr_device.c
Patch 4/4 - Adds MC filtering support for VLAN in HSR mode

[1] https://lore.kernel.org/all/20241216100044.577489-2-danishanwar@ti.com/
[2] https://lore.kernel.org/all/202412210336.BmgcX3Td-lkp@intel.com/#t
[3] https://lore.kernel.org/all/31bb8a3e-5a1c-4c94-8c33-c0dfd6d643fb@kernel.org/
v1 https://lore.kernel.org/all/20241216100044.577489-1-danishanwar@ti.com/
v2 https://lore.kernel.org/all/20241223092557.2077526-1-danishanwar@ti.com/
v3 https://lore.kernel.org/all/20250103092033.1533374-1-danishanwar@ti.com/
====================

Link: https://patch.msgid.link/20250110082852.3899027-1-danishanwar@ti.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
Paolo Abeni 2025-01-14 12:17:29 +01:00
commit 2b1d9116b0
7 changed files with 173 additions and 59 deletions

View File

@ -561,61 +561,134 @@ const struct icss_iep_clockops prueth_iep_clockops = {
static int icssg_prueth_add_mcast(struct net_device *ndev, const u8 *addr)
{
struct prueth_emac *emac = netdev_priv(ndev);
int port_mask = BIT(emac->port_id);
struct net_device *real_dev;
struct prueth_emac *emac;
int port_mask;
u8 vlan_id;
port_mask |= icssg_fdb_lookup(emac, addr, 0);
icssg_fdb_add_del(emac, addr, 0, port_mask, true);
icssg_vtbl_modify(emac, 0, port_mask, port_mask, true);
vlan_id = is_vlan_dev(ndev) ? vlan_dev_vlan_id(ndev) : PRUETH_DFLT_VLAN_MAC;
real_dev = is_vlan_dev(ndev) ? vlan_dev_real_dev(ndev) : ndev;
emac = netdev_priv(real_dev);
port_mask = BIT(emac->port_id) | icssg_fdb_lookup(emac, addr, vlan_id);
icssg_fdb_add_del(emac, addr, vlan_id, port_mask, true);
icssg_vtbl_modify(emac, vlan_id, port_mask, port_mask, true);
return 0;
}
static int icssg_prueth_del_mcast(struct net_device *ndev, const u8 *addr)
{
struct prueth_emac *emac = netdev_priv(ndev);
int port_mask = BIT(emac->port_id);
struct net_device *real_dev;
struct prueth_emac *emac;
int other_port_mask;
int port_mask;
u8 vlan_id;
other_port_mask = port_mask ^ icssg_fdb_lookup(emac, addr, 0);
vlan_id = is_vlan_dev(ndev) ? vlan_dev_vlan_id(ndev) : PRUETH_DFLT_VLAN_MAC;
real_dev = is_vlan_dev(ndev) ? vlan_dev_real_dev(ndev) : ndev;
emac = netdev_priv(real_dev);
icssg_fdb_add_del(emac, addr, 0, port_mask, false);
icssg_vtbl_modify(emac, 0, port_mask, port_mask, false);
port_mask = BIT(emac->port_id);
other_port_mask = port_mask ^ icssg_fdb_lookup(emac, addr, vlan_id);
icssg_fdb_add_del(emac, addr, vlan_id, port_mask, false);
icssg_vtbl_modify(emac, vlan_id, port_mask, port_mask, false);
if (other_port_mask) {
icssg_fdb_add_del(emac, addr, 0, other_port_mask, true);
icssg_vtbl_modify(emac, 0, other_port_mask, other_port_mask, true);
icssg_fdb_add_del(emac, addr, vlan_id, other_port_mask, true);
icssg_vtbl_modify(emac, vlan_id, other_port_mask,
other_port_mask, true);
}
return 0;
}
static int icssg_prueth_hsr_add_mcast(struct net_device *ndev, const u8 *addr)
static void icssg_prueth_hsr_fdb_add_del(struct prueth_emac *emac,
const u8 *addr, u8 vid, bool add)
{
struct prueth_emac *emac = netdev_priv(ndev);
struct prueth *prueth = emac->prueth;
icssg_fdb_add_del(emac, addr, prueth->default_vlan,
icssg_fdb_add_del(emac, addr, vid,
ICSSG_FDB_ENTRY_P0_MEMBERSHIP |
ICSSG_FDB_ENTRY_P1_MEMBERSHIP |
ICSSG_FDB_ENTRY_P2_MEMBERSHIP |
ICSSG_FDB_ENTRY_BLOCK, true);
ICSSG_FDB_ENTRY_BLOCK, add);
if (add)
icssg_vtbl_modify(emac, vid, BIT(emac->port_id),
BIT(emac->port_id), add);
}
static int icssg_prueth_hsr_add_mcast(struct net_device *ndev, const u8 *addr)
{
struct net_device *real_dev;
struct prueth_emac *emac;
u8 vlan_id, i;
vlan_id = is_vlan_dev(ndev) ? vlan_dev_vlan_id(ndev) : PRUETH_DFLT_VLAN_HSR;
real_dev = is_vlan_dev(ndev) ? vlan_dev_real_dev(ndev) : ndev;
if (is_hsr_master(real_dev)) {
for (i = HSR_PT_SLAVE_A; i < HSR_PT_INTERLINK; i++) {
emac = netdev_priv(hsr_get_port_ndev(real_dev, i));
if (!emac)
return -EINVAL;
icssg_prueth_hsr_fdb_add_del(emac, addr, vlan_id,
true);
}
} else {
emac = netdev_priv(real_dev);
icssg_prueth_hsr_fdb_add_del(emac, addr, vlan_id, true);
}
icssg_vtbl_modify(emac, emac->port_vlan, BIT(emac->port_id),
BIT(emac->port_id), true);
return 0;
}
static int icssg_prueth_hsr_del_mcast(struct net_device *ndev, const u8 *addr)
{
struct prueth_emac *emac = netdev_priv(ndev);
struct prueth *prueth = emac->prueth;
struct net_device *real_dev;
struct prueth_emac *emac;
u8 vlan_id, i;
icssg_fdb_add_del(emac, addr, prueth->default_vlan,
ICSSG_FDB_ENTRY_P0_MEMBERSHIP |
ICSSG_FDB_ENTRY_P1_MEMBERSHIP |
ICSSG_FDB_ENTRY_P2_MEMBERSHIP |
ICSSG_FDB_ENTRY_BLOCK, false);
vlan_id = is_vlan_dev(ndev) ? vlan_dev_vlan_id(ndev) : PRUETH_DFLT_VLAN_HSR;
real_dev = is_vlan_dev(ndev) ? vlan_dev_real_dev(ndev) : ndev;
if (is_hsr_master(real_dev)) {
for (i = HSR_PT_SLAVE_A; i < HSR_PT_INTERLINK; i++) {
emac = netdev_priv(hsr_get_port_ndev(real_dev, i));
if (!emac)
return -EINVAL;
icssg_prueth_hsr_fdb_add_del(emac, addr, vlan_id,
false);
}
} else {
emac = netdev_priv(real_dev);
icssg_prueth_hsr_fdb_add_del(emac, addr, vlan_id, false);
}
return 0;
}
static int icssg_update_vlan_mcast(struct net_device *vdev, int vid,
void *args)
{
struct prueth_emac *emac = args;
if (!vdev || !vid)
return 0;
netif_addr_lock_bh(vdev);
__hw_addr_sync_multiple(&emac->vlan_mcast_list[vid], &vdev->mc,
vdev->addr_len);
netif_addr_unlock_bh(vdev);
if (emac->prueth->is_hsr_offload_mode)
__hw_addr_sync_dev(&emac->vlan_mcast_list[vid], vdev,
icssg_prueth_hsr_add_mcast,
icssg_prueth_hsr_del_mcast);
else
__hw_addr_sync_dev(&emac->vlan_mcast_list[vid], vdev,
icssg_prueth_add_mcast,
icssg_prueth_del_mcast);
return 0;
}
@ -857,12 +930,22 @@ static void emac_ndo_set_rx_mode_work(struct work_struct *work)
return;
}
if (emac->prueth->is_hsr_offload_mode)
if (emac->prueth->is_hsr_offload_mode) {
__dev_mc_sync(ndev, icssg_prueth_hsr_add_mcast,
icssg_prueth_hsr_del_mcast);
else
if (rtnl_trylock()) {
vlan_for_each(emac->prueth->hsr_dev,
icssg_update_vlan_mcast, emac);
rtnl_unlock();
}
} else {
__dev_mc_sync(ndev, icssg_prueth_add_mcast,
icssg_prueth_del_mcast);
if (rtnl_trylock()) {
vlan_for_each(ndev, icssg_update_vlan_mcast, emac);
rtnl_unlock();
}
}
}
/**
@ -907,19 +990,19 @@ static int emac_ndo_vlan_rx_add_vid(struct net_device *ndev,
{
struct prueth_emac *emac = netdev_priv(ndev);
struct prueth *prueth = emac->prueth;
int port_mask = BIT(emac->port_id);
int untag_mask = 0;
int port_mask;
if (prueth->is_hsr_offload_mode) {
port_mask = BIT(PRUETH_PORT_HOST) | BIT(emac->port_id);
untag_mask = 0;
if (prueth->is_hsr_offload_mode)
port_mask |= BIT(PRUETH_PORT_HOST);
netdev_dbg(emac->ndev, "VID add vid:%u port_mask:%X untag_mask %X\n",
vid, port_mask, untag_mask);
__hw_addr_init(&emac->vlan_mcast_list[vid]);
netdev_dbg(emac->ndev, "VID add vid:%u port_mask:%X untag_mask %X\n",
vid, port_mask, untag_mask);
icssg_vtbl_modify(emac, vid, port_mask, untag_mask, true);
icssg_set_pvid(emac->prueth, vid, emac->port_id);
icssg_vtbl_modify(emac, vid, port_mask, untag_mask, true);
icssg_set_pvid(emac->prueth, vid, emac->port_id);
}
return 0;
}
@ -928,18 +1011,16 @@ static int emac_ndo_vlan_rx_del_vid(struct net_device *ndev,
{
struct prueth_emac *emac = netdev_priv(ndev);
struct prueth *prueth = emac->prueth;
int port_mask = BIT(emac->port_id);
int untag_mask = 0;
int port_mask;
if (prueth->is_hsr_offload_mode) {
if (prueth->is_hsr_offload_mode)
port_mask = BIT(PRUETH_PORT_HOST);
untag_mask = 0;
netdev_dbg(emac->ndev, "VID del vid:%u port_mask:%X untag_mask %X\n",
vid, port_mask, untag_mask);
netdev_dbg(emac->ndev, "VID del vid:%u port_mask:%X untag_mask %X\n",
vid, port_mask, untag_mask);
icssg_vtbl_modify(emac, vid, port_mask, untag_mask, false);
icssg_vtbl_modify(emac, vid, port_mask, untag_mask, false);
}
return 0;
}
@ -1254,7 +1335,7 @@ static int prueth_netdevice_port_link(struct net_device *ndev,
if (prueth->br_members & BIT(PRUETH_PORT_MII0) &&
prueth->br_members & BIT(PRUETH_PORT_MII1)) {
prueth->is_switch_mode = true;
prueth->default_vlan = 1;
prueth->default_vlan = PRUETH_DFLT_VLAN_SW;
emac->port_vlan = prueth->default_vlan;
icssg_change_mode(prueth);
}
@ -1312,7 +1393,7 @@ static int prueth_hsr_port_link(struct net_device *ndev)
NETIF_PRUETH_HSR_OFFLOAD_FEATURES))
return -EOPNOTSUPP;
prueth->is_hsr_offload_mode = true;
prueth->default_vlan = 1;
prueth->default_vlan = PRUETH_DFLT_VLAN_HSR;
emac0->port_vlan = prueth->default_vlan;
emac1->port_vlan = prueth->default_vlan;
icssg_change_mode(prueth);

View File

@ -83,6 +83,12 @@
#define ICSS_CMD_ADD_FILTER 0x7
#define ICSS_CMD_ADD_MAC 0x8
/* VLAN Filtering Related MACROs */
#define PRUETH_DFLT_VLAN_HSR 1
#define PRUETH_DFLT_VLAN_SW 1
#define PRUETH_DFLT_VLAN_MAC 0
#define MAX_VLAN_ID 256
/* In switch mode there are 3 real ports i.e. 3 mac addrs.
* however Linux sees only the host side port. The other 2 ports
* are the switch ports.
@ -200,6 +206,8 @@ struct prueth_emac {
/* RX IRQ Coalescing Related */
struct hrtimer rx_hrtimer;
unsigned long rx_pace_timeout_ns;
struct netdev_hw_addr_list vlan_mcast_list[MAX_VLAN_ID];
};
/**

View File

@ -13,6 +13,15 @@ enum hsr_version {
PRP_V1,
};
enum hsr_port_type {
HSR_PT_NONE = 0, /* Must be 0, used by framereg */
HSR_PT_SLAVE_A,
HSR_PT_SLAVE_B,
HSR_PT_INTERLINK,
HSR_PT_MASTER,
HSR_PT_PORTS, /* This must be the last item in the enum */
};
/* HSR Tag.
* As defined in IEC-62439-3:2010, the HSR tag is really { ethertype = 0x88FB,
* path, LSDU_size, sequence Nr }. But we let eth_header() create { h_dest,
@ -32,6 +41,8 @@ struct hsr_tag {
#if IS_ENABLED(CONFIG_HSR)
extern bool is_hsr_master(struct net_device *dev);
extern int hsr_get_version(struct net_device *dev, enum hsr_version *ver);
struct net_device *hsr_get_port_ndev(struct net_device *ndev,
enum hsr_port_type pt);
#else
static inline bool is_hsr_master(struct net_device *dev)
{
@ -42,6 +53,12 @@ static inline int hsr_get_version(struct net_device *dev,
{
return -EINVAL;
}
static inline struct net_device *hsr_get_port_ndev(struct net_device *ndev,
enum hsr_port_type pt)
{
return ERR_PTR(-EINVAL);
}
#endif /* CONFIG_HSR */
#endif /*_LINUX_IF_HSR_H_*/

View File

@ -4687,6 +4687,9 @@ int devm_register_netdev(struct device *dev, struct net_device *ndev);
/* General hardware address lists handling functions */
int __hw_addr_sync(struct netdev_hw_addr_list *to_list,
struct netdev_hw_addr_list *from_list, int addr_len);
int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list,
struct netdev_hw_addr_list *from_list,
int addr_len);
void __hw_addr_unsync(struct netdev_hw_addr_list *to_list,
struct netdev_hw_addr_list *from_list, int addr_len);
int __hw_addr_sync_dev(struct netdev_hw_addr_list *list,

View File

@ -242,9 +242,9 @@ static void __hw_addr_unsync_one(struct netdev_hw_addr_list *to_list,
__hw_addr_del_entry(from_list, ha, false, false);
}
static int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list,
struct netdev_hw_addr_list *from_list,
int addr_len)
int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list,
struct netdev_hw_addr_list *from_list,
int addr_len)
{
int err = 0;
struct netdev_hw_addr *ha, *tmp;
@ -260,6 +260,7 @@ static int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list,
}
return err;
}
EXPORT_SYMBOL(__hw_addr_sync_multiple);
/* This function only works where there is a strict 1-1 relationship
* between source and destination of they synch. If you ever need to

View File

@ -663,6 +663,19 @@ bool is_hsr_master(struct net_device *dev)
}
EXPORT_SYMBOL(is_hsr_master);
struct net_device *hsr_get_port_ndev(struct net_device *ndev,
enum hsr_port_type pt)
{
struct hsr_priv *hsr = netdev_priv(ndev);
struct hsr_port *port;
hsr_for_each_port(hsr, port)
if (port->type == pt)
return port->dev;
return NULL;
}
EXPORT_SYMBOL(hsr_get_port_ndev);
/* Default multicast address for HSR Supervision frames */
static const unsigned char def_multicast_addr[ETH_ALEN] __aligned(2) = {
0x01, 0x15, 0x4e, 0x00, 0x01, 0x00

View File

@ -121,15 +121,6 @@ struct hsrv1_ethhdr_sp {
struct hsr_sup_tag hsr_sup;
} __packed;
enum hsr_port_type {
HSR_PT_NONE = 0, /* Must be 0, used by framereg */
HSR_PT_SLAVE_A,
HSR_PT_SLAVE_B,
HSR_PT_INTERLINK,
HSR_PT_MASTER,
HSR_PT_PORTS, /* This must be the last item in the enum */
};
/* PRP Redunancy Control Trailor (RCT).
* As defined in IEC-62439-4:2012, the PRP RCT is really { sequence Nr,
* Lan indentifier (LanId), LSDU_size and PRP_suffix = 0x88FB }.