Commit 2b1d9116 authored by Paolo Abeni's avatar Paolo Abeni
Browse files

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: default avatarPaolo Abeni <pabeni@redhat.com>
parents 624d7a8a 161087db
Loading
Loading
Loading
Loading
+128 −47
Original line number Diff line number Diff line
@@ -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;

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

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

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

	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);
+8 −0
Original line number Diff line number Diff line
@@ -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];
};

/**
+17 −0
Original line number Diff line number Diff line
@@ -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_*/
+3 −0
Original line number Diff line number Diff line
@@ -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,
+4 −3
Original line number Diff line number Diff line
@@ -242,7 +242,7 @@ 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,
int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list,
			    struct netdev_hw_addr_list *from_list,
			    int addr_len)
{
@@ -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
Loading