Subject: Re: [PATCH net-next v6 2/7] net: dsa: Add DSA driver for Hirschmann Hellcreek switches

From: Kurt Kanzenbach <[email-masked]>
Date: Tue, 06 Oct 2020 08:09:39 +0200

Hi Vladimir,

thanks for the review.

On Sun Oct 04 2020, Vladimir Oltean wrote:
> On Sun, Oct 04, 2020 at 01:29:06PM +0200, Kurt Kanzenbach wrote:
>> +static int hellcreek_vlan_del(struct dsa_switch *ds, int port,
>> +			      const struct switchdev_obj_port_vlan *vlan)
>> +{
>> +	struct hellcreek *hellcreek = ds->priv;
>> +	u16 vid;
>> +
>> +	dev_dbg(hellcreek->dev, "Remove VLANs (%d -- %d) on port %d\n",
>> +		vlan->vid_begin, vlan->vid_end, port);
>> +
>> +	for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
>> +		mutex_lock(&hellcreek->ports[port].vlan_lock);
>> +		if (hellcreek->ports[port].vlan_filtering)
>> +			hellcreek_unapply_vlan(hellcreek, port, vid);
>
> I don't think this works.
>
> ip link add br0 type bridge vlan_filtering 1
> ip link set swp0 master br0
> bridge vlan add dev swp0 vid 100
> ip link set br0 type bridge vlan_filtering 0
> bridge vlan del dev swp0 vid 100
> ip link set br0 type bridge vlan_filtering 1
>
> The expectation would be that swp0 blocks vid 100 now, but with your
> scheme it doesn't (it is not unapplied, and not unqueued either, because
> it was never queued in the first place).

Yes, that's correct. So, I think we have to queue not only the addition
of VLANs, but rather the "action" itself such as add or del. And then
apply all pending actions whenever vlan_filtering is set.

>> +static int hellcreek_port_bridge_join(struct dsa_switch *ds, int port,
>> +				      struct net_device *br)
>> +{
>> +	struct hellcreek *hellcreek = ds->priv;
>> +	int i;
>> +
>> +	dev_dbg(hellcreek->dev, "Port %d joins a bridge\n", port);
>> +
>> +	/* Configure port's vid to all other ports as egress untagged */
>> +	for (i = 0; i < ds->num_ports; ++i) {
>> +		if (!dsa_is_user_port(ds, i))
>> +			continue;
>> +
>> +		if (i == port)
>> +			continue;
>> +
>> +		hellcreek_apply_vlan(hellcreek, i, port, false, true);
>> +	}
>
> I think this is buggy when joining a VLAN filtering bridge. Your ports
> will pass frames with VID=2 with no problem, even without the user
> specifying 'bridge vlan add dev swp0 vid 2', and that's an issue. My
> understanding is that VLANs 1, 2, 3 stop having any sort of special
> meaning when the upper bridge has vlan_filtering=1.

Yes, that understanding is correct. So, what happens is when a port is
joining a VLAN filtering bridge is:

|root@tsn:~# ip link add name br0 type bridge
|root@tsn:~# ip link set dev br0 type bridge vlan_filtering 1
|root@tsn:~# ip link set dev lan0 master br0
|[  209.375055] br0: port 1(lan0) entered blocking state
|[  209.380073] br0: port 1(lan0) entered disabled state
|[  209.385340] hellcreek ff240000.switch: Port 2 joins a bridge
|[  209.391584] hellcreek ff240000.switch: Apply VLAN: port=3 vid=2 pvid=0 untagged=1
|[  209.399439] device lan0 entered promiscuous mode
|[  209.404043] device eth0 entered promiscuous mode
|[  209.409204] hellcreek ff240000.switch: Enable VLAN filtering on port 2
|[  209.415716] hellcreek ff240000.switch: Unapply VLAN: port=2 vid=2
|[  209.421840] hellcreek ff240000.switch: Unapply VLAN: port=0 vid=2
|[  209.428170] hellcreek ff240000.switch: Apply queued VLANs: port2
|[  209.434158] hellcreek ff240000.switch: Apply VLAN: port=2 vid=0 pvid=0 untagged=0
|[  209.441649] hellcreek ff240000.switch: Clear queued VLANs: port2
|[  209.447920] hellcreek ff240000.switch: Apply queued VLANs: port0
|[  209.453910] hellcreek ff240000.switch: Apply VLAN: port=0 vid=0 pvid=0 untagged=0
|[  209.461402] hellcreek ff240000.switch: Clear queued VLANs: port0
|[  209.467620] hellcreek ff240000.switch: VLAN prepare for port 2
|[  209.473476] hellcreek ff240000.switch: VLAN prepare for port 0
|[  209.479534] hellcreek ff240000.switch: Add VLANs (1 -- 1) on port 2, untagged, PVID
|[  209.487164] hellcreek ff240000.switch: Apply VLAN: port=2 vid=1 pvid=1 untagged=1
|[  209.494659] hellcreek ff240000.switch: Add VLANs (1 -- 1) on port 0, untagged, no PVID
|[  209.502794] hellcreek ff240000.switch: Apply VLAN: port=0 vid=1 pvid=0 untagged=1
|root@tsn:~# bridge vlan show
|port    vlan ids
|lan0     1 PVID Egress Untagged
|
|br0      1 PVID Egress Untagged

... which looks correct to me. The VLAN 2 is unapplied as expected. Or?

>
> And how do you deal with the case where swp1 and swp2 are bridged and
> have the VLAN 3 installed via 'bridge vlan', but swp3 isn't bridged?
> Will swp1/swp2 communicate with swp3? If yes, that's a problem.

There is no swp3. Currently there are only two ports and either they are
bridged or not.

>> +static int __hellcreek_fdb_del(struct hellcreek *hellcreek,
>> +			       const struct hellcreek_fdb_entry *entry)
>> +{
>> +	dev_dbg(hellcreek->dev, "Delete FDB entry: MAC=%pM!\n", entry->mac);
>> +
>
> Do these dev_dbg statements bring much value at all, even to you?

Yes, they do. See the log snippet above.

>> +/* Default setup for DSA: VLAN <X>: CPU and Port <X> egress untagged. */
>> +static int hellcreek_setup_vlan_membership(struct dsa_switch *ds, int port,
>> +					   bool enabled)
>
> This function always returns zero, so it should be void.

Yes. I noticed that as well and wanted to fix it before sending. Sorry, my bad.

>> +static int hellcreek_vlan_filtering(struct dsa_switch *ds, int port,
>> +				    bool vlan_filtering)
>> +{
>> +	struct hellcreek *hellcreek = ds->priv;
>> +
>> +	dev_dbg(hellcreek->dev, "%s VLAN filtering on port %d\n",
>> +		vlan_filtering ? "Enable" : "Disable", port);
>> +
>> +	/* Configure port to drop packages with not known vids */
>> +	hellcreek_setup_ingressflt(hellcreek, port, vlan_filtering);
>> +
>> +	/* Drop DSA vlan membership config. The user can now do it. */
>> +	hellcreek_setup_vlan_membership(ds, port, !vlan_filtering);
>> +
>> +	/* Apply saved vlan configurations while not filtering for port <X>. */
>> +	hellcreek_apply_vlan_filtering(hellcreek, port, vlan_filtering);
>> +
>> +	/* Do the same for the cpu port. */
>> +	hellcreek_apply_vlan_filtering(hellcreek, CPU_PORT, vlan_filtering);
>
> I think we should create a DSA_NOTIFIER_VLAN_FILTERING so you wouldn't
> have to do this, but not now.

OK.

>> +static int hellcreek_probe(struct platform_device *pdev)
>> +{
>> +	struct device *dev = &pdev->dev;
>> +	struct hellcreek *hellcreek;
>> +	struct resource *res;
>> +	int ret, i;
>> +
>> +	hellcreek = devm_kzalloc(dev, sizeof(*hellcreek), GFP_KERNEL);
>> +	if (!hellcreek)
>> +		return -ENOMEM;
>> +
>> +	hellcreek->vidmbrcfg = devm_kcalloc(dev, 4096,
>
> VLAN_N_VID

Thanks!

>> +static const struct hellcreek_platform_data de1soc_r1_pdata = {
>> +	.num_ports	 = 4,
>> +	.is_100_mbits	 = 1,
>> +	.qbv_support	 = 1,
>> +	.qbv_on_cpu_port = 1,
>
> Why does this matter?

Because Qbv on the CPU port is a feature and not all switch variants
have that. It will matter as soon as TAPRIO is implemented.

>> +#include <linux/bitops.h>
>> +#include <linux/kernel.h>
>> +#include <linux/device.h>
>> +#include <linux/ptp_clock_kernel.h>
>> +#include <linux/timecounter.h>
>> +#include <linux/mutex.h>
>
> Could you sort alphabetically?

Sure.

>
>> +#include <linux/platform_data/hirschmann-hellcreek.h>
>> +#include <net/dsa.h>
>> +
>> +/* Ports:
>> + *  - 0: CPU
>> + *  - 1: Tunnel
>> + *  - 2: TSN front port 1
>> + *  - 3: TSN front port 2
>> + *  - ...
>> + */
>> +#define CPU_PORT			0
>> +#define TUNNEL_PORT			1
>
> What's a tunnel port exactly?

AFAIK it's a debugging port for mirroring or looping traffic. Anyhow,
that is not a regular port and cannot be treated as such.

Thanks,
Kurt