mirror of
https://github.com/torvalds/linux.git
synced 2026-05-27 16:44:58 +02:00
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:
commit
2b1d9116b0
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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_*/
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 }.
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user