Skip to content

Commit

Permalink
Merge branch 'add-multicast-filtering-support-for-vlan-interface'
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Paolo Abeni committed Jan 14, 2025
2 parents 624d7a8 + 161087d commit 2b1d911
Show file tree
Hide file tree
Showing 7 changed files with 173 additions and 59 deletions.
175 changes: 128 additions & 47 deletions drivers/net/ethernet/ti/icssg/icssg_prueth.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

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);

other_port_mask = port_mask ^ icssg_fdb_lookup(emac, addr, 0);
port_mask = BIT(emac->port_id);
other_port_mask = port_mask ^ icssg_fdb_lookup(emac, addr, vlan_id);

icssg_fdb_add_del(emac, addr, 0, port_mask, false);
icssg_vtbl_modify(emac, 0, port_mask, port_mask, false);
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;

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);
}

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);
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;
}
Expand Down Expand Up @@ -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();
}
}
}

/**
Expand Down Expand Up @@ -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;
}

Expand All @@ -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;
}

Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
Expand Down
8 changes: 8 additions & 0 deletions drivers/net/ethernet/ti/icssg/icssg_prueth.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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];
};

/**
Expand Down
17 changes: 17 additions & 0 deletions include/linux/if_hsr.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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)
{
Expand All @@ -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_*/
3 changes: 3 additions & 0 deletions include/linux/netdevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
7 changes: 4 additions & 3 deletions net/core/dev_addr_lists.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand Down
13 changes: 13 additions & 0 deletions net/hsr/hsr_device.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 0 additions & 9 deletions net/hsr/hsr_main.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 }.
Expand Down

0 comments on commit 2b1d911

Please sign in to comment.