Commit 53fdf67d authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files

Merge branch 'introduce-vlan-support-in-hsr'

MD Danish Anwar says:

====================
Introduce VLAN support in HSR

This series adds VLAN support to HSR framework.
This series also adds VLAN support to HSR mode of ICSSG Ethernet driver.

[1] https://gist.githubusercontent.com/danish-ti/d309f92c640134ccc4f2c0c442de5be1/raw/9cfb5f8bd12b374ae591f4bd9ba3e91ae509ed4f/hsr_vlan_logs
v1 https://lore.kernel.org/all/20241004074715.791191-1-danishanwar@ti.com/
v2 https://lore.kernel.org/all/20241024103056.3201071-1-danishanwar@ti.com/
====================

Link: https://patch.msgid.link/20241106091710.3308519-1-danishanwar@ti.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 23462e03 75e3f12f
Loading
Loading
Loading
Loading
+44 −1
Original line number Diff line number Diff line
@@ -808,6 +808,47 @@ static netdev_features_t emac_ndo_fix_features(struct net_device *ndev,
	return features;
}

static int emac_ndo_vlan_rx_add_vid(struct net_device *ndev,
				    __be16 proto, u16 vid)
{
	struct prueth_emac *emac = netdev_priv(ndev);
	struct prueth *prueth = emac->prueth;
	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;

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

static int emac_ndo_vlan_rx_del_vid(struct net_device *ndev,
				    __be16 proto, u16 vid)
{
	struct prueth_emac *emac = netdev_priv(ndev);
	struct prueth *prueth = emac->prueth;
	int untag_mask = 0;
	int port_mask;

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

static const struct net_device_ops emac_netdev_ops = {
	.ndo_open = emac_ndo_open,
	.ndo_stop = emac_ndo_stop,
@@ -820,6 +861,8 @@ static const struct net_device_ops emac_netdev_ops = {
	.ndo_get_stats64 = icssg_ndo_get_stats64,
	.ndo_get_phys_port_name = icssg_ndo_get_phys_port_name,
	.ndo_fix_features = emac_ndo_fix_features,
	.ndo_vlan_rx_add_vid = emac_ndo_vlan_rx_add_vid,
	.ndo_vlan_rx_kill_vid = emac_ndo_vlan_rx_del_vid,
};

static int prueth_netdev_init(struct prueth *prueth,
@@ -947,7 +990,7 @@ static int prueth_netdev_init(struct prueth *prueth,
	ndev->netdev_ops = &emac_netdev_ops;
	ndev->ethtool_ops = &icssg_ethtool_ops;
	ndev->hw_features = NETIF_F_SG;
	ndev->features = ndev->hw_features;
	ndev->features = ndev->hw_features | NETIF_F_HW_VLAN_CTAG_FILTER;
	ndev->hw_features |= NETIF_PRUETH_HSR_OFFLOAD_FEATURES;

	netif_napi_add(ndev, &emac->napi_rx, icssg_napi_rx_poll);
+79 −6
Original line number Diff line number Diff line
@@ -515,6 +515,77 @@ static void hsr_change_rx_flags(struct net_device *dev, int change)
	}
}

static int hsr_ndo_vlan_rx_add_vid(struct net_device *dev,
				   __be16 proto, u16 vid)
{
	bool is_slave_a_added = false;
	bool is_slave_b_added = false;
	struct hsr_port *port;
	struct hsr_priv *hsr;
	int ret = 0;

	hsr = netdev_priv(dev);

	hsr_for_each_port(hsr, port) {
		if (port->type == HSR_PT_MASTER ||
		    port->type == HSR_PT_INTERLINK)
			continue;

		ret = vlan_vid_add(port->dev, proto, vid);
		switch (port->type) {
		case HSR_PT_SLAVE_A:
			if (ret) {
				/* clean up Slave-B */
				netdev_err(dev, "add vid failed for Slave-A\n");
				if (is_slave_b_added)
					vlan_vid_del(port->dev, proto, vid);
				return ret;
			}

			is_slave_a_added = true;
			break;

		case HSR_PT_SLAVE_B:
			if (ret) {
				/* clean up Slave-A */
				netdev_err(dev, "add vid failed for Slave-B\n");
				if (is_slave_a_added)
					vlan_vid_del(port->dev, proto, vid);
				return ret;
			}

			is_slave_b_added = true;
			break;
		default:
			break;
		}
	}

	return 0;
}

static int hsr_ndo_vlan_rx_kill_vid(struct net_device *dev,
				    __be16 proto, u16 vid)
{
	struct hsr_port *port;
	struct hsr_priv *hsr;

	hsr = netdev_priv(dev);

	hsr_for_each_port(hsr, port) {
		switch (port->type) {
		case HSR_PT_SLAVE_A:
		case HSR_PT_SLAVE_B:
			vlan_vid_del(port->dev, proto, vid);
			break;
		default:
			break;
		}
	}

	return 0;
}

static const struct net_device_ops hsr_device_ops = {
	.ndo_change_mtu = hsr_dev_change_mtu,
	.ndo_open = hsr_dev_open,
@@ -523,6 +594,8 @@ static const struct net_device_ops hsr_device_ops = {
	.ndo_change_rx_flags = hsr_change_rx_flags,
	.ndo_fix_features = hsr_fix_features,
	.ndo_set_rx_mode = hsr_set_rx_mode,
	.ndo_vlan_rx_add_vid = hsr_ndo_vlan_rx_add_vid,
	.ndo_vlan_rx_kill_vid = hsr_ndo_vlan_rx_kill_vid,
};

static const struct device_type hsr_type = {
@@ -569,14 +642,10 @@ void hsr_dev_setup(struct net_device *dev)

	dev->hw_features = NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HIGHDMA |
			   NETIF_F_GSO_MASK | NETIF_F_HW_CSUM |
			   NETIF_F_HW_VLAN_CTAG_TX;
			   NETIF_F_HW_VLAN_CTAG_TX |
			   NETIF_F_HW_VLAN_CTAG_FILTER;

	dev->features = dev->hw_features;

	/* VLAN on top of HSR needs testing and probably some work on
	 * hsr_header_create() etc.
	 */
	dev->features |= NETIF_F_VLAN_CHALLENGED;
}

/* Return true if dev is a HSR master; return false otherwise.
@@ -652,6 +721,10 @@ int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2],
	    (slave[1]->features & NETIF_F_HW_HSR_FWD))
		hsr->fwd_offloaded = true;

	if ((slave[0]->features & NETIF_F_HW_VLAN_CTAG_FILTER) &&
	    (slave[1]->features & NETIF_F_HW_VLAN_CTAG_FILTER))
		hsr_dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;

	res = register_netdevice(hsr_dev);
	if (res)
		goto err_unregister;
+14 −5
Original line number Diff line number Diff line
@@ -280,6 +280,7 @@ static struct sk_buff *hsr_fill_tag(struct sk_buff *skb,
				    struct hsr_port *port, u8 proto_version)
{
	struct hsr_ethhdr *hsr_ethhdr;
	unsigned char *pc;
	int lsdu_size;

	/* pad to minimum packet size which is 60 + 6 (HSR tag) */
@@ -290,7 +291,18 @@ static struct sk_buff *hsr_fill_tag(struct sk_buff *skb,
	if (frame->is_vlan)
		lsdu_size -= 4;

	hsr_ethhdr = (struct hsr_ethhdr *)skb_mac_header(skb);
	pc = skb_mac_header(skb);
	if (frame->is_vlan)
		/* This 4-byte shift (size of a vlan tag) does not
		 * mean that the ethhdr starts there. But rather it
		 * provides the proper environment for accessing
		 * the fields, such as hsr_tag etc., just like
		 * when the vlan tag is not there. This is because
		 * the hsr tag is after the vlan tag.
		 */
		hsr_ethhdr = (struct hsr_ethhdr *)(pc + VLAN_HLEN);
	else
		hsr_ethhdr = (struct hsr_ethhdr *)pc;

	hsr_set_path_id(hsr_ethhdr, port);
	set_hsr_tag_LSDU_size(&hsr_ethhdr->hsr_tag, lsdu_size);
@@ -368,7 +380,7 @@ struct sk_buff *prp_create_tagged_frame(struct hsr_frame_info *frame,
		return skb_clone(frame->skb_std, GFP_ATOMIC);
	}

	skb = skb_copy_expand(frame->skb_std, 0,
	skb = skb_copy_expand(frame->skb_std, skb_headroom(frame->skb_std),
			      skb_tailroom(frame->skb_std) + HSR_HLEN,
			      GFP_ATOMIC);
	return prp_fill_rct(skb, frame, port);
@@ -690,9 +702,6 @@ static int fill_frame_info(struct hsr_frame_info *frame,
	if (frame->is_vlan) {
		vlan_hdr = (struct hsr_vlan_ethhdr *)ethhdr;
		proto = vlan_hdr->vlanhdr.h_vlan_encapsulated_proto;
		/* FIXME: */
		netdev_warn_once(skb->dev, "VLAN not yet supported");
		return -EINVAL;
	}

	frame->is_from_san = false;
+1 −0
Original line number Diff line number Diff line
@@ -3,3 +3,4 @@ CONFIG_NET_SCH_NETEM=m
CONFIG_HSR=y
CONFIG_VETH=y
CONFIG_BRIDGE=y
CONFIG_VLAN_8021Q=m
+98 −0
Original line number Diff line number Diff line
@@ -175,6 +175,100 @@ setup_hsr_interfaces()
	ip -net "$ns3" link set hsr3 up
}

setup_vlan_interfaces() {
	ip -net "$ns1" link add link hsr1 name hsr1.2 type vlan id 2
	ip -net "$ns1" link add link hsr1 name hsr1.3 type vlan id 3
	ip -net "$ns1" link add link hsr1 name hsr1.4 type vlan id 4
	ip -net "$ns1" link add link hsr1 name hsr1.5 type vlan id 5

	ip -net "$ns2" link add link hsr2 name hsr2.2 type vlan id 2
	ip -net "$ns2" link add link hsr2 name hsr2.3 type vlan id 3
	ip -net "$ns2" link add link hsr2 name hsr2.4 type vlan id 4
	ip -net "$ns2" link add link hsr2 name hsr2.5 type vlan id 5

	ip -net "$ns3" link add link hsr3 name hsr3.2 type vlan id 2
	ip -net "$ns3" link add link hsr3 name hsr3.3 type vlan id 3
	ip -net "$ns3" link add link hsr3 name hsr3.4 type vlan id 4
	ip -net "$ns3" link add link hsr3 name hsr3.5 type vlan id 5

	ip -net "$ns1" addr add 100.64.2.1/24 dev hsr1.2
	ip -net "$ns1" addr add 100.64.3.1/24 dev hsr1.3
	ip -net "$ns1" addr add 100.64.4.1/24 dev hsr1.4
	ip -net "$ns1" addr add 100.64.5.1/24 dev hsr1.5

	ip -net "$ns2" addr add 100.64.2.2/24 dev hsr2.2
	ip -net "$ns2" addr add 100.64.3.2/24 dev hsr2.3
	ip -net "$ns2" addr add 100.64.4.2/24 dev hsr2.4
	ip -net "$ns2" addr add 100.64.5.2/24 dev hsr2.5

	ip -net "$ns3" addr add 100.64.2.3/24 dev hsr3.2
	ip -net "$ns3" addr add 100.64.3.3/24 dev hsr3.3
	ip -net "$ns3" addr add 100.64.4.3/24 dev hsr3.4
	ip -net "$ns3" addr add 100.64.5.3/24 dev hsr3.5

	ip -net "$ns1" link set dev hsr1.2 up
	ip -net "$ns1" link set dev hsr1.3 up
	ip -net "$ns1" link set dev hsr1.4 up
	ip -net "$ns1" link set dev hsr1.5 up

	ip -net "$ns2" link set dev hsr2.2 up
	ip -net "$ns2" link set dev hsr2.3 up
	ip -net "$ns2" link set dev hsr2.4 up
	ip -net "$ns2" link set dev hsr2.5 up

	ip -net "$ns3" link set dev hsr3.2 up
	ip -net "$ns3" link set dev hsr3.3 up
	ip -net "$ns3" link set dev hsr3.4 up
	ip -net "$ns3" link set dev hsr3.5 up

}

hsr_vlan_ping() {
	do_ping "$ns1" 100.64.2.2
	do_ping "$ns1" 100.64.3.2
	do_ping "$ns1" 100.64.4.2
	do_ping "$ns1" 100.64.5.2

	do_ping "$ns1" 100.64.2.3
	do_ping "$ns1" 100.64.3.3
	do_ping "$ns1" 100.64.4.3
	do_ping "$ns1" 100.64.5.3

	do_ping "$ns2" 100.64.2.1
	do_ping "$ns2" 100.64.3.1
	do_ping "$ns2" 100.64.4.1
	do_ping "$ns2" 100.64.5.1

	do_ping "$ns2" 100.64.2.3
	do_ping "$ns2" 100.64.3.3
	do_ping "$ns2" 100.64.4.3
	do_ping "$ns2" 100.64.5.3

	do_ping "$ns3" 100.64.2.1
	do_ping "$ns3" 100.64.3.1
	do_ping "$ns3" 100.64.4.1
	do_ping "$ns3" 100.64.5.1

	do_ping "$ns3" 100.64.2.2
	do_ping "$ns3" 100.64.3.2
	do_ping "$ns3" 100.64.4.2
	do_ping "$ns3" 100.64.5.2
}

run_vlan_tests() {
	vlan_challenged_hsr1=$(ip net exec "$ns1" ethtool -k hsr1 | grep "vlan-challenged" | awk '{print $2}')
	vlan_challenged_hsr2=$(ip net exec "$ns2" ethtool -k hsr2 | grep "vlan-challenged" | awk '{print $2}')
	vlan_challenged_hsr3=$(ip net exec "$ns3" ethtool -k hsr3 | grep "vlan-challenged" | awk '{print $2}')

	if [[ "$vlan_challenged_hsr1" = "off" || "$vlan_challenged_hsr2" = "off" || "$vlan_challenged_hsr3" = "off" ]]; then
		echo "INFO: Running VLAN tests"
		setup_vlan_interfaces
		hsr_vlan_ping
	else
		echo "INFO: Not Running VLAN tests as the device does not support VLAN"
	fi
}

check_prerequisites
setup_ns ns1 ns2 ns3

@@ -183,9 +277,13 @@ trap cleanup_all_ns EXIT
setup_hsr_interfaces 0
do_complete_ping_test

run_vlan_tests

setup_ns ns1 ns2 ns3

setup_hsr_interfaces 1
do_complete_ping_test

run_vlan_tests

exit $ret